xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision d712c5ed8fab4940ae0480e01fc72a944cbb79e6)
1b000b770SStanislav Gatev //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//
2b000b770SStanislav Gatev //
3b000b770SStanislav Gatev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b000b770SStanislav Gatev // See https://llvm.org/LICENSE.txt for license information.
5b000b770SStanislav Gatev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b000b770SStanislav Gatev //
7b000b770SStanislav Gatev //===----------------------------------------------------------------------===//
8b000b770SStanislav Gatev //
9b000b770SStanislav Gatev //  This file defines a dataflow analysis that detects unsafe uses of optional
10b000b770SStanislav Gatev //  values.
11b000b770SStanislav Gatev //
12b000b770SStanislav Gatev //===----------------------------------------------------------------------===//
13b000b770SStanislav Gatev 
14af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
15af98b0afSStanislav Gatev #include "clang/AST/ASTContext.h"
167e63a0d4SYitzhak Mandelbaum #include "clang/AST/DeclCXX.h"
17af98b0afSStanislav Gatev #include "clang/AST/Expr.h"
18af98b0afSStanislav Gatev #include "clang/AST/ExprCXX.h"
19af98b0afSStanislav Gatev #include "clang/AST/Stmt.h"
20af98b0afSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h"
2109b462efSYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchersMacros.h"
227538b360SWei Yi Tee #include "clang/Analysis/CFG.h"
237538b360SWei Yi Tee #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
24af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
256272226bSSam McCall #include "clang/Analysis/FlowSensitive/Formula.h"
26cf1f978dSSam Estep #include "clang/Analysis/FlowSensitive/NoopLattice.h"
270086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h"
28af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h"
2958fe7f96SSam Estep #include "clang/Basic/SourceLocation.h"
30af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h"
31af98b0afSStanislav Gatev #include "llvm/Support/Casting.h"
32d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h"
33af98b0afSStanislav Gatev #include <cassert>
349e0fc676SStanislav Gatev #include <memory>
35a1580d7bSKazu Hirata #include <optional>
369e0fc676SStanislav Gatev #include <utility>
37af98b0afSStanislav Gatev 
38af98b0afSStanislav Gatev namespace clang {
39af98b0afSStanislav Gatev namespace dataflow {
4009b462efSYitzhak Mandelbaum 
4109b462efSYitzhak Mandelbaum static bool isTopLevelNamespaceWithName(const NamespaceDecl &NS,
4209b462efSYitzhak Mandelbaum                                         llvm::StringRef Name) {
4309b462efSYitzhak Mandelbaum   return NS.getDeclName().isIdentifier() && NS.getName() == Name &&
4409b462efSYitzhak Mandelbaum          NS.getParent() != nullptr && NS.getParent()->isTranslationUnit();
4509b462efSYitzhak Mandelbaum }
4609b462efSYitzhak Mandelbaum 
4709b462efSYitzhak Mandelbaum static bool hasOptionalClassName(const CXXRecordDecl &RD) {
4809b462efSYitzhak Mandelbaum   if (!RD.getDeclName().isIdentifier())
4909b462efSYitzhak Mandelbaum     return false;
5009b462efSYitzhak Mandelbaum 
5109b462efSYitzhak Mandelbaum   if (RD.getName() == "optional") {
5209b462efSYitzhak Mandelbaum     if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
5309b462efSYitzhak Mandelbaum       return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl");
5409b462efSYitzhak Mandelbaum     return false;
5509b462efSYitzhak Mandelbaum   }
5609b462efSYitzhak Mandelbaum 
5709b462efSYitzhak Mandelbaum   if (RD.getName() == "Optional") {
582f0630f8SAnton Dukeman     // Check whether namespace is "::base" or "::folly".
5909b462efSYitzhak Mandelbaum     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
602f0630f8SAnton Dukeman     return N != nullptr && (isTopLevelNamespaceWithName(*N, "base") ||
612f0630f8SAnton Dukeman                             isTopLevelNamespaceWithName(*N, "folly"));
6209b462efSYitzhak Mandelbaum   }
6309b462efSYitzhak Mandelbaum 
6409b462efSYitzhak Mandelbaum   return false;
6509b462efSYitzhak Mandelbaum }
6609b462efSYitzhak Mandelbaum 
67*d712c5edSmartinboehme static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) {
68*d712c5edSmartinboehme   if (RD == nullptr)
69*d712c5edSmartinboehme     return nullptr;
70*d712c5edSmartinboehme   if (hasOptionalClassName(*RD))
71*d712c5edSmartinboehme     return RD;
72*d712c5edSmartinboehme 
73*d712c5edSmartinboehme   if (!RD->hasDefinition())
74*d712c5edSmartinboehme     return nullptr;
75*d712c5edSmartinboehme 
76*d712c5edSmartinboehme   for (const CXXBaseSpecifier &Base : RD->bases())
77*d712c5edSmartinboehme     if (const CXXRecordDecl *BaseClass =
78*d712c5edSmartinboehme             getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl()))
79*d712c5edSmartinboehme       return BaseClass;
80*d712c5edSmartinboehme 
81*d712c5edSmartinboehme   return nullptr;
82*d712c5edSmartinboehme }
83*d712c5edSmartinboehme 
84af98b0afSStanislav Gatev namespace {
85af98b0afSStanislav Gatev 
86af98b0afSStanislav Gatev using namespace ::clang::ast_matchers;
87cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>;
88af98b0afSStanislav Gatev 
89*d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); }
90*d712c5edSmartinboehme 
91*d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
92*d712c5edSmartinboehme   return getOptionalBaseClass(&Node) != nullptr;
9309b462efSYitzhak Mandelbaum }
9409b462efSYitzhak Mandelbaum 
95*d712c5edSmartinboehme auto desugarsToOptionalType() {
9665e710c3SStanislav Gatev   return hasUnqualifiedDesugaredType(
97*d712c5edSmartinboehme       recordType(hasDeclaration(cxxRecordDecl(optionalClass()))));
9865e710c3SStanislav Gatev }
9965e710c3SStanislav Gatev 
100*d712c5edSmartinboehme auto desugarsToOptionalOrDerivedType() {
101*d712c5edSmartinboehme   return hasUnqualifiedDesugaredType(
102*d712c5edSmartinboehme       recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass()))));
103*d712c5edSmartinboehme }
104*d712c5edSmartinboehme 
105*d712c5edSmartinboehme auto hasOptionalType() { return hasType(desugarsToOptionalType()); }
106*d712c5edSmartinboehme 
107*d712c5edSmartinboehme /// Matches any of the spellings of the optional types and sugar, aliases,
108*d712c5edSmartinboehme /// derived classes, etc.
109*d712c5edSmartinboehme auto hasOptionalOrDerivedType() {
110*d712c5edSmartinboehme   return hasType(desugarsToOptionalOrDerivedType());
111*d712c5edSmartinboehme }
112*d712c5edSmartinboehme 
113*d712c5edSmartinboehme QualType getPublicType(const Expr *E) {
114*d712c5edSmartinboehme   auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());
115*d712c5edSmartinboehme   if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) {
116*d712c5edSmartinboehme     QualType Ty = E->getType();
117*d712c5edSmartinboehme     if (Ty->isPointerType())
118*d712c5edSmartinboehme       return Ty->getPointeeType();
119*d712c5edSmartinboehme     return Ty;
120*d712c5edSmartinboehme   }
121*d712c5edSmartinboehme 
122*d712c5edSmartinboehme   // Is the derived type that we're casting from the type of `*this`? In this
123*d712c5edSmartinboehme   // special case, we can upcast to the base class even if the base is
124*d712c5edSmartinboehme   // non-public.
125*d712c5edSmartinboehme   bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr());
126*d712c5edSmartinboehme 
127*d712c5edSmartinboehme   // Find the least-derived type in the path (i.e. the last entry in the list)
128*d712c5edSmartinboehme   // that we can access.
129*d712c5edSmartinboehme   const CXXBaseSpecifier *PublicBase = nullptr;
130*d712c5edSmartinboehme   for (const CXXBaseSpecifier *Base : Cast->path()) {
131*d712c5edSmartinboehme     if (Base->getAccessSpecifier() != AS_public && !CastingFromThis)
132*d712c5edSmartinboehme       break;
133*d712c5edSmartinboehme     PublicBase = Base;
134*d712c5edSmartinboehme     CastingFromThis = false;
135*d712c5edSmartinboehme   }
136*d712c5edSmartinboehme 
137*d712c5edSmartinboehme   if (PublicBase != nullptr)
138*d712c5edSmartinboehme     return PublicBase->getType();
139*d712c5edSmartinboehme 
140*d712c5edSmartinboehme   // We didn't find any public type that we could cast to. There may be more
141*d712c5edSmartinboehme   // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this
142*d712c5edSmartinboehme   // will return the type of `getSubExpr()`.)
143*d712c5edSmartinboehme   return getPublicType(Cast->getSubExpr());
144*d712c5edSmartinboehme }
145*d712c5edSmartinboehme 
146*d712c5edSmartinboehme // Returns the least-derived type for the receiver of `MCE` that
147*d712c5edSmartinboehme // `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to.
148*d712c5edSmartinboehme // Effectively, we upcast until we reach a non-public base class, unless that
149*d712c5edSmartinboehme // base is a base of `*this`.
150*d712c5edSmartinboehme //
151*d712c5edSmartinboehme // This is needed to correctly match methods called on types derived from
152*d712c5edSmartinboehme // `std::optional`.
153*d712c5edSmartinboehme //
154*d712c5edSmartinboehme // Say we have a `struct Derived : public std::optional<int> {} d;` For a call
155*d712c5edSmartinboehme // `d.has_value()`, the `getImplicitObjectArgument()` looks like this:
156*d712c5edSmartinboehme //
157*d712c5edSmartinboehme //   ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue
158*d712c5edSmartinboehme //   |            <UncheckedDerivedToBase (optional -> __optional_storage_base)>
159*d712c5edSmartinboehme //   `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived'
160*d712c5edSmartinboehme //
161*d712c5edSmartinboehme // The type of the implicit object argument is `__optional_storage_base`
162*d712c5edSmartinboehme // (since this is the internal type that `has_value()` is declared on). If we
163*d712c5edSmartinboehme // call `IgnoreParenImpCasts()` on the implicit object argument, we get the
164*d712c5edSmartinboehme // `DeclRefExpr`, which has type `Derived`. Neither of these types is
165*d712c5edSmartinboehme // `optional`, and hence neither is sufficient for querying whether we are
166*d712c5edSmartinboehme // calling a method on `optional`.
167*d712c5edSmartinboehme //
168*d712c5edSmartinboehme // Instead, starting with the most derived type, we need to follow the chain of
169*d712c5edSmartinboehme // casts
170*d712c5edSmartinboehme QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) {
171*d712c5edSmartinboehme   return getPublicType(MCE.getImplicitObjectArgument());
172*d712c5edSmartinboehme }
173*d712c5edSmartinboehme 
174*d712c5edSmartinboehme AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType,
175*d712c5edSmartinboehme               ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
176*d712c5edSmartinboehme   return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder);
177*d712c5edSmartinboehme }
1786adfc64eSYitzhak Mandelbaum 
1792f0630f8SAnton Dukeman auto isOptionalMemberCallWithNameMatcher(
1802f0630f8SAnton Dukeman     ast_matchers::internal::Matcher<NamedDecl> matcher,
1816ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
182*d712c5edSmartinboehme   return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable)))
183*d712c5edSmartinboehme                                      : anything(),
184*d712c5edSmartinboehme                            publicReceiverType(desugarsToOptionalType()),
1852f0630f8SAnton Dukeman                            callee(cxxMethodDecl(matcher)));
186af98b0afSStanislav Gatev }
187af98b0afSStanislav Gatev 
188a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName(
189a184a0d8SYitzhak Mandelbaum     llvm::StringRef operator_name,
1906ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
191a184a0d8SYitzhak Mandelbaum   return cxxOperatorCallExpr(
192a184a0d8SYitzhak Mandelbaum       hasOverloadedOperatorName(operator_name),
193a184a0d8SYitzhak Mandelbaum       callee(cxxMethodDecl(ofClass(optionalClass()))),
194a184a0d8SYitzhak Mandelbaum       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
195af98b0afSStanislav Gatev }
196af98b0afSStanislav Gatev 
197092a530cSStanislav Gatev auto isMakeOptionalCall() {
1982f0630f8SAnton Dukeman   return callExpr(callee(functionDecl(hasAnyName(
1992f0630f8SAnton Dukeman                       "std::make_optional", "base::make_optional",
2002f0630f8SAnton Dukeman                       "absl::make_optional", "folly::make_optional"))),
2019e0fc676SStanislav Gatev                   hasOptionalType());
2029e0fc676SStanislav Gatev }
2039e0fc676SStanislav Gatev 
204390029beSYitzhak Mandelbaum auto nulloptTypeDecl() {
2052f0630f8SAnton Dukeman   return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",
2062f0630f8SAnton Dukeman                               "base::nullopt_t", "folly::None"));
207092a530cSStanislav Gatev }
208092a530cSStanislav Gatev 
209390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
210390029beSYitzhak Mandelbaum 
211092a530cSStanislav Gatev auto inPlaceClass() {
2122f0630f8SAnton Dukeman   return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
2132f0630f8SAnton Dukeman                                "base::in_place_t", "folly::in_place_t"));
214092a530cSStanislav Gatev }
215092a530cSStanislav Gatev 
216092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
2170086a355SYitzhak Mandelbaum   return cxxConstructExpr(
2180086a355SYitzhak Mandelbaum       hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
219*d712c5edSmartinboehme                                         hasParameter(0, hasNulloptType()))),
220*d712c5edSmartinboehme       hasOptionalOrDerivedType());
221092a530cSStanislav Gatev }
222092a530cSStanislav Gatev 
223092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
224*d712c5edSmartinboehme   return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())),
225*d712c5edSmartinboehme                           hasOptionalOrDerivedType());
226092a530cSStanislav Gatev }
227092a530cSStanislav Gatev 
228092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
229092a530cSStanislav Gatev   return cxxConstructExpr(
230092a530cSStanislav Gatev       unless(hasDeclaration(
231092a530cSStanislav Gatev           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
232*d712c5edSmartinboehme       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())),
233*d712c5edSmartinboehme       hasOptionalOrDerivedType());
234092a530cSStanislav Gatev }
235092a530cSStanislav Gatev 
236b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
237b000b770SStanislav Gatev   return cxxOperatorCallExpr(
238b000b770SStanislav Gatev       hasOverloadedOperatorName("="),
239*d712c5edSmartinboehme       callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
240b000b770SStanislav Gatev       unless(hasDeclaration(cxxMethodDecl(
241b000b770SStanislav Gatev           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
242b000b770SStanislav Gatev       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
243b000b770SStanislav Gatev }
244b000b770SStanislav Gatev 
245b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
246*d712c5edSmartinboehme   return cxxOperatorCallExpr(
247*d712c5edSmartinboehme       hasOverloadedOperatorName("="),
248*d712c5edSmartinboehme       callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
249*d712c5edSmartinboehme       argumentCountIs(2), hasArgument(1, hasNulloptType()));
250b000b770SStanislav Gatev }
251b000b770SStanislav Gatev 
2522ddd57aeSStanislav Gatev auto isStdSwapCall() {
2532ddd57aeSStanislav Gatev   return callExpr(callee(functionDecl(hasName("std::swap"))),
254*d712c5edSmartinboehme                   argumentCountIs(2),
255*d712c5edSmartinboehme                   hasArgument(0, hasOptionalOrDerivedType()),
256*d712c5edSmartinboehme                   hasArgument(1, hasOptionalOrDerivedType()));
2572ddd57aeSStanislav Gatev }
2582ddd57aeSStanislav Gatev 
25925956d55SAMS21 auto isStdForwardCall() {
26025956d55SAMS21   return callExpr(callee(functionDecl(hasName("std::forward"))),
261*d712c5edSmartinboehme                   argumentCountIs(1),
262*d712c5edSmartinboehme                   hasArgument(0, hasOptionalOrDerivedType()));
26325956d55SAMS21 }
26425956d55SAMS21 
2657f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
2667f076004SYitzhak Mandelbaum 
2677f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() {
2687f076004SYitzhak Mandelbaum   // `opt.value_or("").empty()`
2697f076004SYitzhak Mandelbaum   return cxxMemberCallExpr(
2707f076004SYitzhak Mandelbaum       callee(cxxMethodDecl(hasName("empty"))),
2717f076004SYitzhak Mandelbaum       onImplicitObjectArgument(ignoringImplicit(
2727f076004SYitzhak Mandelbaum           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
2737f076004SYitzhak Mandelbaum                             callee(cxxMethodDecl(hasName("value_or"),
2747f076004SYitzhak Mandelbaum                                                  ofClass(optionalClass()))),
2757f076004SYitzhak Mandelbaum                             hasArgument(0, stringLiteral(hasSize(0))))
2767f076004SYitzhak Mandelbaum               .bind(ValueOrCallID))));
2777f076004SYitzhak Mandelbaum }
2787f076004SYitzhak Mandelbaum 
2797f076004SYitzhak Mandelbaum auto isValueOrNotEqX() {
2807f076004SYitzhak Mandelbaum   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
2817f076004SYitzhak Mandelbaum     return hasOperands(
2827f076004SYitzhak Mandelbaum         ignoringImplicit(
2837f076004SYitzhak Mandelbaum             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
2847f076004SYitzhak Mandelbaum                               callee(cxxMethodDecl(hasName("value_or"),
2857f076004SYitzhak Mandelbaum                                                    ofClass(optionalClass()))),
2867f076004SYitzhak Mandelbaum                               hasArgument(0, Arg))
2877f076004SYitzhak Mandelbaum                 .bind(ValueOrCallID)),
2887f076004SYitzhak Mandelbaum         ignoringImplicit(Arg));
2897f076004SYitzhak Mandelbaum   };
2907f076004SYitzhak Mandelbaum 
2917f076004SYitzhak Mandelbaum   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
2927f076004SYitzhak Mandelbaum   // support this pattern for any expression, but the AST does not have a
2937f076004SYitzhak Mandelbaum   // generic expression comparison facility, so we specialize to common cases
2947f076004SYitzhak Mandelbaum   // seen in practice.  FIXME: define a matcher that compares values across
2957f076004SYitzhak Mandelbaum   // nodes, which would let us generalize this to any `X`.
2967f076004SYitzhak Mandelbaum   return binaryOperation(hasOperatorName("!="),
2977f076004SYitzhak Mandelbaum                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
2987f076004SYitzhak Mandelbaum                                ComparesToSame(stringLiteral(hasSize(0))),
2997f076004SYitzhak Mandelbaum                                ComparesToSame(integerLiteral(equals(0)))));
3007f076004SYitzhak Mandelbaum }
3017f076004SYitzhak Mandelbaum 
30265e710c3SStanislav Gatev auto isCallReturningOptional() {
303*d712c5edSmartinboehme   return callExpr(hasType(qualType(
304*d712c5edSmartinboehme       anyOf(desugarsToOptionalOrDerivedType(),
305*d712c5edSmartinboehme             referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
30665e710c3SStanislav Gatev }
30765e710c3SStanislav Gatev 
308390029beSYitzhak Mandelbaum template <typename L, typename R>
309390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
310390029beSYitzhak Mandelbaum   return cxxOperatorCallExpr(
311390029beSYitzhak Mandelbaum       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
312390029beSYitzhak Mandelbaum       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
313390029beSYitzhak Mandelbaum       hasArgument(1, rhs_arg_matcher));
314390029beSYitzhak Mandelbaum }
315390029beSYitzhak Mandelbaum 
3166272226bSSam McCall /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
3176272226bSSam McCall const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
3182ee396b0Smartinboehme   auto *Value = Env.get<BoolValue>(Expr);
319390029beSYitzhak Mandelbaum   if (Value != nullptr)
3206272226bSSam McCall     return Value->formula();
321390029beSYitzhak Mandelbaum 
322390029beSYitzhak Mandelbaum   Value = &Env.makeAtomicBoolValue();
323b244b6aeSMartin Braenne   Env.setValue(Expr, *Value);
3246272226bSSam McCall   return Value->formula();
325390029beSYitzhak Mandelbaum }
326390029beSYitzhak Mandelbaum 
32771f2ec2dSmartinboehme StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) {
32871f2ec2dSmartinboehme   return OptionalLoc.getSyntheticField("has_value");
32971f2ec2dSmartinboehme }
33071f2ec2dSmartinboehme 
33171f2ec2dSmartinboehme StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) {
33271f2ec2dSmartinboehme   return OptionalLoc.getSyntheticField("value");
33371f2ec2dSmartinboehme }
33471f2ec2dSmartinboehme 
3358fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
33671f2ec2dSmartinboehme /// property of the optional at `OptionalLoc`.
33771f2ec2dSmartinboehme void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,
33871f2ec2dSmartinboehme                  Environment &Env) {
33971f2ec2dSmartinboehme   Env.setValue(locForHasValue(OptionalLoc), HasValueVal);
3408fcdd625SStanislav Gatev }
3418fcdd625SStanislav Gatev 
342f653d140SMartin Braenne /// Creates a symbolic value for an `optional` value at an existing storage
343f653d140SMartin Braenne /// location. Uses `HasValueVal` as the symbolic value of the "has_value"
344f653d140SMartin Braenne /// property.
3459ecdbe38SMartin Braenne RecordValue &createOptionalValue(RecordStorageLocation &Loc,
346f653d140SMartin Braenne                                  BoolValue &HasValueVal, Environment &Env) {
3479ecdbe38SMartin Braenne   auto &OptionalVal = Env.create<RecordValue>(Loc);
348f653d140SMartin Braenne   Env.setValue(Loc, OptionalVal);
34971f2ec2dSmartinboehme   setHasValue(Loc, HasValueVal, Env);
350f653d140SMartin Braenne   return OptionalVal;
351f653d140SMartin Braenne }
352f653d140SMartin Braenne 
353af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
35471f2ec2dSmartinboehme /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null.
35571f2ec2dSmartinboehme BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {
35671f2ec2dSmartinboehme   if (OptionalLoc == nullptr)
35771f2ec2dSmartinboehme     return nullptr;
35871f2ec2dSmartinboehme   StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
3592ee396b0Smartinboehme   auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);
360dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr) {
361dd38caf3SYitzhak Mandelbaum     HasValueVal = &Env.makeAtomicBoolValue();
36271f2ec2dSmartinboehme     Env.setValue(HasValueLoc, *HasValueVal);
363dd38caf3SYitzhak Mandelbaum   }
364dd38caf3SYitzhak Mandelbaum   return HasValueVal;
365af98b0afSStanislav Gatev }
366af98b0afSStanislav Gatev 
367*d712c5edSmartinboehme QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {
368*d712c5edSmartinboehme   auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);
369*d712c5edSmartinboehme   return CTSD.getTemplateArgs()[0].getAsType();
370092a530cSStanislav Gatev }
371092a530cSStanislav Gatev 
372092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
373092a530cSStanislav Gatev ///
374092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
375092a530cSStanislav Gatev /// function will be 2.
376092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
377*d712c5edSmartinboehme   const CXXRecordDecl *Optional =
378*d712c5edSmartinboehme       getOptionalBaseClass(Type->getAsCXXRecordDecl());
379*d712c5edSmartinboehme   if (Optional == nullptr)
380092a530cSStanislav Gatev     return 0;
381092a530cSStanislav Gatev   return 1 + countOptionalWrappers(
382092a530cSStanislav Gatev                  ASTCtx,
383*d712c5edSmartinboehme                  valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
384092a530cSStanislav Gatev }
385092a530cSStanislav Gatev 
38671f2ec2dSmartinboehme StorageLocation *getLocBehindPossiblePointer(const Expr &E,
38771f2ec2dSmartinboehme                                              const Environment &Env) {
38871f2ec2dSmartinboehme   if (E.isPRValue()) {
38971f2ec2dSmartinboehme     if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
39071f2ec2dSmartinboehme       return &PointerVal->getPointeeLoc();
391dd38caf3SYitzhak Mandelbaum     return nullptr;
392dd38caf3SYitzhak Mandelbaum   }
39371f2ec2dSmartinboehme   return Env.getStorageLocation(E);
39448bc7150SMartin Braenne }
39548bc7150SMartin Braenne 
396092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
397af98b0afSStanislav Gatev                         LatticeTransferState &State) {
39871f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
39971f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
400b244b6aeSMartin Braenne     if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)
40171f2ec2dSmartinboehme       State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
402af98b0afSStanislav Gatev   }
403dd38caf3SYitzhak Mandelbaum }
404af98b0afSStanislav Gatev 
4053bc1ea5bSMartin Braenne void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
4063bc1ea5bSMartin Braenne                          LatticeTransferState &State) {
40771f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
40871f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
40971f2ec2dSmartinboehme     State.Env.setValue(
41071f2ec2dSmartinboehme         *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));
4113bc1ea5bSMartin Braenne }
4123bc1ea5bSMartin Braenne 
413092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
414092a530cSStanislav Gatev                               const MatchFinder::MatchResult &,
415092a530cSStanislav Gatev                               LatticeTransferState &State) {
416330d5bcbSMartin Braenne   State.Env.setValue(
417330d5bcbSMartin Braenne       *E, createOptionalValue(State.Env.getResultObjectLocation(*E),
418330d5bcbSMartin Braenne                               State.Env.getBoolLiteralValue(true), State.Env));
4199e0fc676SStanislav Gatev }
4209e0fc676SStanislav Gatev 
421092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
422092a530cSStanislav Gatev                                   const MatchFinder::MatchResult &,
423af98b0afSStanislav Gatev                                   LatticeTransferState &State) {
424dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal = getHasValue(
42571f2ec2dSmartinboehme           State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
426b244b6aeSMartin Braenne     State.Env.setValue(*CallExpr, *HasValueVal);
427af98b0afSStanislav Gatev   }
428af98b0afSStanislav Gatev }
429af98b0afSStanislav Gatev 
4307f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in
4317f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property.
4326272226bSSam McCall void transferValueOrImpl(
4336272226bSSam McCall     const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
4347f076004SYitzhak Mandelbaum     LatticeTransferState &State,
4356272226bSSam McCall     const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
4366272226bSSam McCall                                 const Formula &HasValueVal)) {
4377f076004SYitzhak Mandelbaum   auto &Env = State.Env;
4387f076004SYitzhak Mandelbaum 
43971f2ec2dSmartinboehme   const auto *MCE =
44071f2ec2dSmartinboehme       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);
4417f076004SYitzhak Mandelbaum 
44271f2ec2dSmartinboehme   auto *HasValueVal =
44371f2ec2dSmartinboehme       getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env));
444dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr)
4457f076004SYitzhak Mandelbaum     return;
4467f076004SYitzhak Mandelbaum 
447526c9b7eSmartinboehme   Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
4486272226bSSam McCall                        HasValueVal->formula()));
4497f076004SYitzhak Mandelbaum }
4507f076004SYitzhak Mandelbaum 
4517f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
4527f076004SYitzhak Mandelbaum                                     const MatchFinder::MatchResult &Result,
4537f076004SYitzhak Mandelbaum                                     LatticeTransferState &State) {
4547f076004SYitzhak Mandelbaum   return transferValueOrImpl(ComparisonExpr, Result, State,
4556272226bSSam McCall                              [](Environment &Env, const Formula &ExprVal,
4566272226bSSam McCall                                 const Formula &HasValueVal) -> const Formula & {
4576272226bSSam McCall                                auto &A = Env.arena();
4587f076004SYitzhak Mandelbaum                                // If the result is *not* empty, then we know the
4597f076004SYitzhak Mandelbaum                                // optional must have been holding a value. If
4607f076004SYitzhak Mandelbaum                                // `ExprVal` is true, though, we don't learn
4617f076004SYitzhak Mandelbaum                                // anything definite about `has_value`, so we
4627f076004SYitzhak Mandelbaum                                // don't add any corresponding implications to
4637f076004SYitzhak Mandelbaum                                // the flow condition.
4646272226bSSam McCall                                return A.makeImplies(A.makeNot(ExprVal),
4657f076004SYitzhak Mandelbaum                                                     HasValueVal);
4667f076004SYitzhak Mandelbaum                              });
4677f076004SYitzhak Mandelbaum }
4687f076004SYitzhak Mandelbaum 
4697f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr,
4707f076004SYitzhak Mandelbaum                            const MatchFinder::MatchResult &Result,
4717f076004SYitzhak Mandelbaum                            LatticeTransferState &State) {
4727f076004SYitzhak Mandelbaum   transferValueOrImpl(ComparisonExpr, Result, State,
4736272226bSSam McCall                       [](Environment &Env, const Formula &ExprVal,
4746272226bSSam McCall                          const Formula &HasValueVal) -> const Formula & {
4756272226bSSam McCall                         auto &A = Env.arena();
4767f076004SYitzhak Mandelbaum                         // We know that if `(opt.value_or(X) != X)` then
4777f076004SYitzhak Mandelbaum                         // `opt.hasValue()`, even without knowing further
4787f076004SYitzhak Mandelbaum                         // details about the contents of `opt`.
4796272226bSSam McCall                         return A.makeImplies(ExprVal, HasValueVal);
4807f076004SYitzhak Mandelbaum                       });
4817f076004SYitzhak Mandelbaum }
4827f076004SYitzhak Mandelbaum 
48365e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E,
48465e710c3SStanislav Gatev                                    const MatchFinder::MatchResult &Result,
48565e710c3SStanislav Gatev                                    LatticeTransferState &State) {
486f76f6674SMartin Braenne   if (State.Env.getValue(*E) != nullptr)
48765e710c3SStanislav Gatev     return;
48865e710c3SStanislav Gatev 
4899ecdbe38SMartin Braenne   RecordStorageLocation *Loc = nullptr;
49044f98d01SMartin Braenne   if (E->isPRValue()) {
49144f98d01SMartin Braenne     Loc = &State.Env.getResultObjectLocation(*E);
49244f98d01SMartin Braenne   } else {
4932ee396b0Smartinboehme     Loc = State.Env.get<RecordStorageLocation>(*E);
494f76f6674SMartin Braenne     if (Loc == nullptr) {
4959ecdbe38SMartin Braenne       Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));
496b244b6aeSMartin Braenne       State.Env.setStorageLocation(*E, *Loc);
49744f98d01SMartin Braenne     }
498f76f6674SMartin Braenne   }
49944f98d01SMartin Braenne 
500330d5bcbSMartin Braenne   RecordValue &Val =
50144f98d01SMartin Braenne       createOptionalValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
502330d5bcbSMartin Braenne   if (E->isPRValue())
503330d5bcbSMartin Braenne     State.Env.setValue(*E, Val);
50465e710c3SStanislav Gatev }
50565e710c3SStanislav Gatev 
506f653d140SMartin Braenne void constructOptionalValue(const Expr &E, Environment &Env,
507092a530cSStanislav Gatev                             BoolValue &HasValueVal) {
5089ecdbe38SMartin Braenne   RecordStorageLocation &Loc = Env.getResultObjectLocation(E);
509b244b6aeSMartin Braenne   Env.setValue(E, createOptionalValue(Loc, HasValueVal, Env));
5109e0fc676SStanislav Gatev }
5119e0fc676SStanislav Gatev 
512b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
513b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
514b000b770SStanislav Gatev /// where `T` is constructible from `U`.
515390029beSYitzhak Mandelbaum BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
516b000b770SStanislav Gatev                                      const MatchFinder::MatchResult &MatchRes,
517b000b770SStanislav Gatev                                      LatticeTransferState &State) {
5180086a355SYitzhak Mandelbaum   assert(F.getTemplateSpecializationArgs() != nullptr);
519b000b770SStanislav Gatev   assert(F.getTemplateSpecializationArgs()->size() > 0);
520b000b770SStanislav Gatev 
521c849843cSMartin Braenne   const int TemplateParamOptionalWrappersCount =
522c849843cSMartin Braenne       countOptionalWrappers(*MatchRes.Context, F.getTemplateSpecializationArgs()
523c849843cSMartin Braenne                                                    ->get(0)
524c849843cSMartin Braenne                                                    .getAsType()
525c849843cSMartin Braenne                                                    .getNonReferenceType());
526c849843cSMartin Braenne   const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
527c849843cSMartin Braenne       *MatchRes.Context, E.getType().getNonReferenceType());
528b000b770SStanislav Gatev 
529b000b770SStanislav Gatev   // Check if this is a constructor/assignment call for `optional<T>` with
530b000b770SStanislav Gatev   // argument of type `U` such that `T` is constructible from `U`.
531b000b770SStanislav Gatev   if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
532b000b770SStanislav Gatev     return State.Env.getBoolLiteralValue(true);
533b000b770SStanislav Gatev 
534b000b770SStanislav Gatev   // This is a constructor/assignment call for `optional<T>` with argument of
535b000b770SStanislav Gatev   // type `optional<U>` such that `T` is constructible from `U`.
5362ee396b0Smartinboehme   auto *Loc = State.Env.get<RecordStorageLocation>(E);
53771f2ec2dSmartinboehme   if (auto *HasValueVal = getHasValue(State.Env, Loc))
538dd38caf3SYitzhak Mandelbaum     return *HasValueVal;
539b000b770SStanislav Gatev   return State.Env.makeAtomicBoolValue();
540b000b770SStanislav Gatev }
541b000b770SStanislav Gatev 
542092a530cSStanislav Gatev void transferValueOrConversionConstructor(
543092a530cSStanislav Gatev     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
5449e0fc676SStanislav Gatev     LatticeTransferState &State) {
545092a530cSStanislav Gatev   assert(E->getNumArgs() > 0);
546092a530cSStanislav Gatev 
547f653d140SMartin Braenne   constructOptionalValue(*E, State.Env,
548390029beSYitzhak Mandelbaum                          valueOrConversionHasValue(*E->getConstructor(),
549b000b770SStanislav Gatev                                                    *E->getArg(0), MatchRes,
550b000b770SStanislav Gatev                                                    State));
551b000b770SStanislav Gatev }
552092a530cSStanislav Gatev 
553b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
554b000b770SStanislav Gatev                         LatticeTransferState &State) {
555b000b770SStanislav Gatev   assert(E->getNumArgs() > 0);
556b000b770SStanislav Gatev 
5572ee396b0Smartinboehme   if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) {
558f653d140SMartin Braenne     createOptionalValue(*Loc, HasValueVal, State.Env);
559b000b770SStanislav Gatev 
560b000b770SStanislav Gatev     // Assign a storage location for the whole expression.
561b244b6aeSMartin Braenne     State.Env.setStorageLocation(*E, *Loc);
562f653d140SMartin Braenne   }
563b000b770SStanislav Gatev }
564b000b770SStanislav Gatev 
565b000b770SStanislav Gatev void transferValueOrConversionAssignment(
566b000b770SStanislav Gatev     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
567b000b770SStanislav Gatev     LatticeTransferState &State) {
568b000b770SStanislav Gatev   assert(E->getNumArgs() > 1);
569b000b770SStanislav Gatev   transferAssignment(E,
570390029beSYitzhak Mandelbaum                      valueOrConversionHasValue(*E->getDirectCallee(),
57106decd0bSKazu Hirata                                                *E->getArg(1), MatchRes, State),
572b000b770SStanislav Gatev                      State);
573b000b770SStanislav Gatev }
574b000b770SStanislav Gatev 
575b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
576b000b770SStanislav Gatev                                const MatchFinder::MatchResult &,
577b000b770SStanislav Gatev                                LatticeTransferState &State) {
578b000b770SStanislav Gatev   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
5799e0fc676SStanislav Gatev }
5809e0fc676SStanislav Gatev 
5819ecdbe38SMartin Braenne void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
5829ecdbe38SMartin Braenne                   Environment &Env) {
583d4fb829bSYitzhak Mandelbaum   // We account for cases where one or both of the optionals are not modeled,
584d4fb829bSYitzhak Mandelbaum   // either lacking associated storage locations, or lacking values associated
585d4fb829bSYitzhak Mandelbaum   // to such storage locations.
5862ddd57aeSStanislav Gatev 
587d4fb829bSYitzhak Mandelbaum   if (Loc1 == nullptr) {
588d4fb829bSYitzhak Mandelbaum     if (Loc2 != nullptr)
589f653d140SMartin Braenne       createOptionalValue(*Loc2, Env.makeAtomicBoolValue(), Env);
590d4fb829bSYitzhak Mandelbaum     return;
591d4fb829bSYitzhak Mandelbaum   }
592d4fb829bSYitzhak Mandelbaum   if (Loc2 == nullptr) {
593f653d140SMartin Braenne     createOptionalValue(*Loc1, Env.makeAtomicBoolValue(), Env);
594d4fb829bSYitzhak Mandelbaum     return;
595d4fb829bSYitzhak Mandelbaum   }
5962ddd57aeSStanislav Gatev 
597d4fb829bSYitzhak Mandelbaum   // Both expressions have locations, though they may not have corresponding
598d4fb829bSYitzhak Mandelbaum   // values. In that case, we create a fresh value at this point. Note that if
599d4fb829bSYitzhak Mandelbaum   // two branches both do this, they will not share the value, but it at least
600d4fb829bSYitzhak Mandelbaum   // allows for local reasoning about the value. To avoid the above, we would
601d4fb829bSYitzhak Mandelbaum   // need *lazy* value allocation.
602d4fb829bSYitzhak Mandelbaum   // FIXME: allocate values lazily, instead of just creating a fresh value.
60371f2ec2dSmartinboehme   BoolValue *BoolVal1 = getHasValue(Env, Loc1);
604f653d140SMartin Braenne   if (BoolVal1 == nullptr)
605f653d140SMartin Braenne     BoolVal1 = &Env.makeAtomicBoolValue();
606d4fb829bSYitzhak Mandelbaum 
60771f2ec2dSmartinboehme   BoolValue *BoolVal2 = getHasValue(Env, Loc2);
608f653d140SMartin Braenne   if (BoolVal2 == nullptr)
609f653d140SMartin Braenne     BoolVal2 = &Env.makeAtomicBoolValue();
610d4fb829bSYitzhak Mandelbaum 
611f653d140SMartin Braenne   createOptionalValue(*Loc1, *BoolVal2, Env);
612f653d140SMartin Braenne   createOptionalValue(*Loc2, *BoolVal1, Env);
6132ddd57aeSStanislav Gatev }
6142ddd57aeSStanislav Gatev 
6152ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
6162ddd57aeSStanislav Gatev                       const MatchFinder::MatchResult &,
6172ddd57aeSStanislav Gatev                       LatticeTransferState &State) {
6182ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 1);
6192ee396b0Smartinboehme   auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
620f653d140SMartin Braenne   transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
6212ddd57aeSStanislav Gatev }
6222ddd57aeSStanislav Gatev 
6232ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
6242ddd57aeSStanislav Gatev                          LatticeTransferState &State) {
6252ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 2);
6262ee396b0Smartinboehme   auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
6272ee396b0Smartinboehme   auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1));
628f653d140SMartin Braenne   transferSwap(Arg0Loc, Arg1Loc, State.Env);
6292ddd57aeSStanislav Gatev }
6302ddd57aeSStanislav Gatev 
63125956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
63225956d55SAMS21                             LatticeTransferState &State) {
63325956d55SAMS21   assert(E->getNumArgs() == 1);
63425956d55SAMS21 
635b244b6aeSMartin Braenne   if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
636b244b6aeSMartin Braenne     State.Env.setStorageLocation(*E, *Loc);
63725956d55SAMS21 }
63825956d55SAMS21 
6396272226bSSam McCall const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
6406272226bSSam McCall                                 const Formula &LHS, const Formula &RHS) {
641390029beSYitzhak Mandelbaum   // Logically, an optional<T> object is composed of two values - a `has_value`
642390029beSYitzhak Mandelbaum   // bit and a value of type T. Equality of optional objects compares both
643390029beSYitzhak Mandelbaum   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
644390029beSYitzhak Mandelbaum   // when two optional objects are engaged, the equality of their respective
645390029beSYitzhak Mandelbaum   // values of type T matters. Since we only track the `has_value` bits, we
646390029beSYitzhak Mandelbaum   // can't make any conclusions about equality when we know that two optional
647390029beSYitzhak Mandelbaum   // objects are engaged.
648390029beSYitzhak Mandelbaum   //
649390029beSYitzhak Mandelbaum   // We express this as two facts about the equality:
650390029beSYitzhak Mandelbaum   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
651390029beSYitzhak Mandelbaum   //    If they are equal, then either both are set or both are unset.
652390029beSYitzhak Mandelbaum   // b) (!LHS & !RHS) => EqVal
653390029beSYitzhak Mandelbaum   //    If neither is set, then they are equal.
654390029beSYitzhak Mandelbaum   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
6556272226bSSam McCall   return A.makeAnd(
6566272226bSSam McCall       A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
6576272226bSSam McCall                                     A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
6586272226bSSam McCall       A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
659390029beSYitzhak Mandelbaum }
660390029beSYitzhak Mandelbaum 
661390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
662390029beSYitzhak Mandelbaum                                     const MatchFinder::MatchResult &,
663390029beSYitzhak Mandelbaum                                     LatticeTransferState &State) {
664390029beSYitzhak Mandelbaum   Environment &Env = State.Env;
6656272226bSSam McCall   auto &A = Env.arena();
666390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
6672ee396b0Smartinboehme   auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0));
66871f2ec2dSmartinboehme   if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {
6692ee396b0Smartinboehme     auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1));
67071f2ec2dSmartinboehme     if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {
671390029beSYitzhak Mandelbaum       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
6726272226bSSam McCall         CmpValue = &A.makeNot(*CmpValue);
673526c9b7eSmartinboehme       Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
6746272226bSSam McCall                                   RHasVal->formula()));
675390029beSYitzhak Mandelbaum     }
676390029beSYitzhak Mandelbaum   }
67771f2ec2dSmartinboehme }
678390029beSYitzhak Mandelbaum 
679390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
680390029beSYitzhak Mandelbaum                                  const clang::Expr *E, Environment &Env) {
6816272226bSSam McCall   auto &A = Env.arena();
682390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
6832ee396b0Smartinboehme   auto *Loc = Env.get<RecordStorageLocation>(*E);
68471f2ec2dSmartinboehme   if (auto *HasVal = getHasValue(Env, Loc)) {
685390029beSYitzhak Mandelbaum     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
6866272226bSSam McCall       CmpValue = &A.makeNot(*CmpValue);
687526c9b7eSmartinboehme     Env.assume(
6886272226bSSam McCall         evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
689390029beSYitzhak Mandelbaum   }
690390029beSYitzhak Mandelbaum }
691390029beSYitzhak Mandelbaum 
69271f2ec2dSmartinboehme void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,
69371f2ec2dSmartinboehme                                    const clang::Expr *E, Environment &Env) {
69471f2ec2dSmartinboehme   auto &A = Env.arena();
69571f2ec2dSmartinboehme   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
6962ee396b0Smartinboehme   auto *Loc = Env.get<RecordStorageLocation>(*E);
69771f2ec2dSmartinboehme   if (auto *HasVal = getHasValue(Env, Loc)) {
69871f2ec2dSmartinboehme     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
69971f2ec2dSmartinboehme       CmpValue = &A.makeNot(*CmpValue);
70071f2ec2dSmartinboehme     Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
70171f2ec2dSmartinboehme                                 A.makeLiteral(false)));
70271f2ec2dSmartinboehme   }
70371f2ec2dSmartinboehme }
70471f2ec2dSmartinboehme 
7056ad0788cSKazu Hirata std::optional<StatementMatcher>
706a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
7075d22d1f5SYitzhak Mandelbaum   if (Options.IgnoreSmartPointerDereference) {
7085d22d1f5SYitzhak Mandelbaum     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
7095d22d1f5SYitzhak Mandelbaum         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
7105d22d1f5SYitzhak Mandelbaum         unless(hasArgument(0, expr(hasOptionalType()))))));
7115d22d1f5SYitzhak Mandelbaum     return expr(
7125d22d1f5SYitzhak Mandelbaum         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
7135d22d1f5SYitzhak Mandelbaum   }
71434e0d057SKazu Hirata   return std::nullopt;
715a184a0d8SYitzhak Mandelbaum }
716a184a0d8SYitzhak Mandelbaum 
71758fe7f96SSam Estep StatementMatcher
7186ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
7192f0630f8SAnton Dukeman   return isOptionalMemberCallWithNameMatcher(hasName("value"),
7202f0630f8SAnton Dukeman                                              IgnorableOptional);
72158fe7f96SSam Estep }
72258fe7f96SSam Estep 
72358fe7f96SSam Estep StatementMatcher
7246ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
72558fe7f96SSam Estep   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
72658fe7f96SSam Estep                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
72758fe7f96SSam Estep }
72858fe7f96SSam Estep 
7295d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() {
730b000b770SStanislav Gatev   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
731b000b770SStanislav Gatev   // lot of duplicated work (e.g. string comparisons), consider providing APIs
732b000b770SStanislav Gatev   // that avoid it through memoization.
7337538b360SWei Yi Tee   return CFGMatchSwitchBuilder<LatticeTransferState>()
7349e0fc676SStanislav Gatev       // make_optional
7357538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
736092a530cSStanislav Gatev 
7370e8d4a6dSYitzhak Mandelbaum       // optional::optional (in place)
7387538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
739092a530cSStanislav Gatev           isOptionalInPlaceConstructor(),
740092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
741092a530cSStanislav Gatev              LatticeTransferState &State) {
742f653d140SMartin Braenne             constructOptionalValue(*E, State.Env,
7430e8d4a6dSYitzhak Mandelbaum                                    State.Env.getBoolLiteralValue(true));
744092a530cSStanislav Gatev           })
7450e8d4a6dSYitzhak Mandelbaum       // optional::optional(nullopt_t)
746390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXConstructExpr>(
747390029beSYitzhak Mandelbaum           isOptionalNulloptConstructor(),
748390029beSYitzhak Mandelbaum           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
749390029beSYitzhak Mandelbaum              LatticeTransferState &State) {
750f653d140SMartin Braenne             constructOptionalValue(*E, State.Env,
7510e8d4a6dSYitzhak Mandelbaum                                    State.Env.getBoolLiteralValue(false));
752390029beSYitzhak Mandelbaum           })
7530e8d4a6dSYitzhak Mandelbaum       // optional::optional (value/conversion)
7547538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
755092a530cSStanislav Gatev                                        transferValueOrConversionConstructor)
7569e0fc676SStanislav Gatev 
757b000b770SStanislav Gatev       // optional::operator=
7587538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(
7597538b360SWei Yi Tee           isOptionalValueOrConversionAssignment(),
760b000b770SStanislav Gatev           transferValueOrConversionAssignment)
7617538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
762b000b770SStanislav Gatev                                           transferNulloptAssignment)
763b000b770SStanislav Gatev 
764af98b0afSStanislav Gatev       // optional::value
7657538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
7665d22d1f5SYitzhak Mandelbaum           valueCall(std::nullopt),
767092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
768092a530cSStanislav Gatev              LatticeTransferState &State) {
769af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
770af98b0afSStanislav Gatev           })
771af98b0afSStanislav Gatev 
7723bc1ea5bSMartin Braenne       // optional::operator*
7733bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
7747538b360SWei Yi Tee                                [](const CallExpr *E,
7757538b360SWei Yi Tee                                   const MatchFinder::MatchResult &,
776092a530cSStanislav Gatev                                   LatticeTransferState &State) {
777af98b0afSStanislav Gatev                                  transferUnwrapCall(E, E->getArg(0), State);
778af98b0afSStanislav Gatev                                })
779af98b0afSStanislav Gatev 
7803bc1ea5bSMartin Braenne       // optional::operator->
7813bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
7823bc1ea5bSMartin Braenne                                [](const CallExpr *E,
7833bc1ea5bSMartin Braenne                                   const MatchFinder::MatchResult &,
7843bc1ea5bSMartin Braenne                                   LatticeTransferState &State) {
7853bc1ea5bSMartin Braenne                                  transferArrowOpCall(E, E->getArg(0), State);
7863bc1ea5bSMartin Braenne                                })
7873bc1ea5bSMartin Braenne 
7882f0630f8SAnton Dukeman       // optional::has_value, optional::hasValue
7892f0630f8SAnton Dukeman       // Of the supported optionals only folly::Optional uses hasValue, but this
7902f0630f8SAnton Dukeman       // will also pass for other types
7917538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
7922f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(
7932f0630f8SAnton Dukeman               hasAnyName("has_value", "hasValue")),
794af98b0afSStanislav Gatev           transferOptionalHasValueCall)
795af98b0afSStanislav Gatev 
7969e0fc676SStanislav Gatev       // optional::operator bool
7977538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
7982f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
7999e0fc676SStanislav Gatev           transferOptionalHasValueCall)
8009e0fc676SStanislav Gatev 
8019e0fc676SStanislav Gatev       // optional::emplace
8027538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8032f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("emplace")),
804092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
805092a530cSStanislav Gatev              LatticeTransferState &State) {
8069ecdbe38SMartin Braenne             if (RecordStorageLocation *Loc =
807f653d140SMartin Braenne                     getImplicitObjectLocation(*E, State.Env)) {
808f653d140SMartin Braenne               createOptionalValue(*Loc, State.Env.getBoolLiteralValue(true),
809f653d140SMartin Braenne                                   State.Env);
810f653d140SMartin Braenne             }
811092a530cSStanislav Gatev           })
8129e0fc676SStanislav Gatev 
8139e0fc676SStanislav Gatev       // optional::reset
8147538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8152f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("reset")),
816092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
817092a530cSStanislav Gatev              LatticeTransferState &State) {
8189ecdbe38SMartin Braenne             if (RecordStorageLocation *Loc =
819f653d140SMartin Braenne                     getImplicitObjectLocation(*E, State.Env)) {
820f653d140SMartin Braenne               createOptionalValue(*Loc, State.Env.getBoolLiteralValue(false),
821f653d140SMartin Braenne                                   State.Env);
822f653d140SMartin Braenne             }
823092a530cSStanislav Gatev           })
8249e0fc676SStanislav Gatev 
8252ddd57aeSStanislav Gatev       // optional::swap
8262f0630f8SAnton Dukeman       .CaseOfCFGStmt<CXXMemberCallExpr>(
8272f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("swap")),
8282ddd57aeSStanislav Gatev           transferSwapCall)
8292ddd57aeSStanislav Gatev 
8302ddd57aeSStanislav Gatev       // std::swap
8317538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
8322ddd57aeSStanislav Gatev 
83325956d55SAMS21       // std::forward
83425956d55SAMS21       .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
83525956d55SAMS21 
8367f076004SYitzhak Mandelbaum       // opt.value_or("").empty()
8377538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
8387538b360SWei Yi Tee                            transferValueOrStringEmptyCall)
8397f076004SYitzhak Mandelbaum 
8407f076004SYitzhak Mandelbaum       // opt.value_or(X) != X
8417538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
8427f076004SYitzhak Mandelbaum 
843390029beSYitzhak Mandelbaum       // Comparisons (==, !=):
844390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
84571f2ec2dSmartinboehme           isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
846390029beSYitzhak Mandelbaum           transferOptionalAndOptionalCmp)
847390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
84871f2ec2dSmartinboehme           isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
84971f2ec2dSmartinboehme           [](const clang::CXXOperatorCallExpr *Cmp,
85071f2ec2dSmartinboehme              const MatchFinder::MatchResult &, LatticeTransferState &State) {
85171f2ec2dSmartinboehme             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);
85271f2ec2dSmartinboehme           })
85371f2ec2dSmartinboehme       .CaseOfCFGStmt<CXXOperatorCallExpr>(
85471f2ec2dSmartinboehme           isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
85571f2ec2dSmartinboehme           [](const clang::CXXOperatorCallExpr *Cmp,
85671f2ec2dSmartinboehme              const MatchFinder::MatchResult &, LatticeTransferState &State) {
85771f2ec2dSmartinboehme             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);
85871f2ec2dSmartinboehme           })
85971f2ec2dSmartinboehme       .CaseOfCFGStmt<CXXOperatorCallExpr>(
86071f2ec2dSmartinboehme           isComparisonOperatorCall(
86171f2ec2dSmartinboehme               hasOptionalType(),
86271f2ec2dSmartinboehme               unless(anyOf(hasOptionalType(), hasNulloptType()))),
863390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
864390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
865390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
866390029beSYitzhak Mandelbaum           })
867390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
86871f2ec2dSmartinboehme           isComparisonOperatorCall(
86971f2ec2dSmartinboehme               unless(anyOf(hasOptionalType(), hasNulloptType())),
870390029beSYitzhak Mandelbaum               hasOptionalType()),
871390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
872390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
873390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
874390029beSYitzhak Mandelbaum           })
875390029beSYitzhak Mandelbaum 
87665e710c3SStanislav Gatev       // returns optional
8777538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
87865e710c3SStanislav Gatev                                transferCallReturningOptional)
87965e710c3SStanislav Gatev 
880af98b0afSStanislav Gatev       .Build();
881af98b0afSStanislav Gatev }
882af98b0afSStanislav Gatev 
883004a7ceaSYitzhak Mandelbaum llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
88458fe7f96SSam Estep                                                      const Environment &Env) {
88571f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
88671f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, Env))) {
88771f2ec2dSmartinboehme     auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
88858fe7f96SSam Estep     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
889526c9b7eSmartinboehme       if (Env.proves(HasValueVal->formula()))
89058fe7f96SSam Estep         return {};
89158fe7f96SSam Estep     }
89258fe7f96SSam Estep   }
89358fe7f96SSam Estep 
89458fe7f96SSam Estep   // Record that this unwrap is *not* provably safe.
89558fe7f96SSam Estep   // FIXME: include either the name of the optional (if applicable) or a source
89658fe7f96SSam Estep   // range of the access for easier interpretation of the result.
89758fe7f96SSam Estep   return {ObjectExpr->getBeginLoc()};
89858fe7f96SSam Estep }
89958fe7f96SSam Estep 
90058fe7f96SSam Estep auto buildDiagnoseMatchSwitch(
90158fe7f96SSam Estep     const UncheckedOptionalAccessModelOptions &Options) {
90258fe7f96SSam Estep   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
90358fe7f96SSam Estep   // lot of duplicated work (e.g. string comparisons), consider providing APIs
90458fe7f96SSam Estep   // that avoid it through memoization.
90558fe7f96SSam Estep   auto IgnorableOptional = ignorableOptional(Options);
906004a7ceaSYitzhak Mandelbaum   return CFGMatchSwitchBuilder<const Environment,
907004a7ceaSYitzhak Mandelbaum                                llvm::SmallVector<SourceLocation>>()
90858fe7f96SSam Estep       // optional::value
9097538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
91058fe7f96SSam Estep           valueCall(IgnorableOptional),
91158fe7f96SSam Estep           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
91258fe7f96SSam Estep              const Environment &Env) {
9136a81e694SMartin Braenne             return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
91458fe7f96SSam Estep           })
91558fe7f96SSam Estep 
91658fe7f96SSam Estep       // optional::operator*, optional::operator->
9176a81e694SMartin Braenne       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
9186a81e694SMartin Braenne                                [](const CallExpr *E,
9196a81e694SMartin Braenne                                   const MatchFinder::MatchResult &,
92058fe7f96SSam Estep                                   const Environment &Env) {
9216a81e694SMartin Braenne                                  return diagnoseUnwrapCall(E->getArg(0), Env);
92258fe7f96SSam Estep                                })
92358fe7f96SSam Estep       .Build();
92458fe7f96SSam Estep }
92558fe7f96SSam Estep 
926af98b0afSStanislav Gatev } // namespace
927af98b0afSStanislav Gatev 
9287e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher
9297e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() {
930*d712c5edSmartinboehme   return cxxRecordDecl(optionalClass());
93171f2ec2dSmartinboehme }
93271f2ec2dSmartinboehme 
93371f2ec2dSmartinboehme UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
93471f2ec2dSmartinboehme                                                            Environment &Env)
935cf1f978dSSam Estep     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
93671f2ec2dSmartinboehme       TransferMatchSwitch(buildTransferMatchSwitch()) {
93771f2ec2dSmartinboehme   Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
93871f2ec2dSmartinboehme       [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
939*d712c5edSmartinboehme         const CXXRecordDecl *Optional =
940*d712c5edSmartinboehme             getOptionalBaseClass(Ty->getAsCXXRecordDecl());
941*d712c5edSmartinboehme         if (Optional == nullptr)
94271f2ec2dSmartinboehme           return {};
943*d712c5edSmartinboehme         return {{"value", valueTypeFromOptionalDecl(*Optional)},
94471f2ec2dSmartinboehme                 {"has_value", Ctx.BoolTy}};
94571f2ec2dSmartinboehme       });
94671f2ec2dSmartinboehme }
947af98b0afSStanislav Gatev 
9486b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
9497538b360SWei Yi Tee                                             NoopLattice &L, Environment &Env) {
950af98b0afSStanislav Gatev   LatticeTransferState State(L, Env);
9516b991ba4SYitzhak Mandelbaum   TransferMatchSwitch(Elt, getASTContext(), State);
952af98b0afSStanislav Gatev }
953af98b0afSStanislav Gatev 
95458fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
95558fe7f96SSam Estep     UncheckedOptionalAccessModelOptions Options)
95658fe7f96SSam Estep     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
95758fe7f96SSam Estep 
958af98b0afSStanislav Gatev } // namespace dataflow
959af98b0afSStanislav Gatev } // namespace clang
960