xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision 0ad011ececb978e22a9bff2acf76633b094f1ff6)
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 #include <vector>
38 
39 namespace clang {
40 namespace dataflow {
41 
42 static bool isTopLevelNamespaceWithName(const NamespaceDecl &NS,
43                                         llvm::StringRef Name) {
44   return NS.getDeclName().isIdentifier() && NS.getName() == Name &&
45          NS.getParent() != nullptr && NS.getParent()->isTranslationUnit();
46 }
47 
48 static bool hasOptionalClassName(const CXXRecordDecl &RD) {
49   if (!RD.getDeclName().isIdentifier())
50     return false;
51 
52   if (RD.getName() == "optional") {
53     if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
54       return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl");
55     return false;
56   }
57 
58   if (RD.getName() == "Optional") {
59     // Check whether namespace is "::base" or "::folly".
60     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
61     return N != nullptr && (isTopLevelNamespaceWithName(*N, "base") ||
62                             isTopLevelNamespaceWithName(*N, "folly"));
63   }
64 
65   return false;
66 }
67 
68 namespace {
69 
70 using namespace ::clang::ast_matchers;
71 using LatticeTransferState = TransferState<NoopLattice>;
72 
73 AST_MATCHER(CXXRecordDecl, hasOptionalClassNameMatcher) {
74   return hasOptionalClassName(Node);
75 }
76 
77 DeclarationMatcher optionalClass() {
78   return classTemplateSpecializationDecl(
79       hasOptionalClassNameMatcher(),
80       hasTemplateArgument(0, refersToType(type().bind("T"))));
81 }
82 
83 auto optionalOrAliasType() {
84   return hasUnqualifiedDesugaredType(
85       recordType(hasDeclaration(optionalClass())));
86 }
87 
88 /// Matches any of the spellings of the optional types and sugar, aliases, etc.
89 auto hasOptionalType() { return hasType(optionalOrAliasType()); }
90 
91 auto isOptionalMemberCallWithNameMatcher(
92     ast_matchers::internal::Matcher<NamedDecl> matcher,
93     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
94   auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr()))
95                                     : cxxThisExpr());
96   return cxxMemberCallExpr(
97       on(expr(Exception,
98               anyOf(hasOptionalType(),
99                     hasType(pointerType(pointee(optionalOrAliasType())))))),
100       callee(cxxMethodDecl(matcher)));
101 }
102 
103 auto isOptionalOperatorCallWithName(
104     llvm::StringRef operator_name,
105     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
106   return cxxOperatorCallExpr(
107       hasOverloadedOperatorName(operator_name),
108       callee(cxxMethodDecl(ofClass(optionalClass()))),
109       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
110 }
111 
112 auto isMakeOptionalCall() {
113   return callExpr(callee(functionDecl(hasAnyName(
114                       "std::make_optional", "base::make_optional",
115                       "absl::make_optional", "folly::make_optional"))),
116                   hasOptionalType());
117 }
118 
119 auto nulloptTypeDecl() {
120   return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",
121                               "base::nullopt_t", "folly::None"));
122 }
123 
124 auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
125 
126 // `optional` or `nullopt_t`
127 auto hasAnyOptionalType() {
128   return hasType(hasUnqualifiedDesugaredType(
129       recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass())))));
130 }
131 
132 auto inPlaceClass() {
133   return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
134                                "base::in_place_t", "folly::in_place_t"));
135 }
136 
137 auto isOptionalNulloptConstructor() {
138   return cxxConstructExpr(
139       hasOptionalType(),
140       hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
141                                         hasParameter(0, hasNulloptType()))));
142 }
143 
144 auto isOptionalInPlaceConstructor() {
145   return cxxConstructExpr(hasOptionalType(),
146                           hasArgument(0, hasType(inPlaceClass())));
147 }
148 
149 auto isOptionalValueOrConversionConstructor() {
150   return cxxConstructExpr(
151       hasOptionalType(),
152       unless(hasDeclaration(
153           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
154       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
155 }
156 
157 auto isOptionalValueOrConversionAssignment() {
158   return cxxOperatorCallExpr(
159       hasOverloadedOperatorName("="),
160       callee(cxxMethodDecl(ofClass(optionalClass()))),
161       unless(hasDeclaration(cxxMethodDecl(
162           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
163       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
164 }
165 
166 auto isNulloptConstructor() {
167   return cxxConstructExpr(hasNulloptType(), argumentCountIs(1),
168                           hasArgument(0, hasNulloptType()));
169 }
170 
171 auto isOptionalNulloptAssignment() {
172   return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
173                              callee(cxxMethodDecl(ofClass(optionalClass()))),
174                              argumentCountIs(2),
175                              hasArgument(1, hasNulloptType()));
176 }
177 
178 auto isStdSwapCall() {
179   return callExpr(callee(functionDecl(hasName("std::swap"))),
180                   argumentCountIs(2), hasArgument(0, hasOptionalType()),
181                   hasArgument(1, hasOptionalType()));
182 }
183 
184 auto isStdForwardCall() {
185   return callExpr(callee(functionDecl(hasName("std::forward"))),
186                   argumentCountIs(1), hasArgument(0, hasOptionalType()));
187 }
188 
189 constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
190 
191 auto isValueOrStringEmptyCall() {
192   // `opt.value_or("").empty()`
193   return cxxMemberCallExpr(
194       callee(cxxMethodDecl(hasName("empty"))),
195       onImplicitObjectArgument(ignoringImplicit(
196           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
197                             callee(cxxMethodDecl(hasName("value_or"),
198                                                  ofClass(optionalClass()))),
199                             hasArgument(0, stringLiteral(hasSize(0))))
200               .bind(ValueOrCallID))));
201 }
202 
203 auto isValueOrNotEqX() {
204   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
205     return hasOperands(
206         ignoringImplicit(
207             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
208                               callee(cxxMethodDecl(hasName("value_or"),
209                                                    ofClass(optionalClass()))),
210                               hasArgument(0, Arg))
211                 .bind(ValueOrCallID)),
212         ignoringImplicit(Arg));
213   };
214 
215   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
216   // support this pattern for any expression, but the AST does not have a
217   // generic expression comparison facility, so we specialize to common cases
218   // seen in practice.  FIXME: define a matcher that compares values across
219   // nodes, which would let us generalize this to any `X`.
220   return binaryOperation(hasOperatorName("!="),
221                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
222                                ComparesToSame(stringLiteral(hasSize(0))),
223                                ComparesToSame(integerLiteral(equals(0)))));
224 }
225 
226 auto isCallReturningOptional() {
227   return callExpr(hasType(qualType(anyOf(
228       optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
229 }
230 
231 template <typename L, typename R>
232 auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
233   return cxxOperatorCallExpr(
234       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
235       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
236       hasArgument(1, rhs_arg_matcher));
237 }
238 
239 /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
240 const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
241   auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None));
242   if (Value != nullptr)
243     return Value->formula();
244 
245   auto &Loc = Env.createStorageLocation(Expr);
246   Value = &Env.makeAtomicBoolValue();
247   Env.setValue(Loc, *Value);
248   Env.setStorageLocation(Expr, Loc);
249   return Value->formula();
250 }
251 
252 /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
253 /// property of the optional value `OptionalVal`.
254 void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
255   OptionalVal.setProperty("has_value", HasValueVal);
256 }
257 
258 /// Creates a symbolic value for an `optional` value at an existing storage
259 /// location. Uses `HasValueVal` as the symbolic value of the "has_value"
260 /// property.
261 StructValue &createOptionalValue(AggregateStorageLocation &Loc,
262                                  BoolValue &HasValueVal, Environment &Env) {
263   auto &OptionalVal = Env.create<StructValue>(Loc);
264   Env.setValue(Loc, OptionalVal);
265   setHasValue(OptionalVal, HasValueVal);
266   return OptionalVal;
267 }
268 
269 /// Returns the symbolic value that represents the "has_value" property of the
270 /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
271 BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
272   if (OptionalVal != nullptr) {
273     auto *HasValueVal =
274         cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
275     if (HasValueVal == nullptr) {
276       HasValueVal = &Env.makeAtomicBoolValue();
277       OptionalVal->setProperty("has_value", *HasValueVal);
278     }
279     return HasValueVal;
280   }
281   return nullptr;
282 }
283 
284 /// Returns true if and only if `Type` is an optional type.
285 bool isOptionalType(QualType Type) {
286   if (!Type->isRecordType())
287     return false;
288   const CXXRecordDecl *D = Type->getAsCXXRecordDecl();
289   return D != nullptr && hasOptionalClassName(*D);
290 }
291 
292 /// Returns the number of optional wrappers in `Type`.
293 ///
294 /// For example, if `Type` is `optional<optional<int>>`, the result of this
295 /// function will be 2.
296 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
297   if (!isOptionalType(Type))
298     return 0;
299   return 1 + countOptionalWrappers(
300                  ASTCtx,
301                  cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
302                      ->getTemplateArgs()
303                      .get(0)
304                      .getAsType()
305                      .getDesugaredType(ASTCtx));
306 }
307 
308 /// Tries to initialize the `optional`'s value (that is, contents), and return
309 /// its location. Returns nullptr if the value can't be represented.
310 StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
311                                                     Value &OptionalVal,
312                                                     Environment &Env) {
313   // The "value" property represents a synthetic field. As such, it needs
314   // `StorageLocation`, like normal fields (and other variables). So, we model
315   // it with a `PointerValue`, since that includes a storage location.  Once
316   // the property is set, it will be shared by all environments that access the
317   // `Value` representing the optional (here, `OptionalVal`).
318   if (auto *ValueProp = OptionalVal.getProperty("value")) {
319     auto *ValuePtr = clang::cast<PointerValue>(ValueProp);
320     auto &ValueLoc = ValuePtr->getPointeeLoc();
321     if (Env.getValue(ValueLoc) != nullptr)
322       return &ValueLoc;
323 
324     // The property was previously set, but the value has been lost. This can
325     // happen in various situations, for example:
326     // - Because of an environment merge (where the two environments mapped the
327     //   property to different values, which resulted in them both being
328     //   discarded).
329     // - When two blocks in the CFG, with neither a dominator of the other,
330     //   visit the same optional value. (FIXME: This is something we can and
331     //   should fix -- see also the lengthy FIXME below.)
332     // - Or even when a block is revisited during testing to collect
333     //   per-statement state.
334     // FIXME: This situation means that the optional contents are not shared
335     // between branches and the like. Practically, this lack of sharing
336     // reduces the precision of the model when the contents are relevant to
337     // the check, like another optional or a boolean that influences control
338     // flow.
339     if (ValueLoc.getType()->isRecordType()) {
340       refreshStructValue(cast<AggregateStorageLocation>(ValueLoc), Env);
341       return &ValueLoc;
342     } else {
343       auto *ValueVal = Env.createValue(ValueLoc.getType());
344       if (ValueVal == nullptr)
345         return nullptr;
346       Env.setValue(ValueLoc, *ValueVal);
347       return &ValueLoc;
348     }
349   }
350 
351   auto Ty = Q.getNonReferenceType();
352   auto &ValueLoc = Env.createObject(Ty);
353   auto &ValuePtr = Env.create<PointerValue>(ValueLoc);
354   // FIXME:
355   // The change we make to the `value` property below may become visible to
356   // other blocks that aren't successors of the current block and therefore
357   // don't see the change we made above mapping `ValueLoc` to `ValueVal`. For
358   // example:
359   //
360   //   void target(optional<int> oo, bool b) {
361   //     // `oo` is associated with a `StructValue` here, which we will call
362   //     // `OptionalVal`.
363   //
364   //     // The `has_value` property is set on `OptionalVal` (but not the
365   //     // `value` property yet).
366   //     if (!oo.has_value()) return;
367   //
368   //     if (b) {
369   //       // Let's assume we transfer the `if` branch first.
370   //       //
371   //       // This causes us to call `maybeInitializeOptionalValueMember()`,
372   //       // which causes us to set the `value` property on `OptionalVal`
373   //       // (which had not been set until this point). This `value` property
374   //       // refers to a `PointerValue`, which in turn refers to a
375   //       // StorageLocation` that is associated to an `IntegerValue`.
376   //       oo.value();
377   //     } else {
378   //       // Let's assume we transfer the `else` branch after the `if` branch.
379   //       //
380   //       // We see the `value` property that the `if` branch set on
381   //       // `OptionalVal`, but in the environment for this block, the
382   //       // `StorageLocation` in the `PointerValue` is not associated with any
383   //       // `Value`.
384   //       oo.value();
385   //     }
386   //   }
387   //
388   // This situation is currently "saved" by the code above that checks whether
389   // the `value` property is already set, and if, the `ValueLoc` is not
390   // associated with a `ValueVal`, creates a new `ValueVal`.
391   //
392   // However, what we should really do is to make sure that the change to the
393   // `value` property does not "leak" to other blocks that are not successors
394   // of this block. To do this, instead of simply setting the `value` property
395   // on the existing `OptionalVal`, we should create a new `Value` for the
396   // optional, set the property on that, and associate the storage location that
397   // is currently associated with the existing `OptionalVal` with the newly
398   // created `Value` instead.
399   OptionalVal.setProperty("value", ValuePtr);
400   return &ValueLoc;
401 }
402 
403 void initializeOptionalReference(const Expr *OptionalExpr,
404                                  const MatchFinder::MatchResult &,
405                                  LatticeTransferState &State) {
406   if (auto *OptionalVal =
407           State.Env.getValue(*OptionalExpr, SkipPast::Reference)) {
408     if (OptionalVal->getProperty("has_value") == nullptr) {
409       setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
410     }
411   }
412 }
413 
414 /// Returns true if and only if `OptionalVal` is initialized and known to be
415 /// empty in `Env`.
416 bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
417   auto *HasValueVal =
418       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
419   return HasValueVal != nullptr &&
420          Env.flowConditionImplies(Env.arena().makeNot(HasValueVal->formula()));
421 }
422 
423 /// Returns true if and only if `OptionalVal` is initialized and known to be
424 /// non-empty in `Env`.
425 bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
426   auto *HasValueVal =
427       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
428   return HasValueVal != nullptr &&
429          Env.flowConditionImplies(HasValueVal->formula());
430 }
431 
432 Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) {
433   Value *Val = Env.getValue(E, SkipPast::Reference);
434   if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Val))
435     return Env.getValue(PointerVal->getPointeeLoc());
436   return Val;
437 }
438 
439 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
440                         LatticeTransferState &State) {
441   if (auto *OptionalVal =
442           getValueBehindPossiblePointer(*ObjectExpr, State.Env)) {
443     if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
444       if (auto *Loc = maybeInitializeOptionalValueMember(
445               UnwrapExpr->getType(), *OptionalVal, State.Env))
446         State.Env.setStorageLocation(*UnwrapExpr, *Loc);
447   }
448 }
449 
450 void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
451                          LatticeTransferState &State) {
452   if (auto *OptionalVal =
453           getValueBehindPossiblePointer(*ObjectExpr, State.Env)) {
454     if (auto *Loc = maybeInitializeOptionalValueMember(
455             UnwrapExpr->getType()->getPointeeType(), *OptionalVal, State.Env)) {
456       State.Env.setValueStrict(*UnwrapExpr,
457                                State.Env.create<PointerValue>(*Loc));
458     }
459   }
460 }
461 
462 void transferMakeOptionalCall(const CallExpr *E,
463                               const MatchFinder::MatchResult &,
464                               LatticeTransferState &State) {
465   createOptionalValue(State.Env.getResultObjectLocation(*E),
466                       State.Env.getBoolLiteralValue(true), State.Env);
467 }
468 
469 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
470                                   const MatchFinder::MatchResult &,
471                                   LatticeTransferState &State) {
472   if (auto *HasValueVal = getHasValue(
473           State.Env, getValueBehindPossiblePointer(
474                          *CallExpr->getImplicitObjectArgument(), State.Env))) {
475     auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
476     State.Env.setValue(CallExprLoc, *HasValueVal);
477     State.Env.setStorageLocation(*CallExpr, CallExprLoc);
478   }
479 }
480 
481 /// `ModelPred` builds a logical formula relating the predicate in
482 /// `ValueOrPredExpr` to the optional's `has_value` property.
483 void transferValueOrImpl(
484     const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
485     LatticeTransferState &State,
486     const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
487                                 const Formula &HasValueVal)) {
488   auto &Env = State.Env;
489 
490   const auto *ObjectArgumentExpr =
491       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
492           ->getImplicitObjectArgument();
493 
494   auto *HasValueVal = getHasValue(
495       State.Env, getValueBehindPossiblePointer(*ObjectArgumentExpr, State.Env));
496   if (HasValueVal == nullptr)
497     return;
498 
499   Env.addToFlowCondition(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
500                                    HasValueVal->formula()));
501 }
502 
503 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
504                                     const MatchFinder::MatchResult &Result,
505                                     LatticeTransferState &State) {
506   return transferValueOrImpl(ComparisonExpr, Result, State,
507                              [](Environment &Env, const Formula &ExprVal,
508                                 const Formula &HasValueVal) -> const Formula & {
509                                auto &A = Env.arena();
510                                // If the result is *not* empty, then we know the
511                                // optional must have been holding a value. If
512                                // `ExprVal` is true, though, we don't learn
513                                // anything definite about `has_value`, so we
514                                // don't add any corresponding implications to
515                                // the flow condition.
516                                return A.makeImplies(A.makeNot(ExprVal),
517                                                     HasValueVal);
518                              });
519 }
520 
521 void transferValueOrNotEqX(const Expr *ComparisonExpr,
522                            const MatchFinder::MatchResult &Result,
523                            LatticeTransferState &State) {
524   transferValueOrImpl(ComparisonExpr, Result, State,
525                       [](Environment &Env, const Formula &ExprVal,
526                          const Formula &HasValueVal) -> const Formula & {
527                         auto &A = Env.arena();
528                         // We know that if `(opt.value_or(X) != X)` then
529                         // `opt.hasValue()`, even without knowing further
530                         // details about the contents of `opt`.
531                         return A.makeImplies(ExprVal, HasValueVal);
532                       });
533 }
534 
535 void transferCallReturningOptional(const CallExpr *E,
536                                    const MatchFinder::MatchResult &Result,
537                                    LatticeTransferState &State) {
538   if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
539     return;
540 
541   AggregateStorageLocation *Loc = nullptr;
542   if (E->isPRValue()) {
543     Loc = &State.Env.getResultObjectLocation(*E);
544   } else {
545     Loc = &cast<AggregateStorageLocation>(State.Env.createStorageLocation(*E));
546     State.Env.setStorageLocationStrict(*E, *Loc);
547   }
548 
549   createOptionalValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
550 }
551 
552 void constructOptionalValue(const Expr &E, Environment &Env,
553                             BoolValue &HasValueVal) {
554   AggregateStorageLocation &Loc = Env.getResultObjectLocation(E);
555   Env.setValueStrict(E, createOptionalValue(Loc, HasValueVal, Env));
556 }
557 
558 /// Returns a symbolic value for the "has_value" property of an `optional<T>`
559 /// value that is constructed/assigned from a value of type `U` or `optional<U>`
560 /// where `T` is constructible from `U`.
561 BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
562                                      const MatchFinder::MatchResult &MatchRes,
563                                      LatticeTransferState &State) {
564   assert(F.getTemplateSpecializationArgs() != nullptr);
565   assert(F.getTemplateSpecializationArgs()->size() > 0);
566 
567   const int TemplateParamOptionalWrappersCount =
568       countOptionalWrappers(*MatchRes.Context, F.getTemplateSpecializationArgs()
569                                                    ->get(0)
570                                                    .getAsType()
571                                                    .getNonReferenceType());
572   const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
573       *MatchRes.Context, E.getType().getNonReferenceType());
574 
575   // Check if this is a constructor/assignment call for `optional<T>` with
576   // argument of type `U` such that `T` is constructible from `U`.
577   if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
578     return State.Env.getBoolLiteralValue(true);
579 
580   // This is a constructor/assignment call for `optional<T>` with argument of
581   // type `optional<U>` such that `T` is constructible from `U`.
582   if (auto *HasValueVal =
583           getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference)))
584     return *HasValueVal;
585   return State.Env.makeAtomicBoolValue();
586 }
587 
588 void transferValueOrConversionConstructor(
589     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
590     LatticeTransferState &State) {
591   assert(E->getNumArgs() > 0);
592 
593   constructOptionalValue(*E, State.Env,
594                          valueOrConversionHasValue(*E->getConstructor(),
595                                                    *E->getArg(0), MatchRes,
596                                                    State));
597 }
598 
599 void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
600                         LatticeTransferState &State) {
601   assert(E->getNumArgs() > 0);
602 
603   if (auto *Loc = cast<AggregateStorageLocation>(
604           State.Env.getStorageLocationStrict(*E->getArg(0)))) {
605     createOptionalValue(*Loc, HasValueVal, State.Env);
606 
607     // Assign a storage location for the whole expression.
608     State.Env.setStorageLocationStrict(*E, *Loc);
609   }
610 }
611 
612 void transferValueOrConversionAssignment(
613     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
614     LatticeTransferState &State) {
615   assert(E->getNumArgs() > 1);
616   transferAssignment(E,
617                      valueOrConversionHasValue(*E->getDirectCallee(),
618                                                *E->getArg(1), MatchRes, State),
619                      State);
620 }
621 
622 void transferNulloptAssignment(const CXXOperatorCallExpr *E,
623                                const MatchFinder::MatchResult &,
624                                LatticeTransferState &State) {
625   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
626 }
627 
628 void transferSwap(AggregateStorageLocation *Loc1,
629                   AggregateStorageLocation *Loc2, Environment &Env) {
630   // We account for cases where one or both of the optionals are not modeled,
631   // either lacking associated storage locations, or lacking values associated
632   // to such storage locations.
633 
634   if (Loc1 == nullptr) {
635     if (Loc2 != nullptr)
636       createOptionalValue(*Loc2, Env.makeAtomicBoolValue(), Env);
637     return;
638   }
639   if (Loc2 == nullptr) {
640     createOptionalValue(*Loc1, Env.makeAtomicBoolValue(), Env);
641     return;
642   }
643 
644   // Both expressions have locations, though they may not have corresponding
645   // values. In that case, we create a fresh value at this point. Note that if
646   // two branches both do this, they will not share the value, but it at least
647   // allows for local reasoning about the value. To avoid the above, we would
648   // need *lazy* value allocation.
649   // FIXME: allocate values lazily, instead of just creating a fresh value.
650   BoolValue *BoolVal1 = getHasValue(Env, Env.getValue(*Loc1));
651   if (BoolVal1 == nullptr)
652     BoolVal1 = &Env.makeAtomicBoolValue();
653 
654   BoolValue *BoolVal2 = getHasValue(Env, Env.getValue(*Loc2));
655   if (BoolVal2 == nullptr)
656     BoolVal2 = &Env.makeAtomicBoolValue();
657 
658   createOptionalValue(*Loc1, *BoolVal2, Env);
659   createOptionalValue(*Loc2, *BoolVal1, Env);
660 }
661 
662 void transferSwapCall(const CXXMemberCallExpr *E,
663                       const MatchFinder::MatchResult &,
664                       LatticeTransferState &State) {
665   assert(E->getNumArgs() == 1);
666   auto *OtherLoc = cast_or_null<AggregateStorageLocation>(
667       State.Env.getStorageLocationStrict(*E->getArg(0)));
668   transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
669 }
670 
671 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
672                          LatticeTransferState &State) {
673   assert(E->getNumArgs() == 2);
674   auto *Arg0Loc = cast_or_null<AggregateStorageLocation>(
675       State.Env.getStorageLocationStrict(*E->getArg(0)));
676   auto *Arg1Loc = cast_or_null<AggregateStorageLocation>(
677       State.Env.getStorageLocationStrict(*E->getArg(1)));
678   transferSwap(Arg0Loc, Arg1Loc, State.Env);
679 }
680 
681 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
682                             LatticeTransferState &State) {
683   assert(E->getNumArgs() == 1);
684 
685   if (auto *Loc = State.Env.getStorageLocationStrict(*E->getArg(0)))
686     State.Env.setStorageLocationStrict(*E, *Loc);
687 }
688 
689 const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
690                                 const Formula &LHS, const Formula &RHS) {
691   // Logically, an optional<T> object is composed of two values - a `has_value`
692   // bit and a value of type T. Equality of optional objects compares both
693   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
694   // when two optional objects are engaged, the equality of their respective
695   // values of type T matters. Since we only track the `has_value` bits, we
696   // can't make any conclusions about equality when we know that two optional
697   // objects are engaged.
698   //
699   // We express this as two facts about the equality:
700   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
701   //    If they are equal, then either both are set or both are unset.
702   // b) (!LHS & !RHS) => EqVal
703   //    If neither is set, then they are equal.
704   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
705   return A.makeAnd(
706       A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
707                                     A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
708       A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
709 }
710 
711 void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
712                                     const MatchFinder::MatchResult &,
713                                     LatticeTransferState &State) {
714   Environment &Env = State.Env;
715   auto &A = Env.arena();
716   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
717   if (auto *LHasVal = getHasValue(
718           Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference)))
719     if (auto *RHasVal = getHasValue(
720             Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) {
721       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
722         CmpValue = &A.makeNot(*CmpValue);
723       Env.addToFlowCondition(evaluateEquality(A, *CmpValue, LHasVal->formula(),
724                                               RHasVal->formula()));
725     }
726 }
727 
728 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
729                                  const clang::Expr *E, Environment &Env) {
730   auto &A = Env.arena();
731   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
732   if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) {
733     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
734       CmpValue = &A.makeNot(*CmpValue);
735     Env.addToFlowCondition(
736         evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
737   }
738 }
739 
740 std::optional<StatementMatcher>
741 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
742   if (Options.IgnoreSmartPointerDereference) {
743     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
744         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
745         unless(hasArgument(0, expr(hasOptionalType()))))));
746     return expr(
747         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
748   }
749   return std::nullopt;
750 }
751 
752 StatementMatcher
753 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
754   return isOptionalMemberCallWithNameMatcher(hasName("value"),
755                                              IgnorableOptional);
756 }
757 
758 StatementMatcher
759 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
760   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
761                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
762 }
763 
764 auto buildTransferMatchSwitch() {
765   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
766   // lot of duplicated work (e.g. string comparisons), consider providing APIs
767   // that avoid it through memoization.
768   return CFGMatchSwitchBuilder<LatticeTransferState>()
769       // Attach a symbolic "has_value" state to optional values that we see for
770       // the first time.
771       .CaseOfCFGStmt<Expr>(
772           expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
773           initializeOptionalReference)
774 
775       // make_optional
776       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
777 
778       // optional::optional (in place)
779       .CaseOfCFGStmt<CXXConstructExpr>(
780           isOptionalInPlaceConstructor(),
781           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
782              LatticeTransferState &State) {
783             constructOptionalValue(*E, State.Env,
784                                    State.Env.getBoolLiteralValue(true));
785           })
786       // nullopt_t::nullopt_t
787       .CaseOfCFGStmt<CXXConstructExpr>(
788           isNulloptConstructor(),
789           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
790              LatticeTransferState &State) {
791             constructOptionalValue(*E, State.Env,
792                                    State.Env.getBoolLiteralValue(false));
793           })
794       // optional::optional(nullopt_t)
795       .CaseOfCFGStmt<CXXConstructExpr>(
796           isOptionalNulloptConstructor(),
797           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
798              LatticeTransferState &State) {
799             constructOptionalValue(*E, State.Env,
800                                    State.Env.getBoolLiteralValue(false));
801           })
802       // optional::optional (value/conversion)
803       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
804                                        transferValueOrConversionConstructor)
805 
806       // optional::operator=
807       .CaseOfCFGStmt<CXXOperatorCallExpr>(
808           isOptionalValueOrConversionAssignment(),
809           transferValueOrConversionAssignment)
810       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
811                                           transferNulloptAssignment)
812 
813       // optional::value
814       .CaseOfCFGStmt<CXXMemberCallExpr>(
815           valueCall(std::nullopt),
816           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
817              LatticeTransferState &State) {
818             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
819           })
820 
821       // optional::operator*
822       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
823                                [](const CallExpr *E,
824                                   const MatchFinder::MatchResult &,
825                                   LatticeTransferState &State) {
826                                  transferUnwrapCall(E, E->getArg(0), State);
827                                })
828 
829       // optional::operator->
830       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
831                                [](const CallExpr *E,
832                                   const MatchFinder::MatchResult &,
833                                   LatticeTransferState &State) {
834                                  transferArrowOpCall(E, E->getArg(0), State);
835                                })
836 
837       // optional::has_value, optional::hasValue
838       // Of the supported optionals only folly::Optional uses hasValue, but this
839       // will also pass for other types
840       .CaseOfCFGStmt<CXXMemberCallExpr>(
841           isOptionalMemberCallWithNameMatcher(
842               hasAnyName("has_value", "hasValue")),
843           transferOptionalHasValueCall)
844 
845       // optional::operator bool
846       .CaseOfCFGStmt<CXXMemberCallExpr>(
847           isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
848           transferOptionalHasValueCall)
849 
850       // optional::emplace
851       .CaseOfCFGStmt<CXXMemberCallExpr>(
852           isOptionalMemberCallWithNameMatcher(hasName("emplace")),
853           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
854              LatticeTransferState &State) {
855             if (AggregateStorageLocation *Loc =
856                     getImplicitObjectLocation(*E, State.Env)) {
857               createOptionalValue(*Loc, State.Env.getBoolLiteralValue(true),
858                                   State.Env);
859             }
860           })
861 
862       // optional::reset
863       .CaseOfCFGStmt<CXXMemberCallExpr>(
864           isOptionalMemberCallWithNameMatcher(hasName("reset")),
865           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
866              LatticeTransferState &State) {
867             if (AggregateStorageLocation *Loc =
868                     getImplicitObjectLocation(*E, State.Env)) {
869               createOptionalValue(*Loc, State.Env.getBoolLiteralValue(false),
870                                   State.Env);
871             }
872           })
873 
874       // optional::swap
875       .CaseOfCFGStmt<CXXMemberCallExpr>(
876           isOptionalMemberCallWithNameMatcher(hasName("swap")),
877           transferSwapCall)
878 
879       // std::swap
880       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
881 
882       // std::forward
883       .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
884 
885       // opt.value_or("").empty()
886       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
887                            transferValueOrStringEmptyCall)
888 
889       // opt.value_or(X) != X
890       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
891 
892       // Comparisons (==, !=):
893       .CaseOfCFGStmt<CXXOperatorCallExpr>(
894           isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()),
895           transferOptionalAndOptionalCmp)
896       .CaseOfCFGStmt<CXXOperatorCallExpr>(
897           isComparisonOperatorCall(hasOptionalType(),
898                                    unless(hasAnyOptionalType())),
899           [](const clang::CXXOperatorCallExpr *Cmp,
900              const MatchFinder::MatchResult &, LatticeTransferState &State) {
901             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
902           })
903       .CaseOfCFGStmt<CXXOperatorCallExpr>(
904           isComparisonOperatorCall(unless(hasAnyOptionalType()),
905                                    hasOptionalType()),
906           [](const clang::CXXOperatorCallExpr *Cmp,
907              const MatchFinder::MatchResult &, LatticeTransferState &State) {
908             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
909           })
910 
911       // returns optional
912       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
913                                transferCallReturningOptional)
914 
915       .Build();
916 }
917 
918 std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
919                                                const Environment &Env) {
920   if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) {
921     auto *Prop = OptionalVal->getProperty("has_value");
922     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
923       if (Env.flowConditionImplies(HasValueVal->formula()))
924         return {};
925     }
926   }
927 
928   // Record that this unwrap is *not* provably safe.
929   // FIXME: include either the name of the optional (if applicable) or a source
930   // range of the access for easier interpretation of the result.
931   return {ObjectExpr->getBeginLoc()};
932 }
933 
934 auto buildDiagnoseMatchSwitch(
935     const UncheckedOptionalAccessModelOptions &Options) {
936   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
937   // lot of duplicated work (e.g. string comparisons), consider providing APIs
938   // that avoid it through memoization.
939   auto IgnorableOptional = ignorableOptional(Options);
940   return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
941       // optional::value
942       .CaseOfCFGStmt<CXXMemberCallExpr>(
943           valueCall(IgnorableOptional),
944           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
945              const Environment &Env) {
946             return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
947           })
948 
949       // optional::operator*, optional::operator->
950       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
951                                [](const CallExpr *E,
952                                   const MatchFinder::MatchResult &,
953                                   const Environment &Env) {
954                                  return diagnoseUnwrapCall(E->getArg(0), Env);
955                                })
956       .Build();
957 }
958 
959 } // namespace
960 
961 ast_matchers::DeclarationMatcher
962 UncheckedOptionalAccessModel::optionalClassDecl() {
963   return optionalClass();
964 }
965 
966 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
967     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
968       TransferMatchSwitch(buildTransferMatchSwitch()) {}
969 
970 void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
971                                             NoopLattice &L, Environment &Env) {
972   LatticeTransferState State(L, Env);
973   TransferMatchSwitch(Elt, getASTContext(), State);
974 }
975 
976 ComparisonResult UncheckedOptionalAccessModel::compare(
977     QualType Type, const Value &Val1, const Environment &Env1,
978     const Value &Val2, const Environment &Env2) {
979   if (!isOptionalType(Type))
980     return ComparisonResult::Unknown;
981   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
982   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
983   if (MustNonEmpty1 && MustNonEmpty2)
984     return ComparisonResult::Same;
985   // If exactly one is true, then they're different, no reason to check whether
986   // they're definitely empty.
987   if (MustNonEmpty1 || MustNonEmpty2)
988     return ComparisonResult::Different;
989   // Check if they're both definitely empty.
990   return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
991              ? ComparisonResult::Same
992              : ComparisonResult::Different;
993 }
994 
995 bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
996                                          const Environment &Env1,
997                                          const Value &Val2,
998                                          const Environment &Env2,
999                                          Value &MergedVal,
1000                                          Environment &MergedEnv) {
1001   if (!isOptionalType(Type))
1002     return true;
1003   // FIXME: uses same approach as join for `BoolValues`. Requires non-const
1004   // values, though, so will require updating the interface.
1005   auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
1006   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
1007   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
1008   if (MustNonEmpty1 && MustNonEmpty2)
1009     MergedEnv.addToFlowCondition(HasValueVal.formula());
1010   else if (
1011       // Only make the costly calls to `isEmptyOptional` if we got "unknown"
1012       // (false) for both calls to `isNonEmptyOptional`.
1013       !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) &&
1014       isEmptyOptional(Val2, Env2))
1015     MergedEnv.addToFlowCondition(
1016         MergedEnv.arena().makeNot(HasValueVal.formula()));
1017   setHasValue(MergedVal, HasValueVal);
1018   return true;
1019 }
1020 
1021 Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev,
1022                                            const Environment &PrevEnv,
1023                                            Value &Current,
1024                                            Environment &CurrentEnv) {
1025   switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) {
1026   case ComparisonResult::Same:
1027     return &Prev;
1028   case ComparisonResult::Different:
1029     if (auto *PrevHasVal =
1030             cast_or_null<BoolValue>(Prev.getProperty("has_value"))) {
1031       if (isa<TopBoolValue>(PrevHasVal))
1032         return &Prev;
1033     }
1034     if (auto *CurrentHasVal =
1035             cast_or_null<BoolValue>(Current.getProperty("has_value"))) {
1036       if (isa<TopBoolValue>(CurrentHasVal))
1037         return &Current;
1038     }
1039     return &createOptionalValue(cast<StructValue>(Current).getAggregateLoc(),
1040                                 CurrentEnv.makeTopBoolValue(), CurrentEnv);
1041   case ComparisonResult::Unknown:
1042     return nullptr;
1043   }
1044   llvm_unreachable("all cases covered in switch");
1045 }
1046 
1047 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
1048     UncheckedOptionalAccessModelOptions Options)
1049     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
1050 
1051 std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose(
1052     ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) {
1053   return DiagnoseMatchSwitch(*Elt, Ctx, Env);
1054 }
1055 
1056 } // namespace dataflow
1057 } // namespace clang
1058