xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision e95134b9cb1885b0da929737858163486a5c399c)
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"
2109b462efSYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchersMacros.h"
227538b360SWei Yi Tee #include "clang/Analysis/CFG.h"
237538b360SWei Yi Tee #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
24af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
256272226bSSam McCall #include "clang/Analysis/FlowSensitive/Formula.h"
26cf1f978dSSam Estep #include "clang/Analysis/FlowSensitive/NoopLattice.h"
270086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h"
28af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h"
2958fe7f96SSam Estep #include "clang/Basic/SourceLocation.h"
30af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h"
31af98b0afSStanislav Gatev #include "llvm/Support/Casting.h"
32d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h"
33af98b0afSStanislav Gatev #include <cassert>
349e0fc676SStanislav Gatev #include <memory>
35a1580d7bSKazu Hirata #include <optional>
369e0fc676SStanislav Gatev #include <utility>
3758fe7f96SSam Estep #include <vector>
38af98b0afSStanislav Gatev 
39af98b0afSStanislav Gatev namespace clang {
40af98b0afSStanislav Gatev namespace dataflow {
4109b462efSYitzhak Mandelbaum 
4209b462efSYitzhak Mandelbaum static bool isTopLevelNamespaceWithName(const NamespaceDecl &NS,
4309b462efSYitzhak Mandelbaum                                         llvm::StringRef Name) {
4409b462efSYitzhak Mandelbaum   return NS.getDeclName().isIdentifier() && NS.getName() == Name &&
4509b462efSYitzhak Mandelbaum          NS.getParent() != nullptr && NS.getParent()->isTranslationUnit();
4609b462efSYitzhak Mandelbaum }
4709b462efSYitzhak Mandelbaum 
4809b462efSYitzhak Mandelbaum static bool hasOptionalClassName(const CXXRecordDecl &RD) {
4909b462efSYitzhak Mandelbaum   if (!RD.getDeclName().isIdentifier())
5009b462efSYitzhak Mandelbaum     return false;
5109b462efSYitzhak Mandelbaum 
5209b462efSYitzhak Mandelbaum   if (RD.getName() == "optional") {
5309b462efSYitzhak Mandelbaum     if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
5409b462efSYitzhak Mandelbaum       return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl");
5509b462efSYitzhak Mandelbaum     return false;
5609b462efSYitzhak Mandelbaum   }
5709b462efSYitzhak Mandelbaum 
5809b462efSYitzhak Mandelbaum   if (RD.getName() == "Optional") {
592f0630f8SAnton Dukeman     // Check whether namespace is "::base" or "::folly".
6009b462efSYitzhak Mandelbaum     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
612f0630f8SAnton Dukeman     return N != nullptr && (isTopLevelNamespaceWithName(*N, "base") ||
622f0630f8SAnton Dukeman                             isTopLevelNamespaceWithName(*N, "folly"));
6309b462efSYitzhak Mandelbaum   }
6409b462efSYitzhak Mandelbaum 
6509b462efSYitzhak Mandelbaum   return false;
6609b462efSYitzhak Mandelbaum }
6709b462efSYitzhak Mandelbaum 
68af98b0afSStanislav Gatev namespace {
69af98b0afSStanislav Gatev 
70af98b0afSStanislav Gatev using namespace ::clang::ast_matchers;
71cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>;
72af98b0afSStanislav Gatev 
7309b462efSYitzhak Mandelbaum AST_MATCHER(CXXRecordDecl, hasOptionalClassNameMatcher) {
7409b462efSYitzhak Mandelbaum   return hasOptionalClassName(Node);
7509b462efSYitzhak Mandelbaum }
7609b462efSYitzhak Mandelbaum 
777e63a0d4SYitzhak Mandelbaum DeclarationMatcher optionalClass() {
78af98b0afSStanislav Gatev   return classTemplateSpecializationDecl(
7909b462efSYitzhak Mandelbaum       hasOptionalClassNameMatcher(),
80af98b0afSStanislav Gatev       hasTemplateArgument(0, refersToType(type().bind("T"))));
81af98b0afSStanislav Gatev }
82af98b0afSStanislav Gatev 
836adfc64eSYitzhak Mandelbaum auto optionalOrAliasType() {
8465e710c3SStanislav Gatev   return hasUnqualifiedDesugaredType(
8565e710c3SStanislav Gatev       recordType(hasDeclaration(optionalClass())));
8665e710c3SStanislav Gatev }
8765e710c3SStanislav Gatev 
886adfc64eSYitzhak Mandelbaum /// Matches any of the spellings of the optional types and sugar, aliases, etc.
896adfc64eSYitzhak Mandelbaum auto hasOptionalType() { return hasType(optionalOrAliasType()); }
906adfc64eSYitzhak Mandelbaum 
912f0630f8SAnton Dukeman auto isOptionalMemberCallWithNameMatcher(
922f0630f8SAnton Dukeman     ast_matchers::internal::Matcher<NamedDecl> matcher,
936ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
94a184a0d8SYitzhak Mandelbaum   auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr()))
95a184a0d8SYitzhak Mandelbaum                                     : cxxThisExpr());
96af98b0afSStanislav Gatev   return cxxMemberCallExpr(
9709b462efSYitzhak Mandelbaum       on(expr(Exception,
9809b462efSYitzhak Mandelbaum               anyOf(hasOptionalType(),
9909b462efSYitzhak Mandelbaum                     hasType(pointerType(pointee(optionalOrAliasType())))))),
1002f0630f8SAnton Dukeman       callee(cxxMethodDecl(matcher)));
101af98b0afSStanislav Gatev }
102af98b0afSStanislav Gatev 
103a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName(
104a184a0d8SYitzhak Mandelbaum     llvm::StringRef operator_name,
1056ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
106a184a0d8SYitzhak Mandelbaum   return cxxOperatorCallExpr(
107a184a0d8SYitzhak Mandelbaum       hasOverloadedOperatorName(operator_name),
108a184a0d8SYitzhak Mandelbaum       callee(cxxMethodDecl(ofClass(optionalClass()))),
109a184a0d8SYitzhak Mandelbaum       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
110af98b0afSStanislav Gatev }
111af98b0afSStanislav Gatev 
112092a530cSStanislav Gatev auto isMakeOptionalCall() {
1132f0630f8SAnton Dukeman   return callExpr(callee(functionDecl(hasAnyName(
1142f0630f8SAnton Dukeman                       "std::make_optional", "base::make_optional",
1152f0630f8SAnton Dukeman                       "absl::make_optional", "folly::make_optional"))),
1169e0fc676SStanislav Gatev                   hasOptionalType());
1179e0fc676SStanislav Gatev }
1189e0fc676SStanislav Gatev 
119390029beSYitzhak Mandelbaum auto nulloptTypeDecl() {
1202f0630f8SAnton Dukeman   return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",
1212f0630f8SAnton Dukeman                               "base::nullopt_t", "folly::None"));
122092a530cSStanislav Gatev }
123092a530cSStanislav Gatev 
124390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
125390029beSYitzhak Mandelbaum 
126390029beSYitzhak Mandelbaum // `optional` or `nullopt_t`
127390029beSYitzhak Mandelbaum auto hasAnyOptionalType() {
128390029beSYitzhak Mandelbaum   return hasType(hasUnqualifiedDesugaredType(
129390029beSYitzhak Mandelbaum       recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass())))));
130390029beSYitzhak Mandelbaum }
131390029beSYitzhak Mandelbaum 
132092a530cSStanislav Gatev auto inPlaceClass() {
1332f0630f8SAnton Dukeman   return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
1342f0630f8SAnton Dukeman                                "base::in_place_t", "folly::in_place_t"));
135092a530cSStanislav Gatev }
136092a530cSStanislav Gatev 
137092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
1380086a355SYitzhak Mandelbaum   return cxxConstructExpr(
1390086a355SYitzhak Mandelbaum       hasOptionalType(),
1400086a355SYitzhak Mandelbaum       hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
1410086a355SYitzhak Mandelbaum                                         hasParameter(0, hasNulloptType()))));
142092a530cSStanislav Gatev }
143092a530cSStanislav Gatev 
144092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
145092a530cSStanislav Gatev   return cxxConstructExpr(hasOptionalType(),
146092a530cSStanislav Gatev                           hasArgument(0, hasType(inPlaceClass())));
147092a530cSStanislav Gatev }
148092a530cSStanislav Gatev 
149092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
150092a530cSStanislav Gatev   return cxxConstructExpr(
151092a530cSStanislav Gatev       hasOptionalType(),
152092a530cSStanislav Gatev       unless(hasDeclaration(
153092a530cSStanislav Gatev           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
154092a530cSStanislav Gatev       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
155092a530cSStanislav Gatev }
156092a530cSStanislav Gatev 
157b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
158b000b770SStanislav Gatev   return cxxOperatorCallExpr(
159b000b770SStanislav Gatev       hasOverloadedOperatorName("="),
160b000b770SStanislav Gatev       callee(cxxMethodDecl(ofClass(optionalClass()))),
161b000b770SStanislav Gatev       unless(hasDeclaration(cxxMethodDecl(
162b000b770SStanislav Gatev           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
163b000b770SStanislav Gatev       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
164b000b770SStanislav Gatev }
165b000b770SStanislav Gatev 
166390029beSYitzhak Mandelbaum auto isNulloptConstructor() {
167390029beSYitzhak Mandelbaum   return cxxConstructExpr(hasNulloptType(), argumentCountIs(1),
168390029beSYitzhak Mandelbaum                           hasArgument(0, hasNulloptType()));
169390029beSYitzhak Mandelbaum }
170390029beSYitzhak Mandelbaum 
171b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
172b000b770SStanislav Gatev   return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
173b000b770SStanislav Gatev                              callee(cxxMethodDecl(ofClass(optionalClass()))),
174b000b770SStanislav Gatev                              argumentCountIs(2),
175b000b770SStanislav Gatev                              hasArgument(1, hasNulloptType()));
176b000b770SStanislav Gatev }
177b000b770SStanislav Gatev 
1782ddd57aeSStanislav Gatev auto isStdSwapCall() {
1792ddd57aeSStanislav Gatev   return callExpr(callee(functionDecl(hasName("std::swap"))),
1802ddd57aeSStanislav Gatev                   argumentCountIs(2), hasArgument(0, hasOptionalType()),
1812ddd57aeSStanislav Gatev                   hasArgument(1, hasOptionalType()));
1822ddd57aeSStanislav Gatev }
1832ddd57aeSStanislav Gatev 
18425956d55SAMS21 auto isStdForwardCall() {
18525956d55SAMS21   return callExpr(callee(functionDecl(hasName("std::forward"))),
18625956d55SAMS21                   argumentCountIs(1), hasArgument(0, hasOptionalType()));
18725956d55SAMS21 }
18825956d55SAMS21 
1897f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
1907f076004SYitzhak Mandelbaum 
1917f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() {
1927f076004SYitzhak Mandelbaum   // `opt.value_or("").empty()`
1937f076004SYitzhak Mandelbaum   return cxxMemberCallExpr(
1947f076004SYitzhak Mandelbaum       callee(cxxMethodDecl(hasName("empty"))),
1957f076004SYitzhak Mandelbaum       onImplicitObjectArgument(ignoringImplicit(
1967f076004SYitzhak Mandelbaum           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1977f076004SYitzhak Mandelbaum                             callee(cxxMethodDecl(hasName("value_or"),
1987f076004SYitzhak Mandelbaum                                                  ofClass(optionalClass()))),
1997f076004SYitzhak Mandelbaum                             hasArgument(0, stringLiteral(hasSize(0))))
2007f076004SYitzhak Mandelbaum               .bind(ValueOrCallID))));
2017f076004SYitzhak Mandelbaum }
2027f076004SYitzhak Mandelbaum 
2037f076004SYitzhak Mandelbaum auto isValueOrNotEqX() {
2047f076004SYitzhak Mandelbaum   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
2057f076004SYitzhak Mandelbaum     return hasOperands(
2067f076004SYitzhak Mandelbaum         ignoringImplicit(
2077f076004SYitzhak Mandelbaum             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
2087f076004SYitzhak Mandelbaum                               callee(cxxMethodDecl(hasName("value_or"),
2097f076004SYitzhak Mandelbaum                                                    ofClass(optionalClass()))),
2107f076004SYitzhak Mandelbaum                               hasArgument(0, Arg))
2117f076004SYitzhak Mandelbaum                 .bind(ValueOrCallID)),
2127f076004SYitzhak Mandelbaum         ignoringImplicit(Arg));
2137f076004SYitzhak Mandelbaum   };
2147f076004SYitzhak Mandelbaum 
2157f076004SYitzhak Mandelbaum   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
2167f076004SYitzhak Mandelbaum   // support this pattern for any expression, but the AST does not have a
2177f076004SYitzhak Mandelbaum   // generic expression comparison facility, so we specialize to common cases
2187f076004SYitzhak Mandelbaum   // seen in practice.  FIXME: define a matcher that compares values across
2197f076004SYitzhak Mandelbaum   // nodes, which would let us generalize this to any `X`.
2207f076004SYitzhak Mandelbaum   return binaryOperation(hasOperatorName("!="),
2217f076004SYitzhak Mandelbaum                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
2227f076004SYitzhak Mandelbaum                                ComparesToSame(stringLiteral(hasSize(0))),
2237f076004SYitzhak Mandelbaum                                ComparesToSame(integerLiteral(equals(0)))));
2247f076004SYitzhak Mandelbaum }
2257f076004SYitzhak Mandelbaum 
22665e710c3SStanislav Gatev auto isCallReturningOptional() {
227cd0d5261SSam Estep   return callExpr(hasType(qualType(anyOf(
228cd0d5261SSam Estep       optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
22965e710c3SStanislav Gatev }
23065e710c3SStanislav Gatev 
231390029beSYitzhak Mandelbaum template <typename L, typename R>
232390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
233390029beSYitzhak Mandelbaum   return cxxOperatorCallExpr(
234390029beSYitzhak Mandelbaum       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
235390029beSYitzhak Mandelbaum       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
236390029beSYitzhak Mandelbaum       hasArgument(1, rhs_arg_matcher));
237390029beSYitzhak Mandelbaum }
238390029beSYitzhak Mandelbaum 
2396272226bSSam McCall /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
2406272226bSSam McCall const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
241*e95134b9SMartin Braenne   auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr));
242390029beSYitzhak Mandelbaum   if (Value != nullptr)
2436272226bSSam McCall     return Value->formula();
244390029beSYitzhak Mandelbaum 
245390029beSYitzhak Mandelbaum   auto &Loc = Env.createStorageLocation(Expr);
246390029beSYitzhak Mandelbaum   Value = &Env.makeAtomicBoolValue();
247390029beSYitzhak Mandelbaum   Env.setValue(Loc, *Value);
248390029beSYitzhak Mandelbaum   Env.setStorageLocation(Expr, Loc);
2496272226bSSam McCall   return Value->formula();
250390029beSYitzhak Mandelbaum }
251390029beSYitzhak Mandelbaum 
2528fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
2538fcdd625SStanislav Gatev /// property of the optional value `OptionalVal`.
2548fcdd625SStanislav Gatev void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
2558fcdd625SStanislav Gatev   OptionalVal.setProperty("has_value", HasValueVal);
2568fcdd625SStanislav Gatev }
2578fcdd625SStanislav Gatev 
258f653d140SMartin Braenne /// Creates a symbolic value for an `optional` value at an existing storage
259f653d140SMartin Braenne /// location. Uses `HasValueVal` as the symbolic value of the "has_value"
260f653d140SMartin Braenne /// property.
261f653d140SMartin Braenne StructValue &createOptionalValue(AggregateStorageLocation &Loc,
262f653d140SMartin Braenne                                  BoolValue &HasValueVal, Environment &Env) {
26344f98d01SMartin Braenne   auto &OptionalVal = Env.create<StructValue>(Loc);
264f653d140SMartin Braenne   Env.setValue(Loc, OptionalVal);
26544f98d01SMartin Braenne   setHasValue(OptionalVal, HasValueVal);
266f653d140SMartin Braenne   return OptionalVal;
267f653d140SMartin Braenne }
268f653d140SMartin Braenne 
269af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
27049ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
271dd38caf3SYitzhak Mandelbaum BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
272dd38caf3SYitzhak Mandelbaum   if (OptionalVal != nullptr) {
273dd38caf3SYitzhak Mandelbaum     auto *HasValueVal =
274dd38caf3SYitzhak Mandelbaum         cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
275dd38caf3SYitzhak Mandelbaum     if (HasValueVal == nullptr) {
276dd38caf3SYitzhak Mandelbaum       HasValueVal = &Env.makeAtomicBoolValue();
277dd38caf3SYitzhak Mandelbaum       OptionalVal->setProperty("has_value", *HasValueVal);
278dd38caf3SYitzhak Mandelbaum     }
279dd38caf3SYitzhak Mandelbaum     return HasValueVal;
280af98b0afSStanislav Gatev   }
281af98b0afSStanislav Gatev   return nullptr;
282af98b0afSStanislav Gatev }
283af98b0afSStanislav Gatev 
284092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type.
285c0725865SYitzhak Mandelbaum bool isOptionalType(QualType Type) {
286092a530cSStanislav Gatev   if (!Type->isRecordType())
287092a530cSStanislav Gatev     return false;
288cd22e0dcSYitzhak Mandelbaum   const CXXRecordDecl *D = Type->getAsCXXRecordDecl();
28909b462efSYitzhak Mandelbaum   return D != nullptr && hasOptionalClassName(*D);
290092a530cSStanislav Gatev }
291092a530cSStanislav Gatev 
292092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
293092a530cSStanislav Gatev ///
294092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
295092a530cSStanislav Gatev /// function will be 2.
296092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
297c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
298092a530cSStanislav Gatev     return 0;
299092a530cSStanislav Gatev   return 1 + countOptionalWrappers(
300092a530cSStanislav Gatev                  ASTCtx,
301092a530cSStanislav Gatev                  cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
302092a530cSStanislav Gatev                      ->getTemplateArgs()
303092a530cSStanislav Gatev                      .get(0)
304092a530cSStanislav Gatev                      .getAsType()
305092a530cSStanislav Gatev                      .getDesugaredType(ASTCtx));
306092a530cSStanislav Gatev }
307092a530cSStanislav Gatev 
308dd38caf3SYitzhak Mandelbaum /// Tries to initialize the `optional`'s value (that is, contents), and return
309dd38caf3SYitzhak Mandelbaum /// its location. Returns nullptr if the value can't be represented.
310dd38caf3SYitzhak Mandelbaum StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
311dd38caf3SYitzhak Mandelbaum                                                     Value &OptionalVal,
312dd38caf3SYitzhak Mandelbaum                                                     Environment &Env) {
313dd38caf3SYitzhak Mandelbaum   // The "value" property represents a synthetic field. As such, it needs
314dd38caf3SYitzhak Mandelbaum   // `StorageLocation`, like normal fields (and other variables). So, we model
315af22be39SMartin Braenne   // it with a `PointerValue`, since that includes a storage location.  Once
316dd38caf3SYitzhak Mandelbaum   // the property is set, it will be shared by all environments that access the
317dd38caf3SYitzhak Mandelbaum   // `Value` representing the optional (here, `OptionalVal`).
318dd38caf3SYitzhak Mandelbaum   if (auto *ValueProp = OptionalVal.getProperty("value")) {
319af22be39SMartin Braenne     auto *ValuePtr = clang::cast<PointerValue>(ValueProp);
320af22be39SMartin Braenne     auto &ValueLoc = ValuePtr->getPointeeLoc();
32144f98d01SMartin Braenne     if (Env.getValue(ValueLoc) != nullptr)
32244f98d01SMartin Braenne       return &ValueLoc;
32344f98d01SMartin Braenne 
324dd38caf3SYitzhak Mandelbaum     // The property was previously set, but the value has been lost. This can
325bbeda830SMartin Braenne     // happen in various situations, for example:
32644f98d01SMartin Braenne     // - Because of an environment merge (where the two environments mapped the
32744f98d01SMartin Braenne     //   property to different values, which resulted in them both being
328bbeda830SMartin Braenne     //   discarded).
329bbeda830SMartin Braenne     // - When two blocks in the CFG, with neither a dominator of the other,
330bbeda830SMartin Braenne     //   visit the same optional value. (FIXME: This is something we can and
331bbeda830SMartin Braenne     //   should fix -- see also the lengthy FIXME below.)
332bbeda830SMartin Braenne     // - Or even when a block is revisited during testing to collect
333bbeda830SMartin Braenne     //   per-statement state.
334dd38caf3SYitzhak Mandelbaum     // FIXME: This situation means that the optional contents are not shared
335dd38caf3SYitzhak Mandelbaum     // between branches and the like. Practically, this lack of sharing
336dd38caf3SYitzhak Mandelbaum     // reduces the precision of the model when the contents are relevant to
337dd38caf3SYitzhak Mandelbaum     // the check, like another optional or a boolean that influences control
338dd38caf3SYitzhak Mandelbaum     // flow.
33944f98d01SMartin Braenne     if (ValueLoc.getType()->isRecordType()) {
34044f98d01SMartin Braenne       refreshStructValue(cast<AggregateStorageLocation>(ValueLoc), Env);
34144f98d01SMartin Braenne       return &ValueLoc;
34244f98d01SMartin Braenne     } else {
343dd38caf3SYitzhak Mandelbaum       auto *ValueVal = Env.createValue(ValueLoc.getType());
344dd38caf3SYitzhak Mandelbaum       if (ValueVal == nullptr)
345dd38caf3SYitzhak Mandelbaum         return nullptr;
346dd38caf3SYitzhak Mandelbaum       Env.setValue(ValueLoc, *ValueVal);
347dd38caf3SYitzhak Mandelbaum       return &ValueLoc;
348dd38caf3SYitzhak Mandelbaum     }
34944f98d01SMartin Braenne   }
350dd38caf3SYitzhak Mandelbaum 
351c849843cSMartin Braenne   auto Ty = Q.getNonReferenceType();
35244f98d01SMartin Braenne   auto &ValueLoc = Env.createObject(Ty);
353af22be39SMartin Braenne   auto &ValuePtr = Env.create<PointerValue>(ValueLoc);
354bbeda830SMartin Braenne   // FIXME:
355bbeda830SMartin Braenne   // The change we make to the `value` property below may become visible to
356bbeda830SMartin Braenne   // other blocks that aren't successors of the current block and therefore
357bbeda830SMartin Braenne   // don't see the change we made above mapping `ValueLoc` to `ValueVal`. For
358bbeda830SMartin Braenne   // example:
359bbeda830SMartin Braenne   //
360bbeda830SMartin Braenne   //   void target(optional<int> oo, bool b) {
361bbeda830SMartin Braenne   //     // `oo` is associated with a `StructValue` here, which we will call
362bbeda830SMartin Braenne   //     // `OptionalVal`.
363bbeda830SMartin Braenne   //
364bbeda830SMartin Braenne   //     // The `has_value` property is set on `OptionalVal` (but not the
365bbeda830SMartin Braenne   //     // `value` property yet).
366bbeda830SMartin Braenne   //     if (!oo.has_value()) return;
367bbeda830SMartin Braenne   //
368bbeda830SMartin Braenne   //     if (b) {
369bbeda830SMartin Braenne   //       // Let's assume we transfer the `if` branch first.
370bbeda830SMartin Braenne   //       //
371bbeda830SMartin Braenne   //       // This causes us to call `maybeInitializeOptionalValueMember()`,
372bbeda830SMartin Braenne   //       // which causes us to set the `value` property on `OptionalVal`
373bbeda830SMartin Braenne   //       // (which had not been set until this point). This `value` property
374bbeda830SMartin Braenne   //       // refers to a `PointerValue`, which in turn refers to a
375bbeda830SMartin Braenne   //       // StorageLocation` that is associated to an `IntegerValue`.
376bbeda830SMartin Braenne   //       oo.value();
377bbeda830SMartin Braenne   //     } else {
378bbeda830SMartin Braenne   //       // Let's assume we transfer the `else` branch after the `if` branch.
379bbeda830SMartin Braenne   //       //
380bbeda830SMartin Braenne   //       // We see the `value` property that the `if` branch set on
381bbeda830SMartin Braenne   //       // `OptionalVal`, but in the environment for this block, the
382bbeda830SMartin Braenne   //       // `StorageLocation` in the `PointerValue` is not associated with any
383bbeda830SMartin Braenne   //       // `Value`.
384bbeda830SMartin Braenne   //       oo.value();
385bbeda830SMartin Braenne   //     }
386bbeda830SMartin Braenne   //   }
387bbeda830SMartin Braenne   //
388bbeda830SMartin Braenne   // This situation is currently "saved" by the code above that checks whether
389bbeda830SMartin Braenne   // the `value` property is already set, and if, the `ValueLoc` is not
390bbeda830SMartin Braenne   // associated with a `ValueVal`, creates a new `ValueVal`.
391bbeda830SMartin Braenne   //
392bbeda830SMartin Braenne   // However, what we should really do is to make sure that the change to the
393bbeda830SMartin Braenne   // `value` property does not "leak" to other blocks that are not successors
394bbeda830SMartin Braenne   // of this block. To do this, instead of simply setting the `value` property
395bbeda830SMartin Braenne   // on the existing `OptionalVal`, we should create a new `Value` for the
396bbeda830SMartin Braenne   // optional, set the property on that, and associate the storage location that
397bbeda830SMartin Braenne   // is currently associated with the existing `OptionalVal` with the newly
398bbeda830SMartin Braenne   // created `Value` instead.
399af22be39SMartin Braenne   OptionalVal.setProperty("value", ValuePtr);
400dd38caf3SYitzhak Mandelbaum   return &ValueLoc;
401dd38caf3SYitzhak Mandelbaum }
402dd38caf3SYitzhak Mandelbaum 
403092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr,
404092a530cSStanislav Gatev                                  const MatchFinder::MatchResult &,
405af98b0afSStanislav Gatev                                  LatticeTransferState &State) {
406*e95134b9SMartin Braenne   if (auto *OptionalVal = State.Env.getValue(*OptionalExpr)) {
407af98b0afSStanislav Gatev     if (OptionalVal->getProperty("has_value") == nullptr) {
4088fcdd625SStanislav Gatev       setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
409af98b0afSStanislav Gatev     }
410af98b0afSStanislav Gatev   }
411af98b0afSStanislav Gatev }
412af98b0afSStanislav Gatev 
4138fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
414ffb4f4dbSDmitri Gribenko /// empty in `Env`.
4158fcdd625SStanislav Gatev bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
4168fcdd625SStanislav Gatev   auto *HasValueVal =
4178fcdd625SStanislav Gatev       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
4188fcdd625SStanislav Gatev   return HasValueVal != nullptr &&
4196272226bSSam McCall          Env.flowConditionImplies(Env.arena().makeNot(HasValueVal->formula()));
4208fcdd625SStanislav Gatev }
4218fcdd625SStanislav Gatev 
4228fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
423ffb4f4dbSDmitri Gribenko /// non-empty in `Env`.
4248fcdd625SStanislav Gatev bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
4258fcdd625SStanislav Gatev   auto *HasValueVal =
4268fcdd625SStanislav Gatev       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
4276272226bSSam McCall   return HasValueVal != nullptr &&
4286272226bSSam McCall          Env.flowConditionImplies(HasValueVal->formula());
4298fcdd625SStanislav Gatev }
4308fcdd625SStanislav Gatev 
43148bc7150SMartin Braenne Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) {
432*e95134b9SMartin Braenne   Value *Val = Env.getValue(E);
43348bc7150SMartin Braenne   if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Val))
43448bc7150SMartin Braenne     return Env.getValue(PointerVal->getPointeeLoc());
43548bc7150SMartin Braenne   return Val;
43648bc7150SMartin Braenne }
43748bc7150SMartin Braenne 
438092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
439af98b0afSStanislav Gatev                         LatticeTransferState &State) {
44049ed5bf5SWei Yi Tee   if (auto *OptionalVal =
44148bc7150SMartin Braenne           getValueBehindPossiblePointer(*ObjectExpr, State.Env)) {
442dd38caf3SYitzhak Mandelbaum     if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
443dd38caf3SYitzhak Mandelbaum       if (auto *Loc = maybeInitializeOptionalValueMember(
444dd38caf3SYitzhak Mandelbaum               UnwrapExpr->getType(), *OptionalVal, State.Env))
445dd38caf3SYitzhak Mandelbaum         State.Env.setStorageLocation(*UnwrapExpr, *Loc);
446af98b0afSStanislav Gatev   }
447dd38caf3SYitzhak Mandelbaum }
448af98b0afSStanislav Gatev 
4493bc1ea5bSMartin Braenne void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
4503bc1ea5bSMartin Braenne                          LatticeTransferState &State) {
4513bc1ea5bSMartin Braenne   if (auto *OptionalVal =
4523bc1ea5bSMartin Braenne           getValueBehindPossiblePointer(*ObjectExpr, State.Env)) {
4533bc1ea5bSMartin Braenne     if (auto *Loc = maybeInitializeOptionalValueMember(
4543bc1ea5bSMartin Braenne             UnwrapExpr->getType()->getPointeeType(), *OptionalVal, State.Env)) {
4553bc1ea5bSMartin Braenne       State.Env.setValueStrict(*UnwrapExpr,
4563bc1ea5bSMartin Braenne                                State.Env.create<PointerValue>(*Loc));
4573bc1ea5bSMartin Braenne     }
4583bc1ea5bSMartin Braenne   }
4593bc1ea5bSMartin Braenne }
4603bc1ea5bSMartin Braenne 
461092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
462092a530cSStanislav Gatev                               const MatchFinder::MatchResult &,
463092a530cSStanislav Gatev                               LatticeTransferState &State) {
46444f98d01SMartin Braenne   createOptionalValue(State.Env.getResultObjectLocation(*E),
46544f98d01SMartin Braenne                       State.Env.getBoolLiteralValue(true), State.Env);
4669e0fc676SStanislav Gatev }
4679e0fc676SStanislav Gatev 
468092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
469092a530cSStanislav Gatev                                   const MatchFinder::MatchResult &,
470af98b0afSStanislav Gatev                                   LatticeTransferState &State) {
471dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal = getHasValue(
47248bc7150SMartin Braenne           State.Env, getValueBehindPossiblePointer(
47348bc7150SMartin Braenne                          *CallExpr->getImplicitObjectArgument(), State.Env))) {
474af98b0afSStanislav Gatev     auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
475af98b0afSStanislav Gatev     State.Env.setValue(CallExprLoc, *HasValueVal);
476af98b0afSStanislav Gatev     State.Env.setStorageLocation(*CallExpr, CallExprLoc);
477af98b0afSStanislav Gatev   }
478af98b0afSStanislav Gatev }
479af98b0afSStanislav Gatev 
4807f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in
4817f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property.
4826272226bSSam McCall void transferValueOrImpl(
4836272226bSSam McCall     const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
4847f076004SYitzhak Mandelbaum     LatticeTransferState &State,
4856272226bSSam McCall     const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
4866272226bSSam McCall                                 const Formula &HasValueVal)) {
4877f076004SYitzhak Mandelbaum   auto &Env = State.Env;
4887f076004SYitzhak Mandelbaum 
4897f076004SYitzhak Mandelbaum   const auto *ObjectArgumentExpr =
4907f076004SYitzhak Mandelbaum       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
4917f076004SYitzhak Mandelbaum           ->getImplicitObjectArgument();
4927f076004SYitzhak Mandelbaum 
493dd38caf3SYitzhak Mandelbaum   auto *HasValueVal = getHasValue(
49448bc7150SMartin Braenne       State.Env, getValueBehindPossiblePointer(*ObjectArgumentExpr, State.Env));
495dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr)
4967f076004SYitzhak Mandelbaum     return;
4977f076004SYitzhak Mandelbaum 
4986272226bSSam McCall   Env.addToFlowCondition(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
4996272226bSSam McCall                                    HasValueVal->formula()));
5007f076004SYitzhak Mandelbaum }
5017f076004SYitzhak Mandelbaum 
5027f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
5037f076004SYitzhak Mandelbaum                                     const MatchFinder::MatchResult &Result,
5047f076004SYitzhak Mandelbaum                                     LatticeTransferState &State) {
5057f076004SYitzhak Mandelbaum   return transferValueOrImpl(ComparisonExpr, Result, State,
5066272226bSSam McCall                              [](Environment &Env, const Formula &ExprVal,
5076272226bSSam McCall                                 const Formula &HasValueVal) -> const Formula & {
5086272226bSSam McCall                                auto &A = Env.arena();
5097f076004SYitzhak Mandelbaum                                // If the result is *not* empty, then we know the
5107f076004SYitzhak Mandelbaum                                // optional must have been holding a value. If
5117f076004SYitzhak Mandelbaum                                // `ExprVal` is true, though, we don't learn
5127f076004SYitzhak Mandelbaum                                // anything definite about `has_value`, so we
5137f076004SYitzhak Mandelbaum                                // don't add any corresponding implications to
5147f076004SYitzhak Mandelbaum                                // the flow condition.
5156272226bSSam McCall                                return A.makeImplies(A.makeNot(ExprVal),
5167f076004SYitzhak Mandelbaum                                                     HasValueVal);
5177f076004SYitzhak Mandelbaum                              });
5187f076004SYitzhak Mandelbaum }
5197f076004SYitzhak Mandelbaum 
5207f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr,
5217f076004SYitzhak Mandelbaum                            const MatchFinder::MatchResult &Result,
5227f076004SYitzhak Mandelbaum                            LatticeTransferState &State) {
5237f076004SYitzhak Mandelbaum   transferValueOrImpl(ComparisonExpr, Result, State,
5246272226bSSam McCall                       [](Environment &Env, const Formula &ExprVal,
5256272226bSSam McCall                          const Formula &HasValueVal) -> const Formula & {
5266272226bSSam McCall                         auto &A = Env.arena();
5277f076004SYitzhak Mandelbaum                         // We know that if `(opt.value_or(X) != X)` then
5287f076004SYitzhak Mandelbaum                         // `opt.hasValue()`, even without knowing further
5297f076004SYitzhak Mandelbaum                         // details about the contents of `opt`.
5306272226bSSam McCall                         return A.makeImplies(ExprVal, HasValueVal);
5317f076004SYitzhak Mandelbaum                       });
5327f076004SYitzhak Mandelbaum }
5337f076004SYitzhak Mandelbaum 
53465e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E,
53565e710c3SStanislav Gatev                                    const MatchFinder::MatchResult &Result,
53665e710c3SStanislav Gatev                                    LatticeTransferState &State) {
53765e710c3SStanislav Gatev   if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
53865e710c3SStanislav Gatev     return;
53965e710c3SStanislav Gatev 
54044f98d01SMartin Braenne   AggregateStorageLocation *Loc = nullptr;
54144f98d01SMartin Braenne   if (E->isPRValue()) {
54244f98d01SMartin Braenne     Loc = &State.Env.getResultObjectLocation(*E);
54344f98d01SMartin Braenne   } else {
54444f98d01SMartin Braenne     Loc = &cast<AggregateStorageLocation>(State.Env.createStorageLocation(*E));
54544f98d01SMartin Braenne     State.Env.setStorageLocationStrict(*E, *Loc);
54644f98d01SMartin Braenne   }
54744f98d01SMartin Braenne 
54844f98d01SMartin Braenne   createOptionalValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
54965e710c3SStanislav Gatev }
55065e710c3SStanislav Gatev 
551f653d140SMartin Braenne void constructOptionalValue(const Expr &E, Environment &Env,
552092a530cSStanislav Gatev                             BoolValue &HasValueVal) {
55344f98d01SMartin Braenne   AggregateStorageLocation &Loc = Env.getResultObjectLocation(E);
55444f98d01SMartin Braenne   Env.setValueStrict(E, createOptionalValue(Loc, HasValueVal, Env));
5559e0fc676SStanislav Gatev }
5569e0fc676SStanislav Gatev 
557b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
558b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
559b000b770SStanislav Gatev /// where `T` is constructible from `U`.
560390029beSYitzhak Mandelbaum BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
561b000b770SStanislav Gatev                                      const MatchFinder::MatchResult &MatchRes,
562b000b770SStanislav Gatev                                      LatticeTransferState &State) {
5630086a355SYitzhak Mandelbaum   assert(F.getTemplateSpecializationArgs() != nullptr);
564b000b770SStanislav Gatev   assert(F.getTemplateSpecializationArgs()->size() > 0);
565b000b770SStanislav Gatev 
566c849843cSMartin Braenne   const int TemplateParamOptionalWrappersCount =
567c849843cSMartin Braenne       countOptionalWrappers(*MatchRes.Context, F.getTemplateSpecializationArgs()
568c849843cSMartin Braenne                                                    ->get(0)
569c849843cSMartin Braenne                                                    .getAsType()
570c849843cSMartin Braenne                                                    .getNonReferenceType());
571c849843cSMartin Braenne   const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
572c849843cSMartin Braenne       *MatchRes.Context, E.getType().getNonReferenceType());
573b000b770SStanislav Gatev 
574b000b770SStanislav Gatev   // Check if this is a constructor/assignment call for `optional<T>` with
575b000b770SStanislav Gatev   // argument of type `U` such that `T` is constructible from `U`.
576b000b770SStanislav Gatev   if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
577b000b770SStanislav Gatev     return State.Env.getBoolLiteralValue(true);
578b000b770SStanislav Gatev 
579b000b770SStanislav Gatev   // This is a constructor/assignment call for `optional<T>` with argument of
580b000b770SStanislav Gatev   // type `optional<U>` such that `T` is constructible from `U`.
581*e95134b9SMartin Braenne   if (auto *HasValueVal = getHasValue(State.Env, State.Env.getValue(E)))
582dd38caf3SYitzhak Mandelbaum     return *HasValueVal;
583b000b770SStanislav Gatev   return State.Env.makeAtomicBoolValue();
584b000b770SStanislav Gatev }
585b000b770SStanislav Gatev 
586092a530cSStanislav Gatev void transferValueOrConversionConstructor(
587092a530cSStanislav Gatev     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
5889e0fc676SStanislav Gatev     LatticeTransferState &State) {
589092a530cSStanislav Gatev   assert(E->getNumArgs() > 0);
590092a530cSStanislav Gatev 
591f653d140SMartin Braenne   constructOptionalValue(*E, State.Env,
592390029beSYitzhak Mandelbaum                          valueOrConversionHasValue(*E->getConstructor(),
593b000b770SStanislav Gatev                                                    *E->getArg(0), MatchRes,
594b000b770SStanislav Gatev                                                    State));
595b000b770SStanislav Gatev }
596092a530cSStanislav Gatev 
597b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
598b000b770SStanislav Gatev                         LatticeTransferState &State) {
599b000b770SStanislav Gatev   assert(E->getNumArgs() > 0);
600b000b770SStanislav Gatev 
601f653d140SMartin Braenne   if (auto *Loc = cast<AggregateStorageLocation>(
602f653d140SMartin Braenne           State.Env.getStorageLocationStrict(*E->getArg(0)))) {
603f653d140SMartin Braenne     createOptionalValue(*Loc, HasValueVal, State.Env);
604b000b770SStanislav Gatev 
605b000b770SStanislav Gatev     // Assign a storage location for the whole expression.
606f653d140SMartin Braenne     State.Env.setStorageLocationStrict(*E, *Loc);
607f653d140SMartin Braenne   }
608b000b770SStanislav Gatev }
609b000b770SStanislav Gatev 
610b000b770SStanislav Gatev void transferValueOrConversionAssignment(
611b000b770SStanislav Gatev     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
612b000b770SStanislav Gatev     LatticeTransferState &State) {
613b000b770SStanislav Gatev   assert(E->getNumArgs() > 1);
614b000b770SStanislav Gatev   transferAssignment(E,
615390029beSYitzhak Mandelbaum                      valueOrConversionHasValue(*E->getDirectCallee(),
61606decd0bSKazu Hirata                                                *E->getArg(1), MatchRes, State),
617b000b770SStanislav Gatev                      State);
618b000b770SStanislav Gatev }
619b000b770SStanislav Gatev 
620b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
621b000b770SStanislav Gatev                                const MatchFinder::MatchResult &,
622b000b770SStanislav Gatev                                LatticeTransferState &State) {
623b000b770SStanislav Gatev   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
6249e0fc676SStanislav Gatev }
6259e0fc676SStanislav Gatev 
626f653d140SMartin Braenne void transferSwap(AggregateStorageLocation *Loc1,
627f653d140SMartin Braenne                   AggregateStorageLocation *Loc2, Environment &Env) {
628d4fb829bSYitzhak Mandelbaum   // We account for cases where one or both of the optionals are not modeled,
629d4fb829bSYitzhak Mandelbaum   // either lacking associated storage locations, or lacking values associated
630d4fb829bSYitzhak Mandelbaum   // to such storage locations.
6312ddd57aeSStanislav Gatev 
632d4fb829bSYitzhak Mandelbaum   if (Loc1 == nullptr) {
633d4fb829bSYitzhak Mandelbaum     if (Loc2 != nullptr)
634f653d140SMartin Braenne       createOptionalValue(*Loc2, Env.makeAtomicBoolValue(), Env);
635d4fb829bSYitzhak Mandelbaum     return;
636d4fb829bSYitzhak Mandelbaum   }
637d4fb829bSYitzhak Mandelbaum   if (Loc2 == nullptr) {
638f653d140SMartin Braenne     createOptionalValue(*Loc1, Env.makeAtomicBoolValue(), Env);
639d4fb829bSYitzhak Mandelbaum     return;
640d4fb829bSYitzhak Mandelbaum   }
6412ddd57aeSStanislav Gatev 
642d4fb829bSYitzhak Mandelbaum   // Both expressions have locations, though they may not have corresponding
643d4fb829bSYitzhak Mandelbaum   // values. In that case, we create a fresh value at this point. Note that if
644d4fb829bSYitzhak Mandelbaum   // two branches both do this, they will not share the value, but it at least
645d4fb829bSYitzhak Mandelbaum   // allows for local reasoning about the value. To avoid the above, we would
646d4fb829bSYitzhak Mandelbaum   // need *lazy* value allocation.
647d4fb829bSYitzhak Mandelbaum   // FIXME: allocate values lazily, instead of just creating a fresh value.
648f653d140SMartin Braenne   BoolValue *BoolVal1 = getHasValue(Env, Env.getValue(*Loc1));
649f653d140SMartin Braenne   if (BoolVal1 == nullptr)
650f653d140SMartin Braenne     BoolVal1 = &Env.makeAtomicBoolValue();
651d4fb829bSYitzhak Mandelbaum 
652f653d140SMartin Braenne   BoolValue *BoolVal2 = getHasValue(Env, Env.getValue(*Loc2));
653f653d140SMartin Braenne   if (BoolVal2 == nullptr)
654f653d140SMartin Braenne     BoolVal2 = &Env.makeAtomicBoolValue();
655d4fb829bSYitzhak Mandelbaum 
656f653d140SMartin Braenne   createOptionalValue(*Loc1, *BoolVal2, Env);
657f653d140SMartin Braenne   createOptionalValue(*Loc2, *BoolVal1, Env);
6582ddd57aeSStanislav Gatev }
6592ddd57aeSStanislav Gatev 
6602ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
6612ddd57aeSStanislav Gatev                       const MatchFinder::MatchResult &,
6622ddd57aeSStanislav Gatev                       LatticeTransferState &State) {
6632ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 1);
664f653d140SMartin Braenne   auto *OtherLoc = cast_or_null<AggregateStorageLocation>(
665f653d140SMartin Braenne       State.Env.getStorageLocationStrict(*E->getArg(0)));
666f653d140SMartin Braenne   transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
6672ddd57aeSStanislav Gatev }
6682ddd57aeSStanislav Gatev 
6692ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
6702ddd57aeSStanislav Gatev                          LatticeTransferState &State) {
6712ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 2);
672f653d140SMartin Braenne   auto *Arg0Loc = cast_or_null<AggregateStorageLocation>(
673f653d140SMartin Braenne       State.Env.getStorageLocationStrict(*E->getArg(0)));
674f653d140SMartin Braenne   auto *Arg1Loc = cast_or_null<AggregateStorageLocation>(
675f653d140SMartin Braenne       State.Env.getStorageLocationStrict(*E->getArg(1)));
676f653d140SMartin Braenne   transferSwap(Arg0Loc, Arg1Loc, State.Env);
6772ddd57aeSStanislav Gatev }
6782ddd57aeSStanislav Gatev 
67925956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
68025956d55SAMS21                             LatticeTransferState &State) {
68125956d55SAMS21   assert(E->getNumArgs() == 1);
68225956d55SAMS21 
683243a79caSMartin Braenne   if (auto *Loc = State.Env.getStorageLocationStrict(*E->getArg(0)))
684243a79caSMartin Braenne     State.Env.setStorageLocationStrict(*E, *Loc);
68525956d55SAMS21 }
68625956d55SAMS21 
6876272226bSSam McCall const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
6886272226bSSam McCall                                 const Formula &LHS, const Formula &RHS) {
689390029beSYitzhak Mandelbaum   // Logically, an optional<T> object is composed of two values - a `has_value`
690390029beSYitzhak Mandelbaum   // bit and a value of type T. Equality of optional objects compares both
691390029beSYitzhak Mandelbaum   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
692390029beSYitzhak Mandelbaum   // when two optional objects are engaged, the equality of their respective
693390029beSYitzhak Mandelbaum   // values of type T matters. Since we only track the `has_value` bits, we
694390029beSYitzhak Mandelbaum   // can't make any conclusions about equality when we know that two optional
695390029beSYitzhak Mandelbaum   // objects are engaged.
696390029beSYitzhak Mandelbaum   //
697390029beSYitzhak Mandelbaum   // We express this as two facts about the equality:
698390029beSYitzhak Mandelbaum   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
699390029beSYitzhak Mandelbaum   //    If they are equal, then either both are set or both are unset.
700390029beSYitzhak Mandelbaum   // b) (!LHS & !RHS) => EqVal
701390029beSYitzhak Mandelbaum   //    If neither is set, then they are equal.
702390029beSYitzhak Mandelbaum   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
7036272226bSSam McCall   return A.makeAnd(
7046272226bSSam McCall       A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
7056272226bSSam McCall                                     A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
7066272226bSSam McCall       A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
707390029beSYitzhak Mandelbaum }
708390029beSYitzhak Mandelbaum 
709390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
710390029beSYitzhak Mandelbaum                                     const MatchFinder::MatchResult &,
711390029beSYitzhak Mandelbaum                                     LatticeTransferState &State) {
712390029beSYitzhak Mandelbaum   Environment &Env = State.Env;
7136272226bSSam McCall   auto &A = Env.arena();
714390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
715*e95134b9SMartin Braenne   if (auto *LHasVal = getHasValue(Env, Env.getValue(*CmpExpr->getArg(0))))
716*e95134b9SMartin Braenne     if (auto *RHasVal = getHasValue(Env, Env.getValue(*CmpExpr->getArg(1)))) {
717390029beSYitzhak Mandelbaum       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
7186272226bSSam McCall         CmpValue = &A.makeNot(*CmpValue);
7196272226bSSam McCall       Env.addToFlowCondition(evaluateEquality(A, *CmpValue, LHasVal->formula(),
7206272226bSSam McCall                                               RHasVal->formula()));
721390029beSYitzhak Mandelbaum     }
722390029beSYitzhak Mandelbaum }
723390029beSYitzhak Mandelbaum 
724390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
725390029beSYitzhak Mandelbaum                                  const clang::Expr *E, Environment &Env) {
7266272226bSSam McCall   auto &A = Env.arena();
727390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
728*e95134b9SMartin Braenne   if (auto *HasVal = getHasValue(Env, Env.getValue(*E))) {
729390029beSYitzhak Mandelbaum     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
7306272226bSSam McCall       CmpValue = &A.makeNot(*CmpValue);
7316272226bSSam McCall     Env.addToFlowCondition(
7326272226bSSam McCall         evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
733390029beSYitzhak Mandelbaum   }
734390029beSYitzhak Mandelbaum }
735390029beSYitzhak Mandelbaum 
7366ad0788cSKazu Hirata std::optional<StatementMatcher>
737a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
7385d22d1f5SYitzhak Mandelbaum   if (Options.IgnoreSmartPointerDereference) {
7395d22d1f5SYitzhak Mandelbaum     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
7405d22d1f5SYitzhak Mandelbaum         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
7415d22d1f5SYitzhak Mandelbaum         unless(hasArgument(0, expr(hasOptionalType()))))));
7425d22d1f5SYitzhak Mandelbaum     return expr(
7435d22d1f5SYitzhak Mandelbaum         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
7445d22d1f5SYitzhak Mandelbaum   }
74534e0d057SKazu Hirata   return std::nullopt;
746a184a0d8SYitzhak Mandelbaum }
747a184a0d8SYitzhak Mandelbaum 
74858fe7f96SSam Estep StatementMatcher
7496ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
7502f0630f8SAnton Dukeman   return isOptionalMemberCallWithNameMatcher(hasName("value"),
7512f0630f8SAnton Dukeman                                              IgnorableOptional);
75258fe7f96SSam Estep }
75358fe7f96SSam Estep 
75458fe7f96SSam Estep StatementMatcher
7556ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
75658fe7f96SSam Estep   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
75758fe7f96SSam Estep                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
75858fe7f96SSam Estep }
75958fe7f96SSam Estep 
7605d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() {
761b000b770SStanislav Gatev   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
762b000b770SStanislav Gatev   // lot of duplicated work (e.g. string comparisons), consider providing APIs
763b000b770SStanislav Gatev   // that avoid it through memoization.
7647538b360SWei Yi Tee   return CFGMatchSwitchBuilder<LatticeTransferState>()
765af98b0afSStanislav Gatev       // Attach a symbolic "has_value" state to optional values that we see for
766af98b0afSStanislav Gatev       // the first time.
7677538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(
7686adfc64eSYitzhak Mandelbaum           expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
769af98b0afSStanislav Gatev           initializeOptionalReference)
770af98b0afSStanislav Gatev 
7719e0fc676SStanislav Gatev       // make_optional
7727538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
773092a530cSStanislav Gatev 
7740e8d4a6dSYitzhak Mandelbaum       // optional::optional (in place)
7757538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
776092a530cSStanislav Gatev           isOptionalInPlaceConstructor(),
777092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
778092a530cSStanislav Gatev              LatticeTransferState &State) {
779f653d140SMartin Braenne             constructOptionalValue(*E, State.Env,
7800e8d4a6dSYitzhak Mandelbaum                                    State.Env.getBoolLiteralValue(true));
781092a530cSStanislav Gatev           })
7820e8d4a6dSYitzhak Mandelbaum       // nullopt_t::nullopt_t
7837538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
784390029beSYitzhak Mandelbaum           isNulloptConstructor(),
785092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
786092a530cSStanislav Gatev              LatticeTransferState &State) {
787f653d140SMartin Braenne             constructOptionalValue(*E, State.Env,
788092a530cSStanislav Gatev                                    State.Env.getBoolLiteralValue(false));
789092a530cSStanislav Gatev           })
7900e8d4a6dSYitzhak Mandelbaum       // optional::optional(nullopt_t)
791390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXConstructExpr>(
792390029beSYitzhak Mandelbaum           isOptionalNulloptConstructor(),
793390029beSYitzhak Mandelbaum           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
794390029beSYitzhak Mandelbaum              LatticeTransferState &State) {
795f653d140SMartin Braenne             constructOptionalValue(*E, State.Env,
7960e8d4a6dSYitzhak Mandelbaum                                    State.Env.getBoolLiteralValue(false));
797390029beSYitzhak Mandelbaum           })
7980e8d4a6dSYitzhak Mandelbaum       // optional::optional (value/conversion)
7997538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
800092a530cSStanislav Gatev                                        transferValueOrConversionConstructor)
8019e0fc676SStanislav Gatev 
802b000b770SStanislav Gatev       // optional::operator=
8037538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(
8047538b360SWei Yi Tee           isOptionalValueOrConversionAssignment(),
805b000b770SStanislav Gatev           transferValueOrConversionAssignment)
8067538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
807b000b770SStanislav Gatev                                           transferNulloptAssignment)
808b000b770SStanislav Gatev 
809af98b0afSStanislav Gatev       // optional::value
8107538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8115d22d1f5SYitzhak Mandelbaum           valueCall(std::nullopt),
812092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
813092a530cSStanislav Gatev              LatticeTransferState &State) {
814af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
815af98b0afSStanislav Gatev           })
816af98b0afSStanislav Gatev 
8173bc1ea5bSMartin Braenne       // optional::operator*
8183bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
8197538b360SWei Yi Tee                                [](const CallExpr *E,
8207538b360SWei Yi Tee                                   const MatchFinder::MatchResult &,
821092a530cSStanislav Gatev                                   LatticeTransferState &State) {
822af98b0afSStanislav Gatev                                  transferUnwrapCall(E, E->getArg(0), State);
823af98b0afSStanislav Gatev                                })
824af98b0afSStanislav Gatev 
8253bc1ea5bSMartin Braenne       // optional::operator->
8263bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
8273bc1ea5bSMartin Braenne                                [](const CallExpr *E,
8283bc1ea5bSMartin Braenne                                   const MatchFinder::MatchResult &,
8293bc1ea5bSMartin Braenne                                   LatticeTransferState &State) {
8303bc1ea5bSMartin Braenne                                  transferArrowOpCall(E, E->getArg(0), State);
8313bc1ea5bSMartin Braenne                                })
8323bc1ea5bSMartin Braenne 
8332f0630f8SAnton Dukeman       // optional::has_value, optional::hasValue
8342f0630f8SAnton Dukeman       // Of the supported optionals only folly::Optional uses hasValue, but this
8352f0630f8SAnton Dukeman       // will also pass for other types
8367538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8372f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(
8382f0630f8SAnton Dukeman               hasAnyName("has_value", "hasValue")),
839af98b0afSStanislav Gatev           transferOptionalHasValueCall)
840af98b0afSStanislav Gatev 
8419e0fc676SStanislav Gatev       // optional::operator bool
8427538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8432f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
8449e0fc676SStanislav Gatev           transferOptionalHasValueCall)
8459e0fc676SStanislav Gatev 
8469e0fc676SStanislav Gatev       // optional::emplace
8477538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8482f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("emplace")),
849092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
850092a530cSStanislav Gatev              LatticeTransferState &State) {
851f653d140SMartin Braenne             if (AggregateStorageLocation *Loc =
852f653d140SMartin Braenne                     getImplicitObjectLocation(*E, State.Env)) {
853f653d140SMartin Braenne               createOptionalValue(*Loc, State.Env.getBoolLiteralValue(true),
854f653d140SMartin Braenne                                   State.Env);
855f653d140SMartin Braenne             }
856092a530cSStanislav Gatev           })
8579e0fc676SStanislav Gatev 
8589e0fc676SStanislav Gatev       // optional::reset
8597538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8602f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("reset")),
861092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
862092a530cSStanislav Gatev              LatticeTransferState &State) {
863f653d140SMartin Braenne             if (AggregateStorageLocation *Loc =
864f653d140SMartin Braenne                     getImplicitObjectLocation(*E, State.Env)) {
865f653d140SMartin Braenne               createOptionalValue(*Loc, State.Env.getBoolLiteralValue(false),
866f653d140SMartin Braenne                                   State.Env);
867f653d140SMartin Braenne             }
868092a530cSStanislav Gatev           })
8699e0fc676SStanislav Gatev 
8702ddd57aeSStanislav Gatev       // optional::swap
8712f0630f8SAnton Dukeman       .CaseOfCFGStmt<CXXMemberCallExpr>(
8722f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("swap")),
8732ddd57aeSStanislav Gatev           transferSwapCall)
8742ddd57aeSStanislav Gatev 
8752ddd57aeSStanislav Gatev       // std::swap
8767538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
8772ddd57aeSStanislav Gatev 
87825956d55SAMS21       // std::forward
87925956d55SAMS21       .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
88025956d55SAMS21 
8817f076004SYitzhak Mandelbaum       // opt.value_or("").empty()
8827538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
8837538b360SWei Yi Tee                            transferValueOrStringEmptyCall)
8847f076004SYitzhak Mandelbaum 
8857f076004SYitzhak Mandelbaum       // opt.value_or(X) != X
8867538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
8877f076004SYitzhak Mandelbaum 
888390029beSYitzhak Mandelbaum       // Comparisons (==, !=):
889390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
890390029beSYitzhak Mandelbaum           isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()),
891390029beSYitzhak Mandelbaum           transferOptionalAndOptionalCmp)
892390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
893390029beSYitzhak Mandelbaum           isComparisonOperatorCall(hasOptionalType(),
894390029beSYitzhak Mandelbaum                                    unless(hasAnyOptionalType())),
895390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
896390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
897390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
898390029beSYitzhak Mandelbaum           })
899390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
900390029beSYitzhak Mandelbaum           isComparisonOperatorCall(unless(hasAnyOptionalType()),
901390029beSYitzhak Mandelbaum                                    hasOptionalType()),
902390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
903390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
904390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
905390029beSYitzhak Mandelbaum           })
906390029beSYitzhak Mandelbaum 
90765e710c3SStanislav Gatev       // returns optional
9087538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
90965e710c3SStanislav Gatev                                transferCallReturningOptional)
91065e710c3SStanislav Gatev 
911af98b0afSStanislav Gatev       .Build();
912af98b0afSStanislav Gatev }
913af98b0afSStanislav Gatev 
9146a81e694SMartin Braenne std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
91558fe7f96SSam Estep                                                const Environment &Env) {
91648bc7150SMartin Braenne   if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) {
91758fe7f96SSam Estep     auto *Prop = OptionalVal->getProperty("has_value");
91858fe7f96SSam Estep     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
9196272226bSSam McCall       if (Env.flowConditionImplies(HasValueVal->formula()))
92058fe7f96SSam Estep         return {};
92158fe7f96SSam Estep     }
92258fe7f96SSam Estep   }
92358fe7f96SSam Estep 
92458fe7f96SSam Estep   // Record that this unwrap is *not* provably safe.
92558fe7f96SSam Estep   // FIXME: include either the name of the optional (if applicable) or a source
92658fe7f96SSam Estep   // range of the access for easier interpretation of the result.
92758fe7f96SSam Estep   return {ObjectExpr->getBeginLoc()};
92858fe7f96SSam Estep }
92958fe7f96SSam Estep 
93058fe7f96SSam Estep auto buildDiagnoseMatchSwitch(
93158fe7f96SSam Estep     const UncheckedOptionalAccessModelOptions &Options) {
93258fe7f96SSam Estep   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
93358fe7f96SSam Estep   // lot of duplicated work (e.g. string comparisons), consider providing APIs
93458fe7f96SSam Estep   // that avoid it through memoization.
93558fe7f96SSam Estep   auto IgnorableOptional = ignorableOptional(Options);
9367538b360SWei Yi Tee   return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
93758fe7f96SSam Estep       // optional::value
9387538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
93958fe7f96SSam Estep           valueCall(IgnorableOptional),
94058fe7f96SSam Estep           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
94158fe7f96SSam Estep              const Environment &Env) {
9426a81e694SMartin Braenne             return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
94358fe7f96SSam Estep           })
94458fe7f96SSam Estep 
94558fe7f96SSam Estep       // optional::operator*, optional::operator->
9466a81e694SMartin Braenne       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
9476a81e694SMartin Braenne                                [](const CallExpr *E,
9486a81e694SMartin Braenne                                   const MatchFinder::MatchResult &,
94958fe7f96SSam Estep                                   const Environment &Env) {
9506a81e694SMartin Braenne                                  return diagnoseUnwrapCall(E->getArg(0), Env);
95158fe7f96SSam Estep                                })
95258fe7f96SSam Estep       .Build();
95358fe7f96SSam Estep }
95458fe7f96SSam Estep 
955af98b0afSStanislav Gatev } // namespace
956af98b0afSStanislav Gatev 
9577e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher
9587e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() {
9597e63a0d4SYitzhak Mandelbaum   return optionalClass();
9607e63a0d4SYitzhak Mandelbaum }
9617e63a0d4SYitzhak Mandelbaum 
9625d22d1f5SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
963cf1f978dSSam Estep     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
9645d22d1f5SYitzhak Mandelbaum       TransferMatchSwitch(buildTransferMatchSwitch()) {}
965af98b0afSStanislav Gatev 
9666b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
9677538b360SWei Yi Tee                                             NoopLattice &L, Environment &Env) {
968af98b0afSStanislav Gatev   LatticeTransferState State(L, Env);
9696b991ba4SYitzhak Mandelbaum   TransferMatchSwitch(Elt, getASTContext(), State);
970af98b0afSStanislav Gatev }
971af98b0afSStanislav Gatev 
972c0725865SYitzhak Mandelbaum ComparisonResult UncheckedOptionalAccessModel::compare(
973c0725865SYitzhak Mandelbaum     QualType Type, const Value &Val1, const Environment &Env1,
974c0725865SYitzhak Mandelbaum     const Value &Val2, const Environment &Env2) {
975c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
976c0725865SYitzhak Mandelbaum     return ComparisonResult::Unknown;
977d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
978d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
97925956d55SAMS21   if (MustNonEmpty1 && MustNonEmpty2)
98025956d55SAMS21     return ComparisonResult::Same;
981d34fbf2dSYitzhak Mandelbaum   // If exactly one is true, then they're different, no reason to check whether
982d34fbf2dSYitzhak Mandelbaum   // they're definitely empty.
98325956d55SAMS21   if (MustNonEmpty1 || MustNonEmpty2)
98425956d55SAMS21     return ComparisonResult::Different;
985d34fbf2dSYitzhak Mandelbaum   // Check if they're both definitely empty.
986d34fbf2dSYitzhak Mandelbaum   return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
987c0725865SYitzhak Mandelbaum              ? ComparisonResult::Same
988c0725865SYitzhak Mandelbaum              : ComparisonResult::Different;
9898fcdd625SStanislav Gatev }
9908fcdd625SStanislav Gatev 
9918fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
9928fcdd625SStanislav Gatev                                          const Environment &Env1,
9938fcdd625SStanislav Gatev                                          const Value &Val2,
9948fcdd625SStanislav Gatev                                          const Environment &Env2,
9958fcdd625SStanislav Gatev                                          Value &MergedVal,
9968fcdd625SStanislav Gatev                                          Environment &MergedEnv) {
997c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
9988fcdd625SStanislav Gatev     return true;
999d34fbf2dSYitzhak Mandelbaum   // FIXME: uses same approach as join for `BoolValues`. Requires non-const
1000d34fbf2dSYitzhak Mandelbaum   // values, though, so will require updating the interface.
10018fcdd625SStanislav Gatev   auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
1002d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
1003d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
1004d34fbf2dSYitzhak Mandelbaum   if (MustNonEmpty1 && MustNonEmpty2)
10056272226bSSam McCall     MergedEnv.addToFlowCondition(HasValueVal.formula());
1006d34fbf2dSYitzhak Mandelbaum   else if (
1007d34fbf2dSYitzhak Mandelbaum       // Only make the costly calls to `isEmptyOptional` if we got "unknown"
1008d34fbf2dSYitzhak Mandelbaum       // (false) for both calls to `isNonEmptyOptional`.
1009d34fbf2dSYitzhak Mandelbaum       !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) &&
1010d34fbf2dSYitzhak Mandelbaum       isEmptyOptional(Val2, Env2))
10116272226bSSam McCall     MergedEnv.addToFlowCondition(
10126272226bSSam McCall         MergedEnv.arena().makeNot(HasValueVal.formula()));
10138fcdd625SStanislav Gatev   setHasValue(MergedVal, HasValueVal);
10148fcdd625SStanislav Gatev   return true;
10158fcdd625SStanislav Gatev }
10168fcdd625SStanislav Gatev 
1017d34fbf2dSYitzhak Mandelbaum Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev,
1018d34fbf2dSYitzhak Mandelbaum                                            const Environment &PrevEnv,
1019d34fbf2dSYitzhak Mandelbaum                                            Value &Current,
1020d34fbf2dSYitzhak Mandelbaum                                            Environment &CurrentEnv) {
1021d34fbf2dSYitzhak Mandelbaum   switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) {
1022d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Same:
1023d34fbf2dSYitzhak Mandelbaum     return &Prev;
1024d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Different:
1025d34fbf2dSYitzhak Mandelbaum     if (auto *PrevHasVal =
1026d34fbf2dSYitzhak Mandelbaum             cast_or_null<BoolValue>(Prev.getProperty("has_value"))) {
1027d34fbf2dSYitzhak Mandelbaum       if (isa<TopBoolValue>(PrevHasVal))
1028d34fbf2dSYitzhak Mandelbaum         return &Prev;
1029d34fbf2dSYitzhak Mandelbaum     }
1030d34fbf2dSYitzhak Mandelbaum     if (auto *CurrentHasVal =
1031d34fbf2dSYitzhak Mandelbaum             cast_or_null<BoolValue>(Current.getProperty("has_value"))) {
1032d34fbf2dSYitzhak Mandelbaum       if (isa<TopBoolValue>(CurrentHasVal))
1033d34fbf2dSYitzhak Mandelbaum         return &Current;
1034d34fbf2dSYitzhak Mandelbaum     }
103544f98d01SMartin Braenne     return &createOptionalValue(cast<StructValue>(Current).getAggregateLoc(),
103644f98d01SMartin Braenne                                 CurrentEnv.makeTopBoolValue(), CurrentEnv);
1037d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Unknown:
1038d34fbf2dSYitzhak Mandelbaum     return nullptr;
1039d34fbf2dSYitzhak Mandelbaum   }
1040d34fbf2dSYitzhak Mandelbaum   llvm_unreachable("all cases covered in switch");
1041d34fbf2dSYitzhak Mandelbaum }
1042d34fbf2dSYitzhak Mandelbaum 
104358fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
104458fe7f96SSam Estep     UncheckedOptionalAccessModelOptions Options)
104558fe7f96SSam Estep     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
104658fe7f96SSam Estep 
1047af98b0afSStanislav Gatev } // namespace dataflow
1048af98b0afSStanislav Gatev } // namespace clang
1049