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