brintos

brintos / llvm-project-archived public Read only

0
0
Text · 28.2 KiB · b42bfa3 Raw
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