xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision cd22e0dc9d0b5487eb2d54dd028a2aa439627159)
1b000b770SStanislav Gatev //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//
2b000b770SStanislav Gatev //
3b000b770SStanislav Gatev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b000b770SStanislav Gatev // See https://llvm.org/LICENSE.txt for license information.
5b000b770SStanislav Gatev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b000b770SStanislav Gatev //
7b000b770SStanislav Gatev //===----------------------------------------------------------------------===//
8b000b770SStanislav Gatev //
9b000b770SStanislav Gatev //  This file defines a dataflow analysis that detects unsafe uses of optional
10b000b770SStanislav Gatev //  values.
11b000b770SStanislav Gatev //
12b000b770SStanislav Gatev //===----------------------------------------------------------------------===//
13b000b770SStanislav Gatev 
14af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
15af98b0afSStanislav Gatev #include "clang/AST/ASTContext.h"
167e63a0d4SYitzhak Mandelbaum #include "clang/AST/DeclCXX.h"
17af98b0afSStanislav Gatev #include "clang/AST/Expr.h"
18af98b0afSStanislav Gatev #include "clang/AST/ExprCXX.h"
19af98b0afSStanislav Gatev #include "clang/AST/Stmt.h"
20af98b0afSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h"
217538b360SWei Yi Tee #include "clang/Analysis/CFG.h"
227538b360SWei Yi Tee #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
23af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
24cf1f978dSSam Estep #include "clang/Analysis/FlowSensitive/NoopLattice.h"
250086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h"
26af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h"
2758fe7f96SSam Estep #include "clang/Basic/SourceLocation.h"
28af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h"
29af98b0afSStanislav Gatev #include "llvm/Support/Casting.h"
30d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h"
31af98b0afSStanislav Gatev #include <cassert>
329e0fc676SStanislav Gatev #include <memory>
33a1580d7bSKazu Hirata #include <optional>
349e0fc676SStanislav Gatev #include <utility>
3558fe7f96SSam Estep #include <vector>
36af98b0afSStanislav Gatev 
37af98b0afSStanislav Gatev namespace clang {
38af98b0afSStanislav Gatev namespace dataflow {
39af98b0afSStanislav Gatev namespace {
40af98b0afSStanislav Gatev 
41af98b0afSStanislav Gatev using namespace ::clang::ast_matchers;
42cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>;
43af98b0afSStanislav Gatev 
447e63a0d4SYitzhak Mandelbaum DeclarationMatcher optionalClass() {
45af98b0afSStanislav Gatev   return classTemplateSpecializationDecl(
46*cd22e0dcSYitzhak Mandelbaum       hasAnyName("::std::optional", "::std::__optional_storage_base",
47*cd22e0dcSYitzhak Mandelbaum                  "::std::__optional_destruct_base", "::absl::optional",
48*cd22e0dcSYitzhak Mandelbaum                  "::base::Optional"),
49af98b0afSStanislav Gatev       hasTemplateArgument(0, refersToType(type().bind("T"))));
50af98b0afSStanislav Gatev }
51af98b0afSStanislav Gatev 
526adfc64eSYitzhak Mandelbaum auto optionalOrAliasType() {
5365e710c3SStanislav Gatev   return hasUnqualifiedDesugaredType(
5465e710c3SStanislav Gatev       recordType(hasDeclaration(optionalClass())));
5565e710c3SStanislav Gatev }
5665e710c3SStanislav Gatev 
576adfc64eSYitzhak Mandelbaum /// Matches any of the spellings of the optional types and sugar, aliases, etc.
586adfc64eSYitzhak Mandelbaum auto hasOptionalType() { return hasType(optionalOrAliasType()); }
596adfc64eSYitzhak Mandelbaum 
60a184a0d8SYitzhak Mandelbaum auto isOptionalMemberCallWithName(
61a184a0d8SYitzhak Mandelbaum     llvm::StringRef MemberName,
626ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
63a184a0d8SYitzhak Mandelbaum   auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr()))
64a184a0d8SYitzhak Mandelbaum                                     : cxxThisExpr());
65af98b0afSStanislav Gatev   return cxxMemberCallExpr(
66a184a0d8SYitzhak Mandelbaum       on(expr(Exception)),
67af98b0afSStanislav Gatev       callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
68af98b0afSStanislav Gatev }
69af98b0afSStanislav Gatev 
70a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName(
71a184a0d8SYitzhak Mandelbaum     llvm::StringRef operator_name,
726ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
73a184a0d8SYitzhak Mandelbaum   return cxxOperatorCallExpr(
74a184a0d8SYitzhak Mandelbaum       hasOverloadedOperatorName(operator_name),
75a184a0d8SYitzhak Mandelbaum       callee(cxxMethodDecl(ofClass(optionalClass()))),
76a184a0d8SYitzhak Mandelbaum       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
77af98b0afSStanislav Gatev }
78af98b0afSStanislav Gatev 
79092a530cSStanislav Gatev auto isMakeOptionalCall() {
809e0fc676SStanislav Gatev   return callExpr(
819e0fc676SStanislav Gatev       callee(functionDecl(hasAnyName(
829e0fc676SStanislav Gatev           "std::make_optional", "base::make_optional", "absl::make_optional"))),
839e0fc676SStanislav Gatev       hasOptionalType());
849e0fc676SStanislav Gatev }
859e0fc676SStanislav Gatev 
86390029beSYitzhak Mandelbaum auto nulloptTypeDecl() {
87390029beSYitzhak Mandelbaum   return namedDecl(
88390029beSYitzhak Mandelbaum       hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t"));
89092a530cSStanislav Gatev }
90092a530cSStanislav Gatev 
91390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
92390029beSYitzhak Mandelbaum 
93390029beSYitzhak Mandelbaum // `optional` or `nullopt_t`
94390029beSYitzhak Mandelbaum auto hasAnyOptionalType() {
95390029beSYitzhak Mandelbaum   return hasType(hasUnqualifiedDesugaredType(
96390029beSYitzhak Mandelbaum       recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass())))));
97390029beSYitzhak Mandelbaum }
98390029beSYitzhak Mandelbaum 
99092a530cSStanislav Gatev auto inPlaceClass() {
100092a530cSStanislav Gatev   return recordDecl(
101092a530cSStanislav Gatev       hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
102092a530cSStanislav Gatev }
103092a530cSStanislav Gatev 
104092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
1050086a355SYitzhak Mandelbaum   return cxxConstructExpr(
1060086a355SYitzhak Mandelbaum       hasOptionalType(),
1070086a355SYitzhak Mandelbaum       hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
1080086a355SYitzhak Mandelbaum                                         hasParameter(0, hasNulloptType()))));
109092a530cSStanislav Gatev }
110092a530cSStanislav Gatev 
111092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
112092a530cSStanislav Gatev   return cxxConstructExpr(hasOptionalType(),
113092a530cSStanislav Gatev                           hasArgument(0, hasType(inPlaceClass())));
114092a530cSStanislav Gatev }
115092a530cSStanislav Gatev 
116092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
117092a530cSStanislav Gatev   return cxxConstructExpr(
118092a530cSStanislav Gatev       hasOptionalType(),
119092a530cSStanislav Gatev       unless(hasDeclaration(
120092a530cSStanislav Gatev           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
121092a530cSStanislav Gatev       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
122092a530cSStanislav Gatev }
123092a530cSStanislav Gatev 
124b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
125b000b770SStanislav Gatev   return cxxOperatorCallExpr(
126b000b770SStanislav Gatev       hasOverloadedOperatorName("="),
127b000b770SStanislav Gatev       callee(cxxMethodDecl(ofClass(optionalClass()))),
128b000b770SStanislav Gatev       unless(hasDeclaration(cxxMethodDecl(
129b000b770SStanislav Gatev           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
130b000b770SStanislav Gatev       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
131b000b770SStanislav Gatev }
132b000b770SStanislav Gatev 
133390029beSYitzhak Mandelbaum auto isNulloptConstructor() {
134390029beSYitzhak Mandelbaum   return cxxConstructExpr(hasNulloptType(), argumentCountIs(1),
135390029beSYitzhak Mandelbaum                           hasArgument(0, hasNulloptType()));
136390029beSYitzhak Mandelbaum }
137390029beSYitzhak Mandelbaum 
138b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
139b000b770SStanislav Gatev   return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
140b000b770SStanislav Gatev                              callee(cxxMethodDecl(ofClass(optionalClass()))),
141b000b770SStanislav Gatev                              argumentCountIs(2),
142b000b770SStanislav Gatev                              hasArgument(1, hasNulloptType()));
143b000b770SStanislav Gatev }
144b000b770SStanislav Gatev 
1452ddd57aeSStanislav Gatev auto isStdSwapCall() {
1462ddd57aeSStanislav Gatev   return callExpr(callee(functionDecl(hasName("std::swap"))),
1472ddd57aeSStanislav Gatev                   argumentCountIs(2), hasArgument(0, hasOptionalType()),
1482ddd57aeSStanislav Gatev                   hasArgument(1, hasOptionalType()));
1492ddd57aeSStanislav Gatev }
1502ddd57aeSStanislav Gatev 
15125956d55SAMS21 auto isStdForwardCall() {
15225956d55SAMS21   return callExpr(callee(functionDecl(hasName("std::forward"))),
15325956d55SAMS21                   argumentCountIs(1), hasArgument(0, hasOptionalType()));
15425956d55SAMS21 }
15525956d55SAMS21 
1567f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
1577f076004SYitzhak Mandelbaum 
1587f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() {
1597f076004SYitzhak Mandelbaum   // `opt.value_or("").empty()`
1607f076004SYitzhak Mandelbaum   return cxxMemberCallExpr(
1617f076004SYitzhak Mandelbaum       callee(cxxMethodDecl(hasName("empty"))),
1627f076004SYitzhak Mandelbaum       onImplicitObjectArgument(ignoringImplicit(
1637f076004SYitzhak Mandelbaum           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1647f076004SYitzhak Mandelbaum                             callee(cxxMethodDecl(hasName("value_or"),
1657f076004SYitzhak Mandelbaum                                                  ofClass(optionalClass()))),
1667f076004SYitzhak Mandelbaum                             hasArgument(0, stringLiteral(hasSize(0))))
1677f076004SYitzhak Mandelbaum               .bind(ValueOrCallID))));
1687f076004SYitzhak Mandelbaum }
1697f076004SYitzhak Mandelbaum 
1707f076004SYitzhak Mandelbaum auto isValueOrNotEqX() {
1717f076004SYitzhak Mandelbaum   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
1727f076004SYitzhak Mandelbaum     return hasOperands(
1737f076004SYitzhak Mandelbaum         ignoringImplicit(
1747f076004SYitzhak Mandelbaum             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1757f076004SYitzhak Mandelbaum                               callee(cxxMethodDecl(hasName("value_or"),
1767f076004SYitzhak Mandelbaum                                                    ofClass(optionalClass()))),
1777f076004SYitzhak Mandelbaum                               hasArgument(0, Arg))
1787f076004SYitzhak Mandelbaum                 .bind(ValueOrCallID)),
1797f076004SYitzhak Mandelbaum         ignoringImplicit(Arg));
1807f076004SYitzhak Mandelbaum   };
1817f076004SYitzhak Mandelbaum 
1827f076004SYitzhak Mandelbaum   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
1837f076004SYitzhak Mandelbaum   // support this pattern for any expression, but the AST does not have a
1847f076004SYitzhak Mandelbaum   // generic expression comparison facility, so we specialize to common cases
1857f076004SYitzhak Mandelbaum   // seen in practice.  FIXME: define a matcher that compares values across
1867f076004SYitzhak Mandelbaum   // nodes, which would let us generalize this to any `X`.
1877f076004SYitzhak Mandelbaum   return binaryOperation(hasOperatorName("!="),
1887f076004SYitzhak Mandelbaum                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
1897f076004SYitzhak Mandelbaum                                ComparesToSame(stringLiteral(hasSize(0))),
1907f076004SYitzhak Mandelbaum                                ComparesToSame(integerLiteral(equals(0)))));
1917f076004SYitzhak Mandelbaum }
1927f076004SYitzhak Mandelbaum 
19365e710c3SStanislav Gatev auto isCallReturningOptional() {
194cd0d5261SSam Estep   return callExpr(hasType(qualType(anyOf(
195cd0d5261SSam Estep       optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
19665e710c3SStanislav Gatev }
19765e710c3SStanislav Gatev 
198390029beSYitzhak Mandelbaum template <typename L, typename R>
199390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
200390029beSYitzhak Mandelbaum   return cxxOperatorCallExpr(
201390029beSYitzhak Mandelbaum       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
202390029beSYitzhak Mandelbaum       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
203390029beSYitzhak Mandelbaum       hasArgument(1, rhs_arg_matcher));
204390029beSYitzhak Mandelbaum }
205390029beSYitzhak Mandelbaum 
206390029beSYitzhak Mandelbaum // Ensures that `Expr` is mapped to a `BoolValue` and returns it.
207390029beSYitzhak Mandelbaum BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) {
208390029beSYitzhak Mandelbaum   auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None));
209390029beSYitzhak Mandelbaum   if (Value != nullptr)
210390029beSYitzhak Mandelbaum     return *Value;
211390029beSYitzhak Mandelbaum 
212390029beSYitzhak Mandelbaum   auto &Loc = Env.createStorageLocation(Expr);
213390029beSYitzhak Mandelbaum   Value = &Env.makeAtomicBoolValue();
214390029beSYitzhak Mandelbaum   Env.setValue(Loc, *Value);
215390029beSYitzhak Mandelbaum   Env.setStorageLocation(Expr, Loc);
216390029beSYitzhak Mandelbaum   return *Value;
217390029beSYitzhak Mandelbaum }
218390029beSYitzhak Mandelbaum 
2198fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
2208fcdd625SStanislav Gatev /// property of the optional value `OptionalVal`.
2218fcdd625SStanislav Gatev void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
2228fcdd625SStanislav Gatev   OptionalVal.setProperty("has_value", HasValueVal);
2238fcdd625SStanislav Gatev }
2248fcdd625SStanislav Gatev 
2259e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the
2269e0fc676SStanislav Gatev /// symbolic value of its "has_value" property.
2279e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
228745a957fSMartin Braenne   auto &OptionalVal = Env.create<StructValue>();
229745a957fSMartin Braenne   setHasValue(OptionalVal, HasValueVal);
230745a957fSMartin Braenne   return OptionalVal;
2319e0fc676SStanislav Gatev }
2329e0fc676SStanislav Gatev 
233af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
23449ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
235dd38caf3SYitzhak Mandelbaum BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
236dd38caf3SYitzhak Mandelbaum   if (OptionalVal != nullptr) {
237dd38caf3SYitzhak Mandelbaum     auto *HasValueVal =
238dd38caf3SYitzhak Mandelbaum         cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
239dd38caf3SYitzhak Mandelbaum     if (HasValueVal == nullptr) {
240dd38caf3SYitzhak Mandelbaum       HasValueVal = &Env.makeAtomicBoolValue();
241dd38caf3SYitzhak Mandelbaum       OptionalVal->setProperty("has_value", *HasValueVal);
242dd38caf3SYitzhak Mandelbaum     }
243dd38caf3SYitzhak Mandelbaum     return HasValueVal;
244af98b0afSStanislav Gatev   }
245af98b0afSStanislav Gatev   return nullptr;
246af98b0afSStanislav Gatev }
247af98b0afSStanislav Gatev 
248092a530cSStanislav Gatev /// If `Type` is a reference type, returns the type of its pointee. Otherwise,
249092a530cSStanislav Gatev /// returns `Type` itself.
250092a530cSStanislav Gatev QualType stripReference(QualType Type) {
251092a530cSStanislav Gatev   return Type->isReferenceType() ? Type->getPointeeType() : Type;
252092a530cSStanislav Gatev }
253092a530cSStanislav Gatev 
254*cd22e0dcSYitzhak Mandelbaum bool isTopLevelNamespaceWithName(const NamespaceDecl &NS,
255*cd22e0dcSYitzhak Mandelbaum                                  llvm::StringRef Name) {
256*cd22e0dcSYitzhak Mandelbaum   return NS.getDeclName().isIdentifier() && NS.getName() == Name &&
257*cd22e0dcSYitzhak Mandelbaum          NS.getParent() != nullptr && NS.getParent()->isTranslationUnit();
258*cd22e0dcSYitzhak Mandelbaum }
259*cd22e0dcSYitzhak Mandelbaum 
260092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type.
261c0725865SYitzhak Mandelbaum bool isOptionalType(QualType Type) {
262092a530cSStanislav Gatev   if (!Type->isRecordType())
263092a530cSStanislav Gatev     return false;
264*cd22e0dcSYitzhak Mandelbaum   const CXXRecordDecl *D = Type->getAsCXXRecordDecl();
265*cd22e0dcSYitzhak Mandelbaum   if (D == nullptr || !D->getDeclName().isIdentifier())
266*cd22e0dcSYitzhak Mandelbaum     return false;
267*cd22e0dcSYitzhak Mandelbaum   if (D->getName() == "optional") {
268*cd22e0dcSYitzhak Mandelbaum     if (const auto *N =
269*cd22e0dcSYitzhak Mandelbaum         dyn_cast_or_null<NamespaceDecl>(D->getDeclContext()))
270*cd22e0dcSYitzhak Mandelbaum       return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl");
271*cd22e0dcSYitzhak Mandelbaum     return false;
272*cd22e0dcSYitzhak Mandelbaum   }
273*cd22e0dcSYitzhak Mandelbaum 
274*cd22e0dcSYitzhak Mandelbaum   if (D->getName() == "Optional") {
275*cd22e0dcSYitzhak Mandelbaum     // Check whether namespace is "::base".
276*cd22e0dcSYitzhak Mandelbaum     const auto *N =
277*cd22e0dcSYitzhak Mandelbaum         dyn_cast_or_null<NamespaceDecl>(D->getDeclContext());
278*cd22e0dcSYitzhak Mandelbaum     return N != nullptr && isTopLevelNamespaceWithName(*N, "base");
279*cd22e0dcSYitzhak Mandelbaum   }
280*cd22e0dcSYitzhak Mandelbaum 
281*cd22e0dcSYitzhak Mandelbaum   return false;
282092a530cSStanislav Gatev }
283092a530cSStanislav Gatev 
284092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
285092a530cSStanislav Gatev ///
286092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
287092a530cSStanislav Gatev /// function will be 2.
288092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
289c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
290092a530cSStanislav Gatev     return 0;
291092a530cSStanislav Gatev   return 1 + countOptionalWrappers(
292092a530cSStanislav Gatev                  ASTCtx,
293092a530cSStanislav Gatev                  cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
294092a530cSStanislav Gatev                      ->getTemplateArgs()
295092a530cSStanislav Gatev                      .get(0)
296092a530cSStanislav Gatev                      .getAsType()
297092a530cSStanislav Gatev                      .getDesugaredType(ASTCtx));
298092a530cSStanislav Gatev }
299092a530cSStanislav Gatev 
300dd38caf3SYitzhak Mandelbaum /// Tries to initialize the `optional`'s value (that is, contents), and return
301dd38caf3SYitzhak Mandelbaum /// its location. Returns nullptr if the value can't be represented.
302dd38caf3SYitzhak Mandelbaum StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
303dd38caf3SYitzhak Mandelbaum                                                     Value &OptionalVal,
304dd38caf3SYitzhak Mandelbaum                                                     Environment &Env) {
305dd38caf3SYitzhak Mandelbaum   // The "value" property represents a synthetic field. As such, it needs
306dd38caf3SYitzhak Mandelbaum   // `StorageLocation`, like normal fields (and other variables). So, we model
307dd38caf3SYitzhak Mandelbaum   // it with a `ReferenceValue`, since that includes a storage location.  Once
308dd38caf3SYitzhak Mandelbaum   // the property is set, it will be shared by all environments that access the
309dd38caf3SYitzhak Mandelbaum   // `Value` representing the optional (here, `OptionalVal`).
310dd38caf3SYitzhak Mandelbaum   if (auto *ValueProp = OptionalVal.getProperty("value")) {
311dd38caf3SYitzhak Mandelbaum     auto *ValueRef = clang::cast<ReferenceValue>(ValueProp);
31297d69cdaSWei Yi Tee     auto &ValueLoc = ValueRef->getReferentLoc();
313dd38caf3SYitzhak Mandelbaum     if (Env.getValue(ValueLoc) == nullptr) {
314dd38caf3SYitzhak Mandelbaum       // The property was previously set, but the value has been lost. This can
315dd38caf3SYitzhak Mandelbaum       // happen, for example, because of an environment merge (where the two
316dd38caf3SYitzhak Mandelbaum       // environments mapped the property to different values, which resulted in
317dd38caf3SYitzhak Mandelbaum       // them both being discarded), or when two blocks in the CFG, with neither
318dd38caf3SYitzhak Mandelbaum       // a dominator of the other, visit the same optional value, or even when a
319dd38caf3SYitzhak Mandelbaum       // block is revisited during testing to collect per-statement state.
320dd38caf3SYitzhak Mandelbaum       // FIXME: This situation means that the optional contents are not shared
321dd38caf3SYitzhak Mandelbaum       // between branches and the like. Practically, this lack of sharing
322dd38caf3SYitzhak Mandelbaum       // reduces the precision of the model when the contents are relevant to
323dd38caf3SYitzhak Mandelbaum       // the check, like another optional or a boolean that influences control
324dd38caf3SYitzhak Mandelbaum       // flow.
325dd38caf3SYitzhak Mandelbaum       auto *ValueVal = Env.createValue(ValueLoc.getType());
326dd38caf3SYitzhak Mandelbaum       if (ValueVal == nullptr)
327dd38caf3SYitzhak Mandelbaum         return nullptr;
328dd38caf3SYitzhak Mandelbaum       Env.setValue(ValueLoc, *ValueVal);
329dd38caf3SYitzhak Mandelbaum     }
330dd38caf3SYitzhak Mandelbaum     return &ValueLoc;
331dd38caf3SYitzhak Mandelbaum   }
332dd38caf3SYitzhak Mandelbaum 
333dd38caf3SYitzhak Mandelbaum   auto Ty = stripReference(Q);
334dd38caf3SYitzhak Mandelbaum   auto *ValueVal = Env.createValue(Ty);
335dd38caf3SYitzhak Mandelbaum   if (ValueVal == nullptr)
336dd38caf3SYitzhak Mandelbaum     return nullptr;
337dd38caf3SYitzhak Mandelbaum   auto &ValueLoc = Env.createStorageLocation(Ty);
338dd38caf3SYitzhak Mandelbaum   Env.setValue(ValueLoc, *ValueVal);
339745a957fSMartin Braenne   auto &ValueRef = Env.create<ReferenceValue>(ValueLoc);
340745a957fSMartin Braenne   OptionalVal.setProperty("value", ValueRef);
341dd38caf3SYitzhak Mandelbaum   return &ValueLoc;
342dd38caf3SYitzhak Mandelbaum }
343dd38caf3SYitzhak Mandelbaum 
344092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr,
345092a530cSStanislav Gatev                                  const MatchFinder::MatchResult &,
346af98b0afSStanislav Gatev                                  LatticeTransferState &State) {
34749ed5bf5SWei Yi Tee   if (auto *OptionalVal =
34849ed5bf5SWei Yi Tee           State.Env.getValue(*OptionalExpr, SkipPast::Reference)) {
349af98b0afSStanislav Gatev     if (OptionalVal->getProperty("has_value") == nullptr) {
3508fcdd625SStanislav Gatev       setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
351af98b0afSStanislav Gatev     }
352af98b0afSStanislav Gatev   }
353af98b0afSStanislav Gatev }
354af98b0afSStanislav Gatev 
3558fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
3568fcdd625SStanislav Gatev /// empty in `Env.
3578fcdd625SStanislav Gatev bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
3588fcdd625SStanislav Gatev   auto *HasValueVal =
3598fcdd625SStanislav Gatev       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
3608fcdd625SStanislav Gatev   return HasValueVal != nullptr &&
3618fcdd625SStanislav Gatev          Env.flowConditionImplies(Env.makeNot(*HasValueVal));
3628fcdd625SStanislav Gatev }
3638fcdd625SStanislav Gatev 
3648fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
3658fcdd625SStanislav Gatev /// non-empty in `Env.
3668fcdd625SStanislav Gatev bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
3678fcdd625SStanislav Gatev   auto *HasValueVal =
3688fcdd625SStanislav Gatev       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
3698fcdd625SStanislav Gatev   return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal);
3708fcdd625SStanislav Gatev }
3718fcdd625SStanislav Gatev 
372092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
373af98b0afSStanislav Gatev                         LatticeTransferState &State) {
37449ed5bf5SWei Yi Tee   if (auto *OptionalVal =
37549ed5bf5SWei Yi Tee           State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
376dd38caf3SYitzhak Mandelbaum     if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
377dd38caf3SYitzhak Mandelbaum       if (auto *Loc = maybeInitializeOptionalValueMember(
378dd38caf3SYitzhak Mandelbaum               UnwrapExpr->getType(), *OptionalVal, State.Env))
379dd38caf3SYitzhak Mandelbaum         State.Env.setStorageLocation(*UnwrapExpr, *Loc);
380af98b0afSStanislav Gatev   }
381dd38caf3SYitzhak Mandelbaum }
382af98b0afSStanislav Gatev 
383092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
384092a530cSStanislav Gatev                               const MatchFinder::MatchResult &,
385092a530cSStanislav Gatev                               LatticeTransferState &State) {
3869e0fc676SStanislav Gatev   auto &Loc = State.Env.createStorageLocation(*E);
3879e0fc676SStanislav Gatev   State.Env.setStorageLocation(*E, Loc);
3889e0fc676SStanislav Gatev   State.Env.setValue(
3899e0fc676SStanislav Gatev       Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
3909e0fc676SStanislav Gatev }
3919e0fc676SStanislav Gatev 
392092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
393092a530cSStanislav Gatev                                   const MatchFinder::MatchResult &,
394af98b0afSStanislav Gatev                                   LatticeTransferState &State) {
395dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal = getHasValue(
396dd38caf3SYitzhak Mandelbaum           State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
397af98b0afSStanislav Gatev                                         SkipPast::ReferenceThenPointer))) {
398af98b0afSStanislav Gatev     auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
399af98b0afSStanislav Gatev     State.Env.setValue(CallExprLoc, *HasValueVal);
400af98b0afSStanislav Gatev     State.Env.setStorageLocation(*CallExpr, CallExprLoc);
401af98b0afSStanislav Gatev   }
402af98b0afSStanislav Gatev }
403af98b0afSStanislav Gatev 
4047f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in
4057f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property.
4067f076004SYitzhak Mandelbaum void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
4077f076004SYitzhak Mandelbaum                          const MatchFinder::MatchResult &Result,
4087f076004SYitzhak Mandelbaum                          LatticeTransferState &State,
4097f076004SYitzhak Mandelbaum                          BoolValue &(*ModelPred)(Environment &Env,
4107f076004SYitzhak Mandelbaum                                                  BoolValue &ExprVal,
4117f076004SYitzhak Mandelbaum                                                  BoolValue &HasValueVal)) {
4127f076004SYitzhak Mandelbaum   auto &Env = State.Env;
4137f076004SYitzhak Mandelbaum 
4147f076004SYitzhak Mandelbaum   const auto *ObjectArgumentExpr =
4157f076004SYitzhak Mandelbaum       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
4167f076004SYitzhak Mandelbaum           ->getImplicitObjectArgument();
4177f076004SYitzhak Mandelbaum 
418dd38caf3SYitzhak Mandelbaum   auto *HasValueVal = getHasValue(
419dd38caf3SYitzhak Mandelbaum       State.Env,
420dd38caf3SYitzhak Mandelbaum       State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
421dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr)
4227f076004SYitzhak Mandelbaum     return;
4237f076004SYitzhak Mandelbaum 
424390029beSYitzhak Mandelbaum   Env.addToFlowCondition(
425390029beSYitzhak Mandelbaum       ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal));
4267f076004SYitzhak Mandelbaum }
4277f076004SYitzhak Mandelbaum 
4287f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
4297f076004SYitzhak Mandelbaum                                     const MatchFinder::MatchResult &Result,
4307f076004SYitzhak Mandelbaum                                     LatticeTransferState &State) {
4317f076004SYitzhak Mandelbaum   return transferValueOrImpl(ComparisonExpr, Result, State,
4327f076004SYitzhak Mandelbaum                              [](Environment &Env, BoolValue &ExprVal,
4337f076004SYitzhak Mandelbaum                                 BoolValue &HasValueVal) -> BoolValue & {
4347f076004SYitzhak Mandelbaum                                // If the result is *not* empty, then we know the
4357f076004SYitzhak Mandelbaum                                // optional must have been holding a value. If
4367f076004SYitzhak Mandelbaum                                // `ExprVal` is true, though, we don't learn
4377f076004SYitzhak Mandelbaum                                // anything definite about `has_value`, so we
4387f076004SYitzhak Mandelbaum                                // don't add any corresponding implications to
4397f076004SYitzhak Mandelbaum                                // the flow condition.
4407f076004SYitzhak Mandelbaum                                return Env.makeImplication(Env.makeNot(ExprVal),
4417f076004SYitzhak Mandelbaum                                                           HasValueVal);
4427f076004SYitzhak Mandelbaum                              });
4437f076004SYitzhak Mandelbaum }
4447f076004SYitzhak Mandelbaum 
4457f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr,
4467f076004SYitzhak Mandelbaum                            const MatchFinder::MatchResult &Result,
4477f076004SYitzhak Mandelbaum                            LatticeTransferState &State) {
4487f076004SYitzhak Mandelbaum   transferValueOrImpl(ComparisonExpr, Result, State,
4497f076004SYitzhak Mandelbaum                       [](Environment &Env, BoolValue &ExprVal,
4507f076004SYitzhak Mandelbaum                          BoolValue &HasValueVal) -> BoolValue & {
4517f076004SYitzhak Mandelbaum                         // We know that if `(opt.value_or(X) != X)` then
4527f076004SYitzhak Mandelbaum                         // `opt.hasValue()`, even without knowing further
4537f076004SYitzhak Mandelbaum                         // details about the contents of `opt`.
4547f076004SYitzhak Mandelbaum                         return Env.makeImplication(ExprVal, HasValueVal);
4557f076004SYitzhak Mandelbaum                       });
4567f076004SYitzhak Mandelbaum }
4577f076004SYitzhak Mandelbaum 
45865e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E,
45965e710c3SStanislav Gatev                                    const MatchFinder::MatchResult &Result,
46065e710c3SStanislav Gatev                                    LatticeTransferState &State) {
46165e710c3SStanislav Gatev   if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
46265e710c3SStanislav Gatev     return;
46365e710c3SStanislav Gatev 
46465e710c3SStanislav Gatev   auto &Loc = State.Env.createStorageLocation(*E);
46565e710c3SStanislav Gatev   State.Env.setStorageLocation(*E, Loc);
46665e710c3SStanislav Gatev   State.Env.setValue(
46765e710c3SStanislav Gatev       Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()));
46865e710c3SStanislav Gatev }
46965e710c3SStanislav Gatev 
4700e8d4a6dSYitzhak Mandelbaum void assignOptionalValue(const Expr &E, Environment &Env,
471092a530cSStanislav Gatev                          BoolValue &HasValueVal) {
472092a530cSStanislav Gatev   if (auto *OptionalLoc =
4730e8d4a6dSYitzhak Mandelbaum           Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
4740e8d4a6dSYitzhak Mandelbaum     Env.setValue(*OptionalLoc, createOptionalValue(Env, HasValueVal));
4759e0fc676SStanislav Gatev   }
4769e0fc676SStanislav Gatev }
4779e0fc676SStanislav Gatev 
478b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
479b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
480b000b770SStanislav Gatev /// where `T` is constructible from `U`.
481390029beSYitzhak Mandelbaum BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
482b000b770SStanislav Gatev                                      const MatchFinder::MatchResult &MatchRes,
483b000b770SStanislav Gatev                                      LatticeTransferState &State) {
4840086a355SYitzhak Mandelbaum   assert(F.getTemplateSpecializationArgs() != nullptr);
485b000b770SStanislav Gatev   assert(F.getTemplateSpecializationArgs()->size() > 0);
486b000b770SStanislav Gatev 
487b000b770SStanislav Gatev   const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
488b000b770SStanislav Gatev       *MatchRes.Context,
489b000b770SStanislav Gatev       stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType()));
490b000b770SStanislav Gatev   const int ArgTypeOptionalWrappersCount =
491b000b770SStanislav Gatev       countOptionalWrappers(*MatchRes.Context, stripReference(E.getType()));
492b000b770SStanislav Gatev 
493b000b770SStanislav Gatev   // Check if this is a constructor/assignment call for `optional<T>` with
494b000b770SStanislav Gatev   // argument of type `U` such that `T` is constructible from `U`.
495b000b770SStanislav Gatev   if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
496b000b770SStanislav Gatev     return State.Env.getBoolLiteralValue(true);
497b000b770SStanislav Gatev 
498b000b770SStanislav Gatev   // This is a constructor/assignment call for `optional<T>` with argument of
499b000b770SStanislav Gatev   // type `optional<U>` such that `T` is constructible from `U`.
500dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal =
501dd38caf3SYitzhak Mandelbaum           getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference)))
502dd38caf3SYitzhak Mandelbaum     return *HasValueVal;
503b000b770SStanislav Gatev   return State.Env.makeAtomicBoolValue();
504b000b770SStanislav Gatev }
505b000b770SStanislav Gatev 
506092a530cSStanislav Gatev void transferValueOrConversionConstructor(
507092a530cSStanislav Gatev     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
5089e0fc676SStanislav Gatev     LatticeTransferState &State) {
509092a530cSStanislav Gatev   assert(E->getNumArgs() > 0);
510092a530cSStanislav Gatev 
5110e8d4a6dSYitzhak Mandelbaum   assignOptionalValue(*E, State.Env,
512390029beSYitzhak Mandelbaum                       valueOrConversionHasValue(*E->getConstructor(),
513b000b770SStanislav Gatev                                                 *E->getArg(0), MatchRes,
514b000b770SStanislav Gatev                                                 State));
515b000b770SStanislav Gatev }
516092a530cSStanislav Gatev 
517b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
518b000b770SStanislav Gatev                         LatticeTransferState &State) {
519b000b770SStanislav Gatev   assert(E->getNumArgs() > 0);
520b000b770SStanislav Gatev 
521b000b770SStanislav Gatev   auto *OptionalLoc =
522b000b770SStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
523a9ad689eSSam Estep   if (OptionalLoc == nullptr)
524a9ad689eSSam Estep     return;
525b000b770SStanislav Gatev 
526b000b770SStanislav Gatev   State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
527b000b770SStanislav Gatev 
528b000b770SStanislav Gatev   // Assign a storage location for the whole expression.
529b000b770SStanislav Gatev   State.Env.setStorageLocation(*E, *OptionalLoc);
530b000b770SStanislav Gatev }
531b000b770SStanislav Gatev 
532b000b770SStanislav Gatev void transferValueOrConversionAssignment(
533b000b770SStanislav Gatev     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
534b000b770SStanislav Gatev     LatticeTransferState &State) {
535b000b770SStanislav Gatev   assert(E->getNumArgs() > 1);
536b000b770SStanislav Gatev   transferAssignment(E,
537390029beSYitzhak Mandelbaum                      valueOrConversionHasValue(*E->getDirectCallee(),
53806decd0bSKazu Hirata                                                *E->getArg(1), MatchRes, State),
539b000b770SStanislav Gatev                      State);
540b000b770SStanislav Gatev }
541b000b770SStanislav Gatev 
542b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
543b000b770SStanislav Gatev                                const MatchFinder::MatchResult &,
544b000b770SStanislav Gatev                                LatticeTransferState &State) {
545b000b770SStanislav Gatev   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
5469e0fc676SStanislav Gatev }
5479e0fc676SStanislav Gatev 
548d4fb829bSYitzhak Mandelbaum void transferSwap(const Expr &E1, SkipPast E1Skip, const Expr &E2,
549d4fb829bSYitzhak Mandelbaum                   Environment &Env) {
550d4fb829bSYitzhak Mandelbaum   // We account for cases where one or both of the optionals are not modeled,
551d4fb829bSYitzhak Mandelbaum   // either lacking associated storage locations, or lacking values associated
552d4fb829bSYitzhak Mandelbaum   // to such storage locations.
553d4fb829bSYitzhak Mandelbaum   auto *Loc1 = Env.getStorageLocation(E1, E1Skip);
554d4fb829bSYitzhak Mandelbaum   auto *Loc2 = Env.getStorageLocation(E2, SkipPast::Reference);
5552ddd57aeSStanislav Gatev 
556d4fb829bSYitzhak Mandelbaum   if (Loc1 == nullptr) {
557d4fb829bSYitzhak Mandelbaum     if (Loc2 != nullptr)
558d4fb829bSYitzhak Mandelbaum       Env.setValue(*Loc2, createOptionalValue(Env, Env.makeAtomicBoolValue()));
559d4fb829bSYitzhak Mandelbaum     return;
560d4fb829bSYitzhak Mandelbaum   }
561d4fb829bSYitzhak Mandelbaum   if (Loc2 == nullptr) {
562d4fb829bSYitzhak Mandelbaum     Env.setValue(*Loc1, createOptionalValue(Env, Env.makeAtomicBoolValue()));
563d4fb829bSYitzhak Mandelbaum     return;
564d4fb829bSYitzhak Mandelbaum   }
5652ddd57aeSStanislav Gatev 
566d4fb829bSYitzhak Mandelbaum   // Both expressions have locations, though they may not have corresponding
567d4fb829bSYitzhak Mandelbaum   // values. In that case, we create a fresh value at this point. Note that if
568d4fb829bSYitzhak Mandelbaum   // two branches both do this, they will not share the value, but it at least
569d4fb829bSYitzhak Mandelbaum   // allows for local reasoning about the value. To avoid the above, we would
570d4fb829bSYitzhak Mandelbaum   // need *lazy* value allocation.
571d4fb829bSYitzhak Mandelbaum   // FIXME: allocate values lazily, instead of just creating a fresh value.
572d4fb829bSYitzhak Mandelbaum   auto *Val1 = Env.getValue(*Loc1);
573d4fb829bSYitzhak Mandelbaum   if (Val1 == nullptr)
574d4fb829bSYitzhak Mandelbaum     Val1 = &createOptionalValue(Env, Env.makeAtomicBoolValue());
575d4fb829bSYitzhak Mandelbaum 
576d4fb829bSYitzhak Mandelbaum   auto *Val2 = Env.getValue(*Loc2);
577d4fb829bSYitzhak Mandelbaum   if (Val2 == nullptr)
578d4fb829bSYitzhak Mandelbaum     Val2 = &createOptionalValue(Env, Env.makeAtomicBoolValue());
579d4fb829bSYitzhak Mandelbaum 
580d4fb829bSYitzhak Mandelbaum   Env.setValue(*Loc1, *Val2);
581d4fb829bSYitzhak Mandelbaum   Env.setValue(*Loc2, *Val1);
5822ddd57aeSStanislav Gatev }
5832ddd57aeSStanislav Gatev 
5842ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
5852ddd57aeSStanislav Gatev                       const MatchFinder::MatchResult &,
5862ddd57aeSStanislav Gatev                       LatticeTransferState &State) {
5872ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 1);
588d4fb829bSYitzhak Mandelbaum   transferSwap(*E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer,
589d4fb829bSYitzhak Mandelbaum                *E->getArg(0), State.Env);
5902ddd57aeSStanislav Gatev }
5912ddd57aeSStanislav Gatev 
5922ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
5932ddd57aeSStanislav Gatev                          LatticeTransferState &State) {
5942ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 2);
595d4fb829bSYitzhak Mandelbaum   transferSwap(*E->getArg(0), SkipPast::Reference, *E->getArg(1), State.Env);
5962ddd57aeSStanislav Gatev }
5972ddd57aeSStanislav Gatev 
59825956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
59925956d55SAMS21                             LatticeTransferState &State) {
60025956d55SAMS21   assert(E->getNumArgs() == 1);
60125956d55SAMS21 
60225956d55SAMS21   StorageLocation *LocRet = State.Env.getStorageLocation(*E, SkipPast::None);
60325956d55SAMS21   if (LocRet != nullptr)
60425956d55SAMS21     return;
60525956d55SAMS21 
60625956d55SAMS21   StorageLocation *LocArg =
60725956d55SAMS21       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
60825956d55SAMS21 
60925956d55SAMS21   if (LocArg == nullptr)
61025956d55SAMS21     return;
61125956d55SAMS21 
61225956d55SAMS21   Value *ValArg = State.Env.getValue(*LocArg);
61325956d55SAMS21   if (ValArg == nullptr)
61425956d55SAMS21     ValArg = &createOptionalValue(State.Env, State.Env.makeAtomicBoolValue());
61525956d55SAMS21 
61625956d55SAMS21   // Create a new storage location
61725956d55SAMS21   LocRet = &State.Env.createStorageLocation(*E);
61825956d55SAMS21   State.Env.setStorageLocation(*E, *LocRet);
61925956d55SAMS21 
62025956d55SAMS21   State.Env.setValue(*LocRet, *ValArg);
62125956d55SAMS21 }
62225956d55SAMS21 
623390029beSYitzhak Mandelbaum BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS,
624390029beSYitzhak Mandelbaum                             BoolValue &RHS) {
625390029beSYitzhak Mandelbaum   // Logically, an optional<T> object is composed of two values - a `has_value`
626390029beSYitzhak Mandelbaum   // bit and a value of type T. Equality of optional objects compares both
627390029beSYitzhak Mandelbaum   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
628390029beSYitzhak Mandelbaum   // when two optional objects are engaged, the equality of their respective
629390029beSYitzhak Mandelbaum   // values of type T matters. Since we only track the `has_value` bits, we
630390029beSYitzhak Mandelbaum   // can't make any conclusions about equality when we know that two optional
631390029beSYitzhak Mandelbaum   // objects are engaged.
632390029beSYitzhak Mandelbaum   //
633390029beSYitzhak Mandelbaum   // We express this as two facts about the equality:
634390029beSYitzhak Mandelbaum   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
635390029beSYitzhak Mandelbaum   //    If they are equal, then either both are set or both are unset.
636390029beSYitzhak Mandelbaum   // b) (!LHS & !RHS) => EqVal
637390029beSYitzhak Mandelbaum   //    If neither is set, then they are equal.
638390029beSYitzhak Mandelbaum   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
639390029beSYitzhak Mandelbaum   return Env.makeAnd(
640390029beSYitzhak Mandelbaum       Env.makeImplication(
641390029beSYitzhak Mandelbaum           EqVal, Env.makeOr(Env.makeAnd(LHS, RHS),
642390029beSYitzhak Mandelbaum                             Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))),
643390029beSYitzhak Mandelbaum       Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS)));
644390029beSYitzhak Mandelbaum }
645390029beSYitzhak Mandelbaum 
646390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
647390029beSYitzhak Mandelbaum                                     const MatchFinder::MatchResult &,
648390029beSYitzhak Mandelbaum                                     LatticeTransferState &State) {
649390029beSYitzhak Mandelbaum   Environment &Env = State.Env;
650390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
651390029beSYitzhak Mandelbaum   if (auto *LHasVal = getHasValue(
652390029beSYitzhak Mandelbaum           Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference)))
653390029beSYitzhak Mandelbaum     if (auto *RHasVal = getHasValue(
654390029beSYitzhak Mandelbaum             Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) {
655390029beSYitzhak Mandelbaum       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
656390029beSYitzhak Mandelbaum         CmpValue = &State.Env.makeNot(*CmpValue);
657390029beSYitzhak Mandelbaum       Env.addToFlowCondition(
658390029beSYitzhak Mandelbaum           evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal));
659390029beSYitzhak Mandelbaum     }
660390029beSYitzhak Mandelbaum }
661390029beSYitzhak Mandelbaum 
662390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
663390029beSYitzhak Mandelbaum                                  const clang::Expr *E, Environment &Env) {
664390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
665390029beSYitzhak Mandelbaum   if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) {
666390029beSYitzhak Mandelbaum     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
667390029beSYitzhak Mandelbaum       CmpValue = &Env.makeNot(*CmpValue);
668390029beSYitzhak Mandelbaum     Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal,
669390029beSYitzhak Mandelbaum                                             Env.getBoolLiteralValue(true)));
670390029beSYitzhak Mandelbaum   }
671390029beSYitzhak Mandelbaum }
672390029beSYitzhak Mandelbaum 
6736ad0788cSKazu Hirata std::optional<StatementMatcher>
674a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
6755d22d1f5SYitzhak Mandelbaum   if (Options.IgnoreSmartPointerDereference) {
6765d22d1f5SYitzhak Mandelbaum     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
6775d22d1f5SYitzhak Mandelbaum         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
6785d22d1f5SYitzhak Mandelbaum         unless(hasArgument(0, expr(hasOptionalType()))))));
6795d22d1f5SYitzhak Mandelbaum     return expr(
6805d22d1f5SYitzhak Mandelbaum         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
6815d22d1f5SYitzhak Mandelbaum   }
68234e0d057SKazu Hirata   return std::nullopt;
683a184a0d8SYitzhak Mandelbaum }
684a184a0d8SYitzhak Mandelbaum 
68558fe7f96SSam Estep StatementMatcher
6866ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
68758fe7f96SSam Estep   return isOptionalMemberCallWithName("value", IgnorableOptional);
68858fe7f96SSam Estep }
68958fe7f96SSam Estep 
69058fe7f96SSam Estep StatementMatcher
6916ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
69258fe7f96SSam Estep   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
69358fe7f96SSam Estep                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
69458fe7f96SSam Estep }
69558fe7f96SSam Estep 
6965d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() {
697b000b770SStanislav Gatev   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
698b000b770SStanislav Gatev   // lot of duplicated work (e.g. string comparisons), consider providing APIs
699b000b770SStanislav Gatev   // that avoid it through memoization.
7007538b360SWei Yi Tee   return CFGMatchSwitchBuilder<LatticeTransferState>()
701af98b0afSStanislav Gatev       // Attach a symbolic "has_value" state to optional values that we see for
702af98b0afSStanislav Gatev       // the first time.
7037538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(
7046adfc64eSYitzhak Mandelbaum           expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
705af98b0afSStanislav Gatev           initializeOptionalReference)
706af98b0afSStanislav Gatev 
7079e0fc676SStanislav Gatev       // make_optional
7087538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
709092a530cSStanislav Gatev 
7100e8d4a6dSYitzhak Mandelbaum       // optional::optional (in place)
7117538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
712092a530cSStanislav Gatev           isOptionalInPlaceConstructor(),
713092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
714092a530cSStanislav Gatev              LatticeTransferState &State) {
7150e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E, State.Env,
7160e8d4a6dSYitzhak Mandelbaum                                 State.Env.getBoolLiteralValue(true));
717092a530cSStanislav Gatev           })
7180e8d4a6dSYitzhak Mandelbaum       // nullopt_t::nullopt_t
7197538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
720390029beSYitzhak Mandelbaum           isNulloptConstructor(),
721092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
722092a530cSStanislav Gatev              LatticeTransferState &State) {
7230e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E, State.Env,
724092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
725092a530cSStanislav Gatev           })
7260e8d4a6dSYitzhak Mandelbaum       // optional::optional(nullopt_t)
727390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXConstructExpr>(
728390029beSYitzhak Mandelbaum           isOptionalNulloptConstructor(),
729390029beSYitzhak Mandelbaum           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
730390029beSYitzhak Mandelbaum              LatticeTransferState &State) {
7310e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E, State.Env,
7320e8d4a6dSYitzhak Mandelbaum                                 State.Env.getBoolLiteralValue(false));
733390029beSYitzhak Mandelbaum           })
7340e8d4a6dSYitzhak Mandelbaum       // optional::optional (value/conversion)
7357538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
736092a530cSStanislav Gatev                                        transferValueOrConversionConstructor)
7379e0fc676SStanislav Gatev 
738b000b770SStanislav Gatev       // optional::operator=
7397538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(
7407538b360SWei Yi Tee           isOptionalValueOrConversionAssignment(),
741b000b770SStanislav Gatev           transferValueOrConversionAssignment)
7427538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
743b000b770SStanislav Gatev                                           transferNulloptAssignment)
744b000b770SStanislav Gatev 
745af98b0afSStanislav Gatev       // optional::value
7467538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
7475d22d1f5SYitzhak Mandelbaum           valueCall(std::nullopt),
748092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
749092a530cSStanislav Gatev              LatticeTransferState &State) {
750af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
751af98b0afSStanislav Gatev           })
752af98b0afSStanislav Gatev 
753af98b0afSStanislav Gatev       // optional::operator*, optional::operator->
7545d22d1f5SYitzhak Mandelbaum       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(std::nullopt),
7557538b360SWei Yi Tee                                [](const CallExpr *E,
7567538b360SWei Yi Tee                                   const MatchFinder::MatchResult &,
757092a530cSStanislav Gatev                                   LatticeTransferState &State) {
758af98b0afSStanislav Gatev                                  transferUnwrapCall(E, E->getArg(0), State);
759af98b0afSStanislav Gatev                                })
760af98b0afSStanislav Gatev 
761af98b0afSStanislav Gatev       // optional::has_value
7627538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
7637538b360SWei Yi Tee           isOptionalMemberCallWithName("has_value"),
764af98b0afSStanislav Gatev           transferOptionalHasValueCall)
765af98b0afSStanislav Gatev 
7669e0fc676SStanislav Gatev       // optional::operator bool
7677538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
7687538b360SWei Yi Tee           isOptionalMemberCallWithName("operator bool"),
7699e0fc676SStanislav Gatev           transferOptionalHasValueCall)
7709e0fc676SStanislav Gatev 
7719e0fc676SStanislav Gatev       // optional::emplace
7727538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
773092a530cSStanislav Gatev           isOptionalMemberCallWithName("emplace"),
774092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
775092a530cSStanislav Gatev              LatticeTransferState &State) {
7760e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
777092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(true));
778092a530cSStanislav Gatev           })
7799e0fc676SStanislav Gatev 
7809e0fc676SStanislav Gatev       // optional::reset
7817538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
782092a530cSStanislav Gatev           isOptionalMemberCallWithName("reset"),
783092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
784092a530cSStanislav Gatev              LatticeTransferState &State) {
7850e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
786092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
787092a530cSStanislav Gatev           })
7889e0fc676SStanislav Gatev 
7892ddd57aeSStanislav Gatev       // optional::swap
7907538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
7912ddd57aeSStanislav Gatev                                         transferSwapCall)
7922ddd57aeSStanislav Gatev 
7932ddd57aeSStanislav Gatev       // std::swap
7947538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
7952ddd57aeSStanislav Gatev 
79625956d55SAMS21       // std::forward
79725956d55SAMS21       .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
79825956d55SAMS21 
7997f076004SYitzhak Mandelbaum       // opt.value_or("").empty()
8007538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
8017538b360SWei Yi Tee                            transferValueOrStringEmptyCall)
8027f076004SYitzhak Mandelbaum 
8037f076004SYitzhak Mandelbaum       // opt.value_or(X) != X
8047538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
8057f076004SYitzhak Mandelbaum 
806390029beSYitzhak Mandelbaum       // Comparisons (==, !=):
807390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
808390029beSYitzhak Mandelbaum           isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()),
809390029beSYitzhak Mandelbaum           transferOptionalAndOptionalCmp)
810390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
811390029beSYitzhak Mandelbaum           isComparisonOperatorCall(hasOptionalType(),
812390029beSYitzhak Mandelbaum                                    unless(hasAnyOptionalType())),
813390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
814390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
815390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
816390029beSYitzhak Mandelbaum           })
817390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
818390029beSYitzhak Mandelbaum           isComparisonOperatorCall(unless(hasAnyOptionalType()),
819390029beSYitzhak Mandelbaum                                    hasOptionalType()),
820390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
821390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
822390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
823390029beSYitzhak Mandelbaum           })
824390029beSYitzhak Mandelbaum 
82565e710c3SStanislav Gatev       // returns optional
8267538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
82765e710c3SStanislav Gatev                                transferCallReturningOptional)
82865e710c3SStanislav Gatev 
829af98b0afSStanislav Gatev       .Build();
830af98b0afSStanislav Gatev }
831af98b0afSStanislav Gatev 
83258fe7f96SSam Estep std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr,
83358fe7f96SSam Estep                                                const Expr *ObjectExpr,
83458fe7f96SSam Estep                                                const Environment &Env) {
83558fe7f96SSam Estep   if (auto *OptionalVal =
83658fe7f96SSam Estep           Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
83758fe7f96SSam Estep     auto *Prop = OptionalVal->getProperty("has_value");
83858fe7f96SSam Estep     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
83958fe7f96SSam Estep       if (Env.flowConditionImplies(*HasValueVal))
84058fe7f96SSam Estep         return {};
84158fe7f96SSam Estep     }
84258fe7f96SSam Estep   }
84358fe7f96SSam Estep 
84458fe7f96SSam Estep   // Record that this unwrap is *not* provably safe.
84558fe7f96SSam Estep   // FIXME: include either the name of the optional (if applicable) or a source
84658fe7f96SSam Estep   // range of the access for easier interpretation of the result.
84758fe7f96SSam Estep   return {ObjectExpr->getBeginLoc()};
84858fe7f96SSam Estep }
84958fe7f96SSam Estep 
85058fe7f96SSam Estep auto buildDiagnoseMatchSwitch(
85158fe7f96SSam Estep     const UncheckedOptionalAccessModelOptions &Options) {
85258fe7f96SSam Estep   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
85358fe7f96SSam Estep   // lot of duplicated work (e.g. string comparisons), consider providing APIs
85458fe7f96SSam Estep   // that avoid it through memoization.
85558fe7f96SSam Estep   auto IgnorableOptional = ignorableOptional(Options);
8567538b360SWei Yi Tee   return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
85758fe7f96SSam Estep       // optional::value
8587538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
85958fe7f96SSam Estep           valueCall(IgnorableOptional),
86058fe7f96SSam Estep           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
86158fe7f96SSam Estep              const Environment &Env) {
86258fe7f96SSam Estep             return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env);
86358fe7f96SSam Estep           })
86458fe7f96SSam Estep 
86558fe7f96SSam Estep       // optional::operator*, optional::operator->
8667538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(
86758fe7f96SSam Estep           valueOperatorCall(IgnorableOptional),
86858fe7f96SSam Estep           [](const CallExpr *E, const MatchFinder::MatchResult &,
86958fe7f96SSam Estep              const Environment &Env) {
87058fe7f96SSam Estep             return diagnoseUnwrapCall(E, E->getArg(0), Env);
87158fe7f96SSam Estep           })
87258fe7f96SSam Estep       .Build();
87358fe7f96SSam Estep }
87458fe7f96SSam Estep 
875af98b0afSStanislav Gatev } // namespace
876af98b0afSStanislav Gatev 
8777e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher
8787e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() {
8797e63a0d4SYitzhak Mandelbaum   return optionalClass();
8807e63a0d4SYitzhak Mandelbaum }
8817e63a0d4SYitzhak Mandelbaum 
8825d22d1f5SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
883cf1f978dSSam Estep     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
8845d22d1f5SYitzhak Mandelbaum       TransferMatchSwitch(buildTransferMatchSwitch()) {}
885af98b0afSStanislav Gatev 
8866b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
8877538b360SWei Yi Tee                                             NoopLattice &L, Environment &Env) {
888af98b0afSStanislav Gatev   LatticeTransferState State(L, Env);
8896b991ba4SYitzhak Mandelbaum   TransferMatchSwitch(Elt, getASTContext(), State);
890af98b0afSStanislav Gatev }
891af98b0afSStanislav Gatev 
892c0725865SYitzhak Mandelbaum ComparisonResult UncheckedOptionalAccessModel::compare(
893c0725865SYitzhak Mandelbaum     QualType Type, const Value &Val1, const Environment &Env1,
894c0725865SYitzhak Mandelbaum     const Value &Val2, const Environment &Env2) {
895c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
896c0725865SYitzhak Mandelbaum     return ComparisonResult::Unknown;
897d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
898d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
89925956d55SAMS21   if (MustNonEmpty1 && MustNonEmpty2)
90025956d55SAMS21     return ComparisonResult::Same;
901d34fbf2dSYitzhak Mandelbaum   // If exactly one is true, then they're different, no reason to check whether
902d34fbf2dSYitzhak Mandelbaum   // they're definitely empty.
90325956d55SAMS21   if (MustNonEmpty1 || MustNonEmpty2)
90425956d55SAMS21     return ComparisonResult::Different;
905d34fbf2dSYitzhak Mandelbaum   // Check if they're both definitely empty.
906d34fbf2dSYitzhak Mandelbaum   return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
907c0725865SYitzhak Mandelbaum              ? ComparisonResult::Same
908c0725865SYitzhak Mandelbaum              : ComparisonResult::Different;
9098fcdd625SStanislav Gatev }
9108fcdd625SStanislav Gatev 
9118fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
9128fcdd625SStanislav Gatev                                          const Environment &Env1,
9138fcdd625SStanislav Gatev                                          const Value &Val2,
9148fcdd625SStanislav Gatev                                          const Environment &Env2,
9158fcdd625SStanislav Gatev                                          Value &MergedVal,
9168fcdd625SStanislav Gatev                                          Environment &MergedEnv) {
917c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
9188fcdd625SStanislav Gatev     return true;
919d34fbf2dSYitzhak Mandelbaum   // FIXME: uses same approach as join for `BoolValues`. Requires non-const
920d34fbf2dSYitzhak Mandelbaum   // values, though, so will require updating the interface.
9218fcdd625SStanislav Gatev   auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
922d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
923d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
924d34fbf2dSYitzhak Mandelbaum   if (MustNonEmpty1 && MustNonEmpty2)
9258fcdd625SStanislav Gatev     MergedEnv.addToFlowCondition(HasValueVal);
926d34fbf2dSYitzhak Mandelbaum   else if (
927d34fbf2dSYitzhak Mandelbaum       // Only make the costly calls to `isEmptyOptional` if we got "unknown"
928d34fbf2dSYitzhak Mandelbaum       // (false) for both calls to `isNonEmptyOptional`.
929d34fbf2dSYitzhak Mandelbaum       !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) &&
930d34fbf2dSYitzhak Mandelbaum       isEmptyOptional(Val2, Env2))
9318fcdd625SStanislav Gatev     MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal));
9328fcdd625SStanislav Gatev   setHasValue(MergedVal, HasValueVal);
9338fcdd625SStanislav Gatev   return true;
9348fcdd625SStanislav Gatev }
9358fcdd625SStanislav Gatev 
936d34fbf2dSYitzhak Mandelbaum Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev,
937d34fbf2dSYitzhak Mandelbaum                                            const Environment &PrevEnv,
938d34fbf2dSYitzhak Mandelbaum                                            Value &Current,
939d34fbf2dSYitzhak Mandelbaum                                            Environment &CurrentEnv) {
940d34fbf2dSYitzhak Mandelbaum   switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) {
941d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Same:
942d34fbf2dSYitzhak Mandelbaum     return &Prev;
943d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Different:
944d34fbf2dSYitzhak Mandelbaum     if (auto *PrevHasVal =
945d34fbf2dSYitzhak Mandelbaum             cast_or_null<BoolValue>(Prev.getProperty("has_value"))) {
946d34fbf2dSYitzhak Mandelbaum       if (isa<TopBoolValue>(PrevHasVal))
947d34fbf2dSYitzhak Mandelbaum         return &Prev;
948d34fbf2dSYitzhak Mandelbaum     }
949d34fbf2dSYitzhak Mandelbaum     if (auto *CurrentHasVal =
950d34fbf2dSYitzhak Mandelbaum             cast_or_null<BoolValue>(Current.getProperty("has_value"))) {
951d34fbf2dSYitzhak Mandelbaum       if (isa<TopBoolValue>(CurrentHasVal))
952d34fbf2dSYitzhak Mandelbaum         return &Current;
953d34fbf2dSYitzhak Mandelbaum     }
954d34fbf2dSYitzhak Mandelbaum     return &createOptionalValue(CurrentEnv, CurrentEnv.makeTopBoolValue());
955d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Unknown:
956d34fbf2dSYitzhak Mandelbaum     return nullptr;
957d34fbf2dSYitzhak Mandelbaum   }
958d34fbf2dSYitzhak Mandelbaum   llvm_unreachable("all cases covered in switch");
959d34fbf2dSYitzhak Mandelbaum }
960d34fbf2dSYitzhak Mandelbaum 
96158fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
96258fe7f96SSam Estep     UncheckedOptionalAccessModelOptions Options)
96358fe7f96SSam Estep     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
96458fe7f96SSam Estep 
96558fe7f96SSam Estep std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose(
9667538b360SWei Yi Tee     ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) {
9677538b360SWei Yi Tee   return DiagnoseMatchSwitch(*Elt, Ctx, Env);
96858fe7f96SSam Estep }
96958fe7f96SSam Estep 
970af98b0afSStanislav Gatev } // namespace dataflow
971af98b0afSStanislav Gatev } // namespace clang
972