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