brintos

brintos / llvm-project-archived public Read only

0
0
Text · 48.7 KiB · d90f5d4 Raw
1222 lines · cpp
1//===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//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//  This file defines a dataflow analysis that detects unsafe uses of optional10//  values.11//12//===----------------------------------------------------------------------===//13 14#include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"15#include "clang/AST/ASTContext.h"16#include "clang/AST/DeclCXX.h"17#include "clang/AST/Expr.h"18#include "clang/AST/ExprCXX.h"19#include "clang/AST/Stmt.h"20#include "clang/AST/Type.h"21#include "clang/ASTMatchers/ASTMatchers.h"22#include "clang/ASTMatchers/ASTMatchersMacros.h"23#include "clang/Analysis/CFG.h"24#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"25#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"26#include "clang/Analysis/FlowSensitive/Formula.h"27#include "clang/Analysis/FlowSensitive/RecordOps.h"28#include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h"29#include "clang/Analysis/FlowSensitive/StorageLocation.h"30#include "clang/Analysis/FlowSensitive/Value.h"31#include "clang/Basic/OperatorKinds.h"32#include "clang/Basic/SourceLocation.h"33#include "llvm/ADT/StringRef.h"34#include "llvm/Support/ErrorHandling.h"35#include <cassert>36#include <optional>37 38namespace clang {39namespace dataflow {40 41// Note: the Names appear in reverse order. E.g., to check42// if NS is foo::bar::, call isFullyQualifiedNamespaceEqualTo(NS, "bar", "foo")43template <class... NameTypes>44static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS,45                                             llvm::StringRef Name,46                                             NameTypes... Names) {47  if (!(NS.getDeclName().isIdentifier() && NS.getName() == Name &&48        NS.getParent() != nullptr))49    return false;50 51  if constexpr (sizeof...(NameTypes) > 0) {52    if (NS.getParent()->isTranslationUnit())53      return false;54    if (const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.getParent()))55      return isFullyQualifiedNamespaceEqualTo(*NextNS, Names...);56    return false;57  } else {58    return NS.getParent()->isTranslationUnit();59  }60}61 62static bool hasOptionalClassName(const CXXRecordDecl &RD) {63  if (!RD.getDeclName().isIdentifier())64    return false;65 66  if (RD.getName() == "optional") {67    if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))68      return N->isStdNamespace() ||69             isFullyQualifiedNamespaceEqualTo(*N, "absl") ||70             isFullyQualifiedNamespaceEqualTo(*N, "bsl");71    return false;72  }73 74  if (RD.getName() == "Optional") {75    // Check whether namespace is "::base" or "::folly".76    const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());77    return N != nullptr && (isFullyQualifiedNamespaceEqualTo(*N, "base") ||78                            isFullyQualifiedNamespaceEqualTo(*N, "folly"));79  }80 81  if (RD.getName() == "NullableValue") {82    const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());83    return N != nullptr &&84           isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP");85  }86 87  return false;88}89 90static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) {91  if (RD == nullptr)92    return nullptr;93  if (hasOptionalClassName(*RD))94    return RD;95 96  if (!RD->hasDefinition())97    return nullptr;98 99  for (const CXXBaseSpecifier &Base : RD->bases())100    if (const CXXRecordDecl *BaseClass =101            getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl()))102      return BaseClass;103 104  return nullptr;105}106 107static bool isSupportedOptionalType(QualType Ty) {108  const CXXRecordDecl *Optional =109      getOptionalBaseClass(Ty->getAsCXXRecordDecl());110  return Optional != nullptr;111}112 113namespace {114 115using namespace ::clang::ast_matchers;116 117using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>;118 119AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); }120 121AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {122  return getOptionalBaseClass(&Node) != nullptr;123}124 125auto desugarsToOptionalType() {126  return hasUnqualifiedDesugaredType(127      recordType(hasDeclaration(cxxRecordDecl(optionalClass()))));128}129 130auto desugarsToOptionalOrDerivedType() {131  return hasUnqualifiedDesugaredType(132      recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass()))));133}134 135auto hasOptionalType() { return hasType(desugarsToOptionalType()); }136 137/// Matches any of the spellings of the optional types and sugar, aliases,138/// derived classes, etc.139auto hasOptionalOrDerivedType() {140  return hasType(desugarsToOptionalOrDerivedType());141}142 143QualType getPublicType(const Expr *E) {144  auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());145  if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) {146    QualType Ty = E->getType();147    if (Ty->isPointerType())148      return Ty->getPointeeType();149    return Ty;150  }151 152  // Is the derived type that we're casting from the type of `*this`? In this153  // special case, we can upcast to the base class even if the base is154  // non-public.155  bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr());156 157  // Find the least-derived type in the path (i.e. the last entry in the list)158  // that we can access.159  const CXXBaseSpecifier *PublicBase = nullptr;160  for (const CXXBaseSpecifier *Base : Cast->path()) {161    if (Base->getAccessSpecifier() != AS_public && !CastingFromThis)162      break;163    PublicBase = Base;164    CastingFromThis = false;165  }166 167  if (PublicBase != nullptr)168    return PublicBase->getType();169 170  // We didn't find any public type that we could cast to. There may be more171  // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this172  // will return the type of `getSubExpr()`.)173  return getPublicType(Cast->getSubExpr());174}175 176// Returns the least-derived type for the receiver of `MCE` that177// `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to.178// Effectively, we upcast until we reach a non-public base class, unless that179// base is a base of `*this`.180//181// This is needed to correctly match methods called on types derived from182// `std::optional`.183//184// Say we have a `struct Derived : public std::optional<int> {} d;` For a call185// `d.has_value()`, the `getImplicitObjectArgument()` looks like this:186//187//   ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue188//   |            <UncheckedDerivedToBase (optional -> __optional_storage_base)>189//   `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived'190//191// The type of the implicit object argument is `__optional_storage_base`192// (since this is the internal type that `has_value()` is declared on). If we193// call `IgnoreParenImpCasts()` on the implicit object argument, we get the194// `DeclRefExpr`, which has type `Derived`. Neither of these types is195// `optional`, and hence neither is sufficient for querying whether we are196// calling a method on `optional`.197//198// Instead, starting with the most derived type, we need to follow the chain of199// casts200QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) {201  return getPublicType(MCE.getImplicitObjectArgument());202}203 204AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType,205              ast_matchers::internal::Matcher<QualType>, InnerMatcher) {206  return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder);207}208 209auto isOptionalMemberCallWithNameMatcher(210    ast_matchers::internal::Matcher<NamedDecl> matcher,211    const std::optional<StatementMatcher> &Ignorable = std::nullopt) {212  return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable)))213                                     : anything(),214                           publicReceiverType(desugarsToOptionalType()),215                           callee(cxxMethodDecl(matcher)));216}217 218auto isOptionalOperatorCallWithName(219    llvm::StringRef operator_name,220    const std::optional<StatementMatcher> &Ignorable = std::nullopt) {221  return cxxOperatorCallExpr(222      hasOverloadedOperatorName(operator_name),223      callee(cxxMethodDecl(ofClass(optionalClass()))),224      Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());225}226 227auto isMakeOptionalCall() {228  return callExpr(229      callee(functionDecl(hasAnyName(230          "std::make_optional", "base::make_optional", "absl::make_optional",231          "folly::make_optional", "bsl::make_optional"))),232      hasOptionalType());233}234 235auto nulloptTypeDecl() {236  return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",237                              "base::nullopt_t", "folly::None",238                              "bsl::nullopt_t"));239}240 241auto hasNulloptType() { return hasType(nulloptTypeDecl()); }242 243auto inPlaceClass() {244  return namedDecl(hasAnyName("std::in_place_t", "absl::in_place_t",245                              "base::in_place_t", "folly::in_place_t",246                              "bsl::in_place_t"));247}248 249auto isOptionalNulloptConstructor() {250  return cxxConstructExpr(251      hasDeclaration(cxxConstructorDecl(parameterCountIs(1),252                                        hasParameter(0, hasNulloptType()))),253      hasOptionalOrDerivedType());254}255 256auto isOptionalInPlaceConstructor() {257  return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())),258                          hasOptionalOrDerivedType());259}260 261auto isOptionalValueOrConversionConstructor() {262  return cxxConstructExpr(263      unless(hasDeclaration(264          cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),265      argumentCountIs(1), hasArgument(0, unless(hasNulloptType())),266      hasOptionalOrDerivedType());267}268 269auto isOptionalValueOrConversionAssignment() {270  return cxxOperatorCallExpr(271      hasOverloadedOperatorName("="),272      callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),273      unless(hasDeclaration(cxxMethodDecl(274          anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),275      argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));276}277 278auto isOptionalNulloptAssignment() {279  return cxxOperatorCallExpr(280      hasOverloadedOperatorName("="),281      callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),282      argumentCountIs(2), hasArgument(1, hasNulloptType()));283}284 285auto isStdSwapCall() {286  return callExpr(callee(functionDecl(hasName("std::swap"))),287                  argumentCountIs(2),288                  hasArgument(0, hasOptionalOrDerivedType()),289                  hasArgument(1, hasOptionalOrDerivedType()));290}291 292auto isStdForwardCall() {293  return callExpr(callee(functionDecl(hasName("std::forward"))),294                  argumentCountIs(1),295                  hasArgument(0, hasOptionalOrDerivedType()));296}297 298constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";299 300auto isValueOrStringEmptyCall() {301  // `opt.value_or("").empty()`302  return cxxMemberCallExpr(303      callee(cxxMethodDecl(hasName("empty"))),304      onImplicitObjectArgument(ignoringImplicit(305          cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),306                            callee(cxxMethodDecl(hasName("value_or"),307                                                 ofClass(optionalClass()))),308                            hasArgument(0, stringLiteral(hasSize(0))))309              .bind(ValueOrCallID))));310}311 312auto isValueOrNotEqX() {313  auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {314    return hasOperands(315        ignoringImplicit(316            cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),317                              callee(cxxMethodDecl(hasName("value_or"),318                                                   ofClass(optionalClass()))),319                              hasArgument(0, Arg))320                .bind(ValueOrCallID)),321        ignoringImplicit(Arg));322  };323 324  // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd325  // support this pattern for any expression, but the AST does not have a326  // generic expression comparison facility, so we specialize to common cases327  // seen in practice.  FIXME: define a matcher that compares values across328  // nodes, which would let us generalize this to any `X`.329  return binaryOperation(hasOperatorName("!="),330                         anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),331                               ComparesToSame(stringLiteral(hasSize(0))),332                               ComparesToSame(integerLiteral(equals(0)))));333}334 335auto isZeroParamConstMemberCall() {336  return cxxMemberCallExpr(337      callee(cxxMethodDecl(parameterCountIs(0), isConst())));338}339 340auto isZeroParamConstMemberOperatorCall() {341  return cxxOperatorCallExpr(342      callee(cxxMethodDecl(parameterCountIs(0), isConst())));343}344 345auto isNonConstMemberCall() {346  return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst()))));347}348 349auto isNonConstMemberOperatorCall() {350  return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));351}352 353auto isCallReturningOptional() {354  return callExpr(hasType(qualType(355      anyOf(desugarsToOptionalOrDerivedType(),356            referenceType(pointee(desugarsToOptionalOrDerivedType()))))));357}358 359template <typename L, typename R>360auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {361  return cxxOperatorCallExpr(362      anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),363      argumentCountIs(2), hasArgument(0, lhs_arg_matcher),364      hasArgument(1, rhs_arg_matcher));365}366 367/// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.368const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {369  auto *Value = Env.get<BoolValue>(Expr);370  if (Value != nullptr)371    return Value->formula();372 373  Value = &Env.makeAtomicBoolValue();374  Env.setValue(Expr, *Value);375  return Value->formula();376}377 378StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) {379  return OptionalLoc.getSyntheticField("has_value");380}381 382StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) {383  return OptionalLoc.getSyntheticField("value");384}385 386/// Sets `HasValueVal` as the symbolic value that represents the "has_value"387/// property of the optional at `OptionalLoc`.388void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,389                 Environment &Env) {390  Env.setValue(locForHasValue(OptionalLoc), HasValueVal);391}392 393/// Returns the symbolic value that represents the "has_value" property of the394/// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null.395BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {396  if (OptionalLoc == nullptr)397    return nullptr;398  StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);399  auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);400  if (HasValueVal == nullptr) {401    HasValueVal = &Env.makeAtomicBoolValue();402    Env.setValue(HasValueLoc, *HasValueVal);403  }404  return HasValueVal;405}406 407QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {408  auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);409  return CTSD.getTemplateArgs()[0].getAsType();410}411 412/// Returns the number of optional wrappers in `Type`.413///414/// For example, if `Type` is `optional<optional<int>>`, the result of this415/// function will be 2.416int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {417  const CXXRecordDecl *Optional =418      getOptionalBaseClass(Type->getAsCXXRecordDecl());419  if (Optional == nullptr)420    return 0;421  return 1 + countOptionalWrappers(422                 ASTCtx,423                 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));424}425 426StorageLocation *getLocBehindPossiblePointer(const Expr &E,427                                             const Environment &Env) {428  if (E.isPRValue()) {429    if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))430      return &PointerVal->getPointeeLoc();431    return nullptr;432  }433  return Env.getStorageLocation(E);434}435 436void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,437                        LatticeTransferState &State) {438  if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(439          getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {440    if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)441      State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));442  }443}444 445void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,446                         LatticeTransferState &State) {447  if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(448          getLocBehindPossiblePointer(*ObjectExpr, State.Env)))449    State.Env.setValue(450        *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));451}452 453void transferMakeOptionalCall(const CallExpr *E,454                              const MatchFinder::MatchResult &,455                              LatticeTransferState &State) {456  setHasValue(State.Env.getResultObjectLocation(*E),457              State.Env.getBoolLiteralValue(true), State.Env);458}459 460void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,461                                  const MatchFinder::MatchResult &,462                                  LatticeTransferState &State) {463  if (auto *HasValueVal = getHasValue(464          State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {465    State.Env.setValue(*CallExpr, *HasValueVal);466  }467}468 469void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr,470                                const MatchFinder::MatchResult &,471                                LatticeTransferState &State) {472  if (auto *HasValueVal = getHasValue(473          State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {474    State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal));475  }476}477 478/// `ModelPred` builds a logical formula relating the predicate in479/// `ValueOrPredExpr` to the optional's `has_value` property.480void transferValueOrImpl(481    const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,482    LatticeTransferState &State,483    const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,484                                const Formula &HasValueVal)) {485  auto &Env = State.Env;486 487  const auto *MCE =488      Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);489 490  auto *HasValueVal =491      getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env));492  if (HasValueVal == nullptr)493    return;494 495  Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),496                       HasValueVal->formula()));497}498 499void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,500                                    const MatchFinder::MatchResult &Result,501                                    LatticeTransferState &State) {502  return transferValueOrImpl(ComparisonExpr, Result, State,503                             [](Environment &Env, const Formula &ExprVal,504                                const Formula &HasValueVal) -> const Formula & {505                               auto &A = Env.arena();506                               // If the result is *not* empty, then we know the507                               // optional must have been holding a value. If508                               // `ExprVal` is true, though, we don't learn509                               // anything definite about `has_value`, so we510                               // don't add any corresponding implications to511                               // the flow condition.512                               return A.makeImplies(A.makeNot(ExprVal),513                                                    HasValueVal);514                             });515}516 517void transferValueOrNotEqX(const Expr *ComparisonExpr,518                           const MatchFinder::MatchResult &Result,519                           LatticeTransferState &State) {520  transferValueOrImpl(ComparisonExpr, Result, State,521                      [](Environment &Env, const Formula &ExprVal,522                         const Formula &HasValueVal) -> const Formula & {523                        auto &A = Env.arena();524                        // We know that if `(opt.value_or(X) != X)` then525                        // `opt.hasValue()`, even without knowing further526                        // details about the contents of `opt`.527                        return A.makeImplies(ExprVal, HasValueVal);528                      });529}530 531void transferCallReturningOptional(const CallExpr *E,532                                   const MatchFinder::MatchResult &Result,533                                   LatticeTransferState &State) {534  RecordStorageLocation *Loc = nullptr;535  if (E->isPRValue()) {536    Loc = &State.Env.getResultObjectLocation(*E);537  } else {538    Loc = State.Env.get<RecordStorageLocation>(*E);539    if (Loc == nullptr) {540      Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));541      State.Env.setStorageLocation(*E, *Loc);542    }543  }544 545  if (State.Env.getValue(locForHasValue(*Loc)) != nullptr)546    return;547 548  setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);549}550 551// Returns true if the const accessor is handled by caching.552// Returns false if we could not cache. We should perform default handling553// in that case.554bool handleConstMemberCall(const CallExpr *CE,555                           dataflow::RecordStorageLocation *RecordLoc,556                           const MatchFinder::MatchResult &Result,557                           LatticeTransferState &State) {558  if (RecordLoc == nullptr)559    return false;560 561  // Cache if the const method returns a reference.562  if (CE->isGLValue()) {563    const FunctionDecl *DirectCallee = CE->getDirectCallee();564    if (DirectCallee == nullptr)565      return false;566 567    // Initialize the optional's "has_value" property to true if the type is568    // optional, otherwise no-op. If we want to support const ref to pointers or569    // bools we should initialize their values here too.570    auto Init = [&](StorageLocation &Loc) {571      if (isSupportedOptionalType(CE->getType()))572        setHasValue(cast<RecordStorageLocation>(Loc),573                    State.Env.makeAtomicBoolValue(), State.Env);574    };575    StorageLocation &Loc =576        State.Lattice.getOrCreateConstMethodReturnStorageLocation(577            *RecordLoc, DirectCallee, State.Env, Init);578 579    State.Env.setStorageLocation(*CE, Loc);580    return true;581  }582  // PRValue cases:583  if (CE->getType()->isBooleanType() || CE->getType()->isPointerType()) {584    // If the const method returns a boolean or pointer type.585    Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE,586                                                                 State.Env);587    if (Val == nullptr)588      return false;589    State.Env.setValue(*CE, *Val);590    return true;591  }592  if (isSupportedOptionalType(CE->getType())) {593    // If the const method returns an optional by value.594    const FunctionDecl *DirectCallee = CE->getDirectCallee();595    if (DirectCallee == nullptr)596      return false;597    StorageLocation &Loc =598        State.Lattice.getOrCreateConstMethodReturnStorageLocation(599            *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {600              setHasValue(cast<RecordStorageLocation>(Loc),601                          State.Env.makeAtomicBoolValue(), State.Env);602            });603    // Use copyRecord to link the optional to the result object of the call604    // expression.605    auto &ResultLoc = State.Env.getResultObjectLocation(*CE);606    copyRecord(cast<RecordStorageLocation>(Loc), ResultLoc, State.Env);607    return true;608  }609 610  return false;611}612 613void handleConstMemberCallWithFallbacks(614    const CallExpr *CE, dataflow::RecordStorageLocation *RecordLoc,615    const MatchFinder::MatchResult &Result, LatticeTransferState &State) {616  if (handleConstMemberCall(CE, RecordLoc, Result, State))617    return;618  // Perform default handling if the call returns an optional, but wasn't619  // handled by caching.620  if (isSupportedOptionalType(CE->getType()))621    transferCallReturningOptional(CE, Result, State);622}623 624void transferConstMemberCall(const CXXMemberCallExpr *MCE,625                             const MatchFinder::MatchResult &Result,626                             LatticeTransferState &State) {627  handleConstMemberCallWithFallbacks(628      MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);629}630 631void transferConstMemberOperatorCall(const CXXOperatorCallExpr *OCE,632                                     const MatchFinder::MatchResult &Result,633                                     LatticeTransferState &State) {634  auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(635      State.Env.getStorageLocation(*OCE->getArg(0)));636  handleConstMemberCallWithFallbacks(OCE, RecordLoc, Result, State);637}638 639void handleNonConstMemberCall(const CallExpr *CE,640                              dataflow::RecordStorageLocation *RecordLoc,641                              const MatchFinder::MatchResult &Result,642                              LatticeTransferState &State) {643  if (RecordLoc != nullptr) {644    // When a non-const member function is called, clear all (non-const)645    // optional fields of the receiver. Const-qualified fields can't be646    // changed (at least, not without UB).647    for (const auto &[Field, FieldLoc] : RecordLoc->children()) {648      QualType FieldType = Field->getType();649      if (!FieldType.isConstQualified() &&650          isSupportedOptionalType(Field->getType())) {651        auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);652        if (FieldRecordLoc) {653          setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),654                      State.Env);655        }656      }657    }658    State.Lattice.clearConstMethodReturnValues(*RecordLoc);659    State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc);660  }661 662  // Perform default handling if the call returns an optional.663  if (isSupportedOptionalType(CE->getType())) {664    transferCallReturningOptional(CE, Result, State);665  }666}667 668void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE,669                                      const MatchFinder::MatchResult &Result,670                                      LatticeTransferState &State) {671  handleNonConstMemberCall(672      MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);673}674 675void transferValue_NonConstMemberOperatorCall(676    const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result,677    LatticeTransferState &State) {678  auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(679      State.Env.getStorageLocation(*OCE->getArg(0)));680  handleNonConstMemberCall(OCE, RecordLoc, Result, State);681}682 683void constructOptionalValue(const Expr &E, Environment &Env,684                            BoolValue &HasValueVal) {685  RecordStorageLocation &Loc = Env.getResultObjectLocation(E);686  setHasValue(Loc, HasValueVal, Env);687}688 689/// Returns a symbolic value for the "has_value" property of an `optional<T>`690/// value that is constructed/assigned from a value of type `U` or `optional<U>`691/// where `T` is constructible from `U`.692BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E,693                                     const MatchFinder::MatchResult &MatchRes,694                                     LatticeTransferState &State) {695  const int DestTypeOptionalWrappersCount =696      countOptionalWrappers(*MatchRes.Context, DestType);697  const int ArgTypeOptionalWrappersCount = countOptionalWrappers(698      *MatchRes.Context, E.getType().getNonReferenceType());699 700  // Is this an constructor of the form `template<class U> optional(U &&)` /701  // assignment of the form `template<class U> optional& operator=(U &&)`702  // (where `T` is assignable / constructible from `U`)?703  // We recognize this because the number of optionals in the optional being704  // assigned to is different from the function argument type.705  if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)706    return State.Env.getBoolLiteralValue(true);707 708  // Otherwise, this must be a constructor of the form709  // `template <class U> optional<optional<U> &&)` / assignment of the form710  // `template <class U> optional& operator=(optional<U> &&)711  // (where, again, `T` is assignable / constructible from `U`).712  auto *Loc = State.Env.get<RecordStorageLocation>(E);713  if (auto *HasValueVal = getHasValue(State.Env, Loc))714    return *HasValueVal;715  return State.Env.makeAtomicBoolValue();716}717 718void transferValueOrConversionConstructor(719    const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,720    LatticeTransferState &State) {721  assert(E->getNumArgs() > 0);722 723  constructOptionalValue(724      *E, State.Env,725      valueOrConversionHasValue(726          E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0),727          MatchRes, State));728}729 730void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,731                        LatticeTransferState &State) {732  assert(E->getNumArgs() > 0);733 734  if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) {735    setHasValue(*Loc, HasValueVal, State.Env);736 737    // Assign a storage location for the whole expression.738    State.Env.setStorageLocation(*E, *Loc);739  }740}741 742void transferValueOrConversionAssignment(743    const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,744    LatticeTransferState &State) {745  assert(E->getNumArgs() > 1);746  transferAssignment(747      E,748      valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(),749                                *E->getArg(1), MatchRes, State),750      State);751}752 753void transferNulloptAssignment(const CXXOperatorCallExpr *E,754                               const MatchFinder::MatchResult &,755                               LatticeTransferState &State) {756  transferAssignment(E, State.Env.getBoolLiteralValue(false), State);757}758 759void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,760                  Environment &Env) {761  // We account for cases where one or both of the optionals are not modeled,762  // either lacking associated storage locations, or lacking values associated763  // to such storage locations.764 765  if (Loc1 == nullptr) {766    if (Loc2 != nullptr)767      setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env);768    return;769  }770  if (Loc2 == nullptr) {771    setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env);772    return;773  }774 775  // Both expressions have locations, though they may not have corresponding776  // values. In that case, we create a fresh value at this point. Note that if777  // two branches both do this, they will not share the value, but it at least778  // allows for local reasoning about the value. To avoid the above, we would779  // need *lazy* value allocation.780  // FIXME: allocate values lazily, instead of just creating a fresh value.781  BoolValue *BoolVal1 = getHasValue(Env, Loc1);782  if (BoolVal1 == nullptr)783    BoolVal1 = &Env.makeAtomicBoolValue();784 785  BoolValue *BoolVal2 = getHasValue(Env, Loc2);786  if (BoolVal2 == nullptr)787    BoolVal2 = &Env.makeAtomicBoolValue();788 789  setHasValue(*Loc1, *BoolVal2, Env);790  setHasValue(*Loc2, *BoolVal1, Env);791}792 793void transferSwapCall(const CXXMemberCallExpr *E,794                      const MatchFinder::MatchResult &,795                      LatticeTransferState &State) {796  assert(E->getNumArgs() == 1);797  auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0));798  transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);799}800 801void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,802                         LatticeTransferState &State) {803  assert(E->getNumArgs() == 2);804  auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0));805  auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1));806  transferSwap(Arg0Loc, Arg1Loc, State.Env);807}808 809void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,810                            LatticeTransferState &State) {811  assert(E->getNumArgs() == 1);812 813  if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))814    State.Env.setStorageLocation(*E, *Loc);815}816 817const Formula &evaluateEquality(Arena &A, const Formula &EqVal,818                                const Formula &LHS, const Formula &RHS) {819  // Logically, an optional<T> object is composed of two values - a `has_value`820  // bit and a value of type T. Equality of optional objects compares both821  // values. Therefore, merely comparing the `has_value` bits isn't sufficient:822  // when two optional objects are engaged, the equality of their respective823  // values of type T matters. Since we only track the `has_value` bits, we824  // can't make any conclusions about equality when we know that two optional825  // objects are engaged.826  //827  // We express this as two facts about the equality:828  // a) EqVal => (LHS & RHS) v (!RHS & !LHS)829  //    If they are equal, then either both are set or both are unset.830  // b) (!LHS & !RHS) => EqVal831  //    If neither is set, then they are equal.832  // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.833  return A.makeAnd(834      A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),835                                    A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),836      A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));837}838 839void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,840                                    const MatchFinder::MatchResult &,841                                    LatticeTransferState &State) {842  Environment &Env = State.Env;843  auto &A = Env.arena();844  auto *CmpValue = &forceBoolValue(Env, *CmpExpr);845  auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0));846  if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {847    auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1));848    if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {849      if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)850        CmpValue = &A.makeNot(*CmpValue);851      Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),852                                  RHasVal->formula()));853    }854  }855}856 857void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,858                                 const clang::Expr *E, Environment &Env) {859  auto &A = Env.arena();860  auto *CmpValue = &forceBoolValue(Env, *CmpExpr);861  auto *Loc = Env.get<RecordStorageLocation>(*E);862  if (auto *HasVal = getHasValue(Env, Loc)) {863    if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)864      CmpValue = &A.makeNot(*CmpValue);865    Env.assume(866        evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));867  }868}869 870void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,871                                   const clang::Expr *E, Environment &Env) {872  auto &A = Env.arena();873  auto *CmpValue = &forceBoolValue(Env, *CmpExpr);874  auto *Loc = Env.get<RecordStorageLocation>(*E);875  if (auto *HasVal = getHasValue(Env, Loc)) {876    if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)877      CmpValue = &A.makeNot(*CmpValue);878    Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),879                                A.makeLiteral(false)));880  }881}882 883std::optional<StatementMatcher>884ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {885  if (Options.IgnoreSmartPointerDereference) {886    auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(887        anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),888        unless(hasArgument(0, expr(hasOptionalType()))))));889    return expr(890        anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));891  }892  return std::nullopt;893}894 895StatementMatcher896valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {897  return isOptionalMemberCallWithNameMatcher(hasName("value"),898                                             IgnorableOptional);899}900 901StatementMatcher902valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {903  return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),904                    isOptionalOperatorCallWithName("->", IgnorableOptional)));905}906 907auto buildTransferMatchSwitch() {908  // FIXME: Evaluate the efficiency of matchers. If using matchers results in a909  // lot of duplicated work (e.g. string comparisons), consider providing APIs910  // that avoid it through memoization.911  return CFGMatchSwitchBuilder<LatticeTransferState>()912      // make_optional913      .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)914 915      // optional::optional (in place)916      .CaseOfCFGStmt<CXXConstructExpr>(917          isOptionalInPlaceConstructor(),918          [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,919             LatticeTransferState &State) {920            constructOptionalValue(*E, State.Env,921                                   State.Env.getBoolLiteralValue(true));922          })923      // optional::optional(nullopt_t)924      .CaseOfCFGStmt<CXXConstructExpr>(925          isOptionalNulloptConstructor(),926          [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,927             LatticeTransferState &State) {928            constructOptionalValue(*E, State.Env,929                                   State.Env.getBoolLiteralValue(false));930          })931      // optional::optional (value/conversion)932      .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),933                                       transferValueOrConversionConstructor)934 935      // optional::operator=936      .CaseOfCFGStmt<CXXOperatorCallExpr>(937          isOptionalValueOrConversionAssignment(),938          transferValueOrConversionAssignment)939      .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),940                                          transferNulloptAssignment)941 942      // optional::value943      .CaseOfCFGStmt<CXXMemberCallExpr>(944          valueCall(std::nullopt),945          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,946             LatticeTransferState &State) {947            transferUnwrapCall(E, E->getImplicitObjectArgument(), State);948          })949 950      // optional::operator*951      .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),952                               [](const CallExpr *E,953                                  const MatchFinder::MatchResult &,954                                  LatticeTransferState &State) {955                                 transferUnwrapCall(E, E->getArg(0), State);956                               })957 958      // optional::operator->959      .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),960                               [](const CallExpr *E,961                                  const MatchFinder::MatchResult &,962                                  LatticeTransferState &State) {963                                 transferArrowOpCall(E, E->getArg(0), State);964                               })965 966      // optional::has_value, optional::hasValue967      // Of the supported optionals only folly::Optional uses hasValue, but this968      // will also pass for other types969      .CaseOfCFGStmt<CXXMemberCallExpr>(970          isOptionalMemberCallWithNameMatcher(971              hasAnyName("has_value", "hasValue")),972          transferOptionalHasValueCall)973 974      // optional::operator bool975      .CaseOfCFGStmt<CXXMemberCallExpr>(976          isOptionalMemberCallWithNameMatcher(hasName("operator bool")),977          transferOptionalHasValueCall)978 979      // NullableValue::isNull980      // Only NullableValue has isNull981      .CaseOfCFGStmt<CXXMemberCallExpr>(982          isOptionalMemberCallWithNameMatcher(hasName("isNull")),983          transferOptionalIsNullCall)984 985      // NullableValue::makeValue, NullableValue::makeValueInplace986      // Only NullableValue has these methods, but this987      // will also pass for other types988      .CaseOfCFGStmt<CXXMemberCallExpr>(989          isOptionalMemberCallWithNameMatcher(990              hasAnyName("makeValue", "makeValueInplace")),991          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,992             LatticeTransferState &State) {993            if (RecordStorageLocation *Loc =994                    getImplicitObjectLocation(*E, State.Env)) {995              setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);996            }997          })998 999      // optional::emplace1000      .CaseOfCFGStmt<CXXMemberCallExpr>(1001          isOptionalMemberCallWithNameMatcher(hasName("emplace")),1002          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,1003             LatticeTransferState &State) {1004            if (RecordStorageLocation *Loc =1005                    getImplicitObjectLocation(*E, State.Env)) {1006              setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);1007            }1008          })1009 1010      // optional::reset1011      .CaseOfCFGStmt<CXXMemberCallExpr>(1012          isOptionalMemberCallWithNameMatcher(hasName("reset")),1013          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,1014             LatticeTransferState &State) {1015            if (RecordStorageLocation *Loc =1016                    getImplicitObjectLocation(*E, State.Env)) {1017              setHasValue(*Loc, State.Env.getBoolLiteralValue(false),1018                          State.Env);1019            }1020          })1021 1022      // optional::swap1023      .CaseOfCFGStmt<CXXMemberCallExpr>(1024          isOptionalMemberCallWithNameMatcher(hasName("swap")),1025          transferSwapCall)1026 1027      // std::swap1028      .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)1029 1030      // std::forward1031      .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)1032 1033      // opt.value_or("").empty()1034      .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),1035                           transferValueOrStringEmptyCall)1036 1037      // opt.value_or(X) != X1038      .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)1039 1040      // Comparisons (==, !=):1041      .CaseOfCFGStmt<CXXOperatorCallExpr>(1042          isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),1043          transferOptionalAndOptionalCmp)1044      .CaseOfCFGStmt<CXXOperatorCallExpr>(1045          isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),1046          [](const clang::CXXOperatorCallExpr *Cmp,1047             const MatchFinder::MatchResult &, LatticeTransferState &State) {1048            transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);1049          })1050      .CaseOfCFGStmt<CXXOperatorCallExpr>(1051          isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),1052          [](const clang::CXXOperatorCallExpr *Cmp,1053             const MatchFinder::MatchResult &, LatticeTransferState &State) {1054            transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);1055          })1056      .CaseOfCFGStmt<CXXOperatorCallExpr>(1057          isComparisonOperatorCall(1058              hasOptionalType(),1059              unless(anyOf(hasOptionalType(), hasNulloptType()))),1060          [](const clang::CXXOperatorCallExpr *Cmp,1061             const MatchFinder::MatchResult &, LatticeTransferState &State) {1062            transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);1063          })1064      .CaseOfCFGStmt<CXXOperatorCallExpr>(1065          isComparisonOperatorCall(1066              unless(anyOf(hasOptionalType(), hasNulloptType())),1067              hasOptionalType()),1068          [](const clang::CXXOperatorCallExpr *Cmp,1069             const MatchFinder::MatchResult &, LatticeTransferState &State) {1070            transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);1071          })1072 1073      // Smart-pointer-like operator* and operator-> calls that may look like1074      // const accessors (below) but need special handling to allow mixing1075      // the accessor calls.1076      .CaseOfCFGStmt<CXXOperatorCallExpr>(1077          isSmartPointerLikeOperatorStar(),1078          [](const CXXOperatorCallExpr *E,1079             const MatchFinder::MatchResult &Result,1080             LatticeTransferState &State) {1081            transferSmartPointerLikeCachedDeref(1082                E,1083                dyn_cast_or_null<RecordStorageLocation>(1084                    getLocBehindPossiblePointer(*E->getArg(0), State.Env)),1085                State, [](StorageLocation &Loc) {});1086          })1087      .CaseOfCFGStmt<CXXOperatorCallExpr>(1088          isSmartPointerLikeOperatorArrow(),1089          [](const CXXOperatorCallExpr *E,1090             const MatchFinder::MatchResult &Result,1091             LatticeTransferState &State) {1092            transferSmartPointerLikeCachedGet(1093                E,1094                dyn_cast_or_null<RecordStorageLocation>(1095                    getLocBehindPossiblePointer(*E->getArg(0), State.Env)),1096                State, [](StorageLocation &Loc) {});1097          })1098      .CaseOfCFGStmt<CXXMemberCallExpr>(1099          isSmartPointerLikeValueMethodCall(),1100          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,1101             LatticeTransferState &State) {1102            transferSmartPointerLikeCachedDeref(1103                E, getImplicitObjectLocation(*E, State.Env), State,1104                [](StorageLocation &Loc) {});1105          })1106      .CaseOfCFGStmt<CXXMemberCallExpr>(1107          isSmartPointerLikeGetMethodCall(),1108          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,1109             LatticeTransferState &State) {1110            transferSmartPointerLikeCachedGet(1111                E, getImplicitObjectLocation(*E, State.Env), State,1112                [](StorageLocation &Loc) {});1113          })1114 1115      // const accessor calls1116      .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),1117                                        transferConstMemberCall)1118      .CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(),1119                                          transferConstMemberOperatorCall)1120      // non-const member calls that may modify the state of an object.1121      .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),1122                                        transferValue_NonConstMemberCall)1123      .CaseOfCFGStmt<CXXOperatorCallExpr>(1124          isNonConstMemberOperatorCall(),1125          transferValue_NonConstMemberOperatorCall)1126 1127      // other cases of returning optional1128      .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),1129                               transferCallReturningOptional)1130 1131      .Build();1132}1133 1134llvm::SmallVector<UncheckedOptionalAccessDiagnostic>1135diagnoseUnwrapCall(const Expr *ObjectExpr, const Environment &Env) {1136  if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(1137          getLocBehindPossiblePointer(*ObjectExpr, Env))) {1138    auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));1139    if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {1140      if (Env.proves(HasValueVal->formula()))1141        return {};1142    }1143  }1144 1145  // Record that this unwrap is *not* provably safe.1146  // FIXME: include the name of the optional (if applicable).1147  auto Range = CharSourceRange::getTokenRange(ObjectExpr->getSourceRange());1148  return {UncheckedOptionalAccessDiagnostic{Range}};1149}1150 1151auto buildDiagnoseMatchSwitch(1152    const UncheckedOptionalAccessModelOptions &Options) {1153  // FIXME: Evaluate the efficiency of matchers. If using matchers results in a1154  // lot of duplicated work (e.g. string comparisons), consider providing APIs1155  // that avoid it through memoization.1156  const auto IgnorableOptional = ignorableOptional(Options);1157 1158  auto DiagBuilder =1159      CFGMatchSwitchBuilder<1160          const Environment,1161          llvm::SmallVector<UncheckedOptionalAccessDiagnostic>>()1162          // optional::operator*, optional::operator->1163          .CaseOfCFGStmt<CallExpr>(1164              valueOperatorCall(IgnorableOptional),1165              [](const CallExpr *E, const MatchFinder::MatchResult &,1166                 const Environment &Env) {1167                return diagnoseUnwrapCall(E->getArg(0), Env);1168              });1169 1170  auto Builder = Options.IgnoreValueCalls1171                     ? std::move(DiagBuilder)1172                     : std::move(DiagBuilder)1173                           // optional::value1174                           .CaseOfCFGStmt<CXXMemberCallExpr>(1175                               valueCall(IgnorableOptional),1176                               [](const CXXMemberCallExpr *E,1177                                  const MatchFinder::MatchResult &,1178                                  const Environment &Env) {1179                                 return diagnoseUnwrapCall(1180                                     E->getImplicitObjectArgument(), Env);1181                               });1182 1183  return std::move(Builder).Build();1184}1185 1186} // namespace1187 1188ast_matchers::DeclarationMatcher1189UncheckedOptionalAccessModel::optionalClassDecl() {1190  return cxxRecordDecl(optionalClass());1191}1192 1193UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,1194                                                           Environment &Env)1195    : DataflowAnalysis<UncheckedOptionalAccessModel,1196                       UncheckedOptionalAccessLattice>(Ctx),1197      TransferMatchSwitch(buildTransferMatchSwitch()) {1198  Env.getDataflowAnalysisContext().setSyntheticFieldCallback(1199      [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {1200        const CXXRecordDecl *Optional =1201            getOptionalBaseClass(Ty->getAsCXXRecordDecl());1202        if (Optional == nullptr)1203          return {};1204        return {{"value", valueTypeFromOptionalDecl(*Optional)},1205                {"has_value", Ctx.BoolTy}};1206      });1207}1208 1209void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,1210                                            UncheckedOptionalAccessLattice &L,1211                                            Environment &Env) {1212  LatticeTransferState State(L, Env);1213  TransferMatchSwitch(Elt, getASTContext(), State);1214}1215 1216UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(1217    UncheckedOptionalAccessModelOptions Options)1218    : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}1219 1220} // namespace dataflow1221} // namespace clang1222