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