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