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