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