xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision 526c9b7e37fa12abc17eebc68f21c1d213477ba8)
1 //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //  This file defines a dataflow analysis that detects unsafe uses of optional
10 //  values.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/DeclCXX.h"
17 #include "clang/AST/Expr.h"
18 #include "clang/AST/ExprCXX.h"
19 #include "clang/AST/Stmt.h"
20 #include "clang/ASTMatchers/ASTMatchers.h"
21 #include "clang/ASTMatchers/ASTMatchersMacros.h"
22 #include "clang/Analysis/CFG.h"
23 #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
24 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
25 #include "clang/Analysis/FlowSensitive/Formula.h"
26 #include "clang/Analysis/FlowSensitive/NoopLattice.h"
27 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
28 #include "clang/Analysis/FlowSensitive/Value.h"
29 #include "clang/Basic/SourceLocation.h"
30 #include "llvm/ADT/StringRef.h"
31 #include "llvm/Support/Casting.h"
32 #include "llvm/Support/ErrorHandling.h"
33 #include <cassert>
34 #include <memory>
35 #include <optional>
36 #include <utility>
37 
38 namespace clang {
39 namespace dataflow {
40 
41 static bool isTopLevelNamespaceWithName(const NamespaceDecl &NS,
42                                         llvm::StringRef Name) {
43   return NS.getDeclName().isIdentifier() && NS.getName() == Name &&
44          NS.getParent() != nullptr && NS.getParent()->isTranslationUnit();
45 }
46 
47 static bool hasOptionalClassName(const CXXRecordDecl &RD) {
48   if (!RD.getDeclName().isIdentifier())
49     return false;
50 
51   if (RD.getName() == "optional") {
52     if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
53       return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl");
54     return false;
55   }
56 
57   if (RD.getName() == "Optional") {
58     // Check whether namespace is "::base" or "::folly".
59     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
60     return N != nullptr && (isTopLevelNamespaceWithName(*N, "base") ||
61                             isTopLevelNamespaceWithName(*N, "folly"));
62   }
63 
64   return false;
65 }
66 
67 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 isOptionalMemberCallWithNameMatcher(
91     ast_matchers::internal::Matcher<NamedDecl> matcher,
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(matcher)));
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(callee(functionDecl(hasAnyName(
113                       "std::make_optional", "base::make_optional",
114                       "absl::make_optional", "folly::make_optional"))),
115                   hasOptionalType());
116 }
117 
118 auto nulloptTypeDecl() {
119   return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",
120                               "base::nullopt_t", "folly::None"));
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(hasAnyName("std::in_place_t", "absl::in_place_t",
133                                "base::in_place_t", "folly::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));
241   if (Value != nullptr)
242     return Value->formula();
243 
244   Value = &Env.makeAtomicBoolValue();
245   Env.setValue(Expr, *Value);
246   return Value->formula();
247 }
248 
249 /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
250 /// property of the optional value `OptionalVal`.
251 void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
252   OptionalVal.setProperty("has_value", HasValueVal);
253 }
254 
255 /// Creates a symbolic value for an `optional` value at an existing storage
256 /// location. Uses `HasValueVal` as the symbolic value of the "has_value"
257 /// property.
258 RecordValue &createOptionalValue(RecordStorageLocation &Loc,
259                                  BoolValue &HasValueVal, Environment &Env) {
260   auto &OptionalVal = Env.create<RecordValue>(Loc);
261   Env.setValue(Loc, OptionalVal);
262   setHasValue(OptionalVal, HasValueVal);
263   return OptionalVal;
264 }
265 
266 /// Returns the symbolic value that represents the "has_value" property of the
267 /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
268 BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
269   if (OptionalVal != nullptr) {
270     auto *HasValueVal =
271         cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
272     if (HasValueVal == nullptr) {
273       HasValueVal = &Env.makeAtomicBoolValue();
274       OptionalVal->setProperty("has_value", *HasValueVal);
275     }
276     return HasValueVal;
277   }
278   return nullptr;
279 }
280 
281 /// Returns true if and only if `Type` is an optional type.
282 bool isOptionalType(QualType Type) {
283   if (!Type->isRecordType())
284     return false;
285   const CXXRecordDecl *D = Type->getAsCXXRecordDecl();
286   return D != nullptr && hasOptionalClassName(*D);
287 }
288 
289 /// Returns the number of optional wrappers in `Type`.
290 ///
291 /// For example, if `Type` is `optional<optional<int>>`, the result of this
292 /// function will be 2.
293 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
294   if (!isOptionalType(Type))
295     return 0;
296   return 1 + countOptionalWrappers(
297                  ASTCtx,
298                  cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
299                      ->getTemplateArgs()
300                      .get(0)
301                      .getAsType()
302                      .getDesugaredType(ASTCtx));
303 }
304 
305 /// Tries to initialize the `optional`'s value (that is, contents), and return
306 /// its location. Returns nullptr if the value can't be represented.
307 StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
308                                                     Value &OptionalVal,
309                                                     Environment &Env) {
310   // The "value" property represents a synthetic field. As such, it needs
311   // `StorageLocation`, like normal fields (and other variables). So, we model
312   // it with a `PointerValue`, since that includes a storage location.  Once
313   // the property is set, it will be shared by all environments that access the
314   // `Value` representing the optional (here, `OptionalVal`).
315   if (auto *ValueProp = OptionalVal.getProperty("value")) {
316     auto *ValuePtr = clang::cast<PointerValue>(ValueProp);
317     auto &ValueLoc = ValuePtr->getPointeeLoc();
318     if (Env.getValue(ValueLoc) != nullptr)
319       return &ValueLoc;
320 
321     // The property was previously set, but the value has been lost. This can
322     // happen in various situations, for example:
323     // - Because of an environment merge (where the two environments mapped the
324     //   property to different values, which resulted in them both being
325     //   discarded).
326     // - When two blocks in the CFG, with neither a dominator of the other,
327     //   visit the same optional value. (FIXME: This is something we can and
328     //   should fix -- see also the lengthy FIXME below.)
329     // - Or even when a block is revisited during testing to collect
330     //   per-statement state.
331     // FIXME: This situation means that the optional contents are not shared
332     // between branches and the like. Practically, this lack of sharing
333     // reduces the precision of the model when the contents are relevant to
334     // the check, like another optional or a boolean that influences control
335     // flow.
336     if (ValueLoc.getType()->isRecordType()) {
337       refreshRecordValue(cast<RecordStorageLocation>(ValueLoc), Env);
338       return &ValueLoc;
339     } else {
340       auto *ValueVal = Env.createValue(ValueLoc.getType());
341       if (ValueVal == nullptr)
342         return nullptr;
343       Env.setValue(ValueLoc, *ValueVal);
344       return &ValueLoc;
345     }
346   }
347 
348   auto Ty = Q.getNonReferenceType();
349   auto &ValueLoc = Env.createObject(Ty);
350   auto &ValuePtr = Env.create<PointerValue>(ValueLoc);
351   // FIXME:
352   // The change we make to the `value` property below may become visible to
353   // other blocks that aren't successors of the current block and therefore
354   // don't see the change we made above mapping `ValueLoc` to `ValueVal`. For
355   // example:
356   //
357   //   void target(optional<int> oo, bool b) {
358   //     // `oo` is associated with a `RecordValue` here, which we will call
359   //     // `OptionalVal`.
360   //
361   //     // The `has_value` property is set on `OptionalVal` (but not the
362   //     // `value` property yet).
363   //     if (!oo.has_value()) return;
364   //
365   //     if (b) {
366   //       // Let's assume we transfer the `if` branch first.
367   //       //
368   //       // This causes us to call `maybeInitializeOptionalValueMember()`,
369   //       // which causes us to set the `value` property on `OptionalVal`
370   //       // (which had not been set until this point). This `value` property
371   //       // refers to a `PointerValue`, which in turn refers to a
372   //       // StorageLocation` that is associated to an `IntegerValue`.
373   //       oo.value();
374   //     } else {
375   //       // Let's assume we transfer the `else` branch after the `if` branch.
376   //       //
377   //       // We see the `value` property that the `if` branch set on
378   //       // `OptionalVal`, but in the environment for this block, the
379   //       // `StorageLocation` in the `PointerValue` is not associated with any
380   //       // `Value`.
381   //       oo.value();
382   //     }
383   //   }
384   //
385   // This situation is currently "saved" by the code above that checks whether
386   // the `value` property is already set, and if, the `ValueLoc` is not
387   // associated with a `ValueVal`, creates a new `ValueVal`.
388   //
389   // However, what we should really do is to make sure that the change to the
390   // `value` property does not "leak" to other blocks that are not successors
391   // of this block. To do this, instead of simply setting the `value` property
392   // on the existing `OptionalVal`, we should create a new `Value` for the
393   // optional, set the property on that, and associate the storage location that
394   // is currently associated with the existing `OptionalVal` with the newly
395   // created `Value` instead.
396   OptionalVal.setProperty("value", ValuePtr);
397   return &ValueLoc;
398 }
399 
400 void initializeOptionalReference(const Expr *OptionalExpr,
401                                  const MatchFinder::MatchResult &,
402                                  LatticeTransferState &State) {
403   if (auto *OptionalVal = State.Env.getValue(*OptionalExpr)) {
404     if (OptionalVal->getProperty("has_value") == nullptr) {
405       setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
406     }
407   }
408 }
409 
410 /// Returns true if and only if `OptionalVal` is initialized and known to be
411 /// empty in `Env`.
412 bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
413   auto *HasValueVal =
414       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
415   return HasValueVal != nullptr &&
416          Env.proves(Env.arena().makeNot(HasValueVal->formula()));
417 }
418 
419 /// Returns true if and only if `OptionalVal` is initialized and known to be
420 /// non-empty in `Env`.
421 bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
422   auto *HasValueVal =
423       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
424   return HasValueVal != nullptr && Env.proves(HasValueVal->formula());
425 }
426 
427 Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) {
428   Value *Val = Env.getValue(E);
429   if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Val))
430     return Env.getValue(PointerVal->getPointeeLoc());
431   return Val;
432 }
433 
434 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
435                         LatticeTransferState &State) {
436   if (auto *OptionalVal =
437           getValueBehindPossiblePointer(*ObjectExpr, State.Env)) {
438     if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)
439       if (auto *Loc = maybeInitializeOptionalValueMember(
440               UnwrapExpr->getType(), *OptionalVal, State.Env))
441         State.Env.setStorageLocation(*UnwrapExpr, *Loc);
442   }
443 }
444 
445 void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
446                          LatticeTransferState &State) {
447   if (auto *OptionalVal =
448           getValueBehindPossiblePointer(*ObjectExpr, State.Env)) {
449     if (auto *Loc = maybeInitializeOptionalValueMember(
450             UnwrapExpr->getType()->getPointeeType(), *OptionalVal, State.Env)) {
451       State.Env.setValue(*UnwrapExpr, State.Env.create<PointerValue>(*Loc));
452     }
453   }
454 }
455 
456 void transferMakeOptionalCall(const CallExpr *E,
457                               const MatchFinder::MatchResult &,
458                               LatticeTransferState &State) {
459   State.Env.setValue(
460       *E, createOptionalValue(State.Env.getResultObjectLocation(*E),
461                               State.Env.getBoolLiteralValue(true), State.Env));
462 }
463 
464 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
465                                   const MatchFinder::MatchResult &,
466                                   LatticeTransferState &State) {
467   if (auto *HasValueVal = getHasValue(
468           State.Env, getValueBehindPossiblePointer(
469                          *CallExpr->getImplicitObjectArgument(), State.Env))) {
470     State.Env.setValue(*CallExpr, *HasValueVal);
471   }
472 }
473 
474 /// `ModelPred` builds a logical formula relating the predicate in
475 /// `ValueOrPredExpr` to the optional's `has_value` property.
476 void transferValueOrImpl(
477     const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
478     LatticeTransferState &State,
479     const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
480                                 const Formula &HasValueVal)) {
481   auto &Env = State.Env;
482 
483   const auto *ObjectArgumentExpr =
484       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
485           ->getImplicitObjectArgument();
486 
487   auto *HasValueVal = getHasValue(
488       State.Env, getValueBehindPossiblePointer(*ObjectArgumentExpr, State.Env));
489   if (HasValueVal == nullptr)
490     return;
491 
492   Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
493                        HasValueVal->formula()));
494 }
495 
496 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
497                                     const MatchFinder::MatchResult &Result,
498                                     LatticeTransferState &State) {
499   return transferValueOrImpl(ComparisonExpr, Result, State,
500                              [](Environment &Env, const Formula &ExprVal,
501                                 const Formula &HasValueVal) -> const Formula & {
502                                auto &A = Env.arena();
503                                // If the result is *not* empty, then we know the
504                                // optional must have been holding a value. If
505                                // `ExprVal` is true, though, we don't learn
506                                // anything definite about `has_value`, so we
507                                // don't add any corresponding implications to
508                                // the flow condition.
509                                return A.makeImplies(A.makeNot(ExprVal),
510                                                     HasValueVal);
511                              });
512 }
513 
514 void transferValueOrNotEqX(const Expr *ComparisonExpr,
515                            const MatchFinder::MatchResult &Result,
516                            LatticeTransferState &State) {
517   transferValueOrImpl(ComparisonExpr, Result, State,
518                       [](Environment &Env, const Formula &ExprVal,
519                          const Formula &HasValueVal) -> const Formula & {
520                         auto &A = Env.arena();
521                         // We know that if `(opt.value_or(X) != X)` then
522                         // `opt.hasValue()`, even without knowing further
523                         // details about the contents of `opt`.
524                         return A.makeImplies(ExprVal, HasValueVal);
525                       });
526 }
527 
528 void transferCallReturningOptional(const CallExpr *E,
529                                    const MatchFinder::MatchResult &Result,
530                                    LatticeTransferState &State) {
531   if (State.Env.getValue(*E) != nullptr)
532     return;
533 
534   RecordStorageLocation *Loc = nullptr;
535   if (E->isPRValue()) {
536     Loc = &State.Env.getResultObjectLocation(*E);
537   } else {
538     Loc = cast_or_null<RecordStorageLocation>(State.Env.getStorageLocation(*E));
539     if (Loc == nullptr) {
540       Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));
541       State.Env.setStorageLocation(*E, *Loc);
542     }
543   }
544 
545   RecordValue &Val =
546       createOptionalValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
547   if (E->isPRValue())
548     State.Env.setValue(*E, Val);
549 }
550 
551 void constructOptionalValue(const Expr &E, Environment &Env,
552                             BoolValue &HasValueVal) {
553   RecordStorageLocation &Loc = Env.getResultObjectLocation(E);
554   Env.setValue(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 = getHasValue(State.Env, State.Env.getValue(E)))
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_or_null<RecordStorageLocation>(
602           State.Env.getStorageLocation(*E->getArg(0)))) {
603     createOptionalValue(*Loc, HasValueVal, State.Env);
604 
605     // Assign a storage location for the whole expression.
606     State.Env.setStorageLocation(*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(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
627                   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<RecordStorageLocation>(
665       State.Env.getStorageLocation(*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<RecordStorageLocation>(
673       State.Env.getStorageLocation(*E->getArg(0)));
674   auto *Arg1Loc = cast_or_null<RecordStorageLocation>(
675       State.Env.getStorageLocation(*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   if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
684     State.Env.setStorageLocation(*E, *Loc);
685 }
686 
687 const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
688                                 const Formula &LHS, const Formula &RHS) {
689   // Logically, an optional<T> object is composed of two values - a `has_value`
690   // bit and a value of type T. Equality of optional objects compares both
691   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
692   // when two optional objects are engaged, the equality of their respective
693   // values of type T matters. Since we only track the `has_value` bits, we
694   // can't make any conclusions about equality when we know that two optional
695   // objects are engaged.
696   //
697   // We express this as two facts about the equality:
698   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
699   //    If they are equal, then either both are set or both are unset.
700   // b) (!LHS & !RHS) => EqVal
701   //    If neither is set, then they are equal.
702   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
703   return A.makeAnd(
704       A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
705                                     A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
706       A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
707 }
708 
709 void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
710                                     const MatchFinder::MatchResult &,
711                                     LatticeTransferState &State) {
712   Environment &Env = State.Env;
713   auto &A = Env.arena();
714   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
715   if (auto *LHasVal = getHasValue(Env, Env.getValue(*CmpExpr->getArg(0))))
716     if (auto *RHasVal = getHasValue(Env, Env.getValue(*CmpExpr->getArg(1)))) {
717       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
718         CmpValue = &A.makeNot(*CmpValue);
719       Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
720                                   RHasVal->formula()));
721     }
722 }
723 
724 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
725                                  const clang::Expr *E, Environment &Env) {
726   auto &A = Env.arena();
727   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
728   if (auto *HasVal = getHasValue(Env, Env.getValue(*E))) {
729     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
730       CmpValue = &A.makeNot(*CmpValue);
731     Env.assume(
732         evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
733   }
734 }
735 
736 std::optional<StatementMatcher>
737 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
738   if (Options.IgnoreSmartPointerDereference) {
739     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
740         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
741         unless(hasArgument(0, expr(hasOptionalType()))))));
742     return expr(
743         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
744   }
745   return std::nullopt;
746 }
747 
748 StatementMatcher
749 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
750   return isOptionalMemberCallWithNameMatcher(hasName("value"),
751                                              IgnorableOptional);
752 }
753 
754 StatementMatcher
755 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
756   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
757                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
758 }
759 
760 auto buildTransferMatchSwitch() {
761   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
762   // lot of duplicated work (e.g. string comparisons), consider providing APIs
763   // that avoid it through memoization.
764   return CFGMatchSwitchBuilder<LatticeTransferState>()
765       // Attach a symbolic "has_value" state to optional values that we see for
766       // the first time.
767       .CaseOfCFGStmt<Expr>(
768           expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
769           initializeOptionalReference)
770 
771       // make_optional
772       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
773 
774       // optional::optional (in place)
775       .CaseOfCFGStmt<CXXConstructExpr>(
776           isOptionalInPlaceConstructor(),
777           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
778              LatticeTransferState &State) {
779             constructOptionalValue(*E, State.Env,
780                                    State.Env.getBoolLiteralValue(true));
781           })
782       // nullopt_t::nullopt_t
783       .CaseOfCFGStmt<CXXConstructExpr>(
784           isNulloptConstructor(),
785           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
786              LatticeTransferState &State) {
787             constructOptionalValue(*E, State.Env,
788                                    State.Env.getBoolLiteralValue(false));
789           })
790       // optional::optional(nullopt_t)
791       .CaseOfCFGStmt<CXXConstructExpr>(
792           isOptionalNulloptConstructor(),
793           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
794              LatticeTransferState &State) {
795             constructOptionalValue(*E, State.Env,
796                                    State.Env.getBoolLiteralValue(false));
797           })
798       // optional::optional (value/conversion)
799       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
800                                        transferValueOrConversionConstructor)
801 
802       // optional::operator=
803       .CaseOfCFGStmt<CXXOperatorCallExpr>(
804           isOptionalValueOrConversionAssignment(),
805           transferValueOrConversionAssignment)
806       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
807                                           transferNulloptAssignment)
808 
809       // optional::value
810       .CaseOfCFGStmt<CXXMemberCallExpr>(
811           valueCall(std::nullopt),
812           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
813              LatticeTransferState &State) {
814             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
815           })
816 
817       // optional::operator*
818       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
819                                [](const CallExpr *E,
820                                   const MatchFinder::MatchResult &,
821                                   LatticeTransferState &State) {
822                                  transferUnwrapCall(E, E->getArg(0), State);
823                                })
824 
825       // optional::operator->
826       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
827                                [](const CallExpr *E,
828                                   const MatchFinder::MatchResult &,
829                                   LatticeTransferState &State) {
830                                  transferArrowOpCall(E, E->getArg(0), State);
831                                })
832 
833       // optional::has_value, optional::hasValue
834       // Of the supported optionals only folly::Optional uses hasValue, but this
835       // will also pass for other types
836       .CaseOfCFGStmt<CXXMemberCallExpr>(
837           isOptionalMemberCallWithNameMatcher(
838               hasAnyName("has_value", "hasValue")),
839           transferOptionalHasValueCall)
840 
841       // optional::operator bool
842       .CaseOfCFGStmt<CXXMemberCallExpr>(
843           isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
844           transferOptionalHasValueCall)
845 
846       // optional::emplace
847       .CaseOfCFGStmt<CXXMemberCallExpr>(
848           isOptionalMemberCallWithNameMatcher(hasName("emplace")),
849           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
850              LatticeTransferState &State) {
851             if (RecordStorageLocation *Loc =
852                     getImplicitObjectLocation(*E, State.Env)) {
853               createOptionalValue(*Loc, State.Env.getBoolLiteralValue(true),
854                                   State.Env);
855             }
856           })
857 
858       // optional::reset
859       .CaseOfCFGStmt<CXXMemberCallExpr>(
860           isOptionalMemberCallWithNameMatcher(hasName("reset")),
861           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
862              LatticeTransferState &State) {
863             if (RecordStorageLocation *Loc =
864                     getImplicitObjectLocation(*E, State.Env)) {
865               createOptionalValue(*Loc, State.Env.getBoolLiteralValue(false),
866                                   State.Env);
867             }
868           })
869 
870       // optional::swap
871       .CaseOfCFGStmt<CXXMemberCallExpr>(
872           isOptionalMemberCallWithNameMatcher(hasName("swap")),
873           transferSwapCall)
874 
875       // std::swap
876       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
877 
878       // std::forward
879       .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
880 
881       // opt.value_or("").empty()
882       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
883                            transferValueOrStringEmptyCall)
884 
885       // opt.value_or(X) != X
886       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
887 
888       // Comparisons (==, !=):
889       .CaseOfCFGStmt<CXXOperatorCallExpr>(
890           isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()),
891           transferOptionalAndOptionalCmp)
892       .CaseOfCFGStmt<CXXOperatorCallExpr>(
893           isComparisonOperatorCall(hasOptionalType(),
894                                    unless(hasAnyOptionalType())),
895           [](const clang::CXXOperatorCallExpr *Cmp,
896              const MatchFinder::MatchResult &, LatticeTransferState &State) {
897             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
898           })
899       .CaseOfCFGStmt<CXXOperatorCallExpr>(
900           isComparisonOperatorCall(unless(hasAnyOptionalType()),
901                                    hasOptionalType()),
902           [](const clang::CXXOperatorCallExpr *Cmp,
903              const MatchFinder::MatchResult &, LatticeTransferState &State) {
904             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
905           })
906 
907       // returns optional
908       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
909                                transferCallReturningOptional)
910 
911       .Build();
912 }
913 
914 llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
915                                                      const Environment &Env) {
916   if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) {
917     auto *Prop = OptionalVal->getProperty("has_value");
918     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
919       if (Env.proves(HasValueVal->formula()))
920         return {};
921     }
922   }
923 
924   // Record that this unwrap is *not* provably safe.
925   // FIXME: include either the name of the optional (if applicable) or a source
926   // range of the access for easier interpretation of the result.
927   return {ObjectExpr->getBeginLoc()};
928 }
929 
930 auto buildDiagnoseMatchSwitch(
931     const UncheckedOptionalAccessModelOptions &Options) {
932   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
933   // lot of duplicated work (e.g. string comparisons), consider providing APIs
934   // that avoid it through memoization.
935   auto IgnorableOptional = ignorableOptional(Options);
936   return CFGMatchSwitchBuilder<const Environment,
937                                llvm::SmallVector<SourceLocation>>()
938       // optional::value
939       .CaseOfCFGStmt<CXXMemberCallExpr>(
940           valueCall(IgnorableOptional),
941           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
942              const Environment &Env) {
943             return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
944           })
945 
946       // optional::operator*, optional::operator->
947       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
948                                [](const CallExpr *E,
949                                   const MatchFinder::MatchResult &,
950                                   const Environment &Env) {
951                                  return diagnoseUnwrapCall(E->getArg(0), Env);
952                                })
953       .Build();
954 }
955 
956 } // namespace
957 
958 ast_matchers::DeclarationMatcher
959 UncheckedOptionalAccessModel::optionalClassDecl() {
960   return optionalClass();
961 }
962 
963 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
964     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
965       TransferMatchSwitch(buildTransferMatchSwitch()) {}
966 
967 void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
968                                             NoopLattice &L, Environment &Env) {
969   LatticeTransferState State(L, Env);
970   TransferMatchSwitch(Elt, getASTContext(), State);
971 }
972 
973 ComparisonResult UncheckedOptionalAccessModel::compare(
974     QualType Type, const Value &Val1, const Environment &Env1,
975     const Value &Val2, const Environment &Env2) {
976   if (!isOptionalType(Type))
977     return ComparisonResult::Unknown;
978   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
979   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
980   if (MustNonEmpty1 && MustNonEmpty2)
981     return ComparisonResult::Same;
982   // If exactly one is true, then they're different, no reason to check whether
983   // they're definitely empty.
984   if (MustNonEmpty1 || MustNonEmpty2)
985     return ComparisonResult::Different;
986   // Check if they're both definitely empty.
987   return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
988              ? ComparisonResult::Same
989              : ComparisonResult::Different;
990 }
991 
992 bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
993                                          const Environment &Env1,
994                                          const Value &Val2,
995                                          const Environment &Env2,
996                                          Value &MergedVal,
997                                          Environment &MergedEnv) {
998   if (!isOptionalType(Type))
999     return true;
1000   // FIXME: uses same approach as join for `BoolValues`. Requires non-const
1001   // values, though, so will require updating the interface.
1002   auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
1003   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
1004   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
1005   if (MustNonEmpty1 && MustNonEmpty2)
1006     MergedEnv.assume(HasValueVal.formula());
1007   else if (
1008       // Only make the costly calls to `isEmptyOptional` if we got "unknown"
1009       // (false) for both calls to `isNonEmptyOptional`.
1010       !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) &&
1011       isEmptyOptional(Val2, Env2))
1012     MergedEnv.assume(MergedEnv.arena().makeNot(HasValueVal.formula()));
1013   setHasValue(MergedVal, HasValueVal);
1014   return true;
1015 }
1016 
1017 Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev,
1018                                            const Environment &PrevEnv,
1019                                            Value &Current,
1020                                            Environment &CurrentEnv) {
1021   switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) {
1022   case ComparisonResult::Same:
1023     return &Prev;
1024   case ComparisonResult::Different:
1025     if (auto *PrevHasVal =
1026             cast_or_null<BoolValue>(Prev.getProperty("has_value"))) {
1027       if (isa<TopBoolValue>(PrevHasVal))
1028         return &Prev;
1029     }
1030     if (auto *CurrentHasVal =
1031             cast_or_null<BoolValue>(Current.getProperty("has_value"))) {
1032       if (isa<TopBoolValue>(CurrentHasVal))
1033         return &Current;
1034     }
1035     return &createOptionalValue(cast<RecordValue>(Current).getLoc(),
1036                                 CurrentEnv.makeTopBoolValue(), CurrentEnv);
1037   case ComparisonResult::Unknown:
1038     return nullptr;
1039   }
1040   llvm_unreachable("all cases covered in switch");
1041 }
1042 
1043 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
1044     UncheckedOptionalAccessModelOptions Options)
1045     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
1046 
1047 } // namespace dataflow
1048 } // namespace clang
1049