xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision 49ed5bf51958aaeb209804da794c85d06207c3ed)
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"
21af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
22af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
23af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h"
24af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h"
25af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h"
26af98b0afSStanislav Gatev #include "llvm/Support/Casting.h"
27af98b0afSStanislav Gatev #include <cassert>
289e0fc676SStanislav Gatev #include <memory>
299e0fc676SStanislav Gatev #include <utility>
30af98b0afSStanislav Gatev 
31af98b0afSStanislav Gatev namespace clang {
32af98b0afSStanislav Gatev namespace dataflow {
33af98b0afSStanislav Gatev namespace {
34af98b0afSStanislav Gatev 
35af98b0afSStanislav Gatev using namespace ::clang::ast_matchers;
36af98b0afSStanislav Gatev using LatticeTransferState = TransferState<SourceLocationsLattice>;
37af98b0afSStanislav Gatev 
387e63a0d4SYitzhak Mandelbaum DeclarationMatcher optionalClass() {
39af98b0afSStanislav Gatev   return classTemplateSpecializationDecl(
40af98b0afSStanislav Gatev       anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
41af98b0afSStanislav Gatev             hasName("__optional_destruct_base"), hasName("absl::optional"),
42af98b0afSStanislav Gatev             hasName("base::Optional")),
43af98b0afSStanislav Gatev       hasTemplateArgument(0, refersToType(type().bind("T"))));
44af98b0afSStanislav Gatev }
45af98b0afSStanislav Gatev 
466adfc64eSYitzhak Mandelbaum auto optionalOrAliasType() {
4765e710c3SStanislav Gatev   return hasUnqualifiedDesugaredType(
4865e710c3SStanislav Gatev       recordType(hasDeclaration(optionalClass())));
4965e710c3SStanislav Gatev }
5065e710c3SStanislav Gatev 
516adfc64eSYitzhak Mandelbaum /// Matches any of the spellings of the optional types and sugar, aliases, etc.
526adfc64eSYitzhak Mandelbaum auto hasOptionalType() { return hasType(optionalOrAliasType()); }
536adfc64eSYitzhak Mandelbaum 
54a184a0d8SYitzhak Mandelbaum auto isOptionalMemberCallWithName(
55a184a0d8SYitzhak Mandelbaum     llvm::StringRef MemberName,
56a184a0d8SYitzhak Mandelbaum     llvm::Optional<StatementMatcher> Ignorable = llvm::None) {
57a184a0d8SYitzhak Mandelbaum   auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr()))
58a184a0d8SYitzhak Mandelbaum                                     : cxxThisExpr());
59af98b0afSStanislav Gatev   return cxxMemberCallExpr(
60a184a0d8SYitzhak Mandelbaum       on(expr(Exception)),
61af98b0afSStanislav Gatev       callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
62af98b0afSStanislav Gatev }
63af98b0afSStanislav Gatev 
64a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName(
65a184a0d8SYitzhak Mandelbaum     llvm::StringRef operator_name,
66a184a0d8SYitzhak Mandelbaum     llvm::Optional<StatementMatcher> Ignorable = llvm::None) {
67a184a0d8SYitzhak Mandelbaum   return cxxOperatorCallExpr(
68a184a0d8SYitzhak Mandelbaum       hasOverloadedOperatorName(operator_name),
69a184a0d8SYitzhak Mandelbaum       callee(cxxMethodDecl(ofClass(optionalClass()))),
70a184a0d8SYitzhak Mandelbaum       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
71af98b0afSStanislav Gatev }
72af98b0afSStanislav Gatev 
73092a530cSStanislav Gatev auto isMakeOptionalCall() {
749e0fc676SStanislav Gatev   return callExpr(
759e0fc676SStanislav Gatev       callee(functionDecl(hasAnyName(
769e0fc676SStanislav Gatev           "std::make_optional", "base::make_optional", "absl::make_optional"))),
779e0fc676SStanislav Gatev       hasOptionalType());
789e0fc676SStanislav Gatev }
799e0fc676SStanislav Gatev 
80092a530cSStanislav Gatev auto hasNulloptType() {
81092a530cSStanislav Gatev   return hasType(namedDecl(
82092a530cSStanislav Gatev       hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")));
83092a530cSStanislav Gatev }
84092a530cSStanislav Gatev 
85092a530cSStanislav Gatev auto inPlaceClass() {
86092a530cSStanislav Gatev   return recordDecl(
87092a530cSStanislav Gatev       hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
88092a530cSStanislav Gatev }
89092a530cSStanislav Gatev 
90092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
91092a530cSStanislav Gatev   return cxxConstructExpr(hasOptionalType(), argumentCountIs(1),
92092a530cSStanislav Gatev                           hasArgument(0, hasNulloptType()));
93092a530cSStanislav Gatev }
94092a530cSStanislav Gatev 
95092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
96092a530cSStanislav Gatev   return cxxConstructExpr(hasOptionalType(),
97092a530cSStanislav Gatev                           hasArgument(0, hasType(inPlaceClass())));
98092a530cSStanislav Gatev }
99092a530cSStanislav Gatev 
100092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
101092a530cSStanislav Gatev   return cxxConstructExpr(
102092a530cSStanislav Gatev       hasOptionalType(),
103092a530cSStanislav Gatev       unless(hasDeclaration(
104092a530cSStanislav Gatev           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
105092a530cSStanislav Gatev       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
106092a530cSStanislav Gatev }
107092a530cSStanislav Gatev 
108b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
109b000b770SStanislav Gatev   return cxxOperatorCallExpr(
110b000b770SStanislav Gatev       hasOverloadedOperatorName("="),
111b000b770SStanislav Gatev       callee(cxxMethodDecl(ofClass(optionalClass()))),
112b000b770SStanislav Gatev       unless(hasDeclaration(cxxMethodDecl(
113b000b770SStanislav Gatev           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
114b000b770SStanislav Gatev       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
115b000b770SStanislav Gatev }
116b000b770SStanislav Gatev 
117b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
118b000b770SStanislav Gatev   return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
119b000b770SStanislav Gatev                              callee(cxxMethodDecl(ofClass(optionalClass()))),
120b000b770SStanislav Gatev                              argumentCountIs(2),
121b000b770SStanislav Gatev                              hasArgument(1, hasNulloptType()));
122b000b770SStanislav Gatev }
123b000b770SStanislav Gatev 
1242ddd57aeSStanislav Gatev auto isStdSwapCall() {
1252ddd57aeSStanislav Gatev   return callExpr(callee(functionDecl(hasName("std::swap"))),
1262ddd57aeSStanislav Gatev                   argumentCountIs(2), hasArgument(0, hasOptionalType()),
1272ddd57aeSStanislav Gatev                   hasArgument(1, hasOptionalType()));
1282ddd57aeSStanislav Gatev }
1292ddd57aeSStanislav Gatev 
1307f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
1317f076004SYitzhak Mandelbaum 
1327f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() {
1337f076004SYitzhak Mandelbaum   // `opt.value_or("").empty()`
1347f076004SYitzhak Mandelbaum   return cxxMemberCallExpr(
1357f076004SYitzhak Mandelbaum       callee(cxxMethodDecl(hasName("empty"))),
1367f076004SYitzhak Mandelbaum       onImplicitObjectArgument(ignoringImplicit(
1377f076004SYitzhak Mandelbaum           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1387f076004SYitzhak Mandelbaum                             callee(cxxMethodDecl(hasName("value_or"),
1397f076004SYitzhak Mandelbaum                                                  ofClass(optionalClass()))),
1407f076004SYitzhak Mandelbaum                             hasArgument(0, stringLiteral(hasSize(0))))
1417f076004SYitzhak Mandelbaum               .bind(ValueOrCallID))));
1427f076004SYitzhak Mandelbaum }
1437f076004SYitzhak Mandelbaum 
1447f076004SYitzhak Mandelbaum auto isValueOrNotEqX() {
1457f076004SYitzhak Mandelbaum   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
1467f076004SYitzhak Mandelbaum     return hasOperands(
1477f076004SYitzhak Mandelbaum         ignoringImplicit(
1487f076004SYitzhak Mandelbaum             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1497f076004SYitzhak Mandelbaum                               callee(cxxMethodDecl(hasName("value_or"),
1507f076004SYitzhak Mandelbaum                                                    ofClass(optionalClass()))),
1517f076004SYitzhak Mandelbaum                               hasArgument(0, Arg))
1527f076004SYitzhak Mandelbaum                 .bind(ValueOrCallID)),
1537f076004SYitzhak Mandelbaum         ignoringImplicit(Arg));
1547f076004SYitzhak Mandelbaum   };
1557f076004SYitzhak Mandelbaum 
1567f076004SYitzhak Mandelbaum   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
1577f076004SYitzhak Mandelbaum   // support this pattern for any expression, but the AST does not have a
1587f076004SYitzhak Mandelbaum   // generic expression comparison facility, so we specialize to common cases
1597f076004SYitzhak Mandelbaum   // seen in practice.  FIXME: define a matcher that compares values across
1607f076004SYitzhak Mandelbaum   // nodes, which would let us generalize this to any `X`.
1617f076004SYitzhak Mandelbaum   return binaryOperation(hasOperatorName("!="),
1627f076004SYitzhak Mandelbaum                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
1637f076004SYitzhak Mandelbaum                                ComparesToSame(stringLiteral(hasSize(0))),
1647f076004SYitzhak Mandelbaum                                ComparesToSame(integerLiteral(equals(0)))));
1657f076004SYitzhak Mandelbaum }
1667f076004SYitzhak Mandelbaum 
16765e710c3SStanislav Gatev auto isCallReturningOptional() {
1686adfc64eSYitzhak Mandelbaum   return callExpr(callee(functionDecl(returns(anyOf(
1696adfc64eSYitzhak Mandelbaum       optionalOrAliasType(), referenceType(pointee(optionalOrAliasType())))))));
17065e710c3SStanislav Gatev }
17165e710c3SStanislav Gatev 
1729e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the
1739e0fc676SStanislav Gatev /// symbolic value of its "has_value" property.
1749e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
1759e0fc676SStanislav Gatev   auto OptionalVal = std::make_unique<StructValue>();
1769e0fc676SStanislav Gatev   OptionalVal->setProperty("has_value", HasValueVal);
1779e0fc676SStanislav Gatev   return Env.takeOwnership(std::move(OptionalVal));
1789e0fc676SStanislav Gatev }
1799e0fc676SStanislav Gatev 
180af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
181*49ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
182*49ed5bf5SWei Yi Tee BoolValue *getHasValue(Value *OptionalVal) {
183*49ed5bf5SWei Yi Tee   if (OptionalVal) {
184af98b0afSStanislav Gatev     return cast<BoolValue>(OptionalVal->getProperty("has_value"));
185af98b0afSStanislav Gatev   }
186af98b0afSStanislav Gatev   return nullptr;
187af98b0afSStanislav Gatev }
188af98b0afSStanislav Gatev 
189092a530cSStanislav Gatev /// If `Type` is a reference type, returns the type of its pointee. Otherwise,
190092a530cSStanislav Gatev /// returns `Type` itself.
191092a530cSStanislav Gatev QualType stripReference(QualType Type) {
192092a530cSStanislav Gatev   return Type->isReferenceType() ? Type->getPointeeType() : Type;
193092a530cSStanislav Gatev }
194092a530cSStanislav Gatev 
195092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type.
196092a530cSStanislav Gatev bool IsOptionalType(QualType Type) {
197092a530cSStanislav Gatev   if (!Type->isRecordType())
198092a530cSStanislav Gatev     return false;
199092a530cSStanislav Gatev   // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call.
200092a530cSStanislav Gatev   auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString();
201092a530cSStanislav Gatev   return TypeName == "std::optional" || TypeName == "absl::optional" ||
202092a530cSStanislav Gatev          TypeName == "base::Optional";
203092a530cSStanislav Gatev }
204092a530cSStanislav Gatev 
205092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
206092a530cSStanislav Gatev ///
207092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
208092a530cSStanislav Gatev /// function will be 2.
209092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
210092a530cSStanislav Gatev   if (!IsOptionalType(Type))
211092a530cSStanislav Gatev     return 0;
212092a530cSStanislav Gatev   return 1 + countOptionalWrappers(
213092a530cSStanislav Gatev                  ASTCtx,
214092a530cSStanislav Gatev                  cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
215092a530cSStanislav Gatev                      ->getTemplateArgs()
216092a530cSStanislav Gatev                      .get(0)
217092a530cSStanislav Gatev                      .getAsType()
218092a530cSStanislav Gatev                      .getDesugaredType(ASTCtx));
219092a530cSStanislav Gatev }
220092a530cSStanislav Gatev 
221092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr,
222092a530cSStanislav Gatev                                  const MatchFinder::MatchResult &,
223af98b0afSStanislav Gatev                                  LatticeTransferState &State) {
224*49ed5bf5SWei Yi Tee   if (auto *OptionalVal =
225*49ed5bf5SWei Yi Tee           State.Env.getValue(*OptionalExpr, SkipPast::Reference)) {
226af98b0afSStanislav Gatev     if (OptionalVal->getProperty("has_value") == nullptr) {
227af98b0afSStanislav Gatev       OptionalVal->setProperty("has_value", State.Env.makeAtomicBoolValue());
228af98b0afSStanislav Gatev     }
229af98b0afSStanislav Gatev   }
230af98b0afSStanislav Gatev }
231af98b0afSStanislav Gatev 
232092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
233af98b0afSStanislav Gatev                         LatticeTransferState &State) {
234*49ed5bf5SWei Yi Tee   if (auto *OptionalVal =
235*49ed5bf5SWei Yi Tee           State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
236af98b0afSStanislav Gatev     auto *HasValueVal = getHasValue(OptionalVal);
237af98b0afSStanislav Gatev     assert(HasValueVal != nullptr);
238af98b0afSStanislav Gatev 
239af98b0afSStanislav Gatev     if (State.Env.flowConditionImplies(*HasValueVal))
240af98b0afSStanislav Gatev       return;
241af98b0afSStanislav Gatev   }
242af98b0afSStanislav Gatev 
243af98b0afSStanislav Gatev   // Record that this unwrap is *not* provably safe.
2447e63a0d4SYitzhak Mandelbaum   // FIXME: include either the name of the optional (if applicable) or a source
2457e63a0d4SYitzhak Mandelbaum   // range of the access for easier interpretation of the result.
246af98b0afSStanislav Gatev   State.Lattice.getSourceLocations().insert(ObjectExpr->getBeginLoc());
247af98b0afSStanislav Gatev }
248af98b0afSStanislav Gatev 
249092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
250092a530cSStanislav Gatev                               const MatchFinder::MatchResult &,
251092a530cSStanislav Gatev                               LatticeTransferState &State) {
2529e0fc676SStanislav Gatev   auto &Loc = State.Env.createStorageLocation(*E);
2539e0fc676SStanislav Gatev   State.Env.setStorageLocation(*E, Loc);
2549e0fc676SStanislav Gatev   State.Env.setValue(
2559e0fc676SStanislav Gatev       Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
2569e0fc676SStanislav Gatev }
2579e0fc676SStanislav Gatev 
258092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
259092a530cSStanislav Gatev                                   const MatchFinder::MatchResult &,
260af98b0afSStanislav Gatev                                   LatticeTransferState &State) {
261af98b0afSStanislav Gatev   if (auto *OptionalVal = cast_or_null<StructValue>(
262af98b0afSStanislav Gatev           State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
263af98b0afSStanislav Gatev                              SkipPast::ReferenceThenPointer))) {
264af98b0afSStanislav Gatev     auto *HasValueVal = getHasValue(OptionalVal);
265af98b0afSStanislav Gatev     assert(HasValueVal != nullptr);
266af98b0afSStanislav Gatev 
267af98b0afSStanislav Gatev     auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
268af98b0afSStanislav Gatev     State.Env.setValue(CallExprLoc, *HasValueVal);
269af98b0afSStanislav Gatev     State.Env.setStorageLocation(*CallExpr, CallExprLoc);
270af98b0afSStanislav Gatev   }
271af98b0afSStanislav Gatev }
272af98b0afSStanislav Gatev 
2737f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in
2747f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property.
2757f076004SYitzhak Mandelbaum void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
2767f076004SYitzhak Mandelbaum                          const MatchFinder::MatchResult &Result,
2777f076004SYitzhak Mandelbaum                          LatticeTransferState &State,
2787f076004SYitzhak Mandelbaum                          BoolValue &(*ModelPred)(Environment &Env,
2797f076004SYitzhak Mandelbaum                                                  BoolValue &ExprVal,
2807f076004SYitzhak Mandelbaum                                                  BoolValue &HasValueVal)) {
2817f076004SYitzhak Mandelbaum   auto &Env = State.Env;
2827f076004SYitzhak Mandelbaum 
2837f076004SYitzhak Mandelbaum   const auto *ObjectArgumentExpr =
2847f076004SYitzhak Mandelbaum       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
2857f076004SYitzhak Mandelbaum           ->getImplicitObjectArgument();
2867f076004SYitzhak Mandelbaum 
2877f076004SYitzhak Mandelbaum   auto *OptionalVal = cast_or_null<StructValue>(
2887f076004SYitzhak Mandelbaum       Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
2897f076004SYitzhak Mandelbaum   if (OptionalVal == nullptr)
2907f076004SYitzhak Mandelbaum     return;
2917f076004SYitzhak Mandelbaum   auto *HasValueVal = getHasValue(OptionalVal);
2927f076004SYitzhak Mandelbaum   assert(HasValueVal != nullptr);
2937f076004SYitzhak Mandelbaum 
2947f076004SYitzhak Mandelbaum   auto *ExprValue = cast_or_null<BoolValue>(
2957f076004SYitzhak Mandelbaum       State.Env.getValue(*ValueOrPredExpr, SkipPast::None));
2967f076004SYitzhak Mandelbaum   if (ExprValue == nullptr) {
2977f076004SYitzhak Mandelbaum     auto &ExprLoc = State.Env.createStorageLocation(*ValueOrPredExpr);
2987f076004SYitzhak Mandelbaum     ExprValue = &State.Env.makeAtomicBoolValue();
2997f076004SYitzhak Mandelbaum     State.Env.setValue(ExprLoc, *ExprValue);
3007f076004SYitzhak Mandelbaum     State.Env.setStorageLocation(*ValueOrPredExpr, ExprLoc);
3017f076004SYitzhak Mandelbaum   }
3027f076004SYitzhak Mandelbaum 
3037f076004SYitzhak Mandelbaum   Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal));
3047f076004SYitzhak Mandelbaum }
3057f076004SYitzhak Mandelbaum 
3067f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
3077f076004SYitzhak Mandelbaum                                     const MatchFinder::MatchResult &Result,
3087f076004SYitzhak Mandelbaum                                     LatticeTransferState &State) {
3097f076004SYitzhak Mandelbaum   return transferValueOrImpl(ComparisonExpr, Result, State,
3107f076004SYitzhak Mandelbaum                              [](Environment &Env, BoolValue &ExprVal,
3117f076004SYitzhak Mandelbaum                                 BoolValue &HasValueVal) -> BoolValue & {
3127f076004SYitzhak Mandelbaum                                // If the result is *not* empty, then we know the
3137f076004SYitzhak Mandelbaum                                // optional must have been holding a value. If
3147f076004SYitzhak Mandelbaum                                // `ExprVal` is true, though, we don't learn
3157f076004SYitzhak Mandelbaum                                // anything definite about `has_value`, so we
3167f076004SYitzhak Mandelbaum                                // don't add any corresponding implications to
3177f076004SYitzhak Mandelbaum                                // the flow condition.
3187f076004SYitzhak Mandelbaum                                return Env.makeImplication(Env.makeNot(ExprVal),
3197f076004SYitzhak Mandelbaum                                                           HasValueVal);
3207f076004SYitzhak Mandelbaum                              });
3217f076004SYitzhak Mandelbaum }
3227f076004SYitzhak Mandelbaum 
3237f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr,
3247f076004SYitzhak Mandelbaum                            const MatchFinder::MatchResult &Result,
3257f076004SYitzhak Mandelbaum                            LatticeTransferState &State) {
3267f076004SYitzhak Mandelbaum   transferValueOrImpl(ComparisonExpr, Result, State,
3277f076004SYitzhak Mandelbaum                       [](Environment &Env, BoolValue &ExprVal,
3287f076004SYitzhak Mandelbaum                          BoolValue &HasValueVal) -> BoolValue & {
3297f076004SYitzhak Mandelbaum                         // We know that if `(opt.value_or(X) != X)` then
3307f076004SYitzhak Mandelbaum                         // `opt.hasValue()`, even without knowing further
3317f076004SYitzhak Mandelbaum                         // details about the contents of `opt`.
3327f076004SYitzhak Mandelbaum                         return Env.makeImplication(ExprVal, HasValueVal);
3337f076004SYitzhak Mandelbaum                       });
3347f076004SYitzhak Mandelbaum }
3357f076004SYitzhak Mandelbaum 
33665e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E,
33765e710c3SStanislav Gatev                                    const MatchFinder::MatchResult &Result,
33865e710c3SStanislav Gatev                                    LatticeTransferState &State) {
33965e710c3SStanislav Gatev   if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
34065e710c3SStanislav Gatev     return;
34165e710c3SStanislav Gatev 
34265e710c3SStanislav Gatev   auto &Loc = State.Env.createStorageLocation(*E);
34365e710c3SStanislav Gatev   State.Env.setStorageLocation(*E, Loc);
34465e710c3SStanislav Gatev   State.Env.setValue(
34565e710c3SStanislav Gatev       Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()));
34665e710c3SStanislav Gatev }
34765e710c3SStanislav Gatev 
348092a530cSStanislav Gatev void assignOptionalValue(const Expr &E, LatticeTransferState &State,
349092a530cSStanislav Gatev                          BoolValue &HasValueVal) {
350092a530cSStanislav Gatev   if (auto *OptionalLoc =
351092a530cSStanislav Gatev           State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
352092a530cSStanislav Gatev     State.Env.setValue(*OptionalLoc,
353092a530cSStanislav Gatev                        createOptionalValue(State.Env, HasValueVal));
3549e0fc676SStanislav Gatev   }
3559e0fc676SStanislav Gatev }
3569e0fc676SStanislav Gatev 
357b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
358b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
359b000b770SStanislav Gatev /// where `T` is constructible from `U`.
360b000b770SStanislav Gatev BoolValue &
361b000b770SStanislav Gatev getValueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
362b000b770SStanislav Gatev                              const MatchFinder::MatchResult &MatchRes,
363b000b770SStanislav Gatev                              LatticeTransferState &State) {
364b000b770SStanislav Gatev   assert(F.getTemplateSpecializationArgs()->size() > 0);
365b000b770SStanislav Gatev 
366b000b770SStanislav Gatev   const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
367b000b770SStanislav Gatev       *MatchRes.Context,
368b000b770SStanislav Gatev       stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType()));
369b000b770SStanislav Gatev   const int ArgTypeOptionalWrappersCount =
370b000b770SStanislav Gatev       countOptionalWrappers(*MatchRes.Context, stripReference(E.getType()));
371b000b770SStanislav Gatev 
372b000b770SStanislav Gatev   // Check if this is a constructor/assignment call for `optional<T>` with
373b000b770SStanislav Gatev   // argument of type `U` such that `T` is constructible from `U`.
374b000b770SStanislav Gatev   if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
375b000b770SStanislav Gatev     return State.Env.getBoolLiteralValue(true);
376b000b770SStanislav Gatev 
377b000b770SStanislav Gatev   // This is a constructor/assignment call for `optional<T>` with argument of
378b000b770SStanislav Gatev   // type `optional<U>` such that `T` is constructible from `U`.
379b000b770SStanislav Gatev   if (BoolValue *Val = getHasValue(State.Env.getValue(E, SkipPast::Reference)))
380b000b770SStanislav Gatev     return *Val;
381b000b770SStanislav Gatev   return State.Env.makeAtomicBoolValue();
382b000b770SStanislav Gatev }
383b000b770SStanislav Gatev 
384092a530cSStanislav Gatev void transferValueOrConversionConstructor(
385092a530cSStanislav Gatev     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
3869e0fc676SStanislav Gatev     LatticeTransferState &State) {
387092a530cSStanislav Gatev   assert(E->getNumArgs() > 0);
388092a530cSStanislav Gatev 
389b000b770SStanislav Gatev   assignOptionalValue(*E, State,
390b000b770SStanislav Gatev                       getValueOrConversionHasValue(*E->getConstructor(),
391b000b770SStanislav Gatev                                                    *E->getArg(0), MatchRes,
392b000b770SStanislav Gatev                                                    State));
393b000b770SStanislav Gatev }
394092a530cSStanislav Gatev 
395b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
396b000b770SStanislav Gatev                         LatticeTransferState &State) {
397b000b770SStanislav Gatev   assert(E->getNumArgs() > 0);
398b000b770SStanislav Gatev 
399b000b770SStanislav Gatev   auto *OptionalLoc =
400b000b770SStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
401b000b770SStanislav Gatev   assert(OptionalLoc != nullptr);
402b000b770SStanislav Gatev 
403b000b770SStanislav Gatev   State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
404b000b770SStanislav Gatev 
405b000b770SStanislav Gatev   // Assign a storage location for the whole expression.
406b000b770SStanislav Gatev   State.Env.setStorageLocation(*E, *OptionalLoc);
407b000b770SStanislav Gatev }
408b000b770SStanislav Gatev 
409b000b770SStanislav Gatev void transferValueOrConversionAssignment(
410b000b770SStanislav Gatev     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
411b000b770SStanislav Gatev     LatticeTransferState &State) {
412b000b770SStanislav Gatev   assert(E->getNumArgs() > 1);
413b000b770SStanislav Gatev   transferAssignment(E,
414b000b770SStanislav Gatev                      getValueOrConversionHasValue(
415b000b770SStanislav Gatev                          *E->getDirectCallee(), *E->getArg(1), MatchRes, State),
416b000b770SStanislav Gatev                      State);
417b000b770SStanislav Gatev }
418b000b770SStanislav Gatev 
419b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
420b000b770SStanislav Gatev                                const MatchFinder::MatchResult &,
421b000b770SStanislav Gatev                                LatticeTransferState &State) {
422b000b770SStanislav Gatev   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
4239e0fc676SStanislav Gatev }
4249e0fc676SStanislav Gatev 
4252ddd57aeSStanislav Gatev void transferSwap(const StorageLocation &OptionalLoc1,
4262ddd57aeSStanislav Gatev                   const StorageLocation &OptionalLoc2,
4272ddd57aeSStanislav Gatev                   LatticeTransferState &State) {
4282ddd57aeSStanislav Gatev   auto *OptionalVal1 = State.Env.getValue(OptionalLoc1);
4292ddd57aeSStanislav Gatev   assert(OptionalVal1 != nullptr);
4302ddd57aeSStanislav Gatev 
4312ddd57aeSStanislav Gatev   auto *OptionalVal2 = State.Env.getValue(OptionalLoc2);
4322ddd57aeSStanislav Gatev   assert(OptionalVal2 != nullptr);
4332ddd57aeSStanislav Gatev 
4342ddd57aeSStanislav Gatev   State.Env.setValue(OptionalLoc1, *OptionalVal2);
4352ddd57aeSStanislav Gatev   State.Env.setValue(OptionalLoc2, *OptionalVal1);
4362ddd57aeSStanislav Gatev }
4372ddd57aeSStanislav Gatev 
4382ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
4392ddd57aeSStanislav Gatev                       const MatchFinder::MatchResult &,
4402ddd57aeSStanislav Gatev                       LatticeTransferState &State) {
4412ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 1);
4422ddd57aeSStanislav Gatev 
4432ddd57aeSStanislav Gatev   auto *OptionalLoc1 = State.Env.getStorageLocation(
4442ddd57aeSStanislav Gatev       *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer);
4452ddd57aeSStanislav Gatev   assert(OptionalLoc1 != nullptr);
4462ddd57aeSStanislav Gatev 
4472ddd57aeSStanislav Gatev   auto *OptionalLoc2 =
4482ddd57aeSStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
4492ddd57aeSStanislav Gatev   assert(OptionalLoc2 != nullptr);
4502ddd57aeSStanislav Gatev 
4512ddd57aeSStanislav Gatev   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
4522ddd57aeSStanislav Gatev }
4532ddd57aeSStanislav Gatev 
4542ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
4552ddd57aeSStanislav Gatev                          LatticeTransferState &State) {
4562ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 2);
4572ddd57aeSStanislav Gatev 
4582ddd57aeSStanislav Gatev   auto *OptionalLoc1 =
4592ddd57aeSStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
4602ddd57aeSStanislav Gatev   assert(OptionalLoc1 != nullptr);
4612ddd57aeSStanislav Gatev 
4622ddd57aeSStanislav Gatev   auto *OptionalLoc2 =
4632ddd57aeSStanislav Gatev       State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference);
4642ddd57aeSStanislav Gatev   assert(OptionalLoc2 != nullptr);
4652ddd57aeSStanislav Gatev 
4662ddd57aeSStanislav Gatev   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
4672ddd57aeSStanislav Gatev }
4682ddd57aeSStanislav Gatev 
469a184a0d8SYitzhak Mandelbaum llvm::Optional<StatementMatcher>
470a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
471a184a0d8SYitzhak Mandelbaum   if (Options.IgnoreSmartPointerDereference)
472a184a0d8SYitzhak Mandelbaum     return memberExpr(hasObjectExpression(ignoringParenImpCasts(
473a184a0d8SYitzhak Mandelbaum         cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("->"),
474a184a0d8SYitzhak Mandelbaum                                   hasOverloadedOperatorName("*")),
475a184a0d8SYitzhak Mandelbaum                             unless(hasArgument(0, expr(hasOptionalType())))))));
476a184a0d8SYitzhak Mandelbaum   return llvm::None;
477a184a0d8SYitzhak Mandelbaum }
478a184a0d8SYitzhak Mandelbaum 
479a184a0d8SYitzhak Mandelbaum auto buildTransferMatchSwitch(
480a184a0d8SYitzhak Mandelbaum     const UncheckedOptionalAccessModelOptions &Options) {
481b000b770SStanislav Gatev   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
482b000b770SStanislav Gatev   // lot of duplicated work (e.g. string comparisons), consider providing APIs
483b000b770SStanislav Gatev   // that avoid it through memoization.
484a184a0d8SYitzhak Mandelbaum   auto IgnorableOptional = ignorableOptional(Options);
485af98b0afSStanislav Gatev   return MatchSwitchBuilder<LatticeTransferState>()
486af98b0afSStanislav Gatev       // Attach a symbolic "has_value" state to optional values that we see for
487af98b0afSStanislav Gatev       // the first time.
4886adfc64eSYitzhak Mandelbaum       .CaseOf<Expr>(
4896adfc64eSYitzhak Mandelbaum           expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
490af98b0afSStanislav Gatev           initializeOptionalReference)
491af98b0afSStanislav Gatev 
4929e0fc676SStanislav Gatev       // make_optional
493092a530cSStanislav Gatev       .CaseOf<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
494092a530cSStanislav Gatev 
495b000b770SStanislav Gatev       // optional::optional
496092a530cSStanislav Gatev       .CaseOf<CXXConstructExpr>(
497092a530cSStanislav Gatev           isOptionalInPlaceConstructor(),
498092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
499092a530cSStanislav Gatev              LatticeTransferState &State) {
500092a530cSStanislav Gatev             assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true));
501092a530cSStanislav Gatev           })
502092a530cSStanislav Gatev       .CaseOf<CXXConstructExpr>(
503092a530cSStanislav Gatev           isOptionalNulloptConstructor(),
504092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
505092a530cSStanislav Gatev              LatticeTransferState &State) {
506092a530cSStanislav Gatev             assignOptionalValue(*E, State,
507092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
508092a530cSStanislav Gatev           })
509092a530cSStanislav Gatev       .CaseOf<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
510092a530cSStanislav Gatev                                 transferValueOrConversionConstructor)
5119e0fc676SStanislav Gatev 
512b000b770SStanislav Gatev       // optional::operator=
513b000b770SStanislav Gatev       .CaseOf<CXXOperatorCallExpr>(isOptionalValueOrConversionAssignment(),
514b000b770SStanislav Gatev                                    transferValueOrConversionAssignment)
515b000b770SStanislav Gatev       .CaseOf<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
516b000b770SStanislav Gatev                                    transferNulloptAssignment)
517b000b770SStanislav Gatev 
518af98b0afSStanislav Gatev       // optional::value
519092a530cSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(
520a184a0d8SYitzhak Mandelbaum           isOptionalMemberCallWithName("value", IgnorableOptional),
521092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
522092a530cSStanislav Gatev              LatticeTransferState &State) {
523af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
524af98b0afSStanislav Gatev           })
525af98b0afSStanislav Gatev 
526af98b0afSStanislav Gatev       // optional::operator*, optional::operator->
527a184a0d8SYitzhak Mandelbaum       .CaseOf<CallExpr>(
528a184a0d8SYitzhak Mandelbaum           expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
529a184a0d8SYitzhak Mandelbaum                      isOptionalOperatorCallWithName("->", IgnorableOptional))),
530092a530cSStanislav Gatev           [](const CallExpr *E, const MatchFinder::MatchResult &,
531092a530cSStanislav Gatev              LatticeTransferState &State) {
532af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getArg(0), State);
533af98b0afSStanislav Gatev           })
534af98b0afSStanislav Gatev 
535af98b0afSStanislav Gatev       // optional::has_value
536092a530cSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("has_value"),
537af98b0afSStanislav Gatev                                  transferOptionalHasValueCall)
538af98b0afSStanislav Gatev 
5399e0fc676SStanislav Gatev       // optional::operator bool
540092a530cSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("operator bool"),
5419e0fc676SStanislav Gatev                                  transferOptionalHasValueCall)
5429e0fc676SStanislav Gatev 
5439e0fc676SStanislav Gatev       // optional::emplace
544092a530cSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(
545092a530cSStanislav Gatev           isOptionalMemberCallWithName("emplace"),
546092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
547092a530cSStanislav Gatev              LatticeTransferState &State) {
548092a530cSStanislav Gatev             assignOptionalValue(*E->getImplicitObjectArgument(), State,
549092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(true));
550092a530cSStanislav Gatev           })
5519e0fc676SStanislav Gatev 
5529e0fc676SStanislav Gatev       // optional::reset
553092a530cSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(
554092a530cSStanislav Gatev           isOptionalMemberCallWithName("reset"),
555092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
556092a530cSStanislav Gatev              LatticeTransferState &State) {
557092a530cSStanislav Gatev             assignOptionalValue(*E->getImplicitObjectArgument(), State,
558092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
559092a530cSStanislav Gatev           })
5609e0fc676SStanislav Gatev 
5612ddd57aeSStanislav Gatev       // optional::swap
5622ddd57aeSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
5632ddd57aeSStanislav Gatev                                  transferSwapCall)
5642ddd57aeSStanislav Gatev 
5652ddd57aeSStanislav Gatev       // std::swap
5662ddd57aeSStanislav Gatev       .CaseOf<CallExpr>(isStdSwapCall(), transferStdSwapCall)
5672ddd57aeSStanislav Gatev 
5687f076004SYitzhak Mandelbaum       // opt.value_or("").empty()
5697f076004SYitzhak Mandelbaum       .CaseOf<Expr>(isValueOrStringEmptyCall(), transferValueOrStringEmptyCall)
5707f076004SYitzhak Mandelbaum 
5717f076004SYitzhak Mandelbaum       // opt.value_or(X) != X
5727f076004SYitzhak Mandelbaum       .CaseOf<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
5737f076004SYitzhak Mandelbaum 
57465e710c3SStanislav Gatev       // returns optional
57565e710c3SStanislav Gatev       .CaseOf<CallExpr>(isCallReturningOptional(),
57665e710c3SStanislav Gatev                         transferCallReturningOptional)
57765e710c3SStanislav Gatev 
578af98b0afSStanislav Gatev       .Build();
579af98b0afSStanislav Gatev }
580af98b0afSStanislav Gatev 
581af98b0afSStanislav Gatev } // namespace
582af98b0afSStanislav Gatev 
5837e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher
5847e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() {
5857e63a0d4SYitzhak Mandelbaum   return optionalClass();
5867e63a0d4SYitzhak Mandelbaum }
5877e63a0d4SYitzhak Mandelbaum 
588a184a0d8SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(
589a184a0d8SYitzhak Mandelbaum     ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options)
590af98b0afSStanislav Gatev     : DataflowAnalysis<UncheckedOptionalAccessModel, SourceLocationsLattice>(
591af98b0afSStanislav Gatev           Ctx),
592a184a0d8SYitzhak Mandelbaum       TransferMatchSwitch(buildTransferMatchSwitch(Options)) {}
593af98b0afSStanislav Gatev 
594af98b0afSStanislav Gatev void UncheckedOptionalAccessModel::transfer(const Stmt *S,
595af98b0afSStanislav Gatev                                             SourceLocationsLattice &L,
596af98b0afSStanislav Gatev                                             Environment &Env) {
597af98b0afSStanislav Gatev   LatticeTransferState State(L, Env);
598af98b0afSStanislav Gatev   TransferMatchSwitch(*S, getASTContext(), State);
599af98b0afSStanislav Gatev }
600af98b0afSStanislav Gatev 
601af98b0afSStanislav Gatev } // namespace dataflow
602af98b0afSStanislav Gatev } // namespace clang
603