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