xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision 9f23cbd6cae82fd77edfad7173432fa8dccd0a95)
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 = std::make_unique<StructValue>();
225   setHasValue(*OptionalVal, HasValueVal);
226   return Env.takeOwnership(std::move(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 = std::make_unique<ReferenceValue>(ValueLoc);
316   OptionalVal.setProperty("value", Env.takeOwnership(std::move(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 StorageLocation &OptionalLoc1,
525                   const StorageLocation &OptionalLoc2,
526                   LatticeTransferState &State) {
527   auto *OptionalVal1 = State.Env.getValue(OptionalLoc1);
528   assert(OptionalVal1 != nullptr);
529 
530   auto *OptionalVal2 = State.Env.getValue(OptionalLoc2);
531   assert(OptionalVal2 != nullptr);
532 
533   State.Env.setValue(OptionalLoc1, *OptionalVal2);
534   State.Env.setValue(OptionalLoc2, *OptionalVal1);
535 }
536 
537 void transferSwapCall(const CXXMemberCallExpr *E,
538                       const MatchFinder::MatchResult &,
539                       LatticeTransferState &State) {
540   assert(E->getNumArgs() == 1);
541 
542   auto *OptionalLoc1 = State.Env.getStorageLocation(
543       *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer);
544   assert(OptionalLoc1 != nullptr);
545 
546   auto *OptionalLoc2 =
547       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
548   assert(OptionalLoc2 != nullptr);
549 
550   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
551 }
552 
553 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
554                          LatticeTransferState &State) {
555   assert(E->getNumArgs() == 2);
556 
557   auto *OptionalLoc1 =
558       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
559   assert(OptionalLoc1 != nullptr);
560 
561   auto *OptionalLoc2 =
562       State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference);
563   assert(OptionalLoc2 != nullptr);
564 
565   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
566 }
567 
568 BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS,
569                             BoolValue &RHS) {
570   // Logically, an optional<T> object is composed of two values - a `has_value`
571   // bit and a value of type T. Equality of optional objects compares both
572   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
573   // when two optional objects are engaged, the equality of their respective
574   // values of type T matters. Since we only track the `has_value` bits, we
575   // can't make any conclusions about equality when we know that two optional
576   // objects are engaged.
577   //
578   // We express this as two facts about the equality:
579   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
580   //    If they are equal, then either both are set or both are unset.
581   // b) (!LHS & !RHS) => EqVal
582   //    If neither is set, then they are equal.
583   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
584   return Env.makeAnd(
585       Env.makeImplication(
586           EqVal, Env.makeOr(Env.makeAnd(LHS, RHS),
587                             Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))),
588       Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS)));
589 }
590 
591 void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
592                                     const MatchFinder::MatchResult &,
593                                     LatticeTransferState &State) {
594   Environment &Env = State.Env;
595   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
596   if (auto *LHasVal = getHasValue(
597           Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference)))
598     if (auto *RHasVal = getHasValue(
599             Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) {
600       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
601         CmpValue = &State.Env.makeNot(*CmpValue);
602       Env.addToFlowCondition(
603           evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal));
604     }
605 }
606 
607 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
608                                  const clang::Expr *E, Environment &Env) {
609   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
610   if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) {
611     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
612       CmpValue = &Env.makeNot(*CmpValue);
613     Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal,
614                                             Env.getBoolLiteralValue(true)));
615   }
616 }
617 
618 std::optional<StatementMatcher>
619 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
620   if (Options.IgnoreSmartPointerDereference) {
621     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
622         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
623         unless(hasArgument(0, expr(hasOptionalType()))))));
624     return expr(
625         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
626   }
627   return std::nullopt;
628 }
629 
630 StatementMatcher
631 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
632   return isOptionalMemberCallWithName("value", IgnorableOptional);
633 }
634 
635 StatementMatcher
636 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
637   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
638                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
639 }
640 
641 auto buildTransferMatchSwitch() {
642   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
643   // lot of duplicated work (e.g. string comparisons), consider providing APIs
644   // that avoid it through memoization.
645   return CFGMatchSwitchBuilder<LatticeTransferState>()
646       // Attach a symbolic "has_value" state to optional values that we see for
647       // the first time.
648       .CaseOfCFGStmt<Expr>(
649           expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
650           initializeOptionalReference)
651 
652       // make_optional
653       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
654 
655       // optional::optional (in place)
656       .CaseOfCFGStmt<CXXConstructExpr>(
657           isOptionalInPlaceConstructor(),
658           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
659              LatticeTransferState &State) {
660             assignOptionalValue(*E, State.Env,
661                                 State.Env.getBoolLiteralValue(true));
662           })
663       // nullopt_t::nullopt_t
664       .CaseOfCFGStmt<CXXConstructExpr>(
665           isNulloptConstructor(),
666           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
667              LatticeTransferState &State) {
668             assignOptionalValue(*E, State.Env,
669                                 State.Env.getBoolLiteralValue(false));
670           })
671       // optional::optional(nullopt_t)
672       .CaseOfCFGStmt<CXXConstructExpr>(
673           isOptionalNulloptConstructor(),
674           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
675              LatticeTransferState &State) {
676             assignOptionalValue(*E, State.Env,
677                                 State.Env.getBoolLiteralValue(false));
678           })
679       // optional::optional (value/conversion)
680       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
681                                        transferValueOrConversionConstructor)
682 
683 
684       // optional::operator=
685       .CaseOfCFGStmt<CXXOperatorCallExpr>(
686           isOptionalValueOrConversionAssignment(),
687           transferValueOrConversionAssignment)
688       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
689                                           transferNulloptAssignment)
690 
691       // optional::value
692       .CaseOfCFGStmt<CXXMemberCallExpr>(
693           valueCall(std::nullopt),
694           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
695              LatticeTransferState &State) {
696             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
697           })
698 
699       // optional::operator*, optional::operator->
700       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(std::nullopt),
701                                [](const CallExpr *E,
702                                   const MatchFinder::MatchResult &,
703                                   LatticeTransferState &State) {
704                                  transferUnwrapCall(E, E->getArg(0), State);
705                                })
706 
707       // optional::has_value
708       .CaseOfCFGStmt<CXXMemberCallExpr>(
709           isOptionalMemberCallWithName("has_value"),
710           transferOptionalHasValueCall)
711 
712       // optional::operator bool
713       .CaseOfCFGStmt<CXXMemberCallExpr>(
714           isOptionalMemberCallWithName("operator bool"),
715           transferOptionalHasValueCall)
716 
717       // optional::emplace
718       .CaseOfCFGStmt<CXXMemberCallExpr>(
719           isOptionalMemberCallWithName("emplace"),
720           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
721              LatticeTransferState &State) {
722             assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
723                                 State.Env.getBoolLiteralValue(true));
724           })
725 
726       // optional::reset
727       .CaseOfCFGStmt<CXXMemberCallExpr>(
728           isOptionalMemberCallWithName("reset"),
729           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
730              LatticeTransferState &State) {
731             assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
732                                 State.Env.getBoolLiteralValue(false));
733           })
734 
735       // optional::swap
736       .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
737                                         transferSwapCall)
738 
739       // std::swap
740       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
741 
742       // opt.value_or("").empty()
743       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
744                            transferValueOrStringEmptyCall)
745 
746       // opt.value_or(X) != X
747       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
748 
749       // Comparisons (==, !=):
750       .CaseOfCFGStmt<CXXOperatorCallExpr>(
751           isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()),
752           transferOptionalAndOptionalCmp)
753       .CaseOfCFGStmt<CXXOperatorCallExpr>(
754           isComparisonOperatorCall(hasOptionalType(),
755                                    unless(hasAnyOptionalType())),
756           [](const clang::CXXOperatorCallExpr *Cmp,
757              const MatchFinder::MatchResult &, LatticeTransferState &State) {
758             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
759           })
760       .CaseOfCFGStmt<CXXOperatorCallExpr>(
761           isComparisonOperatorCall(unless(hasAnyOptionalType()),
762                                    hasOptionalType()),
763           [](const clang::CXXOperatorCallExpr *Cmp,
764              const MatchFinder::MatchResult &, LatticeTransferState &State) {
765             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
766           })
767 
768       // returns optional
769       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
770                                transferCallReturningOptional)
771 
772       .Build();
773 }
774 
775 std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr,
776                                                const Expr *ObjectExpr,
777                                                const Environment &Env) {
778   if (auto *OptionalVal =
779           Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
780     auto *Prop = OptionalVal->getProperty("has_value");
781     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
782       if (Env.flowConditionImplies(*HasValueVal))
783         return {};
784     }
785   }
786 
787   // Record that this unwrap is *not* provably safe.
788   // FIXME: include either the name of the optional (if applicable) or a source
789   // range of the access for easier interpretation of the result.
790   return {ObjectExpr->getBeginLoc()};
791 }
792 
793 auto buildDiagnoseMatchSwitch(
794     const UncheckedOptionalAccessModelOptions &Options) {
795   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
796   // lot of duplicated work (e.g. string comparisons), consider providing APIs
797   // that avoid it through memoization.
798   auto IgnorableOptional = ignorableOptional(Options);
799   return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
800       // optional::value
801       .CaseOfCFGStmt<CXXMemberCallExpr>(
802           valueCall(IgnorableOptional),
803           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
804              const Environment &Env) {
805             return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env);
806           })
807 
808       // optional::operator*, optional::operator->
809       .CaseOfCFGStmt<CallExpr>(
810           valueOperatorCall(IgnorableOptional),
811           [](const CallExpr *E, const MatchFinder::MatchResult &,
812              const Environment &Env) {
813             return diagnoseUnwrapCall(E, E->getArg(0), Env);
814           })
815       .Build();
816 }
817 
818 } // namespace
819 
820 ast_matchers::DeclarationMatcher
821 UncheckedOptionalAccessModel::optionalClassDecl() {
822   return optionalClass();
823 }
824 
825 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
826     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
827       TransferMatchSwitch(buildTransferMatchSwitch()) {}
828 
829 void UncheckedOptionalAccessModel::transfer(const CFGElement *Elt,
830                                             NoopLattice &L, Environment &Env) {
831   LatticeTransferState State(L, Env);
832   TransferMatchSwitch(*Elt, getASTContext(), State);
833 }
834 
835 ComparisonResult UncheckedOptionalAccessModel::compare(
836     QualType Type, const Value &Val1, const Environment &Env1,
837     const Value &Val2, const Environment &Env2) {
838   if (!isOptionalType(Type))
839     return ComparisonResult::Unknown;
840   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
841   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
842   if (MustNonEmpty1 && MustNonEmpty2) return ComparisonResult::Same;
843   // If exactly one is true, then they're different, no reason to check whether
844   // they're definitely empty.
845   if (MustNonEmpty1 || MustNonEmpty2) return ComparisonResult::Different;
846   // Check if they're both definitely empty.
847   return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
848              ? ComparisonResult::Same
849              : ComparisonResult::Different;
850 }
851 
852 bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
853                                          const Environment &Env1,
854                                          const Value &Val2,
855                                          const Environment &Env2,
856                                          Value &MergedVal,
857                                          Environment &MergedEnv) {
858   if (!isOptionalType(Type))
859     return true;
860   // FIXME: uses same approach as join for `BoolValues`. Requires non-const
861   // values, though, so will require updating the interface.
862   auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
863   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
864   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
865   if (MustNonEmpty1 && MustNonEmpty2)
866     MergedEnv.addToFlowCondition(HasValueVal);
867   else if (
868       // Only make the costly calls to `isEmptyOptional` if we got "unknown"
869       // (false) for both calls to `isNonEmptyOptional`.
870       !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) &&
871       isEmptyOptional(Val2, Env2))
872     MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal));
873   setHasValue(MergedVal, HasValueVal);
874   return true;
875 }
876 
877 Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev,
878                                            const Environment &PrevEnv,
879                                            Value &Current,
880                                            Environment &CurrentEnv) {
881   switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) {
882   case ComparisonResult::Same:
883     return &Prev;
884   case ComparisonResult::Different:
885     if (auto *PrevHasVal =
886             cast_or_null<BoolValue>(Prev.getProperty("has_value"))) {
887       if (isa<TopBoolValue>(PrevHasVal))
888         return &Prev;
889     }
890     if (auto *CurrentHasVal =
891             cast_or_null<BoolValue>(Current.getProperty("has_value"))) {
892       if (isa<TopBoolValue>(CurrentHasVal))
893         return &Current;
894     }
895     return &createOptionalValue(CurrentEnv, CurrentEnv.makeTopBoolValue());
896   case ComparisonResult::Unknown:
897     return nullptr;
898   }
899   llvm_unreachable("all cases covered in switch");
900 }
901 
902 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
903     UncheckedOptionalAccessModelOptions Options)
904     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
905 
906 std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose(
907     ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) {
908   return DiagnoseMatchSwitch(*Elt, Ctx, Env);
909 }
910 
911 } // namespace dataflow
912 } // namespace clang
913