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