xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision 34e0d0579a3a6617b9b3212f2bc63d959c8f56c6)
1b000b770SStanislav Gatev //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//
2b000b770SStanislav Gatev //
3b000b770SStanislav Gatev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b000b770SStanislav Gatev // See https://llvm.org/LICENSE.txt for license information.
5b000b770SStanislav Gatev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b000b770SStanislav Gatev //
7b000b770SStanislav Gatev //===----------------------------------------------------------------------===//
8b000b770SStanislav Gatev //
9b000b770SStanislav Gatev //  This file defines a dataflow analysis that detects unsafe uses of optional
10b000b770SStanislav Gatev //  values.
11b000b770SStanislav Gatev //
12b000b770SStanislav Gatev //===----------------------------------------------------------------------===//
13b000b770SStanislav Gatev 
14af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
15af98b0afSStanislav Gatev #include "clang/AST/ASTContext.h"
167e63a0d4SYitzhak Mandelbaum #include "clang/AST/DeclCXX.h"
17af98b0afSStanislav Gatev #include "clang/AST/Expr.h"
18af98b0afSStanislav Gatev #include "clang/AST/ExprCXX.h"
19af98b0afSStanislav Gatev #include "clang/AST/Stmt.h"
20af98b0afSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h"
217538b360SWei Yi Tee #include "clang/Analysis/CFG.h"
227538b360SWei Yi Tee #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
23af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
24cf1f978dSSam Estep #include "clang/Analysis/FlowSensitive/NoopLattice.h"
25af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h"
2658fe7f96SSam Estep #include "clang/Basic/SourceLocation.h"
27af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h"
28af98b0afSStanislav Gatev #include "llvm/Support/Casting.h"
29af98b0afSStanislav Gatev #include <cassert>
309e0fc676SStanislav Gatev #include <memory>
319e0fc676SStanislav Gatev #include <utility>
3258fe7f96SSam Estep #include <vector>
33af98b0afSStanislav Gatev 
34af98b0afSStanislav Gatev namespace clang {
35af98b0afSStanislav Gatev namespace dataflow {
36af98b0afSStanislav Gatev namespace {
37af98b0afSStanislav Gatev 
38af98b0afSStanislav Gatev using namespace ::clang::ast_matchers;
39cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>;
40af98b0afSStanislav Gatev 
417e63a0d4SYitzhak Mandelbaum DeclarationMatcher optionalClass() {
42af98b0afSStanislav Gatev   return classTemplateSpecializationDecl(
43af98b0afSStanislav Gatev       anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
44af98b0afSStanislav Gatev             hasName("__optional_destruct_base"), hasName("absl::optional"),
45af98b0afSStanislav Gatev             hasName("base::Optional")),
46af98b0afSStanislav Gatev       hasTemplateArgument(0, refersToType(type().bind("T"))));
47af98b0afSStanislav Gatev }
48af98b0afSStanislav Gatev 
496adfc64eSYitzhak Mandelbaum auto optionalOrAliasType() {
5065e710c3SStanislav Gatev   return hasUnqualifiedDesugaredType(
5165e710c3SStanislav Gatev       recordType(hasDeclaration(optionalClass())));
5265e710c3SStanislav Gatev }
5365e710c3SStanislav Gatev 
546adfc64eSYitzhak Mandelbaum /// Matches any of the spellings of the optional types and sugar, aliases, etc.
556adfc64eSYitzhak Mandelbaum auto hasOptionalType() { return hasType(optionalOrAliasType()); }
566adfc64eSYitzhak Mandelbaum 
57a184a0d8SYitzhak Mandelbaum auto isOptionalMemberCallWithName(
58a184a0d8SYitzhak Mandelbaum     llvm::StringRef MemberName,
59*34e0d057SKazu Hirata     llvm::Optional<StatementMatcher> Ignorable = std::nullopt) {
60a184a0d8SYitzhak Mandelbaum   auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr()))
61a184a0d8SYitzhak Mandelbaum                                     : cxxThisExpr());
62af98b0afSStanislav Gatev   return cxxMemberCallExpr(
63a184a0d8SYitzhak Mandelbaum       on(expr(Exception)),
64af98b0afSStanislav Gatev       callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
65af98b0afSStanislav Gatev }
66af98b0afSStanislav Gatev 
67a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName(
68a184a0d8SYitzhak Mandelbaum     llvm::StringRef operator_name,
69*34e0d057SKazu Hirata     llvm::Optional<StatementMatcher> Ignorable = std::nullopt) {
70a184a0d8SYitzhak Mandelbaum   return cxxOperatorCallExpr(
71a184a0d8SYitzhak Mandelbaum       hasOverloadedOperatorName(operator_name),
72a184a0d8SYitzhak Mandelbaum       callee(cxxMethodDecl(ofClass(optionalClass()))),
73a184a0d8SYitzhak Mandelbaum       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
74af98b0afSStanislav Gatev }
75af98b0afSStanislav Gatev 
76092a530cSStanislav Gatev auto isMakeOptionalCall() {
779e0fc676SStanislav Gatev   return callExpr(
789e0fc676SStanislav Gatev       callee(functionDecl(hasAnyName(
799e0fc676SStanislav Gatev           "std::make_optional", "base::make_optional", "absl::make_optional"))),
809e0fc676SStanislav Gatev       hasOptionalType());
819e0fc676SStanislav Gatev }
829e0fc676SStanislav Gatev 
83092a530cSStanislav Gatev auto hasNulloptType() {
84092a530cSStanislav Gatev   return hasType(namedDecl(
85092a530cSStanislav Gatev       hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")));
86092a530cSStanislav Gatev }
87092a530cSStanislav Gatev 
88092a530cSStanislav Gatev auto inPlaceClass() {
89092a530cSStanislav Gatev   return recordDecl(
90092a530cSStanislav Gatev       hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
91092a530cSStanislav Gatev }
92092a530cSStanislav Gatev 
93092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
94092a530cSStanislav Gatev   return cxxConstructExpr(hasOptionalType(), argumentCountIs(1),
95092a530cSStanislav Gatev                           hasArgument(0, hasNulloptType()));
96092a530cSStanislav Gatev }
97092a530cSStanislav Gatev 
98092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
99092a530cSStanislav Gatev   return cxxConstructExpr(hasOptionalType(),
100092a530cSStanislav Gatev                           hasArgument(0, hasType(inPlaceClass())));
101092a530cSStanislav Gatev }
102092a530cSStanislav Gatev 
103092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
104092a530cSStanislav Gatev   return cxxConstructExpr(
105092a530cSStanislav Gatev       hasOptionalType(),
106092a530cSStanislav Gatev       unless(hasDeclaration(
107092a530cSStanislav Gatev           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
108092a530cSStanislav Gatev       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
109092a530cSStanislav Gatev }
110092a530cSStanislav Gatev 
111b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
112b000b770SStanislav Gatev   return cxxOperatorCallExpr(
113b000b770SStanislav Gatev       hasOverloadedOperatorName("="),
114b000b770SStanislav Gatev       callee(cxxMethodDecl(ofClass(optionalClass()))),
115b000b770SStanislav Gatev       unless(hasDeclaration(cxxMethodDecl(
116b000b770SStanislav Gatev           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
117b000b770SStanislav Gatev       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
118b000b770SStanislav Gatev }
119b000b770SStanislav Gatev 
120b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
121b000b770SStanislav Gatev   return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
122b000b770SStanislav Gatev                              callee(cxxMethodDecl(ofClass(optionalClass()))),
123b000b770SStanislav Gatev                              argumentCountIs(2),
124b000b770SStanislav Gatev                              hasArgument(1, hasNulloptType()));
125b000b770SStanislav Gatev }
126b000b770SStanislav Gatev 
1272ddd57aeSStanislav Gatev auto isStdSwapCall() {
1282ddd57aeSStanislav Gatev   return callExpr(callee(functionDecl(hasName("std::swap"))),
1292ddd57aeSStanislav Gatev                   argumentCountIs(2), hasArgument(0, hasOptionalType()),
1302ddd57aeSStanislav Gatev                   hasArgument(1, hasOptionalType()));
1312ddd57aeSStanislav Gatev }
1322ddd57aeSStanislav Gatev 
1337f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
1347f076004SYitzhak Mandelbaum 
1357f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() {
1367f076004SYitzhak Mandelbaum   // `opt.value_or("").empty()`
1377f076004SYitzhak Mandelbaum   return cxxMemberCallExpr(
1387f076004SYitzhak Mandelbaum       callee(cxxMethodDecl(hasName("empty"))),
1397f076004SYitzhak Mandelbaum       onImplicitObjectArgument(ignoringImplicit(
1407f076004SYitzhak Mandelbaum           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1417f076004SYitzhak Mandelbaum                             callee(cxxMethodDecl(hasName("value_or"),
1427f076004SYitzhak Mandelbaum                                                  ofClass(optionalClass()))),
1437f076004SYitzhak Mandelbaum                             hasArgument(0, stringLiteral(hasSize(0))))
1447f076004SYitzhak Mandelbaum               .bind(ValueOrCallID))));
1457f076004SYitzhak Mandelbaum }
1467f076004SYitzhak Mandelbaum 
1477f076004SYitzhak Mandelbaum auto isValueOrNotEqX() {
1487f076004SYitzhak Mandelbaum   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
1497f076004SYitzhak Mandelbaum     return hasOperands(
1507f076004SYitzhak Mandelbaum         ignoringImplicit(
1517f076004SYitzhak Mandelbaum             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1527f076004SYitzhak Mandelbaum                               callee(cxxMethodDecl(hasName("value_or"),
1537f076004SYitzhak Mandelbaum                                                    ofClass(optionalClass()))),
1547f076004SYitzhak Mandelbaum                               hasArgument(0, Arg))
1557f076004SYitzhak Mandelbaum                 .bind(ValueOrCallID)),
1567f076004SYitzhak Mandelbaum         ignoringImplicit(Arg));
1577f076004SYitzhak Mandelbaum   };
1587f076004SYitzhak Mandelbaum 
1597f076004SYitzhak Mandelbaum   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
1607f076004SYitzhak Mandelbaum   // support this pattern for any expression, but the AST does not have a
1617f076004SYitzhak Mandelbaum   // generic expression comparison facility, so we specialize to common cases
1627f076004SYitzhak Mandelbaum   // seen in practice.  FIXME: define a matcher that compares values across
1637f076004SYitzhak Mandelbaum   // nodes, which would let us generalize this to any `X`.
1647f076004SYitzhak Mandelbaum   return binaryOperation(hasOperatorName("!="),
1657f076004SYitzhak Mandelbaum                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
1667f076004SYitzhak Mandelbaum                                ComparesToSame(stringLiteral(hasSize(0))),
1677f076004SYitzhak Mandelbaum                                ComparesToSame(integerLiteral(equals(0)))));
1687f076004SYitzhak Mandelbaum }
1697f076004SYitzhak Mandelbaum 
17065e710c3SStanislav Gatev auto isCallReturningOptional() {
171cd0d5261SSam Estep   return callExpr(hasType(qualType(anyOf(
172cd0d5261SSam Estep       optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
17365e710c3SStanislav Gatev }
17465e710c3SStanislav Gatev 
1758fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
1768fcdd625SStanislav Gatev /// property of the optional value `OptionalVal`.
1778fcdd625SStanislav Gatev void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
1788fcdd625SStanislav Gatev   OptionalVal.setProperty("has_value", HasValueVal);
1798fcdd625SStanislav Gatev }
1808fcdd625SStanislav Gatev 
1819e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the
1829e0fc676SStanislav Gatev /// symbolic value of its "has_value" property.
1839e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
1849e0fc676SStanislav Gatev   auto OptionalVal = std::make_unique<StructValue>();
1858fcdd625SStanislav Gatev   setHasValue(*OptionalVal, HasValueVal);
1869e0fc676SStanislav Gatev   return Env.takeOwnership(std::move(OptionalVal));
1879e0fc676SStanislav Gatev }
1889e0fc676SStanislav Gatev 
189af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
19049ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
191dd38caf3SYitzhak Mandelbaum BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
192dd38caf3SYitzhak Mandelbaum   if (OptionalVal != nullptr) {
193dd38caf3SYitzhak Mandelbaum     auto *HasValueVal =
194dd38caf3SYitzhak Mandelbaum         cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
195dd38caf3SYitzhak Mandelbaum     if (HasValueVal == nullptr) {
196dd38caf3SYitzhak Mandelbaum       HasValueVal = &Env.makeAtomicBoolValue();
197dd38caf3SYitzhak Mandelbaum       OptionalVal->setProperty("has_value", *HasValueVal);
198dd38caf3SYitzhak Mandelbaum     }
199dd38caf3SYitzhak Mandelbaum     return HasValueVal;
200af98b0afSStanislav Gatev   }
201af98b0afSStanislav Gatev   return nullptr;
202af98b0afSStanislav Gatev }
203af98b0afSStanislav Gatev 
204092a530cSStanislav Gatev /// If `Type` is a reference type, returns the type of its pointee. Otherwise,
205092a530cSStanislav Gatev /// returns `Type` itself.
206092a530cSStanislav Gatev QualType stripReference(QualType Type) {
207092a530cSStanislav Gatev   return Type->isReferenceType() ? Type->getPointeeType() : Type;
208092a530cSStanislav Gatev }
209092a530cSStanislav Gatev 
210092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type.
211c0725865SYitzhak Mandelbaum bool isOptionalType(QualType Type) {
212092a530cSStanislav Gatev   if (!Type->isRecordType())
213092a530cSStanislav Gatev     return false;
214092a530cSStanislav Gatev   // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call.
215092a530cSStanislav Gatev   auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString();
216092a530cSStanislav Gatev   return TypeName == "std::optional" || TypeName == "absl::optional" ||
217092a530cSStanislav Gatev          TypeName == "base::Optional";
218092a530cSStanislav Gatev }
219092a530cSStanislav Gatev 
220092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
221092a530cSStanislav Gatev ///
222092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
223092a530cSStanislav Gatev /// function will be 2.
224092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
225c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
226092a530cSStanislav Gatev     return 0;
227092a530cSStanislav Gatev   return 1 + countOptionalWrappers(
228092a530cSStanislav Gatev                  ASTCtx,
229092a530cSStanislav Gatev                  cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
230092a530cSStanislav Gatev                      ->getTemplateArgs()
231092a530cSStanislav Gatev                      .get(0)
232092a530cSStanislav Gatev                      .getAsType()
233092a530cSStanislav Gatev                      .getDesugaredType(ASTCtx));
234092a530cSStanislav Gatev }
235092a530cSStanislav Gatev 
236dd38caf3SYitzhak Mandelbaum /// Tries to initialize the `optional`'s value (that is, contents), and return
237dd38caf3SYitzhak Mandelbaum /// its location. Returns nullptr if the value can't be represented.
238dd38caf3SYitzhak Mandelbaum StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
239dd38caf3SYitzhak Mandelbaum                                                     Value &OptionalVal,
240dd38caf3SYitzhak Mandelbaum                                                     Environment &Env) {
241dd38caf3SYitzhak Mandelbaum   // The "value" property represents a synthetic field. As such, it needs
242dd38caf3SYitzhak Mandelbaum   // `StorageLocation`, like normal fields (and other variables). So, we model
243dd38caf3SYitzhak Mandelbaum   // it with a `ReferenceValue`, since that includes a storage location.  Once
244dd38caf3SYitzhak Mandelbaum   // the property is set, it will be shared by all environments that access the
245dd38caf3SYitzhak Mandelbaum   // `Value` representing the optional (here, `OptionalVal`).
246dd38caf3SYitzhak Mandelbaum   if (auto *ValueProp = OptionalVal.getProperty("value")) {
247dd38caf3SYitzhak Mandelbaum     auto *ValueRef = clang::cast<ReferenceValue>(ValueProp);
24897d69cdaSWei Yi Tee     auto &ValueLoc = ValueRef->getReferentLoc();
249dd38caf3SYitzhak Mandelbaum     if (Env.getValue(ValueLoc) == nullptr) {
250dd38caf3SYitzhak Mandelbaum       // The property was previously set, but the value has been lost. This can
251dd38caf3SYitzhak Mandelbaum       // happen, for example, because of an environment merge (where the two
252dd38caf3SYitzhak Mandelbaum       // environments mapped the property to different values, which resulted in
253dd38caf3SYitzhak Mandelbaum       // them both being discarded), or when two blocks in the CFG, with neither
254dd38caf3SYitzhak Mandelbaum       // a dominator of the other, visit the same optional value, or even when a
255dd38caf3SYitzhak Mandelbaum       // block is revisited during testing to collect per-statement state.
256dd38caf3SYitzhak Mandelbaum       // FIXME: This situation means that the optional contents are not shared
257dd38caf3SYitzhak Mandelbaum       // between branches and the like. Practically, this lack of sharing
258dd38caf3SYitzhak Mandelbaum       // reduces the precision of the model when the contents are relevant to
259dd38caf3SYitzhak Mandelbaum       // the check, like another optional or a boolean that influences control
260dd38caf3SYitzhak Mandelbaum       // flow.
261dd38caf3SYitzhak Mandelbaum       auto *ValueVal = Env.createValue(ValueLoc.getType());
262dd38caf3SYitzhak Mandelbaum       if (ValueVal == nullptr)
263dd38caf3SYitzhak Mandelbaum         return nullptr;
264dd38caf3SYitzhak Mandelbaum       Env.setValue(ValueLoc, *ValueVal);
265dd38caf3SYitzhak Mandelbaum     }
266dd38caf3SYitzhak Mandelbaum     return &ValueLoc;
267dd38caf3SYitzhak Mandelbaum   }
268dd38caf3SYitzhak Mandelbaum 
269dd38caf3SYitzhak Mandelbaum   auto Ty = stripReference(Q);
270dd38caf3SYitzhak Mandelbaum   auto *ValueVal = Env.createValue(Ty);
271dd38caf3SYitzhak Mandelbaum   if (ValueVal == nullptr)
272dd38caf3SYitzhak Mandelbaum     return nullptr;
273dd38caf3SYitzhak Mandelbaum   auto &ValueLoc = Env.createStorageLocation(Ty);
274dd38caf3SYitzhak Mandelbaum   Env.setValue(ValueLoc, *ValueVal);
275dd38caf3SYitzhak Mandelbaum   auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc);
276dd38caf3SYitzhak Mandelbaum   OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef)));
277dd38caf3SYitzhak Mandelbaum   return &ValueLoc;
278dd38caf3SYitzhak Mandelbaum }
279dd38caf3SYitzhak Mandelbaum 
280092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr,
281092a530cSStanislav Gatev                                  const MatchFinder::MatchResult &,
282af98b0afSStanislav Gatev                                  LatticeTransferState &State) {
28349ed5bf5SWei Yi Tee   if (auto *OptionalVal =
28449ed5bf5SWei Yi Tee           State.Env.getValue(*OptionalExpr, SkipPast::Reference)) {
285af98b0afSStanislav Gatev     if (OptionalVal->getProperty("has_value") == nullptr) {
2868fcdd625SStanislav Gatev       setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
287af98b0afSStanislav Gatev     }
288af98b0afSStanislav Gatev   }
289af98b0afSStanislav Gatev }
290af98b0afSStanislav Gatev 
2918fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
2928fcdd625SStanislav Gatev /// empty in `Env.
2938fcdd625SStanislav Gatev bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
2948fcdd625SStanislav Gatev   auto *HasValueVal =
2958fcdd625SStanislav Gatev       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
2968fcdd625SStanislav Gatev   return HasValueVal != nullptr &&
2978fcdd625SStanislav Gatev          Env.flowConditionImplies(Env.makeNot(*HasValueVal));
2988fcdd625SStanislav Gatev }
2998fcdd625SStanislav Gatev 
3008fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
3018fcdd625SStanislav Gatev /// non-empty in `Env.
3028fcdd625SStanislav Gatev bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
3038fcdd625SStanislav Gatev   auto *HasValueVal =
3048fcdd625SStanislav Gatev       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
3058fcdd625SStanislav Gatev   return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal);
3068fcdd625SStanislav Gatev }
3078fcdd625SStanislav Gatev 
308092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
309af98b0afSStanislav Gatev                         LatticeTransferState &State) {
31049ed5bf5SWei Yi Tee   if (auto *OptionalVal =
31149ed5bf5SWei Yi Tee           State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
312dd38caf3SYitzhak Mandelbaum     if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
313dd38caf3SYitzhak Mandelbaum       if (auto *Loc = maybeInitializeOptionalValueMember(
314dd38caf3SYitzhak Mandelbaum               UnwrapExpr->getType(), *OptionalVal, State.Env))
315dd38caf3SYitzhak Mandelbaum         State.Env.setStorageLocation(*UnwrapExpr, *Loc);
316af98b0afSStanislav Gatev   }
317dd38caf3SYitzhak Mandelbaum }
318af98b0afSStanislav Gatev 
319092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
320092a530cSStanislav Gatev                               const MatchFinder::MatchResult &,
321092a530cSStanislav Gatev                               LatticeTransferState &State) {
3229e0fc676SStanislav Gatev   auto &Loc = State.Env.createStorageLocation(*E);
3239e0fc676SStanislav Gatev   State.Env.setStorageLocation(*E, Loc);
3249e0fc676SStanislav Gatev   State.Env.setValue(
3259e0fc676SStanislav Gatev       Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
3269e0fc676SStanislav Gatev }
3279e0fc676SStanislav Gatev 
328092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
329092a530cSStanislav Gatev                                   const MatchFinder::MatchResult &,
330af98b0afSStanislav Gatev                                   LatticeTransferState &State) {
331dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal = getHasValue(
332dd38caf3SYitzhak Mandelbaum           State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
333af98b0afSStanislav Gatev                                         SkipPast::ReferenceThenPointer))) {
334af98b0afSStanislav Gatev     auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
335af98b0afSStanislav Gatev     State.Env.setValue(CallExprLoc, *HasValueVal);
336af98b0afSStanislav Gatev     State.Env.setStorageLocation(*CallExpr, CallExprLoc);
337af98b0afSStanislav Gatev   }
338af98b0afSStanislav Gatev }
339af98b0afSStanislav Gatev 
3407f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in
3417f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property.
3427f076004SYitzhak Mandelbaum void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
3437f076004SYitzhak Mandelbaum                          const MatchFinder::MatchResult &Result,
3447f076004SYitzhak Mandelbaum                          LatticeTransferState &State,
3457f076004SYitzhak Mandelbaum                          BoolValue &(*ModelPred)(Environment &Env,
3467f076004SYitzhak Mandelbaum                                                  BoolValue &ExprVal,
3477f076004SYitzhak Mandelbaum                                                  BoolValue &HasValueVal)) {
3487f076004SYitzhak Mandelbaum   auto &Env = State.Env;
3497f076004SYitzhak Mandelbaum 
3507f076004SYitzhak Mandelbaum   const auto *ObjectArgumentExpr =
3517f076004SYitzhak Mandelbaum       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
3527f076004SYitzhak Mandelbaum           ->getImplicitObjectArgument();
3537f076004SYitzhak Mandelbaum 
354dd38caf3SYitzhak Mandelbaum   auto *HasValueVal = getHasValue(
355dd38caf3SYitzhak Mandelbaum       State.Env,
356dd38caf3SYitzhak Mandelbaum       State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
357dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr)
3587f076004SYitzhak Mandelbaum     return;
3597f076004SYitzhak Mandelbaum 
3607f076004SYitzhak Mandelbaum   auto *ExprValue = cast_or_null<BoolValue>(
3617f076004SYitzhak Mandelbaum       State.Env.getValue(*ValueOrPredExpr, SkipPast::None));
3627f076004SYitzhak Mandelbaum   if (ExprValue == nullptr) {
3637f076004SYitzhak Mandelbaum     auto &ExprLoc = State.Env.createStorageLocation(*ValueOrPredExpr);
3647f076004SYitzhak Mandelbaum     ExprValue = &State.Env.makeAtomicBoolValue();
3657f076004SYitzhak Mandelbaum     State.Env.setValue(ExprLoc, *ExprValue);
3667f076004SYitzhak Mandelbaum     State.Env.setStorageLocation(*ValueOrPredExpr, ExprLoc);
3677f076004SYitzhak Mandelbaum   }
3687f076004SYitzhak Mandelbaum 
3697f076004SYitzhak Mandelbaum   Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal));
3707f076004SYitzhak Mandelbaum }
3717f076004SYitzhak Mandelbaum 
3727f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
3737f076004SYitzhak Mandelbaum                                     const MatchFinder::MatchResult &Result,
3747f076004SYitzhak Mandelbaum                                     LatticeTransferState &State) {
3757f076004SYitzhak Mandelbaum   return transferValueOrImpl(ComparisonExpr, Result, State,
3767f076004SYitzhak Mandelbaum                              [](Environment &Env, BoolValue &ExprVal,
3777f076004SYitzhak Mandelbaum                                 BoolValue &HasValueVal) -> BoolValue & {
3787f076004SYitzhak Mandelbaum                                // If the result is *not* empty, then we know the
3797f076004SYitzhak Mandelbaum                                // optional must have been holding a value. If
3807f076004SYitzhak Mandelbaum                                // `ExprVal` is true, though, we don't learn
3817f076004SYitzhak Mandelbaum                                // anything definite about `has_value`, so we
3827f076004SYitzhak Mandelbaum                                // don't add any corresponding implications to
3837f076004SYitzhak Mandelbaum                                // the flow condition.
3847f076004SYitzhak Mandelbaum                                return Env.makeImplication(Env.makeNot(ExprVal),
3857f076004SYitzhak Mandelbaum                                                           HasValueVal);
3867f076004SYitzhak Mandelbaum                              });
3877f076004SYitzhak Mandelbaum }
3887f076004SYitzhak Mandelbaum 
3897f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr,
3907f076004SYitzhak Mandelbaum                            const MatchFinder::MatchResult &Result,
3917f076004SYitzhak Mandelbaum                            LatticeTransferState &State) {
3927f076004SYitzhak Mandelbaum   transferValueOrImpl(ComparisonExpr, Result, State,
3937f076004SYitzhak Mandelbaum                       [](Environment &Env, BoolValue &ExprVal,
3947f076004SYitzhak Mandelbaum                          BoolValue &HasValueVal) -> BoolValue & {
3957f076004SYitzhak Mandelbaum                         // We know that if `(opt.value_or(X) != X)` then
3967f076004SYitzhak Mandelbaum                         // `opt.hasValue()`, even without knowing further
3977f076004SYitzhak Mandelbaum                         // details about the contents of `opt`.
3987f076004SYitzhak Mandelbaum                         return Env.makeImplication(ExprVal, HasValueVal);
3997f076004SYitzhak Mandelbaum                       });
4007f076004SYitzhak Mandelbaum }
4017f076004SYitzhak Mandelbaum 
40265e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E,
40365e710c3SStanislav Gatev                                    const MatchFinder::MatchResult &Result,
40465e710c3SStanislav Gatev                                    LatticeTransferState &State) {
40565e710c3SStanislav Gatev   if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
40665e710c3SStanislav Gatev     return;
40765e710c3SStanislav Gatev 
40865e710c3SStanislav Gatev   auto &Loc = State.Env.createStorageLocation(*E);
40965e710c3SStanislav Gatev   State.Env.setStorageLocation(*E, Loc);
41065e710c3SStanislav Gatev   State.Env.setValue(
41165e710c3SStanislav Gatev       Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()));
41265e710c3SStanislav Gatev }
41365e710c3SStanislav Gatev 
414092a530cSStanislav Gatev void assignOptionalValue(const Expr &E, LatticeTransferState &State,
415092a530cSStanislav Gatev                          BoolValue &HasValueVal) {
416092a530cSStanislav Gatev   if (auto *OptionalLoc =
417092a530cSStanislav Gatev           State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
418092a530cSStanislav Gatev     State.Env.setValue(*OptionalLoc,
419092a530cSStanislav Gatev                        createOptionalValue(State.Env, HasValueVal));
4209e0fc676SStanislav Gatev   }
4219e0fc676SStanislav Gatev }
4229e0fc676SStanislav Gatev 
423b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
424b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
425b000b770SStanislav Gatev /// where `T` is constructible from `U`.
42606decd0bSKazu Hirata BoolValue &value_orConversionHasValue(const FunctionDecl &F, const Expr &E,
427b000b770SStanislav Gatev                                       const MatchFinder::MatchResult &MatchRes,
428b000b770SStanislav Gatev                                       LatticeTransferState &State) {
429b000b770SStanislav Gatev   assert(F.getTemplateSpecializationArgs()->size() > 0);
430b000b770SStanislav Gatev 
431b000b770SStanislav Gatev   const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
432b000b770SStanislav Gatev       *MatchRes.Context,
433b000b770SStanislav Gatev       stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType()));
434b000b770SStanislav Gatev   const int ArgTypeOptionalWrappersCount =
435b000b770SStanislav Gatev       countOptionalWrappers(*MatchRes.Context, stripReference(E.getType()));
436b000b770SStanislav Gatev 
437b000b770SStanislav Gatev   // Check if this is a constructor/assignment call for `optional<T>` with
438b000b770SStanislav Gatev   // argument of type `U` such that `T` is constructible from `U`.
439b000b770SStanislav Gatev   if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
440b000b770SStanislav Gatev     return State.Env.getBoolLiteralValue(true);
441b000b770SStanislav Gatev 
442b000b770SStanislav Gatev   // This is a constructor/assignment call for `optional<T>` with argument of
443b000b770SStanislav Gatev   // type `optional<U>` such that `T` is constructible from `U`.
444dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal =
445dd38caf3SYitzhak Mandelbaum           getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference)))
446dd38caf3SYitzhak Mandelbaum     return *HasValueVal;
447b000b770SStanislav Gatev   return State.Env.makeAtomicBoolValue();
448b000b770SStanislav Gatev }
449b000b770SStanislav Gatev 
450092a530cSStanislav Gatev void transferValueOrConversionConstructor(
451092a530cSStanislav Gatev     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
4529e0fc676SStanislav Gatev     LatticeTransferState &State) {
453092a530cSStanislav Gatev   assert(E->getNumArgs() > 0);
454092a530cSStanislav Gatev 
455b000b770SStanislav Gatev   assignOptionalValue(*E, State,
45606decd0bSKazu Hirata                       value_orConversionHasValue(*E->getConstructor(),
457b000b770SStanislav Gatev                                                  *E->getArg(0), MatchRes,
458b000b770SStanislav Gatev                                                  State));
459b000b770SStanislav Gatev }
460092a530cSStanislav Gatev 
461b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
462b000b770SStanislav Gatev                         LatticeTransferState &State) {
463b000b770SStanislav Gatev   assert(E->getNumArgs() > 0);
464b000b770SStanislav Gatev 
465b000b770SStanislav Gatev   auto *OptionalLoc =
466b000b770SStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
467a9ad689eSSam Estep   if (OptionalLoc == nullptr)
468a9ad689eSSam Estep     return;
469b000b770SStanislav Gatev 
470b000b770SStanislav Gatev   State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
471b000b770SStanislav Gatev 
472b000b770SStanislav Gatev   // Assign a storage location for the whole expression.
473b000b770SStanislav Gatev   State.Env.setStorageLocation(*E, *OptionalLoc);
474b000b770SStanislav Gatev }
475b000b770SStanislav Gatev 
476b000b770SStanislav Gatev void transferValueOrConversionAssignment(
477b000b770SStanislav Gatev     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
478b000b770SStanislav Gatev     LatticeTransferState &State) {
479b000b770SStanislav Gatev   assert(E->getNumArgs() > 1);
480b000b770SStanislav Gatev   transferAssignment(E,
48106decd0bSKazu Hirata                      value_orConversionHasValue(*E->getDirectCallee(),
48206decd0bSKazu Hirata                                                 *E->getArg(1), MatchRes, State),
483b000b770SStanislav Gatev                      State);
484b000b770SStanislav Gatev }
485b000b770SStanislav Gatev 
486b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
487b000b770SStanislav Gatev                                const MatchFinder::MatchResult &,
488b000b770SStanislav Gatev                                LatticeTransferState &State) {
489b000b770SStanislav Gatev   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
4909e0fc676SStanislav Gatev }
4919e0fc676SStanislav Gatev 
4922ddd57aeSStanislav Gatev void transferSwap(const StorageLocation &OptionalLoc1,
4932ddd57aeSStanislav Gatev                   const StorageLocation &OptionalLoc2,
4942ddd57aeSStanislav Gatev                   LatticeTransferState &State) {
4952ddd57aeSStanislav Gatev   auto *OptionalVal1 = State.Env.getValue(OptionalLoc1);
4962ddd57aeSStanislav Gatev   assert(OptionalVal1 != nullptr);
4972ddd57aeSStanislav Gatev 
4982ddd57aeSStanislav Gatev   auto *OptionalVal2 = State.Env.getValue(OptionalLoc2);
4992ddd57aeSStanislav Gatev   assert(OptionalVal2 != nullptr);
5002ddd57aeSStanislav Gatev 
5012ddd57aeSStanislav Gatev   State.Env.setValue(OptionalLoc1, *OptionalVal2);
5022ddd57aeSStanislav Gatev   State.Env.setValue(OptionalLoc2, *OptionalVal1);
5032ddd57aeSStanislav Gatev }
5042ddd57aeSStanislav Gatev 
5052ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
5062ddd57aeSStanislav Gatev                       const MatchFinder::MatchResult &,
5072ddd57aeSStanislav Gatev                       LatticeTransferState &State) {
5082ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 1);
5092ddd57aeSStanislav Gatev 
5102ddd57aeSStanislav Gatev   auto *OptionalLoc1 = State.Env.getStorageLocation(
5112ddd57aeSStanislav Gatev       *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer);
5122ddd57aeSStanislav Gatev   assert(OptionalLoc1 != nullptr);
5132ddd57aeSStanislav Gatev 
5142ddd57aeSStanislav Gatev   auto *OptionalLoc2 =
5152ddd57aeSStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
5162ddd57aeSStanislav Gatev   assert(OptionalLoc2 != nullptr);
5172ddd57aeSStanislav Gatev 
5182ddd57aeSStanislav Gatev   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
5192ddd57aeSStanislav Gatev }
5202ddd57aeSStanislav Gatev 
5212ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
5222ddd57aeSStanislav Gatev                          LatticeTransferState &State) {
5232ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 2);
5242ddd57aeSStanislav Gatev 
5252ddd57aeSStanislav Gatev   auto *OptionalLoc1 =
5262ddd57aeSStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
5272ddd57aeSStanislav Gatev   assert(OptionalLoc1 != nullptr);
5282ddd57aeSStanislav Gatev 
5292ddd57aeSStanislav Gatev   auto *OptionalLoc2 =
5302ddd57aeSStanislav Gatev       State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference);
5312ddd57aeSStanislav Gatev   assert(OptionalLoc2 != nullptr);
5322ddd57aeSStanislav Gatev 
5332ddd57aeSStanislav Gatev   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
5342ddd57aeSStanislav Gatev }
5352ddd57aeSStanislav Gatev 
536a184a0d8SYitzhak Mandelbaum llvm::Optional<StatementMatcher>
537a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
538a184a0d8SYitzhak Mandelbaum   if (Options.IgnoreSmartPointerDereference)
539a184a0d8SYitzhak Mandelbaum     return memberExpr(hasObjectExpression(ignoringParenImpCasts(
540a184a0d8SYitzhak Mandelbaum         cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("->"),
541a184a0d8SYitzhak Mandelbaum                                   hasOverloadedOperatorName("*")),
542a184a0d8SYitzhak Mandelbaum                             unless(hasArgument(0, expr(hasOptionalType())))))));
543*34e0d057SKazu Hirata   return std::nullopt;
544a184a0d8SYitzhak Mandelbaum }
545a184a0d8SYitzhak Mandelbaum 
54658fe7f96SSam Estep StatementMatcher
54758fe7f96SSam Estep valueCall(llvm::Optional<StatementMatcher> &IgnorableOptional) {
54858fe7f96SSam Estep   return isOptionalMemberCallWithName("value", IgnorableOptional);
54958fe7f96SSam Estep }
55058fe7f96SSam Estep 
55158fe7f96SSam Estep StatementMatcher
55258fe7f96SSam Estep valueOperatorCall(llvm::Optional<StatementMatcher> &IgnorableOptional) {
55358fe7f96SSam Estep   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
55458fe7f96SSam Estep                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
55558fe7f96SSam Estep }
55658fe7f96SSam Estep 
557a184a0d8SYitzhak Mandelbaum auto buildTransferMatchSwitch(
558a184a0d8SYitzhak Mandelbaum     const UncheckedOptionalAccessModelOptions &Options) {
559b000b770SStanislav Gatev   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
560b000b770SStanislav Gatev   // lot of duplicated work (e.g. string comparisons), consider providing APIs
561b000b770SStanislav Gatev   // that avoid it through memoization.
562a184a0d8SYitzhak Mandelbaum   auto IgnorableOptional = ignorableOptional(Options);
5637538b360SWei Yi Tee   return CFGMatchSwitchBuilder<LatticeTransferState>()
564af98b0afSStanislav Gatev       // Attach a symbolic "has_value" state to optional values that we see for
565af98b0afSStanislav Gatev       // the first time.
5667538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(
5676adfc64eSYitzhak Mandelbaum           expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
568af98b0afSStanislav Gatev           initializeOptionalReference)
569af98b0afSStanislav Gatev 
5709e0fc676SStanislav Gatev       // make_optional
5717538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
572092a530cSStanislav Gatev 
573b000b770SStanislav Gatev       // optional::optional
5747538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
575092a530cSStanislav Gatev           isOptionalInPlaceConstructor(),
576092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
577092a530cSStanislav Gatev              LatticeTransferState &State) {
578092a530cSStanislav Gatev             assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true));
579092a530cSStanislav Gatev           })
5807538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
581092a530cSStanislav Gatev           isOptionalNulloptConstructor(),
582092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
583092a530cSStanislav Gatev              LatticeTransferState &State) {
584092a530cSStanislav Gatev             assignOptionalValue(*E, State,
585092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
586092a530cSStanislav Gatev           })
5877538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
588092a530cSStanislav Gatev                                        transferValueOrConversionConstructor)
5899e0fc676SStanislav Gatev 
590b000b770SStanislav Gatev       // optional::operator=
5917538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(
5927538b360SWei Yi Tee           isOptionalValueOrConversionAssignment(),
593b000b770SStanislav Gatev           transferValueOrConversionAssignment)
5947538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
595b000b770SStanislav Gatev                                           transferNulloptAssignment)
596b000b770SStanislav Gatev 
597af98b0afSStanislav Gatev       // optional::value
5987538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
59958fe7f96SSam Estep           valueCall(IgnorableOptional),
600092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
601092a530cSStanislav Gatev              LatticeTransferState &State) {
602af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
603af98b0afSStanislav Gatev           })
604af98b0afSStanislav Gatev 
605af98b0afSStanislav Gatev       // optional::operator*, optional::operator->
6067538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
6077538b360SWei Yi Tee                                [](const CallExpr *E,
6087538b360SWei Yi Tee                                   const MatchFinder::MatchResult &,
609092a530cSStanislav Gatev                                   LatticeTransferState &State) {
610af98b0afSStanislav Gatev                                  transferUnwrapCall(E, E->getArg(0), State);
611af98b0afSStanislav Gatev                                })
612af98b0afSStanislav Gatev 
613af98b0afSStanislav Gatev       // optional::has_value
6147538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
6157538b360SWei Yi Tee           isOptionalMemberCallWithName("has_value"),
616af98b0afSStanislav Gatev           transferOptionalHasValueCall)
617af98b0afSStanislav Gatev 
6189e0fc676SStanislav Gatev       // optional::operator bool
6197538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
6207538b360SWei Yi Tee           isOptionalMemberCallWithName("operator bool"),
6219e0fc676SStanislav Gatev           transferOptionalHasValueCall)
6229e0fc676SStanislav Gatev 
6239e0fc676SStanislav Gatev       // optional::emplace
6247538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
625092a530cSStanislav Gatev           isOptionalMemberCallWithName("emplace"),
626092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
627092a530cSStanislav Gatev              LatticeTransferState &State) {
628092a530cSStanislav Gatev             assignOptionalValue(*E->getImplicitObjectArgument(), State,
629092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(true));
630092a530cSStanislav Gatev           })
6319e0fc676SStanislav Gatev 
6329e0fc676SStanislav Gatev       // optional::reset
6337538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
634092a530cSStanislav Gatev           isOptionalMemberCallWithName("reset"),
635092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
636092a530cSStanislav Gatev              LatticeTransferState &State) {
637092a530cSStanislav Gatev             assignOptionalValue(*E->getImplicitObjectArgument(), State,
638092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
639092a530cSStanislav Gatev           })
6409e0fc676SStanislav Gatev 
6412ddd57aeSStanislav Gatev       // optional::swap
6427538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
6432ddd57aeSStanislav Gatev                                         transferSwapCall)
6442ddd57aeSStanislav Gatev 
6452ddd57aeSStanislav Gatev       // std::swap
6467538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
6472ddd57aeSStanislav Gatev 
6487f076004SYitzhak Mandelbaum       // opt.value_or("").empty()
6497538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
6507538b360SWei Yi Tee                            transferValueOrStringEmptyCall)
6517f076004SYitzhak Mandelbaum 
6527f076004SYitzhak Mandelbaum       // opt.value_or(X) != X
6537538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
6547f076004SYitzhak Mandelbaum 
65565e710c3SStanislav Gatev       // returns optional
6567538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
65765e710c3SStanislav Gatev                                transferCallReturningOptional)
65865e710c3SStanislav Gatev 
659af98b0afSStanislav Gatev       .Build();
660af98b0afSStanislav Gatev }
661af98b0afSStanislav Gatev 
66258fe7f96SSam Estep std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr,
66358fe7f96SSam Estep                                                const Expr *ObjectExpr,
66458fe7f96SSam Estep                                                const Environment &Env) {
66558fe7f96SSam Estep   if (auto *OptionalVal =
66658fe7f96SSam Estep           Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
66758fe7f96SSam Estep     auto *Prop = OptionalVal->getProperty("has_value");
66858fe7f96SSam Estep     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
66958fe7f96SSam Estep       if (Env.flowConditionImplies(*HasValueVal))
67058fe7f96SSam Estep         return {};
67158fe7f96SSam Estep     }
67258fe7f96SSam Estep   }
67358fe7f96SSam Estep 
67458fe7f96SSam Estep   // Record that this unwrap is *not* provably safe.
67558fe7f96SSam Estep   // FIXME: include either the name of the optional (if applicable) or a source
67658fe7f96SSam Estep   // range of the access for easier interpretation of the result.
67758fe7f96SSam Estep   return {ObjectExpr->getBeginLoc()};
67858fe7f96SSam Estep }
67958fe7f96SSam Estep 
68058fe7f96SSam Estep auto buildDiagnoseMatchSwitch(
68158fe7f96SSam Estep     const UncheckedOptionalAccessModelOptions &Options) {
68258fe7f96SSam Estep   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
68358fe7f96SSam Estep   // lot of duplicated work (e.g. string comparisons), consider providing APIs
68458fe7f96SSam Estep   // that avoid it through memoization.
68558fe7f96SSam Estep   auto IgnorableOptional = ignorableOptional(Options);
6867538b360SWei Yi Tee   return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
68758fe7f96SSam Estep       // optional::value
6887538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
68958fe7f96SSam Estep           valueCall(IgnorableOptional),
69058fe7f96SSam Estep           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
69158fe7f96SSam Estep              const Environment &Env) {
69258fe7f96SSam Estep             return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env);
69358fe7f96SSam Estep           })
69458fe7f96SSam Estep 
69558fe7f96SSam Estep       // optional::operator*, optional::operator->
6967538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(
69758fe7f96SSam Estep           valueOperatorCall(IgnorableOptional),
69858fe7f96SSam Estep           [](const CallExpr *E, const MatchFinder::MatchResult &,
69958fe7f96SSam Estep              const Environment &Env) {
70058fe7f96SSam Estep             return diagnoseUnwrapCall(E, E->getArg(0), Env);
70158fe7f96SSam Estep           })
70258fe7f96SSam Estep       .Build();
70358fe7f96SSam Estep }
70458fe7f96SSam Estep 
705af98b0afSStanislav Gatev } // namespace
706af98b0afSStanislav Gatev 
7077e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher
7087e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() {
7097e63a0d4SYitzhak Mandelbaum   return optionalClass();
7107e63a0d4SYitzhak Mandelbaum }
7117e63a0d4SYitzhak Mandelbaum 
712a184a0d8SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(
713a184a0d8SYitzhak Mandelbaum     ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options)
714cf1f978dSSam Estep     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
715a184a0d8SYitzhak Mandelbaum       TransferMatchSwitch(buildTransferMatchSwitch(Options)) {}
716af98b0afSStanislav Gatev 
7177538b360SWei Yi Tee void UncheckedOptionalAccessModel::transfer(const CFGElement *Elt,
7187538b360SWei Yi Tee                                             NoopLattice &L, Environment &Env) {
719af98b0afSStanislav Gatev   LatticeTransferState State(L, Env);
7207538b360SWei Yi Tee   TransferMatchSwitch(*Elt, getASTContext(), State);
721af98b0afSStanislav Gatev }
722af98b0afSStanislav Gatev 
723c0725865SYitzhak Mandelbaum ComparisonResult UncheckedOptionalAccessModel::compare(
724c0725865SYitzhak Mandelbaum     QualType Type, const Value &Val1, const Environment &Env1,
725c0725865SYitzhak Mandelbaum     const Value &Val2, const Environment &Env2) {
726c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
727c0725865SYitzhak Mandelbaum     return ComparisonResult::Unknown;
728c0725865SYitzhak Mandelbaum   return isNonEmptyOptional(Val1, Env1) == isNonEmptyOptional(Val2, Env2)
729c0725865SYitzhak Mandelbaum              ? ComparisonResult::Same
730c0725865SYitzhak Mandelbaum              : ComparisonResult::Different;
7318fcdd625SStanislav Gatev }
7328fcdd625SStanislav Gatev 
7338fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
7348fcdd625SStanislav Gatev                                          const Environment &Env1,
7358fcdd625SStanislav Gatev                                          const Value &Val2,
7368fcdd625SStanislav Gatev                                          const Environment &Env2,
7378fcdd625SStanislav Gatev                                          Value &MergedVal,
7388fcdd625SStanislav Gatev                                          Environment &MergedEnv) {
739c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
7408fcdd625SStanislav Gatev     return true;
7418fcdd625SStanislav Gatev 
7428fcdd625SStanislav Gatev   auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
7438fcdd625SStanislav Gatev   if (isNonEmptyOptional(Val1, Env1) && isNonEmptyOptional(Val2, Env2))
7448fcdd625SStanislav Gatev     MergedEnv.addToFlowCondition(HasValueVal);
7458fcdd625SStanislav Gatev   else if (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
7468fcdd625SStanislav Gatev     MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal));
7478fcdd625SStanislav Gatev   setHasValue(MergedVal, HasValueVal);
7488fcdd625SStanislav Gatev   return true;
7498fcdd625SStanislav Gatev }
7508fcdd625SStanislav Gatev 
75158fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
75258fe7f96SSam Estep     UncheckedOptionalAccessModelOptions Options)
75358fe7f96SSam Estep     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
75458fe7f96SSam Estep 
75558fe7f96SSam Estep std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose(
7567538b360SWei Yi Tee     ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) {
7577538b360SWei Yi Tee   return DiagnoseMatchSwitch(*Elt, Ctx, Env);
75858fe7f96SSam Estep }
75958fe7f96SSam Estep 
760af98b0afSStanislav Gatev } // namespace dataflow
761af98b0afSStanislav Gatev } // namespace clang
762