xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision bbeda83090adcb3609f9c1331b2345e7fa547f90)
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"
25cf1f978dSSam Estep #include "clang/Analysis/FlowSensitive/NoopLattice.h"
260086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h"
27af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h"
2858fe7f96SSam Estep #include "clang/Basic/SourceLocation.h"
29af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h"
30af98b0afSStanislav Gatev #include "llvm/Support/Casting.h"
31d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h"
32af98b0afSStanislav Gatev #include <cassert>
339e0fc676SStanislav Gatev #include <memory>
34a1580d7bSKazu Hirata #include <optional>
359e0fc676SStanislav Gatev #include <utility>
3658fe7f96SSam Estep #include <vector>
37af98b0afSStanislav Gatev 
38af98b0afSStanislav Gatev namespace clang {
39af98b0afSStanislav Gatev namespace dataflow {
4009b462efSYitzhak Mandelbaum 
4109b462efSYitzhak Mandelbaum static bool isTopLevelNamespaceWithName(const NamespaceDecl &NS,
4209b462efSYitzhak Mandelbaum                                         llvm::StringRef Name) {
4309b462efSYitzhak Mandelbaum   return NS.getDeclName().isIdentifier() && NS.getName() == Name &&
4409b462efSYitzhak Mandelbaum          NS.getParent() != nullptr && NS.getParent()->isTranslationUnit();
4509b462efSYitzhak Mandelbaum }
4609b462efSYitzhak Mandelbaum 
4709b462efSYitzhak Mandelbaum static bool hasOptionalClassName(const CXXRecordDecl &RD) {
4809b462efSYitzhak Mandelbaum   if (!RD.getDeclName().isIdentifier())
4909b462efSYitzhak Mandelbaum     return false;
5009b462efSYitzhak Mandelbaum 
5109b462efSYitzhak Mandelbaum   if (RD.getName() == "optional") {
5209b462efSYitzhak Mandelbaum     if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
5309b462efSYitzhak Mandelbaum       return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl");
5409b462efSYitzhak Mandelbaum     return false;
5509b462efSYitzhak Mandelbaum   }
5609b462efSYitzhak Mandelbaum 
5709b462efSYitzhak Mandelbaum   if (RD.getName() == "Optional") {
5809b462efSYitzhak Mandelbaum     // Check whether namespace is "::base".
5909b462efSYitzhak Mandelbaum     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
6009b462efSYitzhak Mandelbaum     return N != nullptr && isTopLevelNamespaceWithName(*N, "base");
6109b462efSYitzhak Mandelbaum   }
6209b462efSYitzhak Mandelbaum 
6309b462efSYitzhak Mandelbaum   return false;
6409b462efSYitzhak Mandelbaum }
6509b462efSYitzhak Mandelbaum 
66af98b0afSStanislav Gatev namespace {
67af98b0afSStanislav Gatev 
68af98b0afSStanislav Gatev using namespace ::clang::ast_matchers;
69cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>;
70af98b0afSStanislav Gatev 
7109b462efSYitzhak Mandelbaum AST_MATCHER(CXXRecordDecl, hasOptionalClassNameMatcher) {
7209b462efSYitzhak Mandelbaum   return hasOptionalClassName(Node);
7309b462efSYitzhak Mandelbaum }
7409b462efSYitzhak Mandelbaum 
757e63a0d4SYitzhak Mandelbaum DeclarationMatcher optionalClass() {
76af98b0afSStanislav Gatev   return classTemplateSpecializationDecl(
7709b462efSYitzhak Mandelbaum       hasOptionalClassNameMatcher(),
78af98b0afSStanislav Gatev       hasTemplateArgument(0, refersToType(type().bind("T"))));
79af98b0afSStanislav Gatev }
80af98b0afSStanislav Gatev 
816adfc64eSYitzhak Mandelbaum auto optionalOrAliasType() {
8265e710c3SStanislav Gatev   return hasUnqualifiedDesugaredType(
8365e710c3SStanislav Gatev       recordType(hasDeclaration(optionalClass())));
8465e710c3SStanislav Gatev }
8565e710c3SStanislav Gatev 
866adfc64eSYitzhak Mandelbaum /// Matches any of the spellings of the optional types and sugar, aliases, etc.
876adfc64eSYitzhak Mandelbaum auto hasOptionalType() { return hasType(optionalOrAliasType()); }
886adfc64eSYitzhak Mandelbaum 
89a184a0d8SYitzhak Mandelbaum auto isOptionalMemberCallWithName(
90a184a0d8SYitzhak Mandelbaum     llvm::StringRef MemberName,
916ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
92a184a0d8SYitzhak Mandelbaum   auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr()))
93a184a0d8SYitzhak Mandelbaum                                     : cxxThisExpr());
94af98b0afSStanislav Gatev   return cxxMemberCallExpr(
9509b462efSYitzhak Mandelbaum       on(expr(Exception,
9609b462efSYitzhak Mandelbaum               anyOf(hasOptionalType(),
9709b462efSYitzhak Mandelbaum                     hasType(pointerType(pointee(optionalOrAliasType())))))),
9809b462efSYitzhak Mandelbaum       callee(cxxMethodDecl(hasName(MemberName))));
99af98b0afSStanislav Gatev }
100af98b0afSStanislav Gatev 
101a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName(
102a184a0d8SYitzhak Mandelbaum     llvm::StringRef operator_name,
1036ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
104a184a0d8SYitzhak Mandelbaum   return cxxOperatorCallExpr(
105a184a0d8SYitzhak Mandelbaum       hasOverloadedOperatorName(operator_name),
106a184a0d8SYitzhak Mandelbaum       callee(cxxMethodDecl(ofClass(optionalClass()))),
107a184a0d8SYitzhak Mandelbaum       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
108af98b0afSStanislav Gatev }
109af98b0afSStanislav Gatev 
110092a530cSStanislav Gatev auto isMakeOptionalCall() {
1119e0fc676SStanislav Gatev   return callExpr(
1129e0fc676SStanislav Gatev       callee(functionDecl(hasAnyName(
1139e0fc676SStanislav Gatev           "std::make_optional", "base::make_optional", "absl::make_optional"))),
1149e0fc676SStanislav Gatev       hasOptionalType());
1159e0fc676SStanislav Gatev }
1169e0fc676SStanislav Gatev 
117390029beSYitzhak Mandelbaum auto nulloptTypeDecl() {
118390029beSYitzhak Mandelbaum   return namedDecl(
119390029beSYitzhak Mandelbaum       hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t"));
120092a530cSStanislav Gatev }
121092a530cSStanislav Gatev 
122390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
123390029beSYitzhak Mandelbaum 
124390029beSYitzhak Mandelbaum // `optional` or `nullopt_t`
125390029beSYitzhak Mandelbaum auto hasAnyOptionalType() {
126390029beSYitzhak Mandelbaum   return hasType(hasUnqualifiedDesugaredType(
127390029beSYitzhak Mandelbaum       recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass())))));
128390029beSYitzhak Mandelbaum }
129390029beSYitzhak Mandelbaum 
130092a530cSStanislav Gatev auto inPlaceClass() {
131092a530cSStanislav Gatev   return recordDecl(
132092a530cSStanislav Gatev       hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
133092a530cSStanislav Gatev }
134092a530cSStanislav Gatev 
135092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
1360086a355SYitzhak Mandelbaum   return cxxConstructExpr(
1370086a355SYitzhak Mandelbaum       hasOptionalType(),
1380086a355SYitzhak Mandelbaum       hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
1390086a355SYitzhak Mandelbaum                                         hasParameter(0, hasNulloptType()))));
140092a530cSStanislav Gatev }
141092a530cSStanislav Gatev 
142092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
143092a530cSStanislav Gatev   return cxxConstructExpr(hasOptionalType(),
144092a530cSStanislav Gatev                           hasArgument(0, hasType(inPlaceClass())));
145092a530cSStanislav Gatev }
146092a530cSStanislav Gatev 
147092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
148092a530cSStanislav Gatev   return cxxConstructExpr(
149092a530cSStanislav Gatev       hasOptionalType(),
150092a530cSStanislav Gatev       unless(hasDeclaration(
151092a530cSStanislav Gatev           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
152092a530cSStanislav Gatev       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
153092a530cSStanislav Gatev }
154092a530cSStanislav Gatev 
155b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
156b000b770SStanislav Gatev   return cxxOperatorCallExpr(
157b000b770SStanislav Gatev       hasOverloadedOperatorName("="),
158b000b770SStanislav Gatev       callee(cxxMethodDecl(ofClass(optionalClass()))),
159b000b770SStanislav Gatev       unless(hasDeclaration(cxxMethodDecl(
160b000b770SStanislav Gatev           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
161b000b770SStanislav Gatev       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
162b000b770SStanislav Gatev }
163b000b770SStanislav Gatev 
164390029beSYitzhak Mandelbaum auto isNulloptConstructor() {
165390029beSYitzhak Mandelbaum   return cxxConstructExpr(hasNulloptType(), argumentCountIs(1),
166390029beSYitzhak Mandelbaum                           hasArgument(0, hasNulloptType()));
167390029beSYitzhak Mandelbaum }
168390029beSYitzhak Mandelbaum 
169b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
170b000b770SStanislav Gatev   return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
171b000b770SStanislav Gatev                              callee(cxxMethodDecl(ofClass(optionalClass()))),
172b000b770SStanislav Gatev                              argumentCountIs(2),
173b000b770SStanislav Gatev                              hasArgument(1, hasNulloptType()));
174b000b770SStanislav Gatev }
175b000b770SStanislav Gatev 
1762ddd57aeSStanislav Gatev auto isStdSwapCall() {
1772ddd57aeSStanislav Gatev   return callExpr(callee(functionDecl(hasName("std::swap"))),
1782ddd57aeSStanislav Gatev                   argumentCountIs(2), hasArgument(0, hasOptionalType()),
1792ddd57aeSStanislav Gatev                   hasArgument(1, hasOptionalType()));
1802ddd57aeSStanislav Gatev }
1812ddd57aeSStanislav Gatev 
18225956d55SAMS21 auto isStdForwardCall() {
18325956d55SAMS21   return callExpr(callee(functionDecl(hasName("std::forward"))),
18425956d55SAMS21                   argumentCountIs(1), hasArgument(0, hasOptionalType()));
18525956d55SAMS21 }
18625956d55SAMS21 
1877f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
1887f076004SYitzhak Mandelbaum 
1897f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() {
1907f076004SYitzhak Mandelbaum   // `opt.value_or("").empty()`
1917f076004SYitzhak Mandelbaum   return cxxMemberCallExpr(
1927f076004SYitzhak Mandelbaum       callee(cxxMethodDecl(hasName("empty"))),
1937f076004SYitzhak Mandelbaum       onImplicitObjectArgument(ignoringImplicit(
1947f076004SYitzhak Mandelbaum           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1957f076004SYitzhak Mandelbaum                             callee(cxxMethodDecl(hasName("value_or"),
1967f076004SYitzhak Mandelbaum                                                  ofClass(optionalClass()))),
1977f076004SYitzhak Mandelbaum                             hasArgument(0, stringLiteral(hasSize(0))))
1987f076004SYitzhak Mandelbaum               .bind(ValueOrCallID))));
1997f076004SYitzhak Mandelbaum }
2007f076004SYitzhak Mandelbaum 
2017f076004SYitzhak Mandelbaum auto isValueOrNotEqX() {
2027f076004SYitzhak Mandelbaum   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
2037f076004SYitzhak Mandelbaum     return hasOperands(
2047f076004SYitzhak Mandelbaum         ignoringImplicit(
2057f076004SYitzhak Mandelbaum             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
2067f076004SYitzhak Mandelbaum                               callee(cxxMethodDecl(hasName("value_or"),
2077f076004SYitzhak Mandelbaum                                                    ofClass(optionalClass()))),
2087f076004SYitzhak Mandelbaum                               hasArgument(0, Arg))
2097f076004SYitzhak Mandelbaum                 .bind(ValueOrCallID)),
2107f076004SYitzhak Mandelbaum         ignoringImplicit(Arg));
2117f076004SYitzhak Mandelbaum   };
2127f076004SYitzhak Mandelbaum 
2137f076004SYitzhak Mandelbaum   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
2147f076004SYitzhak Mandelbaum   // support this pattern for any expression, but the AST does not have a
2157f076004SYitzhak Mandelbaum   // generic expression comparison facility, so we specialize to common cases
2167f076004SYitzhak Mandelbaum   // seen in practice.  FIXME: define a matcher that compares values across
2177f076004SYitzhak Mandelbaum   // nodes, which would let us generalize this to any `X`.
2187f076004SYitzhak Mandelbaum   return binaryOperation(hasOperatorName("!="),
2197f076004SYitzhak Mandelbaum                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
2207f076004SYitzhak Mandelbaum                                ComparesToSame(stringLiteral(hasSize(0))),
2217f076004SYitzhak Mandelbaum                                ComparesToSame(integerLiteral(equals(0)))));
2227f076004SYitzhak Mandelbaum }
2237f076004SYitzhak Mandelbaum 
22465e710c3SStanislav Gatev auto isCallReturningOptional() {
225cd0d5261SSam Estep   return callExpr(hasType(qualType(anyOf(
226cd0d5261SSam Estep       optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
22765e710c3SStanislav Gatev }
22865e710c3SStanislav Gatev 
229390029beSYitzhak Mandelbaum template <typename L, typename R>
230390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
231390029beSYitzhak Mandelbaum   return cxxOperatorCallExpr(
232390029beSYitzhak Mandelbaum       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
233390029beSYitzhak Mandelbaum       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
234390029beSYitzhak Mandelbaum       hasArgument(1, rhs_arg_matcher));
235390029beSYitzhak Mandelbaum }
236390029beSYitzhak Mandelbaum 
237ffb4f4dbSDmitri Gribenko /// Ensures that `Expr` is mapped to a `BoolValue` and returns it.
238390029beSYitzhak Mandelbaum BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) {
239390029beSYitzhak Mandelbaum   auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None));
240390029beSYitzhak Mandelbaum   if (Value != nullptr)
241390029beSYitzhak Mandelbaum     return *Value;
242390029beSYitzhak Mandelbaum 
243390029beSYitzhak Mandelbaum   auto &Loc = Env.createStorageLocation(Expr);
244390029beSYitzhak Mandelbaum   Value = &Env.makeAtomicBoolValue();
245390029beSYitzhak Mandelbaum   Env.setValue(Loc, *Value);
246390029beSYitzhak Mandelbaum   Env.setStorageLocation(Expr, Loc);
247390029beSYitzhak Mandelbaum   return *Value;
248390029beSYitzhak Mandelbaum }
249390029beSYitzhak Mandelbaum 
2508fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
2518fcdd625SStanislav Gatev /// property of the optional value `OptionalVal`.
2528fcdd625SStanislav Gatev void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
2538fcdd625SStanislav Gatev   OptionalVal.setProperty("has_value", HasValueVal);
2548fcdd625SStanislav Gatev }
2558fcdd625SStanislav Gatev 
2569e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the
2579e0fc676SStanislav Gatev /// symbolic value of its "has_value" property.
2589e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
259745a957fSMartin Braenne   auto &OptionalVal = Env.create<StructValue>();
260745a957fSMartin Braenne   setHasValue(OptionalVal, HasValueVal);
261745a957fSMartin Braenne   return OptionalVal;
2629e0fc676SStanislav Gatev }
2639e0fc676SStanislav Gatev 
264af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
26549ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
266dd38caf3SYitzhak Mandelbaum BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
267dd38caf3SYitzhak Mandelbaum   if (OptionalVal != nullptr) {
268dd38caf3SYitzhak Mandelbaum     auto *HasValueVal =
269dd38caf3SYitzhak Mandelbaum         cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
270dd38caf3SYitzhak Mandelbaum     if (HasValueVal == nullptr) {
271dd38caf3SYitzhak Mandelbaum       HasValueVal = &Env.makeAtomicBoolValue();
272dd38caf3SYitzhak Mandelbaum       OptionalVal->setProperty("has_value", *HasValueVal);
273dd38caf3SYitzhak Mandelbaum     }
274dd38caf3SYitzhak Mandelbaum     return HasValueVal;
275af98b0afSStanislav Gatev   }
276af98b0afSStanislav Gatev   return nullptr;
277af98b0afSStanislav Gatev }
278af98b0afSStanislav Gatev 
279092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type.
280c0725865SYitzhak Mandelbaum bool isOptionalType(QualType Type) {
281092a530cSStanislav Gatev   if (!Type->isRecordType())
282092a530cSStanislav Gatev     return false;
283cd22e0dcSYitzhak Mandelbaum   const CXXRecordDecl *D = Type->getAsCXXRecordDecl();
28409b462efSYitzhak Mandelbaum   return D != nullptr && hasOptionalClassName(*D);
285092a530cSStanislav Gatev }
286092a530cSStanislav Gatev 
287092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
288092a530cSStanislav Gatev ///
289092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
290092a530cSStanislav Gatev /// function will be 2.
291092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
292c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
293092a530cSStanislav Gatev     return 0;
294092a530cSStanislav Gatev   return 1 + countOptionalWrappers(
295092a530cSStanislav Gatev                  ASTCtx,
296092a530cSStanislav Gatev                  cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
297092a530cSStanislav Gatev                      ->getTemplateArgs()
298092a530cSStanislav Gatev                      .get(0)
299092a530cSStanislav Gatev                      .getAsType()
300092a530cSStanislav Gatev                      .getDesugaredType(ASTCtx));
301092a530cSStanislav Gatev }
302092a530cSStanislav Gatev 
303dd38caf3SYitzhak Mandelbaum /// Tries to initialize the `optional`'s value (that is, contents), and return
304dd38caf3SYitzhak Mandelbaum /// its location. Returns nullptr if the value can't be represented.
305dd38caf3SYitzhak Mandelbaum StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
306dd38caf3SYitzhak Mandelbaum                                                     Value &OptionalVal,
307dd38caf3SYitzhak Mandelbaum                                                     Environment &Env) {
308dd38caf3SYitzhak Mandelbaum   // The "value" property represents a synthetic field. As such, it needs
309dd38caf3SYitzhak Mandelbaum   // `StorageLocation`, like normal fields (and other variables). So, we model
310af22be39SMartin Braenne   // it with a `PointerValue`, since that includes a storage location.  Once
311dd38caf3SYitzhak Mandelbaum   // the property is set, it will be shared by all environments that access the
312dd38caf3SYitzhak Mandelbaum   // `Value` representing the optional (here, `OptionalVal`).
313dd38caf3SYitzhak Mandelbaum   if (auto *ValueProp = OptionalVal.getProperty("value")) {
314af22be39SMartin Braenne     auto *ValuePtr = clang::cast<PointerValue>(ValueProp);
315af22be39SMartin Braenne     auto &ValueLoc = ValuePtr->getPointeeLoc();
316dd38caf3SYitzhak Mandelbaum     if (Env.getValue(ValueLoc) == nullptr) {
317dd38caf3SYitzhak Mandelbaum       // The property was previously set, but the value has been lost. This can
318*bbeda830SMartin Braenne       // happen in various situations, for example:
319*bbeda830SMartin Braenne       // - Because of an environment merge (where the two environments mapped
320*bbeda830SMartin Braenne       //   the property to different values, which resulted in them both being
321*bbeda830SMartin Braenne       //   discarded).
322*bbeda830SMartin Braenne       // - When two blocks in the CFG, with neither a dominator of the other,
323*bbeda830SMartin Braenne       //   visit the same optional value. (FIXME: This is something we can and
324*bbeda830SMartin Braenne       //   should fix -- see also the lengthy FIXME below.)
325*bbeda830SMartin Braenne       // - Or even when a block is revisited during testing to collect
326*bbeda830SMartin Braenne       //   per-statement state.
327*bbeda830SMartin Braenne       //
328dd38caf3SYitzhak Mandelbaum       // FIXME: This situation means that the optional contents are not shared
329dd38caf3SYitzhak Mandelbaum       // between branches and the like. Practically, this lack of sharing
330dd38caf3SYitzhak Mandelbaum       // reduces the precision of the model when the contents are relevant to
331dd38caf3SYitzhak Mandelbaum       // the check, like another optional or a boolean that influences control
332dd38caf3SYitzhak Mandelbaum       // flow.
333dd38caf3SYitzhak Mandelbaum       auto *ValueVal = Env.createValue(ValueLoc.getType());
334dd38caf3SYitzhak Mandelbaum       if (ValueVal == nullptr)
335dd38caf3SYitzhak Mandelbaum         return nullptr;
336dd38caf3SYitzhak Mandelbaum       Env.setValue(ValueLoc, *ValueVal);
337dd38caf3SYitzhak Mandelbaum     }
338dd38caf3SYitzhak Mandelbaum     return &ValueLoc;
339dd38caf3SYitzhak Mandelbaum   }
340dd38caf3SYitzhak Mandelbaum 
341c849843cSMartin Braenne   auto Ty = Q.getNonReferenceType();
342dd38caf3SYitzhak Mandelbaum   auto *ValueVal = Env.createValue(Ty);
343dd38caf3SYitzhak Mandelbaum   if (ValueVal == nullptr)
344dd38caf3SYitzhak Mandelbaum     return nullptr;
345dd38caf3SYitzhak Mandelbaum   auto &ValueLoc = Env.createStorageLocation(Ty);
346dd38caf3SYitzhak Mandelbaum   Env.setValue(ValueLoc, *ValueVal);
347af22be39SMartin Braenne   auto &ValuePtr = Env.create<PointerValue>(ValueLoc);
348*bbeda830SMartin Braenne   // FIXME:
349*bbeda830SMartin Braenne   // The change we make to the `value` property below may become visible to
350*bbeda830SMartin Braenne   // other blocks that aren't successors of the current block and therefore
351*bbeda830SMartin Braenne   // don't see the change we made above mapping `ValueLoc` to `ValueVal`. For
352*bbeda830SMartin Braenne   // example:
353*bbeda830SMartin Braenne   //
354*bbeda830SMartin Braenne   //   void target(optional<int> oo, bool b) {
355*bbeda830SMartin Braenne   //     // `oo` is associated with a `StructValue` here, which we will call
356*bbeda830SMartin Braenne   //     // `OptionalVal`.
357*bbeda830SMartin Braenne   //
358*bbeda830SMartin Braenne   //     // The `has_value` property is set on `OptionalVal` (but not the
359*bbeda830SMartin Braenne   //     // `value` property yet).
360*bbeda830SMartin Braenne   //     if (!oo.has_value()) return;
361*bbeda830SMartin Braenne   //
362*bbeda830SMartin Braenne   //     if (b) {
363*bbeda830SMartin Braenne   //       // Let's assume we transfer the `if` branch first.
364*bbeda830SMartin Braenne   //       //
365*bbeda830SMartin Braenne   //       // This causes us to call `maybeInitializeOptionalValueMember()`,
366*bbeda830SMartin Braenne   //       // which causes us to set the `value` property on `OptionalVal`
367*bbeda830SMartin Braenne   //       // (which had not been set until this point). This `value` property
368*bbeda830SMartin Braenne   //       // refers to a `PointerValue`, which in turn refers to a
369*bbeda830SMartin Braenne   //       // StorageLocation` that is associated to an `IntegerValue`.
370*bbeda830SMartin Braenne   //       oo.value();
371*bbeda830SMartin Braenne   //     } else {
372*bbeda830SMartin Braenne   //       // Let's assume we transfer the `else` branch after the `if` branch.
373*bbeda830SMartin Braenne   //       //
374*bbeda830SMartin Braenne   //       // We see the `value` property that the `if` branch set on
375*bbeda830SMartin Braenne   //       // `OptionalVal`, but in the environment for this block, the
376*bbeda830SMartin Braenne   //       // `StorageLocation` in the `PointerValue` is not associated with any
377*bbeda830SMartin Braenne   //       // `Value`.
378*bbeda830SMartin Braenne   //       oo.value();
379*bbeda830SMartin Braenne   //     }
380*bbeda830SMartin Braenne   //   }
381*bbeda830SMartin Braenne   //
382*bbeda830SMartin Braenne   // This situation is currently "saved" by the code above that checks whether
383*bbeda830SMartin Braenne   // the `value` property is already set, and if, the `ValueLoc` is not
384*bbeda830SMartin Braenne   // associated with a `ValueVal`, creates a new `ValueVal`.
385*bbeda830SMartin Braenne   //
386*bbeda830SMartin Braenne   // However, what we should really do is to make sure that the change to the
387*bbeda830SMartin Braenne   // `value` property does not "leak" to other blocks that are not successors
388*bbeda830SMartin Braenne   // of this block. To do this, instead of simply setting the `value` property
389*bbeda830SMartin Braenne   // on the existing `OptionalVal`, we should create a new `Value` for the
390*bbeda830SMartin Braenne   // optional, set the property on that, and associate the storage location that
391*bbeda830SMartin Braenne   // is currently associated with the existing `OptionalVal` with the newly
392*bbeda830SMartin Braenne   // created `Value` instead.
393af22be39SMartin Braenne   OptionalVal.setProperty("value", ValuePtr);
394dd38caf3SYitzhak Mandelbaum   return &ValueLoc;
395dd38caf3SYitzhak Mandelbaum }
396dd38caf3SYitzhak Mandelbaum 
397092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr,
398092a530cSStanislav Gatev                                  const MatchFinder::MatchResult &,
399af98b0afSStanislav Gatev                                  LatticeTransferState &State) {
40049ed5bf5SWei Yi Tee   if (auto *OptionalVal =
40149ed5bf5SWei Yi Tee           State.Env.getValue(*OptionalExpr, SkipPast::Reference)) {
402af98b0afSStanislav Gatev     if (OptionalVal->getProperty("has_value") == nullptr) {
4038fcdd625SStanislav Gatev       setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
404af98b0afSStanislav Gatev     }
405af98b0afSStanislav Gatev   }
406af98b0afSStanislav Gatev }
407af98b0afSStanislav Gatev 
4088fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
409ffb4f4dbSDmitri Gribenko /// empty in `Env`.
4108fcdd625SStanislav Gatev bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
4118fcdd625SStanislav Gatev   auto *HasValueVal =
4128fcdd625SStanislav Gatev       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
4138fcdd625SStanislav Gatev   return HasValueVal != nullptr &&
4148fcdd625SStanislav Gatev          Env.flowConditionImplies(Env.makeNot(*HasValueVal));
4158fcdd625SStanislav Gatev }
4168fcdd625SStanislav Gatev 
4178fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
418ffb4f4dbSDmitri Gribenko /// non-empty in `Env`.
4198fcdd625SStanislav Gatev bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
4208fcdd625SStanislav Gatev   auto *HasValueVal =
4218fcdd625SStanislav Gatev       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
4228fcdd625SStanislav Gatev   return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal);
4238fcdd625SStanislav Gatev }
4248fcdd625SStanislav Gatev 
42548bc7150SMartin Braenne StorageLocation *maybeSkipPointer(StorageLocation *Loc,
42648bc7150SMartin Braenne                                   const Environment &Env) {
42748bc7150SMartin Braenne   if (Loc == nullptr)
42848bc7150SMartin Braenne     return nullptr;
42948bc7150SMartin Braenne   if (auto *Val = dyn_cast_or_null<PointerValue>(Env.getValue(*Loc)))
43048bc7150SMartin Braenne     return &Val->getPointeeLoc();
43148bc7150SMartin Braenne   return Loc;
43248bc7150SMartin Braenne }
43348bc7150SMartin Braenne 
43448bc7150SMartin Braenne Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) {
43548bc7150SMartin Braenne   Value *Val = Env.getValue(E, SkipPast::Reference);
43648bc7150SMartin Braenne   if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Val))
43748bc7150SMartin Braenne     return Env.getValue(PointerVal->getPointeeLoc());
43848bc7150SMartin Braenne   return Val;
43948bc7150SMartin Braenne }
44048bc7150SMartin Braenne 
441092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
442af98b0afSStanislav Gatev                         LatticeTransferState &State) {
44349ed5bf5SWei Yi Tee   if (auto *OptionalVal =
44448bc7150SMartin Braenne           getValueBehindPossiblePointer(*ObjectExpr, State.Env)) {
445dd38caf3SYitzhak Mandelbaum     if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
446dd38caf3SYitzhak Mandelbaum       if (auto *Loc = maybeInitializeOptionalValueMember(
447dd38caf3SYitzhak Mandelbaum               UnwrapExpr->getType(), *OptionalVal, State.Env))
448dd38caf3SYitzhak Mandelbaum         State.Env.setStorageLocation(*UnwrapExpr, *Loc);
449af98b0afSStanislav Gatev   }
450dd38caf3SYitzhak Mandelbaum }
451af98b0afSStanislav Gatev 
4523bc1ea5bSMartin Braenne void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
4533bc1ea5bSMartin Braenne                          LatticeTransferState &State) {
4543bc1ea5bSMartin Braenne   if (auto *OptionalVal =
4553bc1ea5bSMartin Braenne           getValueBehindPossiblePointer(*ObjectExpr, State.Env)) {
4563bc1ea5bSMartin Braenne     if (auto *Loc = maybeInitializeOptionalValueMember(
4573bc1ea5bSMartin Braenne             UnwrapExpr->getType()->getPointeeType(), *OptionalVal, State.Env)) {
4583bc1ea5bSMartin Braenne       State.Env.setValueStrict(*UnwrapExpr,
4593bc1ea5bSMartin Braenne                                State.Env.create<PointerValue>(*Loc));
4603bc1ea5bSMartin Braenne     }
4613bc1ea5bSMartin Braenne   }
4623bc1ea5bSMartin Braenne }
4633bc1ea5bSMartin Braenne 
464092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
465092a530cSStanislav Gatev                               const MatchFinder::MatchResult &,
466092a530cSStanislav Gatev                               LatticeTransferState &State) {
4679e0fc676SStanislav Gatev   auto &Loc = State.Env.createStorageLocation(*E);
4689e0fc676SStanislav Gatev   State.Env.setStorageLocation(*E, Loc);
4699e0fc676SStanislav Gatev   State.Env.setValue(
4709e0fc676SStanislav Gatev       Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
4719e0fc676SStanislav Gatev }
4729e0fc676SStanislav Gatev 
473092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
474092a530cSStanislav Gatev                                   const MatchFinder::MatchResult &,
475af98b0afSStanislav Gatev                                   LatticeTransferState &State) {
476dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal = getHasValue(
47748bc7150SMartin Braenne           State.Env, getValueBehindPossiblePointer(
47848bc7150SMartin Braenne                          *CallExpr->getImplicitObjectArgument(), State.Env))) {
479af98b0afSStanislav Gatev     auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
480af98b0afSStanislav Gatev     State.Env.setValue(CallExprLoc, *HasValueVal);
481af98b0afSStanislav Gatev     State.Env.setStorageLocation(*CallExpr, CallExprLoc);
482af98b0afSStanislav Gatev   }
483af98b0afSStanislav Gatev }
484af98b0afSStanislav Gatev 
4857f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in
4867f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property.
4877f076004SYitzhak Mandelbaum void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
4887f076004SYitzhak Mandelbaum                          const MatchFinder::MatchResult &Result,
4897f076004SYitzhak Mandelbaum                          LatticeTransferState &State,
4907f076004SYitzhak Mandelbaum                          BoolValue &(*ModelPred)(Environment &Env,
4917f076004SYitzhak Mandelbaum                                                  BoolValue &ExprVal,
4927f076004SYitzhak Mandelbaum                                                  BoolValue &HasValueVal)) {
4937f076004SYitzhak Mandelbaum   auto &Env = State.Env;
4947f076004SYitzhak Mandelbaum 
4957f076004SYitzhak Mandelbaum   const auto *ObjectArgumentExpr =
4967f076004SYitzhak Mandelbaum       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
4977f076004SYitzhak Mandelbaum           ->getImplicitObjectArgument();
4987f076004SYitzhak Mandelbaum 
499dd38caf3SYitzhak Mandelbaum   auto *HasValueVal = getHasValue(
50048bc7150SMartin Braenne       State.Env, getValueBehindPossiblePointer(*ObjectArgumentExpr, State.Env));
501dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr)
5027f076004SYitzhak Mandelbaum     return;
5037f076004SYitzhak Mandelbaum 
504390029beSYitzhak Mandelbaum   Env.addToFlowCondition(
505390029beSYitzhak Mandelbaum       ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal));
5067f076004SYitzhak Mandelbaum }
5077f076004SYitzhak Mandelbaum 
5087f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
5097f076004SYitzhak Mandelbaum                                     const MatchFinder::MatchResult &Result,
5107f076004SYitzhak Mandelbaum                                     LatticeTransferState &State) {
5117f076004SYitzhak Mandelbaum   return transferValueOrImpl(ComparisonExpr, Result, State,
5127f076004SYitzhak Mandelbaum                              [](Environment &Env, BoolValue &ExprVal,
5137f076004SYitzhak Mandelbaum                                 BoolValue &HasValueVal) -> BoolValue & {
5147f076004SYitzhak Mandelbaum                                // If the result is *not* empty, then we know the
5157f076004SYitzhak Mandelbaum                                // optional must have been holding a value. If
5167f076004SYitzhak Mandelbaum                                // `ExprVal` is true, though, we don't learn
5177f076004SYitzhak Mandelbaum                                // anything definite about `has_value`, so we
5187f076004SYitzhak Mandelbaum                                // don't add any corresponding implications to
5197f076004SYitzhak Mandelbaum                                // the flow condition.
5207f076004SYitzhak Mandelbaum                                return Env.makeImplication(Env.makeNot(ExprVal),
5217f076004SYitzhak Mandelbaum                                                           HasValueVal);
5227f076004SYitzhak Mandelbaum                              });
5237f076004SYitzhak Mandelbaum }
5247f076004SYitzhak Mandelbaum 
5257f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr,
5267f076004SYitzhak Mandelbaum                            const MatchFinder::MatchResult &Result,
5277f076004SYitzhak Mandelbaum                            LatticeTransferState &State) {
5287f076004SYitzhak Mandelbaum   transferValueOrImpl(ComparisonExpr, Result, State,
5297f076004SYitzhak Mandelbaum                       [](Environment &Env, BoolValue &ExprVal,
5307f076004SYitzhak Mandelbaum                          BoolValue &HasValueVal) -> BoolValue & {
5317f076004SYitzhak Mandelbaum                         // We know that if `(opt.value_or(X) != X)` then
5327f076004SYitzhak Mandelbaum                         // `opt.hasValue()`, even without knowing further
5337f076004SYitzhak Mandelbaum                         // details about the contents of `opt`.
5347f076004SYitzhak Mandelbaum                         return Env.makeImplication(ExprVal, HasValueVal);
5357f076004SYitzhak Mandelbaum                       });
5367f076004SYitzhak Mandelbaum }
5377f076004SYitzhak Mandelbaum 
53865e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E,
53965e710c3SStanislav Gatev                                    const MatchFinder::MatchResult &Result,
54065e710c3SStanislav Gatev                                    LatticeTransferState &State) {
54165e710c3SStanislav Gatev   if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
54265e710c3SStanislav Gatev     return;
54365e710c3SStanislav Gatev 
54465e710c3SStanislav Gatev   auto &Loc = State.Env.createStorageLocation(*E);
54565e710c3SStanislav Gatev   State.Env.setStorageLocation(*E, Loc);
54665e710c3SStanislav Gatev   State.Env.setValue(
54765e710c3SStanislav Gatev       Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()));
54865e710c3SStanislav Gatev }
54965e710c3SStanislav Gatev 
5500e8d4a6dSYitzhak Mandelbaum void assignOptionalValue(const Expr &E, Environment &Env,
551092a530cSStanislav Gatev                          BoolValue &HasValueVal) {
55248bc7150SMartin Braenne   if (auto *OptionalLoc = maybeSkipPointer(
55348bc7150SMartin Braenne           Env.getStorageLocation(E, SkipPast::Reference), Env)) {
5540e8d4a6dSYitzhak Mandelbaum     Env.setValue(*OptionalLoc, createOptionalValue(Env, HasValueVal));
5559e0fc676SStanislav Gatev   }
5569e0fc676SStanislav Gatev }
5579e0fc676SStanislav Gatev 
558b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
559b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
560b000b770SStanislav Gatev /// where `T` is constructible from `U`.
561390029beSYitzhak Mandelbaum BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
562b000b770SStanislav Gatev                                      const MatchFinder::MatchResult &MatchRes,
563b000b770SStanislav Gatev                                      LatticeTransferState &State) {
5640086a355SYitzhak Mandelbaum   assert(F.getTemplateSpecializationArgs() != nullptr);
565b000b770SStanislav Gatev   assert(F.getTemplateSpecializationArgs()->size() > 0);
566b000b770SStanislav Gatev 
567c849843cSMartin Braenne   const int TemplateParamOptionalWrappersCount =
568c849843cSMartin Braenne       countOptionalWrappers(*MatchRes.Context, F.getTemplateSpecializationArgs()
569c849843cSMartin Braenne                                                    ->get(0)
570c849843cSMartin Braenne                                                    .getAsType()
571c849843cSMartin Braenne                                                    .getNonReferenceType());
572c849843cSMartin Braenne   const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
573c849843cSMartin Braenne       *MatchRes.Context, E.getType().getNonReferenceType());
574b000b770SStanislav Gatev 
575b000b770SStanislav Gatev   // Check if this is a constructor/assignment call for `optional<T>` with
576b000b770SStanislav Gatev   // argument of type `U` such that `T` is constructible from `U`.
577b000b770SStanislav Gatev   if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
578b000b770SStanislav Gatev     return State.Env.getBoolLiteralValue(true);
579b000b770SStanislav Gatev 
580b000b770SStanislav Gatev   // This is a constructor/assignment call for `optional<T>` with argument of
581b000b770SStanislav Gatev   // type `optional<U>` such that `T` is constructible from `U`.
582dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal =
583dd38caf3SYitzhak Mandelbaum           getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference)))
584dd38caf3SYitzhak Mandelbaum     return *HasValueVal;
585b000b770SStanislav Gatev   return State.Env.makeAtomicBoolValue();
586b000b770SStanislav Gatev }
587b000b770SStanislav Gatev 
588092a530cSStanislav Gatev void transferValueOrConversionConstructor(
589092a530cSStanislav Gatev     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
5909e0fc676SStanislav Gatev     LatticeTransferState &State) {
591092a530cSStanislav Gatev   assert(E->getNumArgs() > 0);
592092a530cSStanislav Gatev 
5930e8d4a6dSYitzhak Mandelbaum   assignOptionalValue(*E, State.Env,
594390029beSYitzhak Mandelbaum                       valueOrConversionHasValue(*E->getConstructor(),
595b000b770SStanislav Gatev                                                 *E->getArg(0), MatchRes,
596b000b770SStanislav Gatev                                                 State));
597b000b770SStanislav Gatev }
598092a530cSStanislav Gatev 
599b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
600b000b770SStanislav Gatev                         LatticeTransferState &State) {
601b000b770SStanislav Gatev   assert(E->getNumArgs() > 0);
602b000b770SStanislav Gatev 
603b000b770SStanislav Gatev   auto *OptionalLoc =
604b000b770SStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
605a9ad689eSSam Estep   if (OptionalLoc == nullptr)
606a9ad689eSSam Estep     return;
607b000b770SStanislav Gatev 
608b000b770SStanislav Gatev   State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
609b000b770SStanislav Gatev 
610b000b770SStanislav Gatev   // Assign a storage location for the whole expression.
611b000b770SStanislav Gatev   State.Env.setStorageLocation(*E, *OptionalLoc);
612b000b770SStanislav Gatev }
613b000b770SStanislav Gatev 
614b000b770SStanislav Gatev void transferValueOrConversionAssignment(
615b000b770SStanislav Gatev     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
616b000b770SStanislav Gatev     LatticeTransferState &State) {
617b000b770SStanislav Gatev   assert(E->getNumArgs() > 1);
618b000b770SStanislav Gatev   transferAssignment(E,
619390029beSYitzhak Mandelbaum                      valueOrConversionHasValue(*E->getDirectCallee(),
62006decd0bSKazu Hirata                                                *E->getArg(1), MatchRes, State),
621b000b770SStanislav Gatev                      State);
622b000b770SStanislav Gatev }
623b000b770SStanislav Gatev 
624b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
625b000b770SStanislav Gatev                                const MatchFinder::MatchResult &,
626b000b770SStanislav Gatev                                LatticeTransferState &State) {
627b000b770SStanislav Gatev   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
6289e0fc676SStanislav Gatev }
6299e0fc676SStanislav Gatev 
63048bc7150SMartin Braenne void transferSwap(StorageLocation *Loc1, StorageLocation *Loc2,
631d4fb829bSYitzhak Mandelbaum                   Environment &Env) {
632d4fb829bSYitzhak Mandelbaum   // We account for cases where one or both of the optionals are not modeled,
633d4fb829bSYitzhak Mandelbaum   // either lacking associated storage locations, or lacking values associated
634d4fb829bSYitzhak Mandelbaum   // to such storage locations.
6352ddd57aeSStanislav Gatev 
636d4fb829bSYitzhak Mandelbaum   if (Loc1 == nullptr) {
637d4fb829bSYitzhak Mandelbaum     if (Loc2 != nullptr)
638d4fb829bSYitzhak Mandelbaum       Env.setValue(*Loc2, createOptionalValue(Env, Env.makeAtomicBoolValue()));
639d4fb829bSYitzhak Mandelbaum     return;
640d4fb829bSYitzhak Mandelbaum   }
641d4fb829bSYitzhak Mandelbaum   if (Loc2 == nullptr) {
642d4fb829bSYitzhak Mandelbaum     Env.setValue(*Loc1, createOptionalValue(Env, Env.makeAtomicBoolValue()));
643d4fb829bSYitzhak Mandelbaum     return;
644d4fb829bSYitzhak Mandelbaum   }
6452ddd57aeSStanislav Gatev 
646d4fb829bSYitzhak Mandelbaum   // Both expressions have locations, though they may not have corresponding
647d4fb829bSYitzhak Mandelbaum   // values. In that case, we create a fresh value at this point. Note that if
648d4fb829bSYitzhak Mandelbaum   // two branches both do this, they will not share the value, but it at least
649d4fb829bSYitzhak Mandelbaum   // allows for local reasoning about the value. To avoid the above, we would
650d4fb829bSYitzhak Mandelbaum   // need *lazy* value allocation.
651d4fb829bSYitzhak Mandelbaum   // FIXME: allocate values lazily, instead of just creating a fresh value.
652d4fb829bSYitzhak Mandelbaum   auto *Val1 = Env.getValue(*Loc1);
653d4fb829bSYitzhak Mandelbaum   if (Val1 == nullptr)
654d4fb829bSYitzhak Mandelbaum     Val1 = &createOptionalValue(Env, Env.makeAtomicBoolValue());
655d4fb829bSYitzhak Mandelbaum 
656d4fb829bSYitzhak Mandelbaum   auto *Val2 = Env.getValue(*Loc2);
657d4fb829bSYitzhak Mandelbaum   if (Val2 == nullptr)
658d4fb829bSYitzhak Mandelbaum     Val2 = &createOptionalValue(Env, Env.makeAtomicBoolValue());
659d4fb829bSYitzhak Mandelbaum 
660d4fb829bSYitzhak Mandelbaum   Env.setValue(*Loc1, *Val2);
661d4fb829bSYitzhak Mandelbaum   Env.setValue(*Loc2, *Val1);
6622ddd57aeSStanislav Gatev }
6632ddd57aeSStanislav Gatev 
6642ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
6652ddd57aeSStanislav Gatev                       const MatchFinder::MatchResult &,
6662ddd57aeSStanislav Gatev                       LatticeTransferState &State) {
6672ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 1);
66848bc7150SMartin Braenne   transferSwap(maybeSkipPointer(
66948bc7150SMartin Braenne                    State.Env.getStorageLocation(*E->getImplicitObjectArgument(),
67048bc7150SMartin Braenne                                                 SkipPast::Reference),
67148bc7150SMartin Braenne                    State.Env),
67248bc7150SMartin Braenne                State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference),
67348bc7150SMartin Braenne                State.Env);
6742ddd57aeSStanislav Gatev }
6752ddd57aeSStanislav Gatev 
6762ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
6772ddd57aeSStanislav Gatev                          LatticeTransferState &State) {
6782ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 2);
67948bc7150SMartin Braenne   transferSwap(State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference),
68048bc7150SMartin Braenne                State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference),
68148bc7150SMartin Braenne                State.Env);
6822ddd57aeSStanislav Gatev }
6832ddd57aeSStanislav Gatev 
68425956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
68525956d55SAMS21                             LatticeTransferState &State) {
68625956d55SAMS21   assert(E->getNumArgs() == 1);
68725956d55SAMS21 
68825956d55SAMS21   StorageLocation *LocRet = State.Env.getStorageLocation(*E, SkipPast::None);
68925956d55SAMS21   if (LocRet != nullptr)
69025956d55SAMS21     return;
69125956d55SAMS21 
69225956d55SAMS21   StorageLocation *LocArg =
69325956d55SAMS21       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
69425956d55SAMS21 
69525956d55SAMS21   if (LocArg == nullptr)
69625956d55SAMS21     return;
69725956d55SAMS21 
69825956d55SAMS21   Value *ValArg = State.Env.getValue(*LocArg);
69925956d55SAMS21   if (ValArg == nullptr)
70025956d55SAMS21     ValArg = &createOptionalValue(State.Env, State.Env.makeAtomicBoolValue());
70125956d55SAMS21 
70225956d55SAMS21   // Create a new storage location
70325956d55SAMS21   LocRet = &State.Env.createStorageLocation(*E);
70425956d55SAMS21   State.Env.setStorageLocation(*E, *LocRet);
70525956d55SAMS21 
70625956d55SAMS21   State.Env.setValue(*LocRet, *ValArg);
70725956d55SAMS21 }
70825956d55SAMS21 
709390029beSYitzhak Mandelbaum BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS,
710390029beSYitzhak Mandelbaum                             BoolValue &RHS) {
711390029beSYitzhak Mandelbaum   // Logically, an optional<T> object is composed of two values - a `has_value`
712390029beSYitzhak Mandelbaum   // bit and a value of type T. Equality of optional objects compares both
713390029beSYitzhak Mandelbaum   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
714390029beSYitzhak Mandelbaum   // when two optional objects are engaged, the equality of their respective
715390029beSYitzhak Mandelbaum   // values of type T matters. Since we only track the `has_value` bits, we
716390029beSYitzhak Mandelbaum   // can't make any conclusions about equality when we know that two optional
717390029beSYitzhak Mandelbaum   // objects are engaged.
718390029beSYitzhak Mandelbaum   //
719390029beSYitzhak Mandelbaum   // We express this as two facts about the equality:
720390029beSYitzhak Mandelbaum   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
721390029beSYitzhak Mandelbaum   //    If they are equal, then either both are set or both are unset.
722390029beSYitzhak Mandelbaum   // b) (!LHS & !RHS) => EqVal
723390029beSYitzhak Mandelbaum   //    If neither is set, then they are equal.
724390029beSYitzhak Mandelbaum   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
725390029beSYitzhak Mandelbaum   return Env.makeAnd(
726390029beSYitzhak Mandelbaum       Env.makeImplication(
727390029beSYitzhak Mandelbaum           EqVal, Env.makeOr(Env.makeAnd(LHS, RHS),
728390029beSYitzhak Mandelbaum                             Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))),
729390029beSYitzhak Mandelbaum       Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS)));
730390029beSYitzhak Mandelbaum }
731390029beSYitzhak Mandelbaum 
732390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
733390029beSYitzhak Mandelbaum                                     const MatchFinder::MatchResult &,
734390029beSYitzhak Mandelbaum                                     LatticeTransferState &State) {
735390029beSYitzhak Mandelbaum   Environment &Env = State.Env;
736390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
737390029beSYitzhak Mandelbaum   if (auto *LHasVal = getHasValue(
738390029beSYitzhak Mandelbaum           Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference)))
739390029beSYitzhak Mandelbaum     if (auto *RHasVal = getHasValue(
740390029beSYitzhak Mandelbaum             Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) {
741390029beSYitzhak Mandelbaum       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
742390029beSYitzhak Mandelbaum         CmpValue = &State.Env.makeNot(*CmpValue);
743390029beSYitzhak Mandelbaum       Env.addToFlowCondition(
744390029beSYitzhak Mandelbaum           evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal));
745390029beSYitzhak Mandelbaum     }
746390029beSYitzhak Mandelbaum }
747390029beSYitzhak Mandelbaum 
748390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
749390029beSYitzhak Mandelbaum                                  const clang::Expr *E, Environment &Env) {
750390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
751390029beSYitzhak Mandelbaum   if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) {
752390029beSYitzhak Mandelbaum     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
753390029beSYitzhak Mandelbaum       CmpValue = &Env.makeNot(*CmpValue);
754390029beSYitzhak Mandelbaum     Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal,
755390029beSYitzhak Mandelbaum                                             Env.getBoolLiteralValue(true)));
756390029beSYitzhak Mandelbaum   }
757390029beSYitzhak Mandelbaum }
758390029beSYitzhak Mandelbaum 
7596ad0788cSKazu Hirata std::optional<StatementMatcher>
760a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
7615d22d1f5SYitzhak Mandelbaum   if (Options.IgnoreSmartPointerDereference) {
7625d22d1f5SYitzhak Mandelbaum     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
7635d22d1f5SYitzhak Mandelbaum         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
7645d22d1f5SYitzhak Mandelbaum         unless(hasArgument(0, expr(hasOptionalType()))))));
7655d22d1f5SYitzhak Mandelbaum     return expr(
7665d22d1f5SYitzhak Mandelbaum         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
7675d22d1f5SYitzhak Mandelbaum   }
76834e0d057SKazu Hirata   return std::nullopt;
769a184a0d8SYitzhak Mandelbaum }
770a184a0d8SYitzhak Mandelbaum 
77158fe7f96SSam Estep StatementMatcher
7726ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
77358fe7f96SSam Estep   return isOptionalMemberCallWithName("value", IgnorableOptional);
77458fe7f96SSam Estep }
77558fe7f96SSam Estep 
77658fe7f96SSam Estep StatementMatcher
7776ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
77858fe7f96SSam Estep   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
77958fe7f96SSam Estep                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
78058fe7f96SSam Estep }
78158fe7f96SSam Estep 
7825d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() {
783b000b770SStanislav Gatev   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
784b000b770SStanislav Gatev   // lot of duplicated work (e.g. string comparisons), consider providing APIs
785b000b770SStanislav Gatev   // that avoid it through memoization.
7867538b360SWei Yi Tee   return CFGMatchSwitchBuilder<LatticeTransferState>()
787af98b0afSStanislav Gatev       // Attach a symbolic "has_value" state to optional values that we see for
788af98b0afSStanislav Gatev       // the first time.
7897538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(
7906adfc64eSYitzhak Mandelbaum           expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
791af98b0afSStanislav Gatev           initializeOptionalReference)
792af98b0afSStanislav Gatev 
7939e0fc676SStanislav Gatev       // make_optional
7947538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
795092a530cSStanislav Gatev 
7960e8d4a6dSYitzhak Mandelbaum       // optional::optional (in place)
7977538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
798092a530cSStanislav Gatev           isOptionalInPlaceConstructor(),
799092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
800092a530cSStanislav Gatev              LatticeTransferState &State) {
8010e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E, State.Env,
8020e8d4a6dSYitzhak Mandelbaum                                 State.Env.getBoolLiteralValue(true));
803092a530cSStanislav Gatev           })
8040e8d4a6dSYitzhak Mandelbaum       // nullopt_t::nullopt_t
8057538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
806390029beSYitzhak Mandelbaum           isNulloptConstructor(),
807092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
808092a530cSStanislav Gatev              LatticeTransferState &State) {
8090e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E, State.Env,
810092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
811092a530cSStanislav Gatev           })
8120e8d4a6dSYitzhak Mandelbaum       // optional::optional(nullopt_t)
813390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXConstructExpr>(
814390029beSYitzhak Mandelbaum           isOptionalNulloptConstructor(),
815390029beSYitzhak Mandelbaum           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
816390029beSYitzhak Mandelbaum              LatticeTransferState &State) {
8170e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E, State.Env,
8180e8d4a6dSYitzhak Mandelbaum                                 State.Env.getBoolLiteralValue(false));
819390029beSYitzhak Mandelbaum           })
8200e8d4a6dSYitzhak Mandelbaum       // optional::optional (value/conversion)
8217538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
822092a530cSStanislav Gatev                                        transferValueOrConversionConstructor)
8239e0fc676SStanislav Gatev 
824b000b770SStanislav Gatev       // optional::operator=
8257538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(
8267538b360SWei Yi Tee           isOptionalValueOrConversionAssignment(),
827b000b770SStanislav Gatev           transferValueOrConversionAssignment)
8287538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
829b000b770SStanislav Gatev                                           transferNulloptAssignment)
830b000b770SStanislav Gatev 
831af98b0afSStanislav Gatev       // optional::value
8327538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8335d22d1f5SYitzhak Mandelbaum           valueCall(std::nullopt),
834092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
835092a530cSStanislav Gatev              LatticeTransferState &State) {
836af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
837af98b0afSStanislav Gatev           })
838af98b0afSStanislav Gatev 
8393bc1ea5bSMartin Braenne       // optional::operator*
8403bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
8417538b360SWei Yi Tee                                [](const CallExpr *E,
8427538b360SWei Yi Tee                                   const MatchFinder::MatchResult &,
843092a530cSStanislav Gatev                                   LatticeTransferState &State) {
844af98b0afSStanislav Gatev                                  transferUnwrapCall(E, E->getArg(0), State);
845af98b0afSStanislav Gatev                                })
846af98b0afSStanislav Gatev 
8473bc1ea5bSMartin Braenne       // optional::operator->
8483bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
8493bc1ea5bSMartin Braenne                                [](const CallExpr *E,
8503bc1ea5bSMartin Braenne                                   const MatchFinder::MatchResult &,
8513bc1ea5bSMartin Braenne                                   LatticeTransferState &State) {
8523bc1ea5bSMartin Braenne                                  transferArrowOpCall(E, E->getArg(0), State);
8533bc1ea5bSMartin Braenne                                })
8543bc1ea5bSMartin Braenne 
855af98b0afSStanislav Gatev       // optional::has_value
8567538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8577538b360SWei Yi Tee           isOptionalMemberCallWithName("has_value"),
858af98b0afSStanislav Gatev           transferOptionalHasValueCall)
859af98b0afSStanislav Gatev 
8609e0fc676SStanislav Gatev       // optional::operator bool
8617538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8627538b360SWei Yi Tee           isOptionalMemberCallWithName("operator bool"),
8639e0fc676SStanislav Gatev           transferOptionalHasValueCall)
8649e0fc676SStanislav Gatev 
8659e0fc676SStanislav Gatev       // optional::emplace
8667538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
867092a530cSStanislav Gatev           isOptionalMemberCallWithName("emplace"),
868092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
869092a530cSStanislav Gatev              LatticeTransferState &State) {
8700e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
871092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(true));
872092a530cSStanislav Gatev           })
8739e0fc676SStanislav Gatev 
8749e0fc676SStanislav Gatev       // optional::reset
8757538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
876092a530cSStanislav Gatev           isOptionalMemberCallWithName("reset"),
877092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
878092a530cSStanislav Gatev              LatticeTransferState &State) {
8790e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
880092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
881092a530cSStanislav Gatev           })
8829e0fc676SStanislav Gatev 
8832ddd57aeSStanislav Gatev       // optional::swap
8847538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
8852ddd57aeSStanislav Gatev                                         transferSwapCall)
8862ddd57aeSStanislav Gatev 
8872ddd57aeSStanislav Gatev       // std::swap
8887538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
8892ddd57aeSStanislav Gatev 
89025956d55SAMS21       // std::forward
89125956d55SAMS21       .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
89225956d55SAMS21 
8937f076004SYitzhak Mandelbaum       // opt.value_or("").empty()
8947538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
8957538b360SWei Yi Tee                            transferValueOrStringEmptyCall)
8967f076004SYitzhak Mandelbaum 
8977f076004SYitzhak Mandelbaum       // opt.value_or(X) != X
8987538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
8997f076004SYitzhak Mandelbaum 
900390029beSYitzhak Mandelbaum       // Comparisons (==, !=):
901390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
902390029beSYitzhak Mandelbaum           isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()),
903390029beSYitzhak Mandelbaum           transferOptionalAndOptionalCmp)
904390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
905390029beSYitzhak Mandelbaum           isComparisonOperatorCall(hasOptionalType(),
906390029beSYitzhak Mandelbaum                                    unless(hasAnyOptionalType())),
907390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
908390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
909390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
910390029beSYitzhak Mandelbaum           })
911390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
912390029beSYitzhak Mandelbaum           isComparisonOperatorCall(unless(hasAnyOptionalType()),
913390029beSYitzhak Mandelbaum                                    hasOptionalType()),
914390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
915390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
916390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
917390029beSYitzhak Mandelbaum           })
918390029beSYitzhak Mandelbaum 
91965e710c3SStanislav Gatev       // returns optional
9207538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
92165e710c3SStanislav Gatev                                transferCallReturningOptional)
92265e710c3SStanislav Gatev 
923af98b0afSStanislav Gatev       .Build();
924af98b0afSStanislav Gatev }
925af98b0afSStanislav Gatev 
9266a81e694SMartin Braenne std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
92758fe7f96SSam Estep                                                const Environment &Env) {
92848bc7150SMartin Braenne   if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) {
92958fe7f96SSam Estep     auto *Prop = OptionalVal->getProperty("has_value");
93058fe7f96SSam Estep     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
93158fe7f96SSam Estep       if (Env.flowConditionImplies(*HasValueVal))
93258fe7f96SSam Estep         return {};
93358fe7f96SSam Estep     }
93458fe7f96SSam Estep   }
93558fe7f96SSam Estep 
93658fe7f96SSam Estep   // Record that this unwrap is *not* provably safe.
93758fe7f96SSam Estep   // FIXME: include either the name of the optional (if applicable) or a source
93858fe7f96SSam Estep   // range of the access for easier interpretation of the result.
93958fe7f96SSam Estep   return {ObjectExpr->getBeginLoc()};
94058fe7f96SSam Estep }
94158fe7f96SSam Estep 
94258fe7f96SSam Estep auto buildDiagnoseMatchSwitch(
94358fe7f96SSam Estep     const UncheckedOptionalAccessModelOptions &Options) {
94458fe7f96SSam Estep   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
94558fe7f96SSam Estep   // lot of duplicated work (e.g. string comparisons), consider providing APIs
94658fe7f96SSam Estep   // that avoid it through memoization.
94758fe7f96SSam Estep   auto IgnorableOptional = ignorableOptional(Options);
9487538b360SWei Yi Tee   return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
94958fe7f96SSam Estep       // optional::value
9507538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
95158fe7f96SSam Estep           valueCall(IgnorableOptional),
95258fe7f96SSam Estep           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
95358fe7f96SSam Estep              const Environment &Env) {
9546a81e694SMartin Braenne             return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
95558fe7f96SSam Estep           })
95658fe7f96SSam Estep 
95758fe7f96SSam Estep       // optional::operator*, optional::operator->
9586a81e694SMartin Braenne       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
9596a81e694SMartin Braenne                                [](const CallExpr *E,
9606a81e694SMartin Braenne                                   const MatchFinder::MatchResult &,
96158fe7f96SSam Estep                                   const Environment &Env) {
9626a81e694SMartin Braenne                                  return diagnoseUnwrapCall(E->getArg(0), Env);
96358fe7f96SSam Estep                                })
96458fe7f96SSam Estep       .Build();
96558fe7f96SSam Estep }
96658fe7f96SSam Estep 
967af98b0afSStanislav Gatev } // namespace
968af98b0afSStanislav Gatev 
9697e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher
9707e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() {
9717e63a0d4SYitzhak Mandelbaum   return optionalClass();
9727e63a0d4SYitzhak Mandelbaum }
9737e63a0d4SYitzhak Mandelbaum 
9745d22d1f5SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
975cf1f978dSSam Estep     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
9765d22d1f5SYitzhak Mandelbaum       TransferMatchSwitch(buildTransferMatchSwitch()) {}
977af98b0afSStanislav Gatev 
9786b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
9797538b360SWei Yi Tee                                             NoopLattice &L, Environment &Env) {
980af98b0afSStanislav Gatev   LatticeTransferState State(L, Env);
9816b991ba4SYitzhak Mandelbaum   TransferMatchSwitch(Elt, getASTContext(), State);
982af98b0afSStanislav Gatev }
983af98b0afSStanislav Gatev 
984c0725865SYitzhak Mandelbaum ComparisonResult UncheckedOptionalAccessModel::compare(
985c0725865SYitzhak Mandelbaum     QualType Type, const Value &Val1, const Environment &Env1,
986c0725865SYitzhak Mandelbaum     const Value &Val2, const Environment &Env2) {
987c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
988c0725865SYitzhak Mandelbaum     return ComparisonResult::Unknown;
989d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
990d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
99125956d55SAMS21   if (MustNonEmpty1 && MustNonEmpty2)
99225956d55SAMS21     return ComparisonResult::Same;
993d34fbf2dSYitzhak Mandelbaum   // If exactly one is true, then they're different, no reason to check whether
994d34fbf2dSYitzhak Mandelbaum   // they're definitely empty.
99525956d55SAMS21   if (MustNonEmpty1 || MustNonEmpty2)
99625956d55SAMS21     return ComparisonResult::Different;
997d34fbf2dSYitzhak Mandelbaum   // Check if they're both definitely empty.
998d34fbf2dSYitzhak Mandelbaum   return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
999c0725865SYitzhak Mandelbaum              ? ComparisonResult::Same
1000c0725865SYitzhak Mandelbaum              : ComparisonResult::Different;
10018fcdd625SStanislav Gatev }
10028fcdd625SStanislav Gatev 
10038fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
10048fcdd625SStanislav Gatev                                          const Environment &Env1,
10058fcdd625SStanislav Gatev                                          const Value &Val2,
10068fcdd625SStanislav Gatev                                          const Environment &Env2,
10078fcdd625SStanislav Gatev                                          Value &MergedVal,
10088fcdd625SStanislav Gatev                                          Environment &MergedEnv) {
1009c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
10108fcdd625SStanislav Gatev     return true;
1011d34fbf2dSYitzhak Mandelbaum   // FIXME: uses same approach as join for `BoolValues`. Requires non-const
1012d34fbf2dSYitzhak Mandelbaum   // values, though, so will require updating the interface.
10138fcdd625SStanislav Gatev   auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
1014d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
1015d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
1016d34fbf2dSYitzhak Mandelbaum   if (MustNonEmpty1 && MustNonEmpty2)
10178fcdd625SStanislav Gatev     MergedEnv.addToFlowCondition(HasValueVal);
1018d34fbf2dSYitzhak Mandelbaum   else if (
1019d34fbf2dSYitzhak Mandelbaum       // Only make the costly calls to `isEmptyOptional` if we got "unknown"
1020d34fbf2dSYitzhak Mandelbaum       // (false) for both calls to `isNonEmptyOptional`.
1021d34fbf2dSYitzhak Mandelbaum       !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) &&
1022d34fbf2dSYitzhak Mandelbaum       isEmptyOptional(Val2, Env2))
10238fcdd625SStanislav Gatev     MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal));
10248fcdd625SStanislav Gatev   setHasValue(MergedVal, HasValueVal);
10258fcdd625SStanislav Gatev   return true;
10268fcdd625SStanislav Gatev }
10278fcdd625SStanislav Gatev 
1028d34fbf2dSYitzhak Mandelbaum Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev,
1029d34fbf2dSYitzhak Mandelbaum                                            const Environment &PrevEnv,
1030d34fbf2dSYitzhak Mandelbaum                                            Value &Current,
1031d34fbf2dSYitzhak Mandelbaum                                            Environment &CurrentEnv) {
1032d34fbf2dSYitzhak Mandelbaum   switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) {
1033d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Same:
1034d34fbf2dSYitzhak Mandelbaum     return &Prev;
1035d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Different:
1036d34fbf2dSYitzhak Mandelbaum     if (auto *PrevHasVal =
1037d34fbf2dSYitzhak Mandelbaum             cast_or_null<BoolValue>(Prev.getProperty("has_value"))) {
1038d34fbf2dSYitzhak Mandelbaum       if (isa<TopBoolValue>(PrevHasVal))
1039d34fbf2dSYitzhak Mandelbaum         return &Prev;
1040d34fbf2dSYitzhak Mandelbaum     }
1041d34fbf2dSYitzhak Mandelbaum     if (auto *CurrentHasVal =
1042d34fbf2dSYitzhak Mandelbaum             cast_or_null<BoolValue>(Current.getProperty("has_value"))) {
1043d34fbf2dSYitzhak Mandelbaum       if (isa<TopBoolValue>(CurrentHasVal))
1044d34fbf2dSYitzhak Mandelbaum         return &Current;
1045d34fbf2dSYitzhak Mandelbaum     }
1046d34fbf2dSYitzhak Mandelbaum     return &createOptionalValue(CurrentEnv, CurrentEnv.makeTopBoolValue());
1047d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Unknown:
1048d34fbf2dSYitzhak Mandelbaum     return nullptr;
1049d34fbf2dSYitzhak Mandelbaum   }
1050d34fbf2dSYitzhak Mandelbaum   llvm_unreachable("all cases covered in switch");
1051d34fbf2dSYitzhak Mandelbaum }
1052d34fbf2dSYitzhak Mandelbaum 
105358fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
105458fe7f96SSam Estep     UncheckedOptionalAccessModelOptions Options)
105558fe7f96SSam Estep     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
105658fe7f96SSam Estep 
105758fe7f96SSam Estep std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose(
10587538b360SWei Yi Tee     ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) {
10597538b360SWei Yi Tee   return DiagnoseMatchSwitch(*Elt, Ctx, Env);
106058fe7f96SSam Estep }
106158fe7f96SSam Estep 
1062af98b0afSStanislav Gatev } // namespace dataflow
1063af98b0afSStanislav Gatev } // namespace clang
1064