702 lines · cpp
1//===- UncheckedStatusOrAccessModel.cpp -----------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h"10 11#include <cassert>12#include <utility>13 14#include "clang/AST/DeclCXX.h"15#include "clang/AST/DeclTemplate.h"16#include "clang/AST/Expr.h"17#include "clang/AST/ExprCXX.h"18#include "clang/AST/TypeBase.h"19#include "clang/ASTMatchers/ASTMatchFinder.h"20#include "clang/ASTMatchers/ASTMatchers.h"21#include "clang/ASTMatchers/ASTMatchersInternal.h"22#include "clang/Analysis/CFG.h"23#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"24#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"25#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"26#include "clang/Analysis/FlowSensitive/MatchSwitch.h"27#include "clang/Analysis/FlowSensitive/RecordOps.h"28#include "clang/Analysis/FlowSensitive/StorageLocation.h"29#include "clang/Analysis/FlowSensitive/Value.h"30#include "clang/Basic/LLVM.h"31#include "clang/Basic/SourceLocation.h"32#include "llvm/ADT/StringMap.h"33 34namespace clang::dataflow::statusor_model {35namespace {36 37using ::clang::ast_matchers::MatchFinder;38using ::clang::ast_matchers::StatementMatcher;39 40} // namespace41 42static bool namespaceEquals(const NamespaceDecl *NS,43 clang::ArrayRef<clang::StringRef> NamespaceNames) {44 while (!NamespaceNames.empty() && NS) {45 if (NS->getName() != NamespaceNames.consume_back())46 return false;47 NS = dyn_cast_or_null<NamespaceDecl>(NS->getParent());48 }49 return NamespaceNames.empty() && !NS;50}51 52// TODO: move this to a proper place to share with the rest of clang53static bool isTypeNamed(QualType Type, clang::ArrayRef<clang::StringRef> NS,54 StringRef Name) {55 if (Type.isNull())56 return false;57 if (auto *RD = Type->getAsRecordDecl())58 if (RD->getName() == Name)59 if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD->getDeclContext()))60 return namespaceEquals(N, NS);61 return false;62}63 64static bool isStatusOrOperatorBaseType(QualType Type) {65 return isTypeNamed(Type, {"absl", "internal_statusor"}, "OperatorBase");66}67 68static bool isSafeUnwrap(RecordStorageLocation *StatusOrLoc,69 const Environment &Env) {70 if (!StatusOrLoc)71 return false;72 auto &StatusLoc = locForStatus(*StatusOrLoc);73 auto *OkVal = Env.get<BoolValue>(locForOk(StatusLoc));74 return OkVal != nullptr && Env.proves(OkVal->formula());75}76 77static ClassTemplateSpecializationDecl *78getStatusOrBaseClass(const QualType &Ty) {79 auto *RD = Ty->getAsCXXRecordDecl();80 if (RD == nullptr)81 return nullptr;82 if (isStatusOrType(Ty) ||83 // In case we are analyzing code under OperatorBase itself that uses84 // operator* (e.g. to implement operator->).85 isStatusOrOperatorBaseType(Ty))86 return cast<ClassTemplateSpecializationDecl>(RD);87 if (!RD->hasDefinition())88 return nullptr;89 for (const auto &Base : RD->bases())90 if (auto *QT = getStatusOrBaseClass(Base.getType()))91 return QT;92 return nullptr;93}94 95static QualType getStatusOrValueType(ClassTemplateSpecializationDecl *TRD) {96 return TRD->getTemplateArgs().get(0).getAsType();97}98 99static auto ofClassStatus() {100 using namespace ::clang::ast_matchers; // NOLINT: Too many names101 return ofClass(hasName("::absl::Status"));102}103 104static auto isStatusMemberCallWithName(llvm::StringRef member_name) {105 using namespace ::clang::ast_matchers; // NOLINT: Too many names106 return cxxMemberCallExpr(107 on(expr(unless(cxxThisExpr()))),108 callee(cxxMethodDecl(hasName(member_name), ofClassStatus())));109}110 111static auto isStatusOrMemberCallWithName(llvm::StringRef member_name) {112 using namespace ::clang::ast_matchers; // NOLINT: Too many names113 return cxxMemberCallExpr(114 on(expr(unless(cxxThisExpr()))),115 callee(cxxMethodDecl(116 hasName(member_name),117 ofClass(anyOf(statusOrClass(), statusOrOperatorBaseClass())))));118}119 120static auto isStatusOrOperatorCallWithName(llvm::StringRef operator_name) {121 using namespace ::clang::ast_matchers; // NOLINT: Too many names122 return cxxOperatorCallExpr(123 hasOverloadedOperatorName(operator_name),124 callee(cxxMethodDecl(125 ofClass(anyOf(statusOrClass(), statusOrOperatorBaseClass())))));126}127 128static auto valueCall() {129 using namespace ::clang::ast_matchers; // NOLINT: Too many names130 return anyOf(isStatusOrMemberCallWithName("value"),131 isStatusOrMemberCallWithName("ValueOrDie"));132}133 134static auto valueOperatorCall() {135 using namespace ::clang::ast_matchers; // NOLINT: Too many names136 return expr(anyOf(isStatusOrOperatorCallWithName("*"),137 isStatusOrOperatorCallWithName("->")));138}139 140static clang::ast_matchers::TypeMatcher statusType() {141 using namespace ::clang::ast_matchers; // NOLINT: Too many names142 return hasCanonicalType(qualType(hasDeclaration(statusClass())));143}144 145static auto isComparisonOperatorCall(llvm::StringRef operator_name) {146 using namespace ::clang::ast_matchers; // NOLINT: Too many names147 return cxxOperatorCallExpr(148 hasOverloadedOperatorName(operator_name), argumentCountIs(2),149 hasArgument(0, anyOf(hasType(statusType()), hasType(statusOrType()))),150 hasArgument(1, anyOf(hasType(statusType()), hasType(statusOrType()))));151}152 153static auto isOkStatusCall() {154 using namespace ::clang::ast_matchers; // NOLINT: Too many names155 return callExpr(callee(functionDecl(hasName("::absl::OkStatus"))));156}157 158static auto isNotOkStatusCall() {159 using namespace ::clang::ast_matchers; // NOLINT: Too many names160 return callExpr(callee(functionDecl(hasAnyName(161 "::absl::AbortedError", "::absl::AlreadyExistsError",162 "::absl::CancelledError", "::absl::DataLossError",163 "::absl::DeadlineExceededError", "::absl::FailedPreconditionError",164 "::absl::InternalError", "::absl::InvalidArgumentError",165 "::absl::NotFoundError", "::absl::OutOfRangeError",166 "::absl::PermissionDeniedError", "::absl::ResourceExhaustedError",167 "::absl::UnauthenticatedError", "::absl::UnavailableError",168 "::absl::UnimplementedError", "::absl::UnknownError"))));169}170 171static auto isPointerComparisonOperatorCall(std::string operator_name) {172 using namespace ::clang::ast_matchers; // NOLINT: Too many names173 return binaryOperator(hasOperatorName(operator_name),174 hasLHS(hasType(hasCanonicalType(pointerType(175 pointee(anyOf(statusOrType(), statusType())))))),176 hasRHS(hasType(hasCanonicalType(pointerType(177 pointee(anyOf(statusOrType(), statusType())))))));178}179 180// The nullPointerConstant in the two matchers below is to support181// absl::StatusOr<void*> X = nullptr.182// nullptr does not match the bound type.183// TODO: be less restrictive around convertible types in general.184static auto isStatusOrValueAssignmentCall() {185 using namespace ::clang::ast_matchers; // NOLINT: Too many names186 return cxxOperatorCallExpr(187 hasOverloadedOperatorName("="),188 callee(cxxMethodDecl(ofClass(statusOrClass()))),189 hasArgument(1, anyOf(hasType(hasUnqualifiedDesugaredType(190 type(equalsBoundNode("T")))),191 nullPointerConstant())));192}193 194static auto isStatusOrValueConstructor() {195 using namespace ::clang::ast_matchers; // NOLINT: Too many names196 return cxxConstructExpr(197 hasType(statusOrType()),198 hasArgument(0,199 anyOf(hasType(hasCanonicalType(type(equalsBoundNode("T")))),200 nullPointerConstant(),201 hasType(namedDecl(hasAnyName("absl::in_place_t",202 "std::in_place_t"))))));203}204 205static auto isStatusOrConstructor() {206 using namespace ::clang::ast_matchers; // NOLINT: Too many names207 return cxxConstructExpr(hasType(statusOrType()));208}209 210static auto isStatusConstructor() {211 using namespace ::clang::ast_matchers; // NOLINT: Too many names212 return cxxConstructExpr(hasType(statusType()));213}214 215static auto216buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {217 return CFGMatchSwitchBuilder<const Environment,218 llvm::SmallVector<SourceLocation>>()219 // StatusOr::value, StatusOr::ValueOrDie220 .CaseOfCFGStmt<CXXMemberCallExpr>(221 valueCall(),222 [](const CXXMemberCallExpr *E,223 const ast_matchers::MatchFinder::MatchResult &,224 const Environment &Env) {225 if (!isSafeUnwrap(getImplicitObjectLocation(*E, Env), Env))226 return llvm::SmallVector<SourceLocation>({E->getExprLoc()});227 return llvm::SmallVector<SourceLocation>();228 })229 230 // StatusOr::operator*, StatusOr::operator->231 .CaseOfCFGStmt<CXXOperatorCallExpr>(232 valueOperatorCall(),233 [](const CXXOperatorCallExpr *E,234 const ast_matchers::MatchFinder::MatchResult &,235 const Environment &Env) {236 RecordStorageLocation *StatusOrLoc =237 Env.get<RecordStorageLocation>(*E->getArg(0));238 if (!isSafeUnwrap(StatusOrLoc, Env))239 return llvm::SmallVector<SourceLocation>({E->getOperatorLoc()});240 return llvm::SmallVector<SourceLocation>();241 })242 .Build();243}244 245UncheckedStatusOrAccessDiagnoser::UncheckedStatusOrAccessDiagnoser(246 UncheckedStatusOrAccessModelOptions Options)247 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}248 249llvm::SmallVector<SourceLocation> UncheckedStatusOrAccessDiagnoser::operator()(250 const CFGElement &Elt, ASTContext &Ctx,251 const TransferStateForDiagnostics<UncheckedStatusOrAccessModel::Lattice>252 &State) {253 return DiagnoseMatchSwitch(Elt, Ctx, State.Env);254}255 256BoolValue &initializeStatus(RecordStorageLocation &StatusLoc,257 Environment &Env) {258 auto &OkVal = Env.makeAtomicBoolValue();259 Env.setValue(locForOk(StatusLoc), OkVal);260 return OkVal;261}262 263BoolValue &initializeStatusOr(RecordStorageLocation &StatusOrLoc,264 Environment &Env) {265 return initializeStatus(locForStatus(StatusOrLoc), Env);266}267 268clang::ast_matchers::DeclarationMatcher statusOrClass() {269 using namespace ::clang::ast_matchers; // NOLINT: Too many names270 return classTemplateSpecializationDecl(271 hasName("absl::StatusOr"),272 hasTemplateArgument(0, refersToType(type().bind("T"))));273}274 275clang::ast_matchers::DeclarationMatcher statusClass() {276 using namespace ::clang::ast_matchers; // NOLINT: Too many names277 return cxxRecordDecl(hasName("absl::Status"));278}279 280clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass() {281 using namespace ::clang::ast_matchers; // NOLINT: Too many names282 return classTemplateSpecializationDecl(283 hasName("absl::internal_statusor::OperatorBase"));284}285 286clang::ast_matchers::TypeMatcher statusOrType() {287 using namespace ::clang::ast_matchers; // NOLINT: Too many names288 return hasCanonicalType(qualType(hasDeclaration(statusOrClass())));289}290 291bool isStatusOrType(QualType Type) {292 return isTypeNamed(Type, {"absl"}, "StatusOr");293}294 295bool isStatusType(QualType Type) {296 return isTypeNamed(Type, {"absl"}, "Status");297}298 299llvm::StringMap<QualType> getSyntheticFields(QualType Ty, QualType StatusType,300 const CXXRecordDecl &RD) {301 if (auto *TRD = getStatusOrBaseClass(Ty))302 return {{"status", StatusType}, {"value", getStatusOrValueType(TRD)}};303 if (isStatusType(Ty) || (RD.hasDefinition() &&304 RD.isDerivedFrom(StatusType->getAsCXXRecordDecl())))305 return {{"ok", RD.getASTContext().BoolTy}};306 return {};307}308 309RecordStorageLocation &locForStatus(RecordStorageLocation &StatusOrLoc) {310 return cast<RecordStorageLocation>(StatusOrLoc.getSyntheticField("status"));311}312 313StorageLocation &locForOk(RecordStorageLocation &StatusLoc) {314 return StatusLoc.getSyntheticField("ok");315}316 317BoolValue &valForOk(RecordStorageLocation &StatusLoc, Environment &Env) {318 if (auto *Val = Env.get<BoolValue>(locForOk(StatusLoc)))319 return *Val;320 return initializeStatus(StatusLoc, Env);321}322 323static void transferStatusOrOkCall(const CXXMemberCallExpr *Expr,324 const MatchFinder::MatchResult &,325 LatticeTransferState &State) {326 RecordStorageLocation *StatusOrLoc =327 getImplicitObjectLocation(*Expr, State.Env);328 if (StatusOrLoc == nullptr)329 return;330 331 auto &OkVal = valForOk(locForStatus(*StatusOrLoc), State.Env);332 State.Env.setValue(*Expr, OkVal);333}334 335static void transferStatusCall(const CXXMemberCallExpr *Expr,336 const MatchFinder::MatchResult &,337 LatticeTransferState &State) {338 RecordStorageLocation *StatusOrLoc =339 getImplicitObjectLocation(*Expr, State.Env);340 if (StatusOrLoc == nullptr)341 return;342 343 RecordStorageLocation &StatusLoc = locForStatus(*StatusOrLoc);344 345 if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)346 initializeStatusOr(*StatusOrLoc, State.Env);347 348 if (Expr->isPRValue())349 copyRecord(StatusLoc, State.Env.getResultObjectLocation(*Expr), State.Env);350 else351 State.Env.setStorageLocation(*Expr, StatusLoc);352}353 354static void transferStatusOkCall(const CXXMemberCallExpr *Expr,355 const MatchFinder::MatchResult &,356 LatticeTransferState &State) {357 RecordStorageLocation *StatusLoc =358 getImplicitObjectLocation(*Expr, State.Env);359 if (StatusLoc == nullptr)360 return;361 362 if (Value *Val = State.Env.getValue(locForOk(*StatusLoc)))363 State.Env.setValue(*Expr, *Val);364}365 366static void transferStatusUpdateCall(const CXXMemberCallExpr *Expr,367 const MatchFinder::MatchResult &,368 LatticeTransferState &State) {369 // S.Update(OtherS) sets S to the error code of OtherS if it is OK,370 // otherwise does nothing.371 assert(Expr->getNumArgs() == 1);372 auto *Arg = Expr->getArg(0);373 RecordStorageLocation *ArgRecord =374 Arg->isPRValue() ? &State.Env.getResultObjectLocation(*Arg)375 : State.Env.get<RecordStorageLocation>(*Arg);376 RecordStorageLocation *ThisLoc = getImplicitObjectLocation(*Expr, State.Env);377 if (ThisLoc == nullptr || ArgRecord == nullptr)378 return;379 380 auto &ThisOkVal = valForOk(*ThisLoc, State.Env);381 auto &ArgOkVal = valForOk(*ArgRecord, State.Env);382 auto &A = State.Env.arena();383 auto &NewVal = State.Env.makeAtomicBoolValue();384 State.Env.assume(A.makeImplies(A.makeNot(ThisOkVal.formula()),385 A.makeNot(NewVal.formula())));386 State.Env.assume(A.makeImplies(NewVal.formula(), ArgOkVal.formula()));387 State.Env.setValue(locForOk(*ThisLoc), NewVal);388}389 390static BoolValue *evaluateStatusEquality(RecordStorageLocation &LhsStatusLoc,391 RecordStorageLocation &RhsStatusLoc,392 Environment &Env) {393 auto &A = Env.arena();394 // Logically, a Status object is composed of an error code that could take one395 // of multiple possible values, including the "ok" value. We track whether a396 // Status object has an "ok" value and represent this as an `ok` bit. Equality397 // of Status objects compares their error codes. Therefore, merely comparing398 // the `ok` bits isn't sufficient: when two Status objects are assigned non-ok399 // error codes the equality of their respective error codes matters. Since we400 // only track the `ok` bits, we can't make any conclusions about equality when401 // we know that two Status objects have non-ok values.402 403 auto &LhsOkVal = valForOk(LhsStatusLoc, Env);404 auto &RhsOkVal = valForOk(RhsStatusLoc, Env);405 406 auto &Res = Env.makeAtomicBoolValue();407 408 // lhs && rhs => res (a.k.a. !res => !lhs || !rhs)409 Env.assume(A.makeImplies(A.makeAnd(LhsOkVal.formula(), RhsOkVal.formula()),410 Res.formula()));411 // res => (lhs == rhs)412 Env.assume(A.makeImplies(413 Res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));414 415 return &Res;416}417 418static BoolValue *419evaluateStatusOrEquality(RecordStorageLocation &LhsStatusOrLoc,420 RecordStorageLocation &RhsStatusOrLoc,421 Environment &Env) {422 auto &A = Env.arena();423 // Logically, a StatusOr<T> object is composed of two values - a Status and a424 // value of type T. Equality of StatusOr objects compares both values.425 // Therefore, merely comparing the `ok` bits of the Status values isn't426 // sufficient. When two StatusOr objects are engaged, the equality of their427 // respective values of type T matters. Similarly, when two StatusOr objects428 // have Status values that have non-ok error codes, the equality of the error429 // codes matters. Since we only track the `ok` bits of the Status values, we430 // can't make any conclusions about equality when we know that two StatusOr431 // objects are engaged or when their Status values contain non-ok error codes.432 auto &LhsOkVal = valForOk(locForStatus(LhsStatusOrLoc), Env);433 auto &RhsOkVal = valForOk(locForStatus(RhsStatusOrLoc), Env);434 auto &res = Env.makeAtomicBoolValue();435 436 // res => (lhs == rhs)437 Env.assume(A.makeImplies(438 res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));439 return &res;440}441 442static BoolValue *evaluateEquality(const Expr *LhsExpr, const Expr *RhsExpr,443 Environment &Env) {444 // Check the type of both sides in case an operator== is added that admits445 // different types.446 if (isStatusOrType(LhsExpr->getType()) &&447 isStatusOrType(RhsExpr->getType())) {448 auto *LhsStatusOrLoc = Env.get<RecordStorageLocation>(*LhsExpr);449 if (LhsStatusOrLoc == nullptr)450 return nullptr;451 auto *RhsStatusOrLoc = Env.get<RecordStorageLocation>(*RhsExpr);452 if (RhsStatusOrLoc == nullptr)453 return nullptr;454 455 return evaluateStatusOrEquality(*LhsStatusOrLoc, *RhsStatusOrLoc, Env);456 }457 if (isStatusType(LhsExpr->getType()) && isStatusType(RhsExpr->getType())) {458 auto *LhsStatusLoc = Env.get<RecordStorageLocation>(*LhsExpr);459 if (LhsStatusLoc == nullptr)460 return nullptr;461 462 auto *RhsStatusLoc = Env.get<RecordStorageLocation>(*RhsExpr);463 if (RhsStatusLoc == nullptr)464 return nullptr;465 466 return evaluateStatusEquality(*LhsStatusLoc, *RhsStatusLoc, Env);467 }468 return nullptr;469}470 471static void transferComparisonOperator(const CXXOperatorCallExpr *Expr,472 LatticeTransferState &State,473 bool IsNegative) {474 auto *LhsAndRhsVal =475 evaluateEquality(Expr->getArg(0), Expr->getArg(1), State.Env);476 if (LhsAndRhsVal == nullptr)477 return;478 479 if (IsNegative)480 State.Env.setValue(*Expr, State.Env.makeNot(*LhsAndRhsVal));481 else482 State.Env.setValue(*Expr, *LhsAndRhsVal);483}484 485static RecordStorageLocation *getPointeeLocation(const Expr &Expr,486 Environment &Env) {487 if (auto *PointerVal = Env.get<PointerValue>(Expr))488 return dyn_cast<RecordStorageLocation>(&PointerVal->getPointeeLoc());489 return nullptr;490}491 492static BoolValue *evaluatePointerEquality(const Expr *LhsExpr,493 const Expr *RhsExpr,494 Environment &Env) {495 assert(LhsExpr->getType()->isPointerType());496 assert(RhsExpr->getType()->isPointerType());497 RecordStorageLocation *LhsStatusLoc = nullptr;498 RecordStorageLocation *RhsStatusLoc = nullptr;499 if (isStatusOrType(LhsExpr->getType()->getPointeeType()) &&500 isStatusOrType(RhsExpr->getType()->getPointeeType())) {501 auto *LhsStatusOrLoc = getPointeeLocation(*LhsExpr, Env);502 auto *RhsStatusOrLoc = getPointeeLocation(*RhsExpr, Env);503 if (LhsStatusOrLoc == nullptr || RhsStatusOrLoc == nullptr)504 return nullptr;505 LhsStatusLoc = &locForStatus(*LhsStatusOrLoc);506 RhsStatusLoc = &locForStatus(*RhsStatusOrLoc);507 } else if (isStatusType(LhsExpr->getType()->getPointeeType()) &&508 isStatusType(RhsExpr->getType()->getPointeeType())) {509 LhsStatusLoc = getPointeeLocation(*LhsExpr, Env);510 RhsStatusLoc = getPointeeLocation(*RhsExpr, Env);511 }512 if (LhsStatusLoc == nullptr || RhsStatusLoc == nullptr)513 return nullptr;514 auto &LhsOkVal = valForOk(*LhsStatusLoc, Env);515 auto &RhsOkVal = valForOk(*RhsStatusLoc, Env);516 auto &Res = Env.makeAtomicBoolValue();517 auto &A = Env.arena();518 Env.assume(A.makeImplies(519 Res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));520 return &Res;521}522 523static void transferPointerComparisonOperator(const BinaryOperator *Expr,524 LatticeTransferState &State,525 bool IsNegative) {526 auto *LhsAndRhsVal =527 evaluatePointerEquality(Expr->getLHS(), Expr->getRHS(), State.Env);528 if (LhsAndRhsVal == nullptr)529 return;530 531 if (IsNegative)532 State.Env.setValue(*Expr, State.Env.makeNot(*LhsAndRhsVal));533 else534 State.Env.setValue(*Expr, *LhsAndRhsVal);535}536 537static void transferOkStatusCall(const CallExpr *Expr,538 const MatchFinder::MatchResult &,539 LatticeTransferState &State) {540 auto &OkVal =541 initializeStatus(State.Env.getResultObjectLocation(*Expr), State.Env);542 State.Env.assume(OkVal.formula());543}544 545static void transferNotOkStatusCall(const CallExpr *Expr,546 const MatchFinder::MatchResult &,547 LatticeTransferState &State) {548 auto &OkVal =549 initializeStatus(State.Env.getResultObjectLocation(*Expr), State.Env);550 auto &A = State.Env.arena();551 State.Env.assume(A.makeNot(OkVal.formula()));552}553 554static void transferEmplaceCall(const CXXMemberCallExpr *Expr,555 const MatchFinder::MatchResult &,556 LatticeTransferState &State) {557 RecordStorageLocation *StatusOrLoc =558 getImplicitObjectLocation(*Expr, State.Env);559 if (StatusOrLoc == nullptr)560 return;561 562 auto &OkVal = valForOk(locForStatus(*StatusOrLoc), State.Env);563 State.Env.assume(OkVal.formula());564}565 566static void transferValueAssignmentCall(const CXXOperatorCallExpr *Expr,567 const MatchFinder::MatchResult &,568 LatticeTransferState &State) {569 assert(Expr->getNumArgs() > 1);570 571 auto *StatusOrLoc = State.Env.get<RecordStorageLocation>(*Expr->getArg(0));572 if (StatusOrLoc == nullptr)573 return;574 575 auto &OkVal = initializeStatusOr(*StatusOrLoc, State.Env);576 State.Env.assume(OkVal.formula());577}578 579static void transferValueConstructor(const CXXConstructExpr *Expr,580 const MatchFinder::MatchResult &,581 LatticeTransferState &State) {582 auto &OkVal =583 initializeStatusOr(State.Env.getResultObjectLocation(*Expr), State.Env);584 State.Env.assume(OkVal.formula());585}586 587static void transferStatusOrConstructor(const CXXConstructExpr *Expr,588 const MatchFinder::MatchResult &,589 LatticeTransferState &State) {590 RecordStorageLocation &StatusOrLoc = State.Env.getResultObjectLocation(*Expr);591 RecordStorageLocation &StatusLoc = locForStatus(StatusOrLoc);592 593 if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)594 initializeStatusOr(StatusOrLoc, State.Env);595}596 597static void transferStatusConstructor(const CXXConstructExpr *Expr,598 const MatchFinder::MatchResult &,599 LatticeTransferState &State) {600 RecordStorageLocation &StatusLoc = State.Env.getResultObjectLocation(*Expr);601 602 if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)603 initializeStatus(StatusLoc, State.Env);604}605 606CFGMatchSwitch<LatticeTransferState>607buildTransferMatchSwitch(ASTContext &Ctx,608 CFGMatchSwitchBuilder<LatticeTransferState> Builder) {609 using namespace ::clang::ast_matchers; // NOLINT: Too many names610 return std::move(Builder)611 .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("ok"),612 transferStatusOrOkCall)613 .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("status"),614 transferStatusCall)615 .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusMemberCallWithName("ok"),616 transferStatusOkCall)617 .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusMemberCallWithName("Update"),618 transferStatusUpdateCall)619 .CaseOfCFGStmt<CXXOperatorCallExpr>(620 isComparisonOperatorCall("=="),621 [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,622 LatticeTransferState &State) {623 transferComparisonOperator(Expr, State,624 /*IsNegative=*/false);625 })626 .CaseOfCFGStmt<CXXOperatorCallExpr>(627 isComparisonOperatorCall("!="),628 [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,629 LatticeTransferState &State) {630 transferComparisonOperator(Expr, State,631 /*IsNegative=*/true);632 })633 .CaseOfCFGStmt<BinaryOperator>(634 isPointerComparisonOperatorCall("=="),635 [](const BinaryOperator *Expr, const MatchFinder::MatchResult &,636 LatticeTransferState &State) {637 transferPointerComparisonOperator(Expr, State,638 /*IsNegative=*/false);639 })640 .CaseOfCFGStmt<BinaryOperator>(641 isPointerComparisonOperatorCall("!="),642 [](const BinaryOperator *Expr, const MatchFinder::MatchResult &,643 LatticeTransferState &State) {644 transferPointerComparisonOperator(Expr, State,645 /*IsNegative=*/true);646 })647 .CaseOfCFGStmt<CallExpr>(isOkStatusCall(), transferOkStatusCall)648 .CaseOfCFGStmt<CallExpr>(isNotOkStatusCall(), transferNotOkStatusCall)649 .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("emplace"),650 transferEmplaceCall)651 .CaseOfCFGStmt<CXXOperatorCallExpr>(isStatusOrValueAssignmentCall(),652 transferValueAssignmentCall)653 .CaseOfCFGStmt<CXXConstructExpr>(isStatusOrValueConstructor(),654 transferValueConstructor)655 // N.B. These need to come after all other CXXConstructExpr.656 // These are there to make sure that every Status and StatusOr object657 // have their ok boolean initialized when constructed. If we were to658 // lazily initialize them when we first access them, we can produce659 // false positives if that first access is in a control flow statement.660 // You can comment out these two constructors and see tests fail.661 .CaseOfCFGStmt<CXXConstructExpr>(isStatusOrConstructor(),662 transferStatusOrConstructor)663 .CaseOfCFGStmt<CXXConstructExpr>(isStatusConstructor(),664 transferStatusConstructor)665 .Build();666}667 668QualType findStatusType(const ASTContext &Ctx) {669 for (Type *Ty : Ctx.getTypes())670 if (isStatusType(QualType(Ty, 0)))671 return QualType(Ty, 0);672 673 return QualType();674}675 676UncheckedStatusOrAccessModel::UncheckedStatusOrAccessModel(ASTContext &Ctx,677 Environment &Env)678 : DataflowAnalysis<UncheckedStatusOrAccessModel,679 UncheckedStatusOrAccessModel::Lattice>(Ctx),680 TransferMatchSwitch(buildTransferMatchSwitch(Ctx, {})) {681 QualType StatusType = findStatusType(Ctx);682 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(683 [StatusType](QualType Ty) -> llvm::StringMap<QualType> {684 CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();685 if (RD == nullptr)686 return {};687 688 if (auto Fields = getSyntheticFields(Ty, StatusType, *RD);689 !Fields.empty())690 return Fields;691 return {};692 });693}694 695void UncheckedStatusOrAccessModel::transfer(const CFGElement &Elt, Lattice &L,696 Environment &Env) {697 LatticeTransferState State(L, Env);698 TransferMatchSwitch(Elt, getASTContext(), State);699}700 701} // namespace clang::dataflow::statusor_model702