xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision 1f6741c1645954b1f4b2fbca470a20081f5e75af)
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 isNonConstMemberCall() {
342   return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst()))));
343 }
344 
345 auto isNonConstMemberOperatorCall() {
346   return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));
347 }
348 
349 auto isCallReturningOptional() {
350   return callExpr(hasType(qualType(
351       anyOf(desugarsToOptionalOrDerivedType(),
352             referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
353 }
354 
355 template <typename L, typename R>
356 auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
357   return cxxOperatorCallExpr(
358       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
359       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
360       hasArgument(1, rhs_arg_matcher));
361 }
362 
363 /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
364 const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
365   auto *Value = Env.get<BoolValue>(Expr);
366   if (Value != nullptr)
367     return Value->formula();
368 
369   Value = &Env.makeAtomicBoolValue();
370   Env.setValue(Expr, *Value);
371   return Value->formula();
372 }
373 
374 StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) {
375   return OptionalLoc.getSyntheticField("has_value");
376 }
377 
378 StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) {
379   return OptionalLoc.getSyntheticField("value");
380 }
381 
382 /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
383 /// property of the optional at `OptionalLoc`.
384 void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,
385                  Environment &Env) {
386   Env.setValue(locForHasValue(OptionalLoc), HasValueVal);
387 }
388 
389 /// Returns the symbolic value that represents the "has_value" property of the
390 /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null.
391 BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {
392   if (OptionalLoc == nullptr)
393     return nullptr;
394   StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
395   auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);
396   if (HasValueVal == nullptr) {
397     HasValueVal = &Env.makeAtomicBoolValue();
398     Env.setValue(HasValueLoc, *HasValueVal);
399   }
400   return HasValueVal;
401 }
402 
403 QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {
404   auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);
405   return CTSD.getTemplateArgs()[0].getAsType();
406 }
407 
408 /// Returns the number of optional wrappers in `Type`.
409 ///
410 /// For example, if `Type` is `optional<optional<int>>`, the result of this
411 /// function will be 2.
412 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
413   const CXXRecordDecl *Optional =
414       getOptionalBaseClass(Type->getAsCXXRecordDecl());
415   if (Optional == nullptr)
416     return 0;
417   return 1 + countOptionalWrappers(
418                  ASTCtx,
419                  valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
420 }
421 
422 StorageLocation *getLocBehindPossiblePointer(const Expr &E,
423                                              const Environment &Env) {
424   if (E.isPRValue()) {
425     if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
426       return &PointerVal->getPointeeLoc();
427     return nullptr;
428   }
429   return Env.getStorageLocation(E);
430 }
431 
432 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
433                         LatticeTransferState &State) {
434   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
435           getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
436     if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)
437       State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
438   }
439 }
440 
441 void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
442                          LatticeTransferState &State) {
443   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
444           getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
445     State.Env.setValue(
446         *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));
447 }
448 
449 void transferMakeOptionalCall(const CallExpr *E,
450                               const MatchFinder::MatchResult &,
451                               LatticeTransferState &State) {
452   setHasValue(State.Env.getResultObjectLocation(*E),
453               State.Env.getBoolLiteralValue(true), State.Env);
454 }
455 
456 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
457                                   const MatchFinder::MatchResult &,
458                                   LatticeTransferState &State) {
459   if (auto *HasValueVal = getHasValue(
460           State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
461     State.Env.setValue(*CallExpr, *HasValueVal);
462   }
463 }
464 
465 void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr,
466                                 const MatchFinder::MatchResult &,
467                                 LatticeTransferState &State) {
468   if (auto *HasValueVal = getHasValue(
469           State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
470     State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal));
471   }
472 }
473 
474 /// `ModelPred` builds a logical formula relating the predicate in
475 /// `ValueOrPredExpr` to the optional's `has_value` property.
476 void transferValueOrImpl(
477     const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
478     LatticeTransferState &State,
479     const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
480                                 const Formula &HasValueVal)) {
481   auto &Env = State.Env;
482 
483   const auto *MCE =
484       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);
485 
486   auto *HasValueVal =
487       getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env));
488   if (HasValueVal == nullptr)
489     return;
490 
491   Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
492                        HasValueVal->formula()));
493 }
494 
495 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
496                                     const MatchFinder::MatchResult &Result,
497                                     LatticeTransferState &State) {
498   return transferValueOrImpl(ComparisonExpr, Result, State,
499                              [](Environment &Env, const Formula &ExprVal,
500                                 const Formula &HasValueVal) -> const Formula & {
501                                auto &A = Env.arena();
502                                // If the result is *not* empty, then we know the
503                                // optional must have been holding a value. If
504                                // `ExprVal` is true, though, we don't learn
505                                // anything definite about `has_value`, so we
506                                // don't add any corresponding implications to
507                                // the flow condition.
508                                return A.makeImplies(A.makeNot(ExprVal),
509                                                     HasValueVal);
510                              });
511 }
512 
513 void transferValueOrNotEqX(const Expr *ComparisonExpr,
514                            const MatchFinder::MatchResult &Result,
515                            LatticeTransferState &State) {
516   transferValueOrImpl(ComparisonExpr, Result, State,
517                       [](Environment &Env, const Formula &ExprVal,
518                          const Formula &HasValueVal) -> const Formula & {
519                         auto &A = Env.arena();
520                         // We know that if `(opt.value_or(X) != X)` then
521                         // `opt.hasValue()`, even without knowing further
522                         // details about the contents of `opt`.
523                         return A.makeImplies(ExprVal, HasValueVal);
524                       });
525 }
526 
527 void transferCallReturningOptional(const CallExpr *E,
528                                    const MatchFinder::MatchResult &Result,
529                                    LatticeTransferState &State) {
530   RecordStorageLocation *Loc = nullptr;
531   if (E->isPRValue()) {
532     Loc = &State.Env.getResultObjectLocation(*E);
533   } else {
534     Loc = State.Env.get<RecordStorageLocation>(*E);
535     if (Loc == nullptr) {
536       Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));
537       State.Env.setStorageLocation(*E, *Loc);
538     }
539   }
540 
541   if (State.Env.getValue(locForHasValue(*Loc)) != nullptr)
542     return;
543 
544   setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
545 }
546 
547 void handleConstMemberCall(const CallExpr *CE,
548                            dataflow::RecordStorageLocation *RecordLoc,
549                            const MatchFinder::MatchResult &Result,
550                            LatticeTransferState &State) {
551   // If the const method returns an optional or reference to an optional.
552   if (RecordLoc != nullptr && isSupportedOptionalType(CE->getType())) {
553     StorageLocation *Loc =
554         State.Lattice.getOrCreateConstMethodReturnStorageLocation(
555             *RecordLoc, CE, State.Env, [&](StorageLocation &Loc) {
556               setHasValue(cast<RecordStorageLocation>(Loc),
557                           State.Env.makeAtomicBoolValue(), State.Env);
558             });
559     if (Loc == nullptr)
560       return;
561     if (CE->isGLValue()) {
562       // If the call to the const method returns a reference to an optional,
563       // link the call expression to the cached StorageLocation.
564       State.Env.setStorageLocation(*CE, *Loc);
565     } else {
566       // If the call to the const method returns an optional by value, we
567       // need to use CopyRecord to link the optional to the result object
568       // of the call expression.
569       auto &ResultLoc = State.Env.getResultObjectLocation(*CE);
570       copyRecord(*cast<RecordStorageLocation>(Loc), ResultLoc, State.Env);
571     }
572     return;
573   }
574 
575   // Cache if the const method returns a boolean type.
576   // We may decide to cache other return types in the future.
577   if (RecordLoc != nullptr && CE->getType()->isBooleanType()) {
578     Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE,
579                                                                  State.Env);
580     if (Val == nullptr)
581       return;
582     State.Env.setValue(*CE, *Val);
583     return;
584   }
585 
586   // Perform default handling if the call returns an optional
587   // but wasn't handled above (if RecordLoc is nullptr).
588   if (isSupportedOptionalType(CE->getType())) {
589     transferCallReturningOptional(CE, Result, State);
590   }
591 }
592 
593 void transferValue_ConstMemberCall(const CXXMemberCallExpr *MCE,
594                                    const MatchFinder::MatchResult &Result,
595                                    LatticeTransferState &State) {
596   handleConstMemberCall(
597       MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
598 }
599 
600 void handleNonConstMemberCall(const CallExpr *CE,
601                               dataflow::RecordStorageLocation *RecordLoc,
602                               const MatchFinder::MatchResult &Result,
603                               LatticeTransferState &State) {
604   if (RecordLoc != nullptr) {
605     // When a non-const member function is called, clear all (non-const)
606     // optional fields of the receiver. Const-qualified fields can't be
607     // changed (at least, not without UB).
608     for (const auto &[Field, FieldLoc] : RecordLoc->children()) {
609       QualType FieldType = Field->getType();
610       if (!FieldType.isConstQualified() &&
611           isSupportedOptionalType(Field->getType())) {
612         auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
613         if (FieldRecordLoc) {
614           setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),
615                       State.Env);
616         }
617       }
618     }
619     State.Lattice.clearConstMethodReturnValues(*RecordLoc);
620     State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc);
621   }
622 
623   // Perform default handling if the call returns an optional.
624   if (isSupportedOptionalType(CE->getType())) {
625     transferCallReturningOptional(CE, Result, State);
626   }
627 }
628 
629 void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE,
630                                       const MatchFinder::MatchResult &Result,
631                                       LatticeTransferState &State) {
632   handleNonConstMemberCall(
633       MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
634 }
635 
636 void transferValue_NonConstMemberOperatorCall(
637     const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result,
638     LatticeTransferState &State) {
639   auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
640       State.Env.getStorageLocation(*OCE->getArg(0)));
641   handleNonConstMemberCall(OCE, RecordLoc, Result, State);
642 }
643 
644 void constructOptionalValue(const Expr &E, Environment &Env,
645                             BoolValue &HasValueVal) {
646   RecordStorageLocation &Loc = Env.getResultObjectLocation(E);
647   setHasValue(Loc, HasValueVal, Env);
648 }
649 
650 /// Returns a symbolic value for the "has_value" property of an `optional<T>`
651 /// value that is constructed/assigned from a value of type `U` or `optional<U>`
652 /// where `T` is constructible from `U`.
653 BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E,
654                                      const MatchFinder::MatchResult &MatchRes,
655                                      LatticeTransferState &State) {
656   const int DestTypeOptionalWrappersCount =
657       countOptionalWrappers(*MatchRes.Context, DestType);
658   const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
659       *MatchRes.Context, E.getType().getNonReferenceType());
660 
661   // Is this an constructor of the form `template<class U> optional(U &&)` /
662   // assignment of the form `template<class U> optional& operator=(U &&)`
663   // (where `T` is assignable / constructible from `U`)?
664   // We recognize this because the number of optionals in the optional being
665   // assigned to is different from the function argument type.
666   if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)
667     return State.Env.getBoolLiteralValue(true);
668 
669   // Otherwise, this must be a constructor of the form
670   // `template <class U> optional<optional<U> &&)` / assignment of the form
671   // `template <class U> optional& operator=(optional<U> &&)
672   // (where, again, `T` is assignable / constructible from `U`).
673   auto *Loc = State.Env.get<RecordStorageLocation>(E);
674   if (auto *HasValueVal = getHasValue(State.Env, Loc))
675     return *HasValueVal;
676   return State.Env.makeAtomicBoolValue();
677 }
678 
679 void transferValueOrConversionConstructor(
680     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
681     LatticeTransferState &State) {
682   assert(E->getNumArgs() > 0);
683 
684   constructOptionalValue(
685       *E, State.Env,
686       valueOrConversionHasValue(
687           E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0),
688           MatchRes, State));
689 }
690 
691 void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
692                         LatticeTransferState &State) {
693   assert(E->getNumArgs() > 0);
694 
695   if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) {
696     setHasValue(*Loc, HasValueVal, State.Env);
697 
698     // Assign a storage location for the whole expression.
699     State.Env.setStorageLocation(*E, *Loc);
700   }
701 }
702 
703 void transferValueOrConversionAssignment(
704     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
705     LatticeTransferState &State) {
706   assert(E->getNumArgs() > 1);
707   transferAssignment(
708       E,
709       valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(),
710                                 *E->getArg(1), MatchRes, State),
711       State);
712 }
713 
714 void transferNulloptAssignment(const CXXOperatorCallExpr *E,
715                                const MatchFinder::MatchResult &,
716                                LatticeTransferState &State) {
717   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
718 }
719 
720 void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
721                   Environment &Env) {
722   // We account for cases where one or both of the optionals are not modeled,
723   // either lacking associated storage locations, or lacking values associated
724   // to such storage locations.
725 
726   if (Loc1 == nullptr) {
727     if (Loc2 != nullptr)
728       setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env);
729     return;
730   }
731   if (Loc2 == nullptr) {
732     setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env);
733     return;
734   }
735 
736   // Both expressions have locations, though they may not have corresponding
737   // values. In that case, we create a fresh value at this point. Note that if
738   // two branches both do this, they will not share the value, but it at least
739   // allows for local reasoning about the value. To avoid the above, we would
740   // need *lazy* value allocation.
741   // FIXME: allocate values lazily, instead of just creating a fresh value.
742   BoolValue *BoolVal1 = getHasValue(Env, Loc1);
743   if (BoolVal1 == nullptr)
744     BoolVal1 = &Env.makeAtomicBoolValue();
745 
746   BoolValue *BoolVal2 = getHasValue(Env, Loc2);
747   if (BoolVal2 == nullptr)
748     BoolVal2 = &Env.makeAtomicBoolValue();
749 
750   setHasValue(*Loc1, *BoolVal2, Env);
751   setHasValue(*Loc2, *BoolVal1, Env);
752 }
753 
754 void transferSwapCall(const CXXMemberCallExpr *E,
755                       const MatchFinder::MatchResult &,
756                       LatticeTransferState &State) {
757   assert(E->getNumArgs() == 1);
758   auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
759   transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
760 }
761 
762 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
763                          LatticeTransferState &State) {
764   assert(E->getNumArgs() == 2);
765   auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
766   auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1));
767   transferSwap(Arg0Loc, Arg1Loc, State.Env);
768 }
769 
770 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
771                             LatticeTransferState &State) {
772   assert(E->getNumArgs() == 1);
773 
774   if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
775     State.Env.setStorageLocation(*E, *Loc);
776 }
777 
778 const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
779                                 const Formula &LHS, const Formula &RHS) {
780   // Logically, an optional<T> object is composed of two values - a `has_value`
781   // bit and a value of type T. Equality of optional objects compares both
782   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
783   // when two optional objects are engaged, the equality of their respective
784   // values of type T matters. Since we only track the `has_value` bits, we
785   // can't make any conclusions about equality when we know that two optional
786   // objects are engaged.
787   //
788   // We express this as two facts about the equality:
789   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
790   //    If they are equal, then either both are set or both are unset.
791   // b) (!LHS & !RHS) => EqVal
792   //    If neither is set, then they are equal.
793   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
794   return A.makeAnd(
795       A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
796                                     A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
797       A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
798 }
799 
800 void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
801                                     const MatchFinder::MatchResult &,
802                                     LatticeTransferState &State) {
803   Environment &Env = State.Env;
804   auto &A = Env.arena();
805   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
806   auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0));
807   if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {
808     auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1));
809     if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {
810       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
811         CmpValue = &A.makeNot(*CmpValue);
812       Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
813                                   RHasVal->formula()));
814     }
815   }
816 }
817 
818 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
819                                  const clang::Expr *E, Environment &Env) {
820   auto &A = Env.arena();
821   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
822   auto *Loc = Env.get<RecordStorageLocation>(*E);
823   if (auto *HasVal = getHasValue(Env, Loc)) {
824     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
825       CmpValue = &A.makeNot(*CmpValue);
826     Env.assume(
827         evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
828   }
829 }
830 
831 void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,
832                                    const clang::Expr *E, Environment &Env) {
833   auto &A = Env.arena();
834   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
835   auto *Loc = Env.get<RecordStorageLocation>(*E);
836   if (auto *HasVal = getHasValue(Env, Loc)) {
837     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
838       CmpValue = &A.makeNot(*CmpValue);
839     Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
840                                 A.makeLiteral(false)));
841   }
842 }
843 
844 std::optional<StatementMatcher>
845 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
846   if (Options.IgnoreSmartPointerDereference) {
847     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
848         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
849         unless(hasArgument(0, expr(hasOptionalType()))))));
850     return expr(
851         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
852   }
853   return std::nullopt;
854 }
855 
856 StatementMatcher
857 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
858   return isOptionalMemberCallWithNameMatcher(hasName("value"),
859                                              IgnorableOptional);
860 }
861 
862 StatementMatcher
863 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
864   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
865                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
866 }
867 
868 auto buildTransferMatchSwitch() {
869   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
870   // lot of duplicated work (e.g. string comparisons), consider providing APIs
871   // that avoid it through memoization.
872   return CFGMatchSwitchBuilder<LatticeTransferState>()
873       // make_optional
874       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
875 
876       // optional::optional (in place)
877       .CaseOfCFGStmt<CXXConstructExpr>(
878           isOptionalInPlaceConstructor(),
879           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
880              LatticeTransferState &State) {
881             constructOptionalValue(*E, State.Env,
882                                    State.Env.getBoolLiteralValue(true));
883           })
884       // optional::optional(nullopt_t)
885       .CaseOfCFGStmt<CXXConstructExpr>(
886           isOptionalNulloptConstructor(),
887           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
888              LatticeTransferState &State) {
889             constructOptionalValue(*E, State.Env,
890                                    State.Env.getBoolLiteralValue(false));
891           })
892       // optional::optional (value/conversion)
893       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
894                                        transferValueOrConversionConstructor)
895 
896       // optional::operator=
897       .CaseOfCFGStmt<CXXOperatorCallExpr>(
898           isOptionalValueOrConversionAssignment(),
899           transferValueOrConversionAssignment)
900       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
901                                           transferNulloptAssignment)
902 
903       // optional::value
904       .CaseOfCFGStmt<CXXMemberCallExpr>(
905           valueCall(std::nullopt),
906           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
907              LatticeTransferState &State) {
908             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
909           })
910 
911       // optional::operator*
912       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
913                                [](const CallExpr *E,
914                                   const MatchFinder::MatchResult &,
915                                   LatticeTransferState &State) {
916                                  transferUnwrapCall(E, E->getArg(0), State);
917                                })
918 
919       // optional::operator->
920       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
921                                [](const CallExpr *E,
922                                   const MatchFinder::MatchResult &,
923                                   LatticeTransferState &State) {
924                                  transferArrowOpCall(E, E->getArg(0), State);
925                                })
926 
927       // optional::has_value, optional::hasValue
928       // Of the supported optionals only folly::Optional uses hasValue, but this
929       // will also pass for other types
930       .CaseOfCFGStmt<CXXMemberCallExpr>(
931           isOptionalMemberCallWithNameMatcher(
932               hasAnyName("has_value", "hasValue")),
933           transferOptionalHasValueCall)
934 
935       // optional::operator bool
936       .CaseOfCFGStmt<CXXMemberCallExpr>(
937           isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
938           transferOptionalHasValueCall)
939 
940       // NullableValue::isNull
941       // Only NullableValue has isNull
942       .CaseOfCFGStmt<CXXMemberCallExpr>(
943           isOptionalMemberCallWithNameMatcher(hasName("isNull")),
944           transferOptionalIsNullCall)
945 
946       // optional::emplace
947       .CaseOfCFGStmt<CXXMemberCallExpr>(
948           isOptionalMemberCallWithNameMatcher(hasName("emplace")),
949           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
950              LatticeTransferState &State) {
951             if (RecordStorageLocation *Loc =
952                     getImplicitObjectLocation(*E, State.Env)) {
953               setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);
954             }
955           })
956 
957       // optional::reset
958       .CaseOfCFGStmt<CXXMemberCallExpr>(
959           isOptionalMemberCallWithNameMatcher(hasName("reset")),
960           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
961              LatticeTransferState &State) {
962             if (RecordStorageLocation *Loc =
963                     getImplicitObjectLocation(*E, State.Env)) {
964               setHasValue(*Loc, State.Env.getBoolLiteralValue(false),
965                           State.Env);
966             }
967           })
968 
969       // optional::swap
970       .CaseOfCFGStmt<CXXMemberCallExpr>(
971           isOptionalMemberCallWithNameMatcher(hasName("swap")),
972           transferSwapCall)
973 
974       // std::swap
975       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
976 
977       // std::forward
978       .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
979 
980       // opt.value_or("").empty()
981       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
982                            transferValueOrStringEmptyCall)
983 
984       // opt.value_or(X) != X
985       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
986 
987       // Comparisons (==, !=):
988       .CaseOfCFGStmt<CXXOperatorCallExpr>(
989           isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
990           transferOptionalAndOptionalCmp)
991       .CaseOfCFGStmt<CXXOperatorCallExpr>(
992           isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
993           [](const clang::CXXOperatorCallExpr *Cmp,
994              const MatchFinder::MatchResult &, LatticeTransferState &State) {
995             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);
996           })
997       .CaseOfCFGStmt<CXXOperatorCallExpr>(
998           isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
999           [](const clang::CXXOperatorCallExpr *Cmp,
1000              const MatchFinder::MatchResult &, LatticeTransferState &State) {
1001             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);
1002           })
1003       .CaseOfCFGStmt<CXXOperatorCallExpr>(
1004           isComparisonOperatorCall(
1005               hasOptionalType(),
1006               unless(anyOf(hasOptionalType(), hasNulloptType()))),
1007           [](const clang::CXXOperatorCallExpr *Cmp,
1008              const MatchFinder::MatchResult &, LatticeTransferState &State) {
1009             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
1010           })
1011       .CaseOfCFGStmt<CXXOperatorCallExpr>(
1012           isComparisonOperatorCall(
1013               unless(anyOf(hasOptionalType(), hasNulloptType())),
1014               hasOptionalType()),
1015           [](const clang::CXXOperatorCallExpr *Cmp,
1016              const MatchFinder::MatchResult &, LatticeTransferState &State) {
1017             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
1018           })
1019 
1020       // const accessor calls
1021       .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),
1022                                         transferValue_ConstMemberCall)
1023       // non-const member calls that may modify the state of an object.
1024       .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
1025                                         transferValue_NonConstMemberCall)
1026       .CaseOfCFGStmt<CXXOperatorCallExpr>(
1027           isNonConstMemberOperatorCall(),
1028           transferValue_NonConstMemberOperatorCall)
1029 
1030       // other cases of returning optional
1031       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
1032                                transferCallReturningOptional)
1033 
1034       .Build();
1035 }
1036 
1037 llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
1038                                                      const Environment &Env) {
1039   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
1040           getLocBehindPossiblePointer(*ObjectExpr, Env))) {
1041     auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
1042     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
1043       if (Env.proves(HasValueVal->formula()))
1044         return {};
1045     }
1046   }
1047 
1048   // Record that this unwrap is *not* provably safe.
1049   // FIXME: include either the name of the optional (if applicable) or a source
1050   // range of the access for easier interpretation of the result.
1051   return {ObjectExpr->getBeginLoc()};
1052 }
1053 
1054 auto buildDiagnoseMatchSwitch(
1055     const UncheckedOptionalAccessModelOptions &Options) {
1056   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
1057   // lot of duplicated work (e.g. string comparisons), consider providing APIs
1058   // that avoid it through memoization.
1059   auto IgnorableOptional = ignorableOptional(Options);
1060   return CFGMatchSwitchBuilder<const Environment,
1061                                llvm::SmallVector<SourceLocation>>()
1062       // optional::value
1063       .CaseOfCFGStmt<CXXMemberCallExpr>(
1064           valueCall(IgnorableOptional),
1065           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
1066              const Environment &Env) {
1067             return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
1068           })
1069 
1070       // optional::operator*, optional::operator->
1071       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
1072                                [](const CallExpr *E,
1073                                   const MatchFinder::MatchResult &,
1074                                   const Environment &Env) {
1075                                  return diagnoseUnwrapCall(E->getArg(0), Env);
1076                                })
1077       .Build();
1078 }
1079 
1080 } // namespace
1081 
1082 ast_matchers::DeclarationMatcher
1083 UncheckedOptionalAccessModel::optionalClassDecl() {
1084   return cxxRecordDecl(optionalClass());
1085 }
1086 
1087 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
1088                                                            Environment &Env)
1089     : DataflowAnalysis<UncheckedOptionalAccessModel,
1090                        UncheckedOptionalAccessLattice>(Ctx),
1091       TransferMatchSwitch(buildTransferMatchSwitch()) {
1092   Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
1093       [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
1094         const CXXRecordDecl *Optional =
1095             getOptionalBaseClass(Ty->getAsCXXRecordDecl());
1096         if (Optional == nullptr)
1097           return {};
1098         return {{"value", valueTypeFromOptionalDecl(*Optional)},
1099                 {"has_value", Ctx.BoolTy}};
1100       });
1101 }
1102 
1103 void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
1104                                             UncheckedOptionalAccessLattice &L,
1105                                             Environment &Env) {
1106   LatticeTransferState State(L, Env);
1107   TransferMatchSwitch(Elt, getASTContext(), State);
1108 }
1109 
1110 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
1111     UncheckedOptionalAccessModelOptions Options)
1112     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
1113 
1114 } // namespace dataflow
1115 } // namespace clang
1116