xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision 745a957f9dc562477cbe587fb3fa8305713b51b3)
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(
46af98b0afSStanislav Gatev       anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
47af98b0afSStanislav Gatev             hasName("__optional_destruct_base"), hasName("absl::optional"),
48af98b0afSStanislav Gatev             hasName("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 
99390029beSYitzhak Mandelbaum 
100092a530cSStanislav Gatev auto inPlaceClass() {
101092a530cSStanislav Gatev   return recordDecl(
102092a530cSStanislav Gatev       hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
103092a530cSStanislav Gatev }
104092a530cSStanislav Gatev 
105092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
1060086a355SYitzhak Mandelbaum   return cxxConstructExpr(
1070086a355SYitzhak Mandelbaum       hasOptionalType(),
1080086a355SYitzhak Mandelbaum       hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
1090086a355SYitzhak Mandelbaum                                         hasParameter(0, hasNulloptType()))));
110092a530cSStanislav Gatev }
111092a530cSStanislav Gatev 
112092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
113092a530cSStanislav Gatev   return cxxConstructExpr(hasOptionalType(),
114092a530cSStanislav Gatev                           hasArgument(0, hasType(inPlaceClass())));
115092a530cSStanislav Gatev }
116092a530cSStanislav Gatev 
117092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
118092a530cSStanislav Gatev   return cxxConstructExpr(
119092a530cSStanislav Gatev       hasOptionalType(),
120092a530cSStanislav Gatev       unless(hasDeclaration(
121092a530cSStanislav Gatev           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
122092a530cSStanislav Gatev       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
123092a530cSStanislav Gatev }
124092a530cSStanislav Gatev 
125b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
126b000b770SStanislav Gatev   return cxxOperatorCallExpr(
127b000b770SStanislav Gatev       hasOverloadedOperatorName("="),
128b000b770SStanislav Gatev       callee(cxxMethodDecl(ofClass(optionalClass()))),
129b000b770SStanislav Gatev       unless(hasDeclaration(cxxMethodDecl(
130b000b770SStanislav Gatev           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
131b000b770SStanislav Gatev       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
132b000b770SStanislav Gatev }
133b000b770SStanislav Gatev 
134390029beSYitzhak Mandelbaum auto isNulloptConstructor() {
135390029beSYitzhak Mandelbaum   return cxxConstructExpr(hasNulloptType(), argumentCountIs(1),
136390029beSYitzhak Mandelbaum                           hasArgument(0, hasNulloptType()));
137390029beSYitzhak Mandelbaum }
138390029beSYitzhak Mandelbaum 
139b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
140b000b770SStanislav Gatev   return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
141b000b770SStanislav Gatev                              callee(cxxMethodDecl(ofClass(optionalClass()))),
142b000b770SStanislav Gatev                              argumentCountIs(2),
143b000b770SStanislav Gatev                              hasArgument(1, hasNulloptType()));
144b000b770SStanislav Gatev }
145b000b770SStanislav Gatev 
1462ddd57aeSStanislav Gatev auto isStdSwapCall() {
1472ddd57aeSStanislav Gatev   return callExpr(callee(functionDecl(hasName("std::swap"))),
1482ddd57aeSStanislav Gatev                   argumentCountIs(2), hasArgument(0, hasOptionalType()),
1492ddd57aeSStanislav Gatev                   hasArgument(1, hasOptionalType()));
1502ddd57aeSStanislav Gatev }
1512ddd57aeSStanislav Gatev 
1527f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
1537f076004SYitzhak Mandelbaum 
1547f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() {
1557f076004SYitzhak Mandelbaum   // `opt.value_or("").empty()`
1567f076004SYitzhak Mandelbaum   return cxxMemberCallExpr(
1577f076004SYitzhak Mandelbaum       callee(cxxMethodDecl(hasName("empty"))),
1587f076004SYitzhak Mandelbaum       onImplicitObjectArgument(ignoringImplicit(
1597f076004SYitzhak Mandelbaum           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1607f076004SYitzhak Mandelbaum                             callee(cxxMethodDecl(hasName("value_or"),
1617f076004SYitzhak Mandelbaum                                                  ofClass(optionalClass()))),
1627f076004SYitzhak Mandelbaum                             hasArgument(0, stringLiteral(hasSize(0))))
1637f076004SYitzhak Mandelbaum               .bind(ValueOrCallID))));
1647f076004SYitzhak Mandelbaum }
1657f076004SYitzhak Mandelbaum 
1667f076004SYitzhak Mandelbaum auto isValueOrNotEqX() {
1677f076004SYitzhak Mandelbaum   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
1687f076004SYitzhak Mandelbaum     return hasOperands(
1697f076004SYitzhak Mandelbaum         ignoringImplicit(
1707f076004SYitzhak Mandelbaum             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1717f076004SYitzhak Mandelbaum                               callee(cxxMethodDecl(hasName("value_or"),
1727f076004SYitzhak Mandelbaum                                                    ofClass(optionalClass()))),
1737f076004SYitzhak Mandelbaum                               hasArgument(0, Arg))
1747f076004SYitzhak Mandelbaum                 .bind(ValueOrCallID)),
1757f076004SYitzhak Mandelbaum         ignoringImplicit(Arg));
1767f076004SYitzhak Mandelbaum   };
1777f076004SYitzhak Mandelbaum 
1787f076004SYitzhak Mandelbaum   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
1797f076004SYitzhak Mandelbaum   // support this pattern for any expression, but the AST does not have a
1807f076004SYitzhak Mandelbaum   // generic expression comparison facility, so we specialize to common cases
1817f076004SYitzhak Mandelbaum   // seen in practice.  FIXME: define a matcher that compares values across
1827f076004SYitzhak Mandelbaum   // nodes, which would let us generalize this to any `X`.
1837f076004SYitzhak Mandelbaum   return binaryOperation(hasOperatorName("!="),
1847f076004SYitzhak Mandelbaum                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
1857f076004SYitzhak Mandelbaum                                ComparesToSame(stringLiteral(hasSize(0))),
1867f076004SYitzhak Mandelbaum                                ComparesToSame(integerLiteral(equals(0)))));
1877f076004SYitzhak Mandelbaum }
1887f076004SYitzhak Mandelbaum 
18965e710c3SStanislav Gatev auto isCallReturningOptional() {
190cd0d5261SSam Estep   return callExpr(hasType(qualType(anyOf(
191cd0d5261SSam Estep       optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
19265e710c3SStanislav Gatev }
19365e710c3SStanislav Gatev 
194390029beSYitzhak Mandelbaum template <typename L, typename R>
195390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
196390029beSYitzhak Mandelbaum   return cxxOperatorCallExpr(
197390029beSYitzhak Mandelbaum       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
198390029beSYitzhak Mandelbaum       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
199390029beSYitzhak Mandelbaum       hasArgument(1, rhs_arg_matcher));
200390029beSYitzhak Mandelbaum }
201390029beSYitzhak Mandelbaum 
202390029beSYitzhak Mandelbaum // Ensures that `Expr` is mapped to a `BoolValue` and returns it.
203390029beSYitzhak Mandelbaum BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) {
204390029beSYitzhak Mandelbaum   auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None));
205390029beSYitzhak Mandelbaum   if (Value != nullptr)
206390029beSYitzhak Mandelbaum     return *Value;
207390029beSYitzhak Mandelbaum 
208390029beSYitzhak Mandelbaum   auto &Loc = Env.createStorageLocation(Expr);
209390029beSYitzhak Mandelbaum   Value = &Env.makeAtomicBoolValue();
210390029beSYitzhak Mandelbaum   Env.setValue(Loc, *Value);
211390029beSYitzhak Mandelbaum   Env.setStorageLocation(Expr, Loc);
212390029beSYitzhak Mandelbaum   return *Value;
213390029beSYitzhak Mandelbaum }
214390029beSYitzhak Mandelbaum 
2158fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
2168fcdd625SStanislav Gatev /// property of the optional value `OptionalVal`.
2178fcdd625SStanislav Gatev void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
2188fcdd625SStanislav Gatev   OptionalVal.setProperty("has_value", HasValueVal);
2198fcdd625SStanislav Gatev }
2208fcdd625SStanislav Gatev 
2219e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the
2229e0fc676SStanislav Gatev /// symbolic value of its "has_value" property.
2239e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
224*745a957fSMartin Braenne   auto &OptionalVal = Env.create<StructValue>();
225*745a957fSMartin Braenne   setHasValue(OptionalVal, HasValueVal);
226*745a957fSMartin Braenne   return OptionalVal;
2279e0fc676SStanislav Gatev }
2289e0fc676SStanislav Gatev 
229af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
23049ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
231dd38caf3SYitzhak Mandelbaum BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
232dd38caf3SYitzhak Mandelbaum   if (OptionalVal != nullptr) {
233dd38caf3SYitzhak Mandelbaum     auto *HasValueVal =
234dd38caf3SYitzhak Mandelbaum         cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
235dd38caf3SYitzhak Mandelbaum     if (HasValueVal == nullptr) {
236dd38caf3SYitzhak Mandelbaum       HasValueVal = &Env.makeAtomicBoolValue();
237dd38caf3SYitzhak Mandelbaum       OptionalVal->setProperty("has_value", *HasValueVal);
238dd38caf3SYitzhak Mandelbaum     }
239dd38caf3SYitzhak Mandelbaum     return HasValueVal;
240af98b0afSStanislav Gatev   }
241af98b0afSStanislav Gatev   return nullptr;
242af98b0afSStanislav Gatev }
243af98b0afSStanislav Gatev 
244092a530cSStanislav Gatev /// If `Type` is a reference type, returns the type of its pointee. Otherwise,
245092a530cSStanislav Gatev /// returns `Type` itself.
246092a530cSStanislav Gatev QualType stripReference(QualType Type) {
247092a530cSStanislav Gatev   return Type->isReferenceType() ? Type->getPointeeType() : Type;
248092a530cSStanislav Gatev }
249092a530cSStanislav Gatev 
250092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type.
251c0725865SYitzhak Mandelbaum bool isOptionalType(QualType Type) {
252092a530cSStanislav Gatev   if (!Type->isRecordType())
253092a530cSStanislav Gatev     return false;
254092a530cSStanislav Gatev   // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call.
255092a530cSStanislav Gatev   auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString();
256092a530cSStanislav Gatev   return TypeName == "std::optional" || TypeName == "absl::optional" ||
257092a530cSStanislav Gatev          TypeName == "base::Optional";
258092a530cSStanislav Gatev }
259092a530cSStanislav Gatev 
260092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
261092a530cSStanislav Gatev ///
262092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
263092a530cSStanislav Gatev /// function will be 2.
264092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
265c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
266092a530cSStanislav Gatev     return 0;
267092a530cSStanislav Gatev   return 1 + countOptionalWrappers(
268092a530cSStanislav Gatev                  ASTCtx,
269092a530cSStanislav Gatev                  cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
270092a530cSStanislav Gatev                      ->getTemplateArgs()
271092a530cSStanislav Gatev                      .get(0)
272092a530cSStanislav Gatev                      .getAsType()
273092a530cSStanislav Gatev                      .getDesugaredType(ASTCtx));
274092a530cSStanislav Gatev }
275092a530cSStanislav Gatev 
276dd38caf3SYitzhak Mandelbaum /// Tries to initialize the `optional`'s value (that is, contents), and return
277dd38caf3SYitzhak Mandelbaum /// its location. Returns nullptr if the value can't be represented.
278dd38caf3SYitzhak Mandelbaum StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
279dd38caf3SYitzhak Mandelbaum                                                     Value &OptionalVal,
280dd38caf3SYitzhak Mandelbaum                                                     Environment &Env) {
281dd38caf3SYitzhak Mandelbaum   // The "value" property represents a synthetic field. As such, it needs
282dd38caf3SYitzhak Mandelbaum   // `StorageLocation`, like normal fields (and other variables). So, we model
283dd38caf3SYitzhak Mandelbaum   // it with a `ReferenceValue`, since that includes a storage location.  Once
284dd38caf3SYitzhak Mandelbaum   // the property is set, it will be shared by all environments that access the
285dd38caf3SYitzhak Mandelbaum   // `Value` representing the optional (here, `OptionalVal`).
286dd38caf3SYitzhak Mandelbaum   if (auto *ValueProp = OptionalVal.getProperty("value")) {
287dd38caf3SYitzhak Mandelbaum     auto *ValueRef = clang::cast<ReferenceValue>(ValueProp);
28897d69cdaSWei Yi Tee     auto &ValueLoc = ValueRef->getReferentLoc();
289dd38caf3SYitzhak Mandelbaum     if (Env.getValue(ValueLoc) == nullptr) {
290dd38caf3SYitzhak Mandelbaum       // The property was previously set, but the value has been lost. This can
291dd38caf3SYitzhak Mandelbaum       // happen, for example, because of an environment merge (where the two
292dd38caf3SYitzhak Mandelbaum       // environments mapped the property to different values, which resulted in
293dd38caf3SYitzhak Mandelbaum       // them both being discarded), or when two blocks in the CFG, with neither
294dd38caf3SYitzhak Mandelbaum       // a dominator of the other, visit the same optional value, or even when a
295dd38caf3SYitzhak Mandelbaum       // block is revisited during testing to collect per-statement state.
296dd38caf3SYitzhak Mandelbaum       // FIXME: This situation means that the optional contents are not shared
297dd38caf3SYitzhak Mandelbaum       // between branches and the like. Practically, this lack of sharing
298dd38caf3SYitzhak Mandelbaum       // reduces the precision of the model when the contents are relevant to
299dd38caf3SYitzhak Mandelbaum       // the check, like another optional or a boolean that influences control
300dd38caf3SYitzhak Mandelbaum       // flow.
301dd38caf3SYitzhak Mandelbaum       auto *ValueVal = Env.createValue(ValueLoc.getType());
302dd38caf3SYitzhak Mandelbaum       if (ValueVal == nullptr)
303dd38caf3SYitzhak Mandelbaum         return nullptr;
304dd38caf3SYitzhak Mandelbaum       Env.setValue(ValueLoc, *ValueVal);
305dd38caf3SYitzhak Mandelbaum     }
306dd38caf3SYitzhak Mandelbaum     return &ValueLoc;
307dd38caf3SYitzhak Mandelbaum   }
308dd38caf3SYitzhak Mandelbaum 
309dd38caf3SYitzhak Mandelbaum   auto Ty = stripReference(Q);
310dd38caf3SYitzhak Mandelbaum   auto *ValueVal = Env.createValue(Ty);
311dd38caf3SYitzhak Mandelbaum   if (ValueVal == nullptr)
312dd38caf3SYitzhak Mandelbaum     return nullptr;
313dd38caf3SYitzhak Mandelbaum   auto &ValueLoc = Env.createStorageLocation(Ty);
314dd38caf3SYitzhak Mandelbaum   Env.setValue(ValueLoc, *ValueVal);
315*745a957fSMartin Braenne   auto &ValueRef = Env.create<ReferenceValue>(ValueLoc);
316*745a957fSMartin Braenne   OptionalVal.setProperty("value", ValueRef);
317dd38caf3SYitzhak Mandelbaum   return &ValueLoc;
318dd38caf3SYitzhak Mandelbaum }
319dd38caf3SYitzhak Mandelbaum 
320092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr,
321092a530cSStanislav Gatev                                  const MatchFinder::MatchResult &,
322af98b0afSStanislav Gatev                                  LatticeTransferState &State) {
32349ed5bf5SWei Yi Tee   if (auto *OptionalVal =
32449ed5bf5SWei Yi Tee           State.Env.getValue(*OptionalExpr, SkipPast::Reference)) {
325af98b0afSStanislav Gatev     if (OptionalVal->getProperty("has_value") == nullptr) {
3268fcdd625SStanislav Gatev       setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
327af98b0afSStanislav Gatev     }
328af98b0afSStanislav Gatev   }
329af98b0afSStanislav Gatev }
330af98b0afSStanislav Gatev 
3318fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
3328fcdd625SStanislav Gatev /// empty in `Env.
3338fcdd625SStanislav Gatev bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
3348fcdd625SStanislav Gatev   auto *HasValueVal =
3358fcdd625SStanislav Gatev       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
3368fcdd625SStanislav Gatev   return HasValueVal != nullptr &&
3378fcdd625SStanislav Gatev          Env.flowConditionImplies(Env.makeNot(*HasValueVal));
3388fcdd625SStanislav Gatev }
3398fcdd625SStanislav Gatev 
3408fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
3418fcdd625SStanislav Gatev /// non-empty in `Env.
3428fcdd625SStanislav Gatev bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
3438fcdd625SStanislav Gatev   auto *HasValueVal =
3448fcdd625SStanislav Gatev       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
3458fcdd625SStanislav Gatev   return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal);
3468fcdd625SStanislav Gatev }
3478fcdd625SStanislav Gatev 
348092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
349af98b0afSStanislav Gatev                         LatticeTransferState &State) {
35049ed5bf5SWei Yi Tee   if (auto *OptionalVal =
35149ed5bf5SWei Yi Tee           State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
352dd38caf3SYitzhak Mandelbaum     if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
353dd38caf3SYitzhak Mandelbaum       if (auto *Loc = maybeInitializeOptionalValueMember(
354dd38caf3SYitzhak Mandelbaum               UnwrapExpr->getType(), *OptionalVal, State.Env))
355dd38caf3SYitzhak Mandelbaum         State.Env.setStorageLocation(*UnwrapExpr, *Loc);
356af98b0afSStanislav Gatev   }
357dd38caf3SYitzhak Mandelbaum }
358af98b0afSStanislav Gatev 
359092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
360092a530cSStanislav Gatev                               const MatchFinder::MatchResult &,
361092a530cSStanislav Gatev                               LatticeTransferState &State) {
3629e0fc676SStanislav Gatev   auto &Loc = State.Env.createStorageLocation(*E);
3639e0fc676SStanislav Gatev   State.Env.setStorageLocation(*E, Loc);
3649e0fc676SStanislav Gatev   State.Env.setValue(
3659e0fc676SStanislav Gatev       Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
3669e0fc676SStanislav Gatev }
3679e0fc676SStanislav Gatev 
368092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
369092a530cSStanislav Gatev                                   const MatchFinder::MatchResult &,
370af98b0afSStanislav Gatev                                   LatticeTransferState &State) {
371dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal = getHasValue(
372dd38caf3SYitzhak Mandelbaum           State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
373af98b0afSStanislav Gatev                                         SkipPast::ReferenceThenPointer))) {
374af98b0afSStanislav Gatev     auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
375af98b0afSStanislav Gatev     State.Env.setValue(CallExprLoc, *HasValueVal);
376af98b0afSStanislav Gatev     State.Env.setStorageLocation(*CallExpr, CallExprLoc);
377af98b0afSStanislav Gatev   }
378af98b0afSStanislav Gatev }
379af98b0afSStanislav Gatev 
3807f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in
3817f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property.
3827f076004SYitzhak Mandelbaum void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
3837f076004SYitzhak Mandelbaum                          const MatchFinder::MatchResult &Result,
3847f076004SYitzhak Mandelbaum                          LatticeTransferState &State,
3857f076004SYitzhak Mandelbaum                          BoolValue &(*ModelPred)(Environment &Env,
3867f076004SYitzhak Mandelbaum                                                  BoolValue &ExprVal,
3877f076004SYitzhak Mandelbaum                                                  BoolValue &HasValueVal)) {
3887f076004SYitzhak Mandelbaum   auto &Env = State.Env;
3897f076004SYitzhak Mandelbaum 
3907f076004SYitzhak Mandelbaum   const auto *ObjectArgumentExpr =
3917f076004SYitzhak Mandelbaum       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
3927f076004SYitzhak Mandelbaum           ->getImplicitObjectArgument();
3937f076004SYitzhak Mandelbaum 
394dd38caf3SYitzhak Mandelbaum   auto *HasValueVal = getHasValue(
395dd38caf3SYitzhak Mandelbaum       State.Env,
396dd38caf3SYitzhak Mandelbaum       State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
397dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr)
3987f076004SYitzhak Mandelbaum     return;
3997f076004SYitzhak Mandelbaum 
400390029beSYitzhak Mandelbaum   Env.addToFlowCondition(
401390029beSYitzhak Mandelbaum       ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal));
4027f076004SYitzhak Mandelbaum }
4037f076004SYitzhak Mandelbaum 
4047f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
4057f076004SYitzhak Mandelbaum                                     const MatchFinder::MatchResult &Result,
4067f076004SYitzhak Mandelbaum                                     LatticeTransferState &State) {
4077f076004SYitzhak Mandelbaum   return transferValueOrImpl(ComparisonExpr, Result, State,
4087f076004SYitzhak Mandelbaum                              [](Environment &Env, BoolValue &ExprVal,
4097f076004SYitzhak Mandelbaum                                 BoolValue &HasValueVal) -> BoolValue & {
4107f076004SYitzhak Mandelbaum                                // If the result is *not* empty, then we know the
4117f076004SYitzhak Mandelbaum                                // optional must have been holding a value. If
4127f076004SYitzhak Mandelbaum                                // `ExprVal` is true, though, we don't learn
4137f076004SYitzhak Mandelbaum                                // anything definite about `has_value`, so we
4147f076004SYitzhak Mandelbaum                                // don't add any corresponding implications to
4157f076004SYitzhak Mandelbaum                                // the flow condition.
4167f076004SYitzhak Mandelbaum                                return Env.makeImplication(Env.makeNot(ExprVal),
4177f076004SYitzhak Mandelbaum                                                           HasValueVal);
4187f076004SYitzhak Mandelbaum                              });
4197f076004SYitzhak Mandelbaum }
4207f076004SYitzhak Mandelbaum 
4217f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr,
4227f076004SYitzhak Mandelbaum                            const MatchFinder::MatchResult &Result,
4237f076004SYitzhak Mandelbaum                            LatticeTransferState &State) {
4247f076004SYitzhak Mandelbaum   transferValueOrImpl(ComparisonExpr, Result, State,
4257f076004SYitzhak Mandelbaum                       [](Environment &Env, BoolValue &ExprVal,
4267f076004SYitzhak Mandelbaum                          BoolValue &HasValueVal) -> BoolValue & {
4277f076004SYitzhak Mandelbaum                         // We know that if `(opt.value_or(X) != X)` then
4287f076004SYitzhak Mandelbaum                         // `opt.hasValue()`, even without knowing further
4297f076004SYitzhak Mandelbaum                         // details about the contents of `opt`.
4307f076004SYitzhak Mandelbaum                         return Env.makeImplication(ExprVal, HasValueVal);
4317f076004SYitzhak Mandelbaum                       });
4327f076004SYitzhak Mandelbaum }
4337f076004SYitzhak Mandelbaum 
43465e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E,
43565e710c3SStanislav Gatev                                    const MatchFinder::MatchResult &Result,
43665e710c3SStanislav Gatev                                    LatticeTransferState &State) {
43765e710c3SStanislav Gatev   if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
43865e710c3SStanislav Gatev     return;
43965e710c3SStanislav Gatev 
44065e710c3SStanislav Gatev   auto &Loc = State.Env.createStorageLocation(*E);
44165e710c3SStanislav Gatev   State.Env.setStorageLocation(*E, Loc);
44265e710c3SStanislav Gatev   State.Env.setValue(
44365e710c3SStanislav Gatev       Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()));
44465e710c3SStanislav Gatev }
44565e710c3SStanislav Gatev 
4460e8d4a6dSYitzhak Mandelbaum void assignOptionalValue(const Expr &E, Environment &Env,
447092a530cSStanislav Gatev                          BoolValue &HasValueVal) {
448092a530cSStanislav Gatev   if (auto *OptionalLoc =
4490e8d4a6dSYitzhak Mandelbaum           Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
4500e8d4a6dSYitzhak Mandelbaum     Env.setValue(*OptionalLoc, createOptionalValue(Env, HasValueVal));
4519e0fc676SStanislav Gatev   }
4529e0fc676SStanislav Gatev }
4539e0fc676SStanislav Gatev 
454b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
455b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
456b000b770SStanislav Gatev /// where `T` is constructible from `U`.
457390029beSYitzhak Mandelbaum BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
458b000b770SStanislav Gatev                                      const MatchFinder::MatchResult &MatchRes,
459b000b770SStanislav Gatev                                      LatticeTransferState &State) {
4600086a355SYitzhak Mandelbaum   assert(F.getTemplateSpecializationArgs() != nullptr);
461b000b770SStanislav Gatev   assert(F.getTemplateSpecializationArgs()->size() > 0);
462b000b770SStanislav Gatev 
463b000b770SStanislav Gatev   const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
464b000b770SStanislav Gatev       *MatchRes.Context,
465b000b770SStanislav Gatev       stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType()));
466b000b770SStanislav Gatev   const int ArgTypeOptionalWrappersCount =
467b000b770SStanislav Gatev       countOptionalWrappers(*MatchRes.Context, stripReference(E.getType()));
468b000b770SStanislav Gatev 
469b000b770SStanislav Gatev   // Check if this is a constructor/assignment call for `optional<T>` with
470b000b770SStanislav Gatev   // argument of type `U` such that `T` is constructible from `U`.
471b000b770SStanislav Gatev   if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
472b000b770SStanislav Gatev     return State.Env.getBoolLiteralValue(true);
473b000b770SStanislav Gatev 
474b000b770SStanislav Gatev   // This is a constructor/assignment call for `optional<T>` with argument of
475b000b770SStanislav Gatev   // type `optional<U>` such that `T` is constructible from `U`.
476dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal =
477dd38caf3SYitzhak Mandelbaum           getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference)))
478dd38caf3SYitzhak Mandelbaum     return *HasValueVal;
479b000b770SStanislav Gatev   return State.Env.makeAtomicBoolValue();
480b000b770SStanislav Gatev }
481b000b770SStanislav Gatev 
482092a530cSStanislav Gatev void transferValueOrConversionConstructor(
483092a530cSStanislav Gatev     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
4849e0fc676SStanislav Gatev     LatticeTransferState &State) {
485092a530cSStanislav Gatev   assert(E->getNumArgs() > 0);
486092a530cSStanislav Gatev 
4870e8d4a6dSYitzhak Mandelbaum   assignOptionalValue(*E, State.Env,
488390029beSYitzhak Mandelbaum                       valueOrConversionHasValue(*E->getConstructor(),
489b000b770SStanislav Gatev                                                 *E->getArg(0), MatchRes,
490b000b770SStanislav Gatev                                                 State));
491b000b770SStanislav Gatev }
492092a530cSStanislav Gatev 
493b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
494b000b770SStanislav Gatev                         LatticeTransferState &State) {
495b000b770SStanislav Gatev   assert(E->getNumArgs() > 0);
496b000b770SStanislav Gatev 
497b000b770SStanislav Gatev   auto *OptionalLoc =
498b000b770SStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
499a9ad689eSSam Estep   if (OptionalLoc == nullptr)
500a9ad689eSSam Estep     return;
501b000b770SStanislav Gatev 
502b000b770SStanislav Gatev   State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
503b000b770SStanislav Gatev 
504b000b770SStanislav Gatev   // Assign a storage location for the whole expression.
505b000b770SStanislav Gatev   State.Env.setStorageLocation(*E, *OptionalLoc);
506b000b770SStanislav Gatev }
507b000b770SStanislav Gatev 
508b000b770SStanislav Gatev void transferValueOrConversionAssignment(
509b000b770SStanislav Gatev     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
510b000b770SStanislav Gatev     LatticeTransferState &State) {
511b000b770SStanislav Gatev   assert(E->getNumArgs() > 1);
512b000b770SStanislav Gatev   transferAssignment(E,
513390029beSYitzhak Mandelbaum                      valueOrConversionHasValue(*E->getDirectCallee(),
51406decd0bSKazu Hirata                                                *E->getArg(1), MatchRes, State),
515b000b770SStanislav Gatev                      State);
516b000b770SStanislav Gatev }
517b000b770SStanislav Gatev 
518b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
519b000b770SStanislav Gatev                                const MatchFinder::MatchResult &,
520b000b770SStanislav Gatev                                LatticeTransferState &State) {
521b000b770SStanislav Gatev   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
5229e0fc676SStanislav Gatev }
5239e0fc676SStanislav Gatev 
524d4fb829bSYitzhak Mandelbaum void transferSwap(const Expr &E1, SkipPast E1Skip, const Expr &E2,
525d4fb829bSYitzhak Mandelbaum                   Environment &Env) {
526d4fb829bSYitzhak Mandelbaum   // We account for cases where one or both of the optionals are not modeled,
527d4fb829bSYitzhak Mandelbaum   // either lacking associated storage locations, or lacking values associated
528d4fb829bSYitzhak Mandelbaum   // to such storage locations.
529d4fb829bSYitzhak Mandelbaum   auto *Loc1 = Env.getStorageLocation(E1, E1Skip);
530d4fb829bSYitzhak Mandelbaum   auto *Loc2 = Env.getStorageLocation(E2, SkipPast::Reference);
5312ddd57aeSStanislav Gatev 
532d4fb829bSYitzhak Mandelbaum   if (Loc1 == nullptr) {
533d4fb829bSYitzhak Mandelbaum     if (Loc2 != nullptr)
534d4fb829bSYitzhak Mandelbaum       Env.setValue(*Loc2, createOptionalValue(Env, Env.makeAtomicBoolValue()));
535d4fb829bSYitzhak Mandelbaum     return;
536d4fb829bSYitzhak Mandelbaum   }
537d4fb829bSYitzhak Mandelbaum   if (Loc2 == nullptr) {
538d4fb829bSYitzhak Mandelbaum     Env.setValue(*Loc1, createOptionalValue(Env, Env.makeAtomicBoolValue()));
539d4fb829bSYitzhak Mandelbaum     return;
540d4fb829bSYitzhak Mandelbaum   }
5412ddd57aeSStanislav Gatev 
542d4fb829bSYitzhak Mandelbaum   // Both expressions have locations, though they may not have corresponding
543d4fb829bSYitzhak Mandelbaum   // values. In that case, we create a fresh value at this point. Note that if
544d4fb829bSYitzhak Mandelbaum   // two branches both do this, they will not share the value, but it at least
545d4fb829bSYitzhak Mandelbaum   // allows for local reasoning about the value. To avoid the above, we would
546d4fb829bSYitzhak Mandelbaum   // need *lazy* value allocation.
547d4fb829bSYitzhak Mandelbaum   // FIXME: allocate values lazily, instead of just creating a fresh value.
548d4fb829bSYitzhak Mandelbaum   auto *Val1 = Env.getValue(*Loc1);
549d4fb829bSYitzhak Mandelbaum   if (Val1 == nullptr)
550d4fb829bSYitzhak Mandelbaum     Val1 = &createOptionalValue(Env, Env.makeAtomicBoolValue());
551d4fb829bSYitzhak Mandelbaum 
552d4fb829bSYitzhak Mandelbaum   auto *Val2 = Env.getValue(*Loc2);
553d4fb829bSYitzhak Mandelbaum   if (Val2 == nullptr)
554d4fb829bSYitzhak Mandelbaum     Val2 = &createOptionalValue(Env, Env.makeAtomicBoolValue());
555d4fb829bSYitzhak Mandelbaum 
556d4fb829bSYitzhak Mandelbaum   Env.setValue(*Loc1, *Val2);
557d4fb829bSYitzhak Mandelbaum   Env.setValue(*Loc2, *Val1);
5582ddd57aeSStanislav Gatev }
5592ddd57aeSStanislav Gatev 
5602ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
5612ddd57aeSStanislav Gatev                       const MatchFinder::MatchResult &,
5622ddd57aeSStanislav Gatev                       LatticeTransferState &State) {
5632ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 1);
564d4fb829bSYitzhak Mandelbaum   transferSwap(*E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer,
565d4fb829bSYitzhak Mandelbaum                *E->getArg(0), State.Env);
5662ddd57aeSStanislav Gatev }
5672ddd57aeSStanislav Gatev 
5682ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
5692ddd57aeSStanislav Gatev                          LatticeTransferState &State) {
5702ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 2);
571d4fb829bSYitzhak Mandelbaum   transferSwap(*E->getArg(0), SkipPast::Reference, *E->getArg(1), State.Env);
5722ddd57aeSStanislav Gatev }
5732ddd57aeSStanislav Gatev 
574390029beSYitzhak Mandelbaum BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS,
575390029beSYitzhak Mandelbaum                             BoolValue &RHS) {
576390029beSYitzhak Mandelbaum   // Logically, an optional<T> object is composed of two values - a `has_value`
577390029beSYitzhak Mandelbaum   // bit and a value of type T. Equality of optional objects compares both
578390029beSYitzhak Mandelbaum   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
579390029beSYitzhak Mandelbaum   // when two optional objects are engaged, the equality of their respective
580390029beSYitzhak Mandelbaum   // values of type T matters. Since we only track the `has_value` bits, we
581390029beSYitzhak Mandelbaum   // can't make any conclusions about equality when we know that two optional
582390029beSYitzhak Mandelbaum   // objects are engaged.
583390029beSYitzhak Mandelbaum   //
584390029beSYitzhak Mandelbaum   // We express this as two facts about the equality:
585390029beSYitzhak Mandelbaum   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
586390029beSYitzhak Mandelbaum   //    If they are equal, then either both are set or both are unset.
587390029beSYitzhak Mandelbaum   // b) (!LHS & !RHS) => EqVal
588390029beSYitzhak Mandelbaum   //    If neither is set, then they are equal.
589390029beSYitzhak Mandelbaum   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
590390029beSYitzhak Mandelbaum   return Env.makeAnd(
591390029beSYitzhak Mandelbaum       Env.makeImplication(
592390029beSYitzhak Mandelbaum           EqVal, Env.makeOr(Env.makeAnd(LHS, RHS),
593390029beSYitzhak Mandelbaum                             Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))),
594390029beSYitzhak Mandelbaum       Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS)));
595390029beSYitzhak Mandelbaum }
596390029beSYitzhak Mandelbaum 
597390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
598390029beSYitzhak Mandelbaum                                     const MatchFinder::MatchResult &,
599390029beSYitzhak Mandelbaum                                     LatticeTransferState &State) {
600390029beSYitzhak Mandelbaum   Environment &Env = State.Env;
601390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
602390029beSYitzhak Mandelbaum   if (auto *LHasVal = getHasValue(
603390029beSYitzhak Mandelbaum           Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference)))
604390029beSYitzhak Mandelbaum     if (auto *RHasVal = getHasValue(
605390029beSYitzhak Mandelbaum             Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) {
606390029beSYitzhak Mandelbaum       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
607390029beSYitzhak Mandelbaum         CmpValue = &State.Env.makeNot(*CmpValue);
608390029beSYitzhak Mandelbaum       Env.addToFlowCondition(
609390029beSYitzhak Mandelbaum           evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal));
610390029beSYitzhak Mandelbaum     }
611390029beSYitzhak Mandelbaum }
612390029beSYitzhak Mandelbaum 
613390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
614390029beSYitzhak Mandelbaum                                  const clang::Expr *E, Environment &Env) {
615390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
616390029beSYitzhak Mandelbaum   if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) {
617390029beSYitzhak Mandelbaum     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
618390029beSYitzhak Mandelbaum       CmpValue = &Env.makeNot(*CmpValue);
619390029beSYitzhak Mandelbaum     Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal,
620390029beSYitzhak Mandelbaum                                             Env.getBoolLiteralValue(true)));
621390029beSYitzhak Mandelbaum   }
622390029beSYitzhak Mandelbaum }
623390029beSYitzhak Mandelbaum 
6246ad0788cSKazu Hirata std::optional<StatementMatcher>
625a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
6265d22d1f5SYitzhak Mandelbaum   if (Options.IgnoreSmartPointerDereference) {
6275d22d1f5SYitzhak Mandelbaum     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
6285d22d1f5SYitzhak Mandelbaum         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
6295d22d1f5SYitzhak Mandelbaum         unless(hasArgument(0, expr(hasOptionalType()))))));
6305d22d1f5SYitzhak Mandelbaum     return expr(
6315d22d1f5SYitzhak Mandelbaum         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
6325d22d1f5SYitzhak Mandelbaum   }
63334e0d057SKazu Hirata   return std::nullopt;
634a184a0d8SYitzhak Mandelbaum }
635a184a0d8SYitzhak Mandelbaum 
63658fe7f96SSam Estep StatementMatcher
6376ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
63858fe7f96SSam Estep   return isOptionalMemberCallWithName("value", IgnorableOptional);
63958fe7f96SSam Estep }
64058fe7f96SSam Estep 
64158fe7f96SSam Estep StatementMatcher
6426ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
64358fe7f96SSam Estep   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
64458fe7f96SSam Estep                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
64558fe7f96SSam Estep }
64658fe7f96SSam Estep 
6475d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() {
648b000b770SStanislav Gatev   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
649b000b770SStanislav Gatev   // lot of duplicated work (e.g. string comparisons), consider providing APIs
650b000b770SStanislav Gatev   // that avoid it through memoization.
6517538b360SWei Yi Tee   return CFGMatchSwitchBuilder<LatticeTransferState>()
652af98b0afSStanislav Gatev       // Attach a symbolic "has_value" state to optional values that we see for
653af98b0afSStanislav Gatev       // the first time.
6547538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(
6556adfc64eSYitzhak Mandelbaum           expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
656af98b0afSStanislav Gatev           initializeOptionalReference)
657af98b0afSStanislav Gatev 
6589e0fc676SStanislav Gatev       // make_optional
6597538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
660092a530cSStanislav Gatev 
6610e8d4a6dSYitzhak Mandelbaum       // optional::optional (in place)
6627538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
663092a530cSStanislav Gatev           isOptionalInPlaceConstructor(),
664092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
665092a530cSStanislav Gatev              LatticeTransferState &State) {
6660e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E, State.Env,
6670e8d4a6dSYitzhak Mandelbaum                                 State.Env.getBoolLiteralValue(true));
668092a530cSStanislav Gatev           })
6690e8d4a6dSYitzhak Mandelbaum       // nullopt_t::nullopt_t
6707538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
671390029beSYitzhak Mandelbaum           isNulloptConstructor(),
672092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
673092a530cSStanislav Gatev              LatticeTransferState &State) {
6740e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E, State.Env,
675092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
676092a530cSStanislav Gatev           })
6770e8d4a6dSYitzhak Mandelbaum       // optional::optional(nullopt_t)
678390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXConstructExpr>(
679390029beSYitzhak Mandelbaum           isOptionalNulloptConstructor(),
680390029beSYitzhak Mandelbaum           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
681390029beSYitzhak Mandelbaum              LatticeTransferState &State) {
6820e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E, State.Env,
6830e8d4a6dSYitzhak Mandelbaum                                 State.Env.getBoolLiteralValue(false));
684390029beSYitzhak Mandelbaum           })
6850e8d4a6dSYitzhak Mandelbaum       // optional::optional (value/conversion)
6867538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
687092a530cSStanislav Gatev                                        transferValueOrConversionConstructor)
6889e0fc676SStanislav Gatev 
6890e8d4a6dSYitzhak Mandelbaum 
690b000b770SStanislav Gatev       // optional::operator=
6917538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(
6927538b360SWei Yi Tee           isOptionalValueOrConversionAssignment(),
693b000b770SStanislav Gatev           transferValueOrConversionAssignment)
6947538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
695b000b770SStanislav Gatev                                           transferNulloptAssignment)
696b000b770SStanislav Gatev 
697af98b0afSStanislav Gatev       // optional::value
6987538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
6995d22d1f5SYitzhak Mandelbaum           valueCall(std::nullopt),
700092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
701092a530cSStanislav Gatev              LatticeTransferState &State) {
702af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
703af98b0afSStanislav Gatev           })
704af98b0afSStanislav Gatev 
705af98b0afSStanislav Gatev       // optional::operator*, optional::operator->
7065d22d1f5SYitzhak Mandelbaum       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(std::nullopt),
7077538b360SWei Yi Tee                                [](const CallExpr *E,
7087538b360SWei Yi Tee                                   const MatchFinder::MatchResult &,
709092a530cSStanislav Gatev                                   LatticeTransferState &State) {
710af98b0afSStanislav Gatev                                  transferUnwrapCall(E, E->getArg(0), State);
711af98b0afSStanislav Gatev                                })
712af98b0afSStanislav Gatev 
713af98b0afSStanislav Gatev       // optional::has_value
7147538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
7157538b360SWei Yi Tee           isOptionalMemberCallWithName("has_value"),
716af98b0afSStanislav Gatev           transferOptionalHasValueCall)
717af98b0afSStanislav Gatev 
7189e0fc676SStanislav Gatev       // optional::operator bool
7197538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
7207538b360SWei Yi Tee           isOptionalMemberCallWithName("operator bool"),
7219e0fc676SStanislav Gatev           transferOptionalHasValueCall)
7229e0fc676SStanislav Gatev 
7239e0fc676SStanislav Gatev       // optional::emplace
7247538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
725092a530cSStanislav Gatev           isOptionalMemberCallWithName("emplace"),
726092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
727092a530cSStanislav Gatev              LatticeTransferState &State) {
7280e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
729092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(true));
730092a530cSStanislav Gatev           })
7319e0fc676SStanislav Gatev 
7329e0fc676SStanislav Gatev       // optional::reset
7337538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
734092a530cSStanislav Gatev           isOptionalMemberCallWithName("reset"),
735092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
736092a530cSStanislav Gatev              LatticeTransferState &State) {
7370e8d4a6dSYitzhak Mandelbaum             assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
738092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
739092a530cSStanislav Gatev           })
7409e0fc676SStanislav Gatev 
7412ddd57aeSStanislav Gatev       // optional::swap
7427538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
7432ddd57aeSStanislav Gatev                                         transferSwapCall)
7442ddd57aeSStanislav Gatev 
7452ddd57aeSStanislav Gatev       // std::swap
7467538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
7472ddd57aeSStanislav Gatev 
7487f076004SYitzhak Mandelbaum       // opt.value_or("").empty()
7497538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
7507538b360SWei Yi Tee                            transferValueOrStringEmptyCall)
7517f076004SYitzhak Mandelbaum 
7527f076004SYitzhak Mandelbaum       // opt.value_or(X) != X
7537538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
7547f076004SYitzhak Mandelbaum 
755390029beSYitzhak Mandelbaum       // Comparisons (==, !=):
756390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
757390029beSYitzhak Mandelbaum           isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()),
758390029beSYitzhak Mandelbaum           transferOptionalAndOptionalCmp)
759390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
760390029beSYitzhak Mandelbaum           isComparisonOperatorCall(hasOptionalType(),
761390029beSYitzhak Mandelbaum                                    unless(hasAnyOptionalType())),
762390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
763390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
764390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
765390029beSYitzhak Mandelbaum           })
766390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
767390029beSYitzhak Mandelbaum           isComparisonOperatorCall(unless(hasAnyOptionalType()),
768390029beSYitzhak Mandelbaum                                    hasOptionalType()),
769390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
770390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
771390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
772390029beSYitzhak Mandelbaum           })
773390029beSYitzhak Mandelbaum 
77465e710c3SStanislav Gatev       // returns optional
7757538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
77665e710c3SStanislav Gatev                                transferCallReturningOptional)
77765e710c3SStanislav Gatev 
778af98b0afSStanislav Gatev       .Build();
779af98b0afSStanislav Gatev }
780af98b0afSStanislav Gatev 
78158fe7f96SSam Estep std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr,
78258fe7f96SSam Estep                                                const Expr *ObjectExpr,
78358fe7f96SSam Estep                                                const Environment &Env) {
78458fe7f96SSam Estep   if (auto *OptionalVal =
78558fe7f96SSam Estep           Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
78658fe7f96SSam Estep     auto *Prop = OptionalVal->getProperty("has_value");
78758fe7f96SSam Estep     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
78858fe7f96SSam Estep       if (Env.flowConditionImplies(*HasValueVal))
78958fe7f96SSam Estep         return {};
79058fe7f96SSam Estep     }
79158fe7f96SSam Estep   }
79258fe7f96SSam Estep 
79358fe7f96SSam Estep   // Record that this unwrap is *not* provably safe.
79458fe7f96SSam Estep   // FIXME: include either the name of the optional (if applicable) or a source
79558fe7f96SSam Estep   // range of the access for easier interpretation of the result.
79658fe7f96SSam Estep   return {ObjectExpr->getBeginLoc()};
79758fe7f96SSam Estep }
79858fe7f96SSam Estep 
79958fe7f96SSam Estep auto buildDiagnoseMatchSwitch(
80058fe7f96SSam Estep     const UncheckedOptionalAccessModelOptions &Options) {
80158fe7f96SSam Estep   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
80258fe7f96SSam Estep   // lot of duplicated work (e.g. string comparisons), consider providing APIs
80358fe7f96SSam Estep   // that avoid it through memoization.
80458fe7f96SSam Estep   auto IgnorableOptional = ignorableOptional(Options);
8057538b360SWei Yi Tee   return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
80658fe7f96SSam Estep       // optional::value
8077538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
80858fe7f96SSam Estep           valueCall(IgnorableOptional),
80958fe7f96SSam Estep           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
81058fe7f96SSam Estep              const Environment &Env) {
81158fe7f96SSam Estep             return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env);
81258fe7f96SSam Estep           })
81358fe7f96SSam Estep 
81458fe7f96SSam Estep       // optional::operator*, optional::operator->
8157538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(
81658fe7f96SSam Estep           valueOperatorCall(IgnorableOptional),
81758fe7f96SSam Estep           [](const CallExpr *E, const MatchFinder::MatchResult &,
81858fe7f96SSam Estep              const Environment &Env) {
81958fe7f96SSam Estep             return diagnoseUnwrapCall(E, E->getArg(0), Env);
82058fe7f96SSam Estep           })
82158fe7f96SSam Estep       .Build();
82258fe7f96SSam Estep }
82358fe7f96SSam Estep 
824af98b0afSStanislav Gatev } // namespace
825af98b0afSStanislav Gatev 
8267e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher
8277e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() {
8287e63a0d4SYitzhak Mandelbaum   return optionalClass();
8297e63a0d4SYitzhak Mandelbaum }
8307e63a0d4SYitzhak Mandelbaum 
8315d22d1f5SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
832cf1f978dSSam Estep     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
8335d22d1f5SYitzhak Mandelbaum       TransferMatchSwitch(buildTransferMatchSwitch()) {}
834af98b0afSStanislav Gatev 
8356b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
8367538b360SWei Yi Tee                                             NoopLattice &L, Environment &Env) {
837af98b0afSStanislav Gatev   LatticeTransferState State(L, Env);
8386b991ba4SYitzhak Mandelbaum   TransferMatchSwitch(Elt, getASTContext(), State);
839af98b0afSStanislav Gatev }
840af98b0afSStanislav Gatev 
841c0725865SYitzhak Mandelbaum ComparisonResult UncheckedOptionalAccessModel::compare(
842c0725865SYitzhak Mandelbaum     QualType Type, const Value &Val1, const Environment &Env1,
843c0725865SYitzhak Mandelbaum     const Value &Val2, const Environment &Env2) {
844c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
845c0725865SYitzhak Mandelbaum     return ComparisonResult::Unknown;
846d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
847d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
848d34fbf2dSYitzhak Mandelbaum   if (MustNonEmpty1 && MustNonEmpty2) return ComparisonResult::Same;
849d34fbf2dSYitzhak Mandelbaum   // If exactly one is true, then they're different, no reason to check whether
850d34fbf2dSYitzhak Mandelbaum   // they're definitely empty.
851d34fbf2dSYitzhak Mandelbaum   if (MustNonEmpty1 || MustNonEmpty2) return ComparisonResult::Different;
852d34fbf2dSYitzhak Mandelbaum   // Check if they're both definitely empty.
853d34fbf2dSYitzhak Mandelbaum   return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
854c0725865SYitzhak Mandelbaum              ? ComparisonResult::Same
855c0725865SYitzhak Mandelbaum              : ComparisonResult::Different;
8568fcdd625SStanislav Gatev }
8578fcdd625SStanislav Gatev 
8588fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
8598fcdd625SStanislav Gatev                                          const Environment &Env1,
8608fcdd625SStanislav Gatev                                          const Value &Val2,
8618fcdd625SStanislav Gatev                                          const Environment &Env2,
8628fcdd625SStanislav Gatev                                          Value &MergedVal,
8638fcdd625SStanislav Gatev                                          Environment &MergedEnv) {
864c0725865SYitzhak Mandelbaum   if (!isOptionalType(Type))
8658fcdd625SStanislav Gatev     return true;
866d34fbf2dSYitzhak Mandelbaum   // FIXME: uses same approach as join for `BoolValues`. Requires non-const
867d34fbf2dSYitzhak Mandelbaum   // values, though, so will require updating the interface.
8688fcdd625SStanislav Gatev   auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
869d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
870d34fbf2dSYitzhak Mandelbaum   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
871d34fbf2dSYitzhak Mandelbaum   if (MustNonEmpty1 && MustNonEmpty2)
8728fcdd625SStanislav Gatev     MergedEnv.addToFlowCondition(HasValueVal);
873d34fbf2dSYitzhak Mandelbaum   else if (
874d34fbf2dSYitzhak Mandelbaum       // Only make the costly calls to `isEmptyOptional` if we got "unknown"
875d34fbf2dSYitzhak Mandelbaum       // (false) for both calls to `isNonEmptyOptional`.
876d34fbf2dSYitzhak Mandelbaum       !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) &&
877d34fbf2dSYitzhak Mandelbaum       isEmptyOptional(Val2, Env2))
8788fcdd625SStanislav Gatev     MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal));
8798fcdd625SStanislav Gatev   setHasValue(MergedVal, HasValueVal);
8808fcdd625SStanislav Gatev   return true;
8818fcdd625SStanislav Gatev }
8828fcdd625SStanislav Gatev 
883d34fbf2dSYitzhak Mandelbaum Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev,
884d34fbf2dSYitzhak Mandelbaum                                            const Environment &PrevEnv,
885d34fbf2dSYitzhak Mandelbaum                                            Value &Current,
886d34fbf2dSYitzhak Mandelbaum                                            Environment &CurrentEnv) {
887d34fbf2dSYitzhak Mandelbaum   switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) {
888d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Same:
889d34fbf2dSYitzhak Mandelbaum     return &Prev;
890d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Different:
891d34fbf2dSYitzhak Mandelbaum     if (auto *PrevHasVal =
892d34fbf2dSYitzhak Mandelbaum             cast_or_null<BoolValue>(Prev.getProperty("has_value"))) {
893d34fbf2dSYitzhak Mandelbaum       if (isa<TopBoolValue>(PrevHasVal))
894d34fbf2dSYitzhak Mandelbaum         return &Prev;
895d34fbf2dSYitzhak Mandelbaum     }
896d34fbf2dSYitzhak Mandelbaum     if (auto *CurrentHasVal =
897d34fbf2dSYitzhak Mandelbaum             cast_or_null<BoolValue>(Current.getProperty("has_value"))) {
898d34fbf2dSYitzhak Mandelbaum       if (isa<TopBoolValue>(CurrentHasVal))
899d34fbf2dSYitzhak Mandelbaum         return &Current;
900d34fbf2dSYitzhak Mandelbaum     }
901d34fbf2dSYitzhak Mandelbaum     return &createOptionalValue(CurrentEnv, CurrentEnv.makeTopBoolValue());
902d34fbf2dSYitzhak Mandelbaum   case ComparisonResult::Unknown:
903d34fbf2dSYitzhak Mandelbaum     return nullptr;
904d34fbf2dSYitzhak Mandelbaum   }
905d34fbf2dSYitzhak Mandelbaum   llvm_unreachable("all cases covered in switch");
906d34fbf2dSYitzhak Mandelbaum }
907d34fbf2dSYitzhak Mandelbaum 
90858fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
90958fe7f96SSam Estep     UncheckedOptionalAccessModelOptions Options)
91058fe7f96SSam Estep     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
91158fe7f96SSam Estep 
91258fe7f96SSam Estep std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose(
9137538b360SWei Yi Tee     ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) {
9147538b360SWei Yi Tee   return DiagnoseMatchSwitch(*Elt, Ctx, Env);
91558fe7f96SSam Estep }
91658fe7f96SSam Estep 
917af98b0afSStanislav Gatev } // namespace dataflow
918af98b0afSStanislav Gatev } // namespace clang
919