181ad6265SDimitry Andric //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric //
981ad6265SDimitry Andric //  This file defines a dataflow analysis that detects unsafe uses of optional
1081ad6265SDimitry Andric //  values.
1181ad6265SDimitry Andric //
1281ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1381ad6265SDimitry Andric 
1481ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
1581ad6265SDimitry Andric #include "clang/AST/ASTContext.h"
1681ad6265SDimitry Andric #include "clang/AST/DeclCXX.h"
1781ad6265SDimitry Andric #include "clang/AST/Expr.h"
1881ad6265SDimitry Andric #include "clang/AST/ExprCXX.h"
1981ad6265SDimitry Andric #include "clang/AST/Stmt.h"
2081ad6265SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h"
2106c3fb27SDimitry Andric #include "clang/ASTMatchers/ASTMatchersMacros.h"
22bdd1243dSDimitry Andric #include "clang/Analysis/CFG.h"
23bdd1243dSDimitry Andric #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
2481ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
2506c3fb27SDimitry Andric #include "clang/Analysis/FlowSensitive/Formula.h"
2681ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/NoopLattice.h"
27bdd1243dSDimitry Andric #include "clang/Analysis/FlowSensitive/StorageLocation.h"
2881ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/Value.h"
2981ad6265SDimitry Andric #include "clang/Basic/SourceLocation.h"
3081ad6265SDimitry Andric #include "llvm/ADT/StringRef.h"
3181ad6265SDimitry Andric #include "llvm/Support/Casting.h"
32bdd1243dSDimitry Andric #include "llvm/Support/ErrorHandling.h"
3381ad6265SDimitry Andric #include <cassert>
3481ad6265SDimitry Andric #include <memory>
35bdd1243dSDimitry Andric #include <optional>
3681ad6265SDimitry Andric #include <utility>
3781ad6265SDimitry Andric 
3881ad6265SDimitry Andric namespace clang {
3981ad6265SDimitry Andric namespace dataflow {
4006c3fb27SDimitry Andric 
4106c3fb27SDimitry Andric static bool isTopLevelNamespaceWithName(const NamespaceDecl &NS,
4206c3fb27SDimitry Andric                                         llvm::StringRef Name) {
4306c3fb27SDimitry Andric   return NS.getDeclName().isIdentifier() && NS.getName() == Name &&
4406c3fb27SDimitry Andric          NS.getParent() != nullptr && NS.getParent()->isTranslationUnit();
4506c3fb27SDimitry Andric }
4606c3fb27SDimitry Andric 
4706c3fb27SDimitry Andric static bool hasOptionalClassName(const CXXRecordDecl &RD) {
4806c3fb27SDimitry Andric   if (!RD.getDeclName().isIdentifier())
4906c3fb27SDimitry Andric     return false;
5006c3fb27SDimitry Andric 
5106c3fb27SDimitry Andric   if (RD.getName() == "optional") {
5206c3fb27SDimitry Andric     if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
5306c3fb27SDimitry Andric       return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl");
5406c3fb27SDimitry Andric     return false;
5506c3fb27SDimitry Andric   }
5606c3fb27SDimitry Andric 
5706c3fb27SDimitry Andric   if (RD.getName() == "Optional") {
5806c3fb27SDimitry Andric     // Check whether namespace is "::base" or "::folly".
5906c3fb27SDimitry Andric     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
6006c3fb27SDimitry Andric     return N != nullptr && (isTopLevelNamespaceWithName(*N, "base") ||
6106c3fb27SDimitry Andric                             isTopLevelNamespaceWithName(*N, "folly"));
6206c3fb27SDimitry Andric   }
6306c3fb27SDimitry Andric 
6406c3fb27SDimitry Andric   return false;
6506c3fb27SDimitry Andric }
6606c3fb27SDimitry Andric 
67*0fca6ea1SDimitry Andric static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) {
68*0fca6ea1SDimitry Andric   if (RD == nullptr)
69*0fca6ea1SDimitry Andric     return nullptr;
70*0fca6ea1SDimitry Andric   if (hasOptionalClassName(*RD))
71*0fca6ea1SDimitry Andric     return RD;
72*0fca6ea1SDimitry Andric 
73*0fca6ea1SDimitry Andric   if (!RD->hasDefinition())
74*0fca6ea1SDimitry Andric     return nullptr;
75*0fca6ea1SDimitry Andric 
76*0fca6ea1SDimitry Andric   for (const CXXBaseSpecifier &Base : RD->bases())
77*0fca6ea1SDimitry Andric     if (const CXXRecordDecl *BaseClass =
78*0fca6ea1SDimitry Andric             getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl()))
79*0fca6ea1SDimitry Andric       return BaseClass;
80*0fca6ea1SDimitry Andric 
81*0fca6ea1SDimitry Andric   return nullptr;
82*0fca6ea1SDimitry Andric }
83*0fca6ea1SDimitry Andric 
8481ad6265SDimitry Andric namespace {
8581ad6265SDimitry Andric 
8681ad6265SDimitry Andric using namespace ::clang::ast_matchers;
8781ad6265SDimitry Andric using LatticeTransferState = TransferState<NoopLattice>;
8881ad6265SDimitry Andric 
89*0fca6ea1SDimitry Andric AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); }
90*0fca6ea1SDimitry Andric 
91*0fca6ea1SDimitry Andric AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
92*0fca6ea1SDimitry Andric   return getOptionalBaseClass(&Node) != nullptr;
9306c3fb27SDimitry Andric }
9406c3fb27SDimitry Andric 
95*0fca6ea1SDimitry Andric auto desugarsToOptionalType() {
9681ad6265SDimitry Andric   return hasUnqualifiedDesugaredType(
97*0fca6ea1SDimitry Andric       recordType(hasDeclaration(cxxRecordDecl(optionalClass()))));
9881ad6265SDimitry Andric }
9981ad6265SDimitry Andric 
100*0fca6ea1SDimitry Andric auto desugarsToOptionalOrDerivedType() {
101*0fca6ea1SDimitry Andric   return hasUnqualifiedDesugaredType(
102*0fca6ea1SDimitry Andric       recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass()))));
103*0fca6ea1SDimitry Andric }
104*0fca6ea1SDimitry Andric 
105*0fca6ea1SDimitry Andric auto hasOptionalType() { return hasType(desugarsToOptionalType()); }
106*0fca6ea1SDimitry Andric 
107*0fca6ea1SDimitry Andric /// Matches any of the spellings of the optional types and sugar, aliases,
108*0fca6ea1SDimitry Andric /// derived classes, etc.
109*0fca6ea1SDimitry Andric auto hasOptionalOrDerivedType() {
110*0fca6ea1SDimitry Andric   return hasType(desugarsToOptionalOrDerivedType());
111*0fca6ea1SDimitry Andric }
112*0fca6ea1SDimitry Andric 
113*0fca6ea1SDimitry Andric QualType getPublicType(const Expr *E) {
114*0fca6ea1SDimitry Andric   auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());
115*0fca6ea1SDimitry Andric   if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) {
116*0fca6ea1SDimitry Andric     QualType Ty = E->getType();
117*0fca6ea1SDimitry Andric     if (Ty->isPointerType())
118*0fca6ea1SDimitry Andric       return Ty->getPointeeType();
119*0fca6ea1SDimitry Andric     return Ty;
120*0fca6ea1SDimitry Andric   }
121*0fca6ea1SDimitry Andric 
122*0fca6ea1SDimitry Andric   // Is the derived type that we're casting from the type of `*this`? In this
123*0fca6ea1SDimitry Andric   // special case, we can upcast to the base class even if the base is
124*0fca6ea1SDimitry Andric   // non-public.
125*0fca6ea1SDimitry Andric   bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr());
126*0fca6ea1SDimitry Andric 
127*0fca6ea1SDimitry Andric   // Find the least-derived type in the path (i.e. the last entry in the list)
128*0fca6ea1SDimitry Andric   // that we can access.
129*0fca6ea1SDimitry Andric   const CXXBaseSpecifier *PublicBase = nullptr;
130*0fca6ea1SDimitry Andric   for (const CXXBaseSpecifier *Base : Cast->path()) {
131*0fca6ea1SDimitry Andric     if (Base->getAccessSpecifier() != AS_public && !CastingFromThis)
132*0fca6ea1SDimitry Andric       break;
133*0fca6ea1SDimitry Andric     PublicBase = Base;
134*0fca6ea1SDimitry Andric     CastingFromThis = false;
135*0fca6ea1SDimitry Andric   }
136*0fca6ea1SDimitry Andric 
137*0fca6ea1SDimitry Andric   if (PublicBase != nullptr)
138*0fca6ea1SDimitry Andric     return PublicBase->getType();
139*0fca6ea1SDimitry Andric 
140*0fca6ea1SDimitry Andric   // We didn't find any public type that we could cast to. There may be more
141*0fca6ea1SDimitry Andric   // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this
142*0fca6ea1SDimitry Andric   // will return the type of `getSubExpr()`.)
143*0fca6ea1SDimitry Andric   return getPublicType(Cast->getSubExpr());
144*0fca6ea1SDimitry Andric }
145*0fca6ea1SDimitry Andric 
146*0fca6ea1SDimitry Andric // Returns the least-derived type for the receiver of `MCE` that
147*0fca6ea1SDimitry Andric // `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to.
148*0fca6ea1SDimitry Andric // Effectively, we upcast until we reach a non-public base class, unless that
149*0fca6ea1SDimitry Andric // base is a base of `*this`.
150*0fca6ea1SDimitry Andric //
151*0fca6ea1SDimitry Andric // This is needed to correctly match methods called on types derived from
152*0fca6ea1SDimitry Andric // `std::optional`.
153*0fca6ea1SDimitry Andric //
154*0fca6ea1SDimitry Andric // Say we have a `struct Derived : public std::optional<int> {} d;` For a call
155*0fca6ea1SDimitry Andric // `d.has_value()`, the `getImplicitObjectArgument()` looks like this:
156*0fca6ea1SDimitry Andric //
157*0fca6ea1SDimitry Andric //   ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue
158*0fca6ea1SDimitry Andric //   |            <UncheckedDerivedToBase (optional -> __optional_storage_base)>
159*0fca6ea1SDimitry Andric //   `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived'
160*0fca6ea1SDimitry Andric //
161*0fca6ea1SDimitry Andric // The type of the implicit object argument is `__optional_storage_base`
162*0fca6ea1SDimitry Andric // (since this is the internal type that `has_value()` is declared on). If we
163*0fca6ea1SDimitry Andric // call `IgnoreParenImpCasts()` on the implicit object argument, we get the
164*0fca6ea1SDimitry Andric // `DeclRefExpr`, which has type `Derived`. Neither of these types is
165*0fca6ea1SDimitry Andric // `optional`, and hence neither is sufficient for querying whether we are
166*0fca6ea1SDimitry Andric // calling a method on `optional`.
167*0fca6ea1SDimitry Andric //
168*0fca6ea1SDimitry Andric // Instead, starting with the most derived type, we need to follow the chain of
169*0fca6ea1SDimitry Andric // casts
170*0fca6ea1SDimitry Andric QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) {
171*0fca6ea1SDimitry Andric   return getPublicType(MCE.getImplicitObjectArgument());
172*0fca6ea1SDimitry Andric }
173*0fca6ea1SDimitry Andric 
174*0fca6ea1SDimitry Andric AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType,
175*0fca6ea1SDimitry Andric               ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
176*0fca6ea1SDimitry Andric   return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder);
177*0fca6ea1SDimitry Andric }
17881ad6265SDimitry Andric 
17906c3fb27SDimitry Andric auto isOptionalMemberCallWithNameMatcher(
18006c3fb27SDimitry Andric     ast_matchers::internal::Matcher<NamedDecl> matcher,
181bdd1243dSDimitry Andric     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
182*0fca6ea1SDimitry Andric   return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable)))
183*0fca6ea1SDimitry Andric                                      : anything(),
184*0fca6ea1SDimitry Andric                            publicReceiverType(desugarsToOptionalType()),
18506c3fb27SDimitry Andric                            callee(cxxMethodDecl(matcher)));
18681ad6265SDimitry Andric }
18781ad6265SDimitry Andric 
18881ad6265SDimitry Andric auto isOptionalOperatorCallWithName(
18981ad6265SDimitry Andric     llvm::StringRef operator_name,
190bdd1243dSDimitry Andric     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
19181ad6265SDimitry Andric   return cxxOperatorCallExpr(
19281ad6265SDimitry Andric       hasOverloadedOperatorName(operator_name),
19381ad6265SDimitry Andric       callee(cxxMethodDecl(ofClass(optionalClass()))),
19481ad6265SDimitry Andric       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
19581ad6265SDimitry Andric }
19681ad6265SDimitry Andric 
19781ad6265SDimitry Andric auto isMakeOptionalCall() {
19806c3fb27SDimitry Andric   return callExpr(callee(functionDecl(hasAnyName(
19906c3fb27SDimitry Andric                       "std::make_optional", "base::make_optional",
20006c3fb27SDimitry Andric                       "absl::make_optional", "folly::make_optional"))),
20181ad6265SDimitry Andric                   hasOptionalType());
20281ad6265SDimitry Andric }
20381ad6265SDimitry Andric 
204bdd1243dSDimitry Andric auto nulloptTypeDecl() {
20506c3fb27SDimitry Andric   return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",
20606c3fb27SDimitry Andric                               "base::nullopt_t", "folly::None"));
20781ad6265SDimitry Andric }
20881ad6265SDimitry Andric 
209bdd1243dSDimitry Andric auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
210bdd1243dSDimitry Andric 
21181ad6265SDimitry Andric auto inPlaceClass() {
21206c3fb27SDimitry Andric   return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
21306c3fb27SDimitry Andric                                "base::in_place_t", "folly::in_place_t"));
21481ad6265SDimitry Andric }
21581ad6265SDimitry Andric 
21681ad6265SDimitry Andric auto isOptionalNulloptConstructor() {
217bdd1243dSDimitry Andric   return cxxConstructExpr(
218bdd1243dSDimitry Andric       hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
219*0fca6ea1SDimitry Andric                                         hasParameter(0, hasNulloptType()))),
220*0fca6ea1SDimitry Andric       hasOptionalOrDerivedType());
22181ad6265SDimitry Andric }
22281ad6265SDimitry Andric 
22381ad6265SDimitry Andric auto isOptionalInPlaceConstructor() {
224*0fca6ea1SDimitry Andric   return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())),
225*0fca6ea1SDimitry Andric                           hasOptionalOrDerivedType());
22681ad6265SDimitry Andric }
22781ad6265SDimitry Andric 
22881ad6265SDimitry Andric auto isOptionalValueOrConversionConstructor() {
22981ad6265SDimitry Andric   return cxxConstructExpr(
23081ad6265SDimitry Andric       unless(hasDeclaration(
23181ad6265SDimitry Andric           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
232*0fca6ea1SDimitry Andric       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())),
233*0fca6ea1SDimitry Andric       hasOptionalOrDerivedType());
23481ad6265SDimitry Andric }
23581ad6265SDimitry Andric 
23681ad6265SDimitry Andric auto isOptionalValueOrConversionAssignment() {
23781ad6265SDimitry Andric   return cxxOperatorCallExpr(
23881ad6265SDimitry Andric       hasOverloadedOperatorName("="),
239*0fca6ea1SDimitry Andric       callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
24081ad6265SDimitry Andric       unless(hasDeclaration(cxxMethodDecl(
24181ad6265SDimitry Andric           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
24281ad6265SDimitry Andric       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
24381ad6265SDimitry Andric }
24481ad6265SDimitry Andric 
24581ad6265SDimitry Andric auto isOptionalNulloptAssignment() {
246*0fca6ea1SDimitry Andric   return cxxOperatorCallExpr(
247*0fca6ea1SDimitry Andric       hasOverloadedOperatorName("="),
248*0fca6ea1SDimitry Andric       callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
249*0fca6ea1SDimitry Andric       argumentCountIs(2), hasArgument(1, hasNulloptType()));
25081ad6265SDimitry Andric }
25181ad6265SDimitry Andric 
25281ad6265SDimitry Andric auto isStdSwapCall() {
25381ad6265SDimitry Andric   return callExpr(callee(functionDecl(hasName("std::swap"))),
254*0fca6ea1SDimitry Andric                   argumentCountIs(2),
255*0fca6ea1SDimitry Andric                   hasArgument(0, hasOptionalOrDerivedType()),
256*0fca6ea1SDimitry Andric                   hasArgument(1, hasOptionalOrDerivedType()));
25781ad6265SDimitry Andric }
25881ad6265SDimitry Andric 
25906c3fb27SDimitry Andric auto isStdForwardCall() {
26006c3fb27SDimitry Andric   return callExpr(callee(functionDecl(hasName("std::forward"))),
261*0fca6ea1SDimitry Andric                   argumentCountIs(1),
262*0fca6ea1SDimitry Andric                   hasArgument(0, hasOptionalOrDerivedType()));
26306c3fb27SDimitry Andric }
26406c3fb27SDimitry Andric 
26581ad6265SDimitry Andric constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
26681ad6265SDimitry Andric 
26781ad6265SDimitry Andric auto isValueOrStringEmptyCall() {
26881ad6265SDimitry Andric   // `opt.value_or("").empty()`
26981ad6265SDimitry Andric   return cxxMemberCallExpr(
27081ad6265SDimitry Andric       callee(cxxMethodDecl(hasName("empty"))),
27181ad6265SDimitry Andric       onImplicitObjectArgument(ignoringImplicit(
27281ad6265SDimitry Andric           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
27381ad6265SDimitry Andric                             callee(cxxMethodDecl(hasName("value_or"),
27481ad6265SDimitry Andric                                                  ofClass(optionalClass()))),
27581ad6265SDimitry Andric                             hasArgument(0, stringLiteral(hasSize(0))))
27681ad6265SDimitry Andric               .bind(ValueOrCallID))));
27781ad6265SDimitry Andric }
27881ad6265SDimitry Andric 
27981ad6265SDimitry Andric auto isValueOrNotEqX() {
28081ad6265SDimitry Andric   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
28181ad6265SDimitry Andric     return hasOperands(
28281ad6265SDimitry Andric         ignoringImplicit(
28381ad6265SDimitry Andric             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
28481ad6265SDimitry Andric                               callee(cxxMethodDecl(hasName("value_or"),
28581ad6265SDimitry Andric                                                    ofClass(optionalClass()))),
28681ad6265SDimitry Andric                               hasArgument(0, Arg))
28781ad6265SDimitry Andric                 .bind(ValueOrCallID)),
28881ad6265SDimitry Andric         ignoringImplicit(Arg));
28981ad6265SDimitry Andric   };
29081ad6265SDimitry Andric 
29181ad6265SDimitry Andric   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
29281ad6265SDimitry Andric   // support this pattern for any expression, but the AST does not have a
29381ad6265SDimitry Andric   // generic expression comparison facility, so we specialize to common cases
29481ad6265SDimitry Andric   // seen in practice.  FIXME: define a matcher that compares values across
29581ad6265SDimitry Andric   // nodes, which would let us generalize this to any `X`.
29681ad6265SDimitry Andric   return binaryOperation(hasOperatorName("!="),
29781ad6265SDimitry Andric                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
29881ad6265SDimitry Andric                                ComparesToSame(stringLiteral(hasSize(0))),
29981ad6265SDimitry Andric                                ComparesToSame(integerLiteral(equals(0)))));
30081ad6265SDimitry Andric }
30181ad6265SDimitry Andric 
30281ad6265SDimitry Andric auto isCallReturningOptional() {
303*0fca6ea1SDimitry Andric   return callExpr(hasType(qualType(
304*0fca6ea1SDimitry Andric       anyOf(desugarsToOptionalOrDerivedType(),
305*0fca6ea1SDimitry Andric             referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
30681ad6265SDimitry Andric }
30781ad6265SDimitry Andric 
308bdd1243dSDimitry Andric template <typename L, typename R>
309bdd1243dSDimitry Andric auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
310bdd1243dSDimitry Andric   return cxxOperatorCallExpr(
311bdd1243dSDimitry Andric       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
312bdd1243dSDimitry Andric       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
313bdd1243dSDimitry Andric       hasArgument(1, rhs_arg_matcher));
314bdd1243dSDimitry Andric }
315bdd1243dSDimitry Andric 
31606c3fb27SDimitry Andric /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
31706c3fb27SDimitry Andric const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
318cb14a3feSDimitry Andric   auto *Value = Env.get<BoolValue>(Expr);
319bdd1243dSDimitry Andric   if (Value != nullptr)
32006c3fb27SDimitry Andric     return Value->formula();
321bdd1243dSDimitry Andric 
322bdd1243dSDimitry Andric   Value = &Env.makeAtomicBoolValue();
3235f757f3fSDimitry Andric   Env.setValue(Expr, *Value);
32406c3fb27SDimitry Andric   return Value->formula();
325bdd1243dSDimitry Andric }
326bdd1243dSDimitry Andric 
3275f757f3fSDimitry Andric StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) {
3285f757f3fSDimitry Andric   return OptionalLoc.getSyntheticField("has_value");
3295f757f3fSDimitry Andric }
3305f757f3fSDimitry Andric 
3315f757f3fSDimitry Andric StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) {
3325f757f3fSDimitry Andric   return OptionalLoc.getSyntheticField("value");
3335f757f3fSDimitry Andric }
3345f757f3fSDimitry Andric 
33581ad6265SDimitry Andric /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
3365f757f3fSDimitry Andric /// property of the optional at `OptionalLoc`.
3375f757f3fSDimitry Andric void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,
3385f757f3fSDimitry Andric                  Environment &Env) {
3395f757f3fSDimitry Andric   Env.setValue(locForHasValue(OptionalLoc), HasValueVal);
34081ad6265SDimitry Andric }
34181ad6265SDimitry Andric 
34281ad6265SDimitry Andric /// Returns the symbolic value that represents the "has_value" property of the
3435f757f3fSDimitry Andric /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null.
3445f757f3fSDimitry Andric BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {
3455f757f3fSDimitry Andric   if (OptionalLoc == nullptr)
3465f757f3fSDimitry Andric     return nullptr;
3475f757f3fSDimitry Andric   StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
348cb14a3feSDimitry Andric   auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);
34981ad6265SDimitry Andric   if (HasValueVal == nullptr) {
35081ad6265SDimitry Andric     HasValueVal = &Env.makeAtomicBoolValue();
3515f757f3fSDimitry Andric     Env.setValue(HasValueLoc, *HasValueVal);
35281ad6265SDimitry Andric   }
35381ad6265SDimitry Andric   return HasValueVal;
35481ad6265SDimitry Andric }
35581ad6265SDimitry Andric 
356*0fca6ea1SDimitry Andric QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {
357*0fca6ea1SDimitry Andric   auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);
358*0fca6ea1SDimitry Andric   return CTSD.getTemplateArgs()[0].getAsType();
35981ad6265SDimitry Andric }
36081ad6265SDimitry Andric 
36181ad6265SDimitry Andric /// Returns the number of optional wrappers in `Type`.
36281ad6265SDimitry Andric ///
36381ad6265SDimitry Andric /// For example, if `Type` is `optional<optional<int>>`, the result of this
36481ad6265SDimitry Andric /// function will be 2.
36581ad6265SDimitry Andric int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
366*0fca6ea1SDimitry Andric   const CXXRecordDecl *Optional =
367*0fca6ea1SDimitry Andric       getOptionalBaseClass(Type->getAsCXXRecordDecl());
368*0fca6ea1SDimitry Andric   if (Optional == nullptr)
36981ad6265SDimitry Andric     return 0;
37081ad6265SDimitry Andric   return 1 + countOptionalWrappers(
37181ad6265SDimitry Andric                  ASTCtx,
372*0fca6ea1SDimitry Andric                  valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
37381ad6265SDimitry Andric }
37481ad6265SDimitry Andric 
3755f757f3fSDimitry Andric StorageLocation *getLocBehindPossiblePointer(const Expr &E,
3765f757f3fSDimitry Andric                                              const Environment &Env) {
3775f757f3fSDimitry Andric   if (E.isPRValue()) {
3785f757f3fSDimitry Andric     if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
3795f757f3fSDimitry Andric       return &PointerVal->getPointeeLoc();
38081ad6265SDimitry Andric     return nullptr;
38181ad6265SDimitry Andric   }
3825f757f3fSDimitry Andric   return Env.getStorageLocation(E);
38381ad6265SDimitry Andric }
38481ad6265SDimitry Andric 
38581ad6265SDimitry Andric void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
38681ad6265SDimitry Andric                         LatticeTransferState &State) {
3875f757f3fSDimitry Andric   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
3885f757f3fSDimitry Andric           getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
3895f757f3fSDimitry Andric     if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)
3905f757f3fSDimitry Andric       State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
39181ad6265SDimitry Andric   }
39281ad6265SDimitry Andric }
39381ad6265SDimitry Andric 
39406c3fb27SDimitry Andric void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
39506c3fb27SDimitry Andric                          LatticeTransferState &State) {
3965f757f3fSDimitry Andric   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
3975f757f3fSDimitry Andric           getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
3985f757f3fSDimitry Andric     State.Env.setValue(
3995f757f3fSDimitry Andric         *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));
40006c3fb27SDimitry Andric }
40106c3fb27SDimitry Andric 
40281ad6265SDimitry Andric void transferMakeOptionalCall(const CallExpr *E,
40381ad6265SDimitry Andric                               const MatchFinder::MatchResult &,
40481ad6265SDimitry Andric                               LatticeTransferState &State) {
405*0fca6ea1SDimitry Andric   setHasValue(State.Env.getResultObjectLocation(*E),
406*0fca6ea1SDimitry Andric               State.Env.getBoolLiteralValue(true), State.Env);
40781ad6265SDimitry Andric }
40881ad6265SDimitry Andric 
40981ad6265SDimitry Andric void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
41081ad6265SDimitry Andric                                   const MatchFinder::MatchResult &,
41181ad6265SDimitry Andric                                   LatticeTransferState &State) {
41281ad6265SDimitry Andric   if (auto *HasValueVal = getHasValue(
4135f757f3fSDimitry Andric           State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
4145f757f3fSDimitry Andric     State.Env.setValue(*CallExpr, *HasValueVal);
41581ad6265SDimitry Andric   }
41681ad6265SDimitry Andric }
41781ad6265SDimitry Andric 
41881ad6265SDimitry Andric /// `ModelPred` builds a logical formula relating the predicate in
41981ad6265SDimitry Andric /// `ValueOrPredExpr` to the optional's `has_value` property.
42006c3fb27SDimitry Andric void transferValueOrImpl(
42106c3fb27SDimitry Andric     const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
42281ad6265SDimitry Andric     LatticeTransferState &State,
42306c3fb27SDimitry Andric     const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
42406c3fb27SDimitry Andric                                 const Formula &HasValueVal)) {
42581ad6265SDimitry Andric   auto &Env = State.Env;
42681ad6265SDimitry Andric 
4275f757f3fSDimitry Andric   const auto *MCE =
4285f757f3fSDimitry Andric       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);
42981ad6265SDimitry Andric 
4305f757f3fSDimitry Andric   auto *HasValueVal =
4315f757f3fSDimitry Andric       getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env));
43281ad6265SDimitry Andric   if (HasValueVal == nullptr)
43381ad6265SDimitry Andric     return;
43481ad6265SDimitry Andric 
4355f757f3fSDimitry Andric   Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
43606c3fb27SDimitry Andric                        HasValueVal->formula()));
43781ad6265SDimitry Andric }
43881ad6265SDimitry Andric 
43981ad6265SDimitry Andric void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
44081ad6265SDimitry Andric                                     const MatchFinder::MatchResult &Result,
44181ad6265SDimitry Andric                                     LatticeTransferState &State) {
44281ad6265SDimitry Andric   return transferValueOrImpl(ComparisonExpr, Result, State,
44306c3fb27SDimitry Andric                              [](Environment &Env, const Formula &ExprVal,
44406c3fb27SDimitry Andric                                 const Formula &HasValueVal) -> const Formula & {
44506c3fb27SDimitry Andric                                auto &A = Env.arena();
44681ad6265SDimitry Andric                                // If the result is *not* empty, then we know the
44781ad6265SDimitry Andric                                // optional must have been holding a value. If
44881ad6265SDimitry Andric                                // `ExprVal` is true, though, we don't learn
44981ad6265SDimitry Andric                                // anything definite about `has_value`, so we
45081ad6265SDimitry Andric                                // don't add any corresponding implications to
45181ad6265SDimitry Andric                                // the flow condition.
45206c3fb27SDimitry Andric                                return A.makeImplies(A.makeNot(ExprVal),
45381ad6265SDimitry Andric                                                     HasValueVal);
45481ad6265SDimitry Andric                              });
45581ad6265SDimitry Andric }
45681ad6265SDimitry Andric 
45781ad6265SDimitry Andric void transferValueOrNotEqX(const Expr *ComparisonExpr,
45881ad6265SDimitry Andric                            const MatchFinder::MatchResult &Result,
45981ad6265SDimitry Andric                            LatticeTransferState &State) {
46081ad6265SDimitry Andric   transferValueOrImpl(ComparisonExpr, Result, State,
46106c3fb27SDimitry Andric                       [](Environment &Env, const Formula &ExprVal,
46206c3fb27SDimitry Andric                          const Formula &HasValueVal) -> const Formula & {
46306c3fb27SDimitry Andric                         auto &A = Env.arena();
46481ad6265SDimitry Andric                         // We know that if `(opt.value_or(X) != X)` then
46581ad6265SDimitry Andric                         // `opt.hasValue()`, even without knowing further
46681ad6265SDimitry Andric                         // details about the contents of `opt`.
46706c3fb27SDimitry Andric                         return A.makeImplies(ExprVal, HasValueVal);
46881ad6265SDimitry Andric                       });
46981ad6265SDimitry Andric }
47081ad6265SDimitry Andric 
47181ad6265SDimitry Andric void transferCallReturningOptional(const CallExpr *E,
47281ad6265SDimitry Andric                                    const MatchFinder::MatchResult &Result,
47381ad6265SDimitry Andric                                    LatticeTransferState &State) {
4745f757f3fSDimitry Andric   RecordStorageLocation *Loc = nullptr;
47506c3fb27SDimitry Andric   if (E->isPRValue()) {
47606c3fb27SDimitry Andric     Loc = &State.Env.getResultObjectLocation(*E);
47706c3fb27SDimitry Andric   } else {
478cb14a3feSDimitry Andric     Loc = State.Env.get<RecordStorageLocation>(*E);
4795f757f3fSDimitry Andric     if (Loc == nullptr) {
4805f757f3fSDimitry Andric       Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));
4815f757f3fSDimitry Andric       State.Env.setStorageLocation(*E, *Loc);
4825f757f3fSDimitry Andric     }
48381ad6265SDimitry Andric   }
48481ad6265SDimitry Andric 
485*0fca6ea1SDimitry Andric   if (State.Env.getValue(locForHasValue(*Loc)) != nullptr)
486*0fca6ea1SDimitry Andric     return;
487*0fca6ea1SDimitry Andric 
488*0fca6ea1SDimitry Andric   setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
48981ad6265SDimitry Andric }
49006c3fb27SDimitry Andric 
49106c3fb27SDimitry Andric void constructOptionalValue(const Expr &E, Environment &Env,
49206c3fb27SDimitry Andric                             BoolValue &HasValueVal) {
4935f757f3fSDimitry Andric   RecordStorageLocation &Loc = Env.getResultObjectLocation(E);
494*0fca6ea1SDimitry Andric   setHasValue(Loc, HasValueVal, Env);
49581ad6265SDimitry Andric }
49681ad6265SDimitry Andric 
49781ad6265SDimitry Andric /// Returns a symbolic value for the "has_value" property of an `optional<T>`
49881ad6265SDimitry Andric /// value that is constructed/assigned from a value of type `U` or `optional<U>`
49981ad6265SDimitry Andric /// where `T` is constructible from `U`.
500*0fca6ea1SDimitry Andric BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E,
50181ad6265SDimitry Andric                                      const MatchFinder::MatchResult &MatchRes,
50281ad6265SDimitry Andric                                      LatticeTransferState &State) {
503*0fca6ea1SDimitry Andric   const int DestTypeOptionalWrappersCount =
504*0fca6ea1SDimitry Andric       countOptionalWrappers(*MatchRes.Context, DestType);
50506c3fb27SDimitry Andric   const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
50606c3fb27SDimitry Andric       *MatchRes.Context, E.getType().getNonReferenceType());
50781ad6265SDimitry Andric 
508*0fca6ea1SDimitry Andric   // Is this an constructor of the form `template<class U> optional(U &&)` /
509*0fca6ea1SDimitry Andric   // assignment of the form `template<class U> optional& operator=(U &&)`
510*0fca6ea1SDimitry Andric   // (where `T` is assignable / constructible from `U`)?
511*0fca6ea1SDimitry Andric   // We recognize this because the number of optionals in the optional being
512*0fca6ea1SDimitry Andric   // assigned to is different from the function argument type.
513*0fca6ea1SDimitry Andric   if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)
51481ad6265SDimitry Andric     return State.Env.getBoolLiteralValue(true);
51581ad6265SDimitry Andric 
516*0fca6ea1SDimitry Andric   // Otherwise, this must be a constructor of the form
517*0fca6ea1SDimitry Andric   // `template <class U> optional<optional<U> &&)` / assignment of the form
518*0fca6ea1SDimitry Andric   // `template <class U> optional& operator=(optional<U> &&)
519*0fca6ea1SDimitry Andric   // (where, again, `T` is assignable / constructible from `U`).
520cb14a3feSDimitry Andric   auto *Loc = State.Env.get<RecordStorageLocation>(E);
5215f757f3fSDimitry Andric   if (auto *HasValueVal = getHasValue(State.Env, Loc))
52281ad6265SDimitry Andric     return *HasValueVal;
52381ad6265SDimitry Andric   return State.Env.makeAtomicBoolValue();
52481ad6265SDimitry Andric }
52581ad6265SDimitry Andric 
52681ad6265SDimitry Andric void transferValueOrConversionConstructor(
52781ad6265SDimitry Andric     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
52881ad6265SDimitry Andric     LatticeTransferState &State) {
52981ad6265SDimitry Andric   assert(E->getNumArgs() > 0);
53081ad6265SDimitry Andric 
531*0fca6ea1SDimitry Andric   constructOptionalValue(
532*0fca6ea1SDimitry Andric       *E, State.Env,
533*0fca6ea1SDimitry Andric       valueOrConversionHasValue(
534*0fca6ea1SDimitry Andric           E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0),
535*0fca6ea1SDimitry Andric           MatchRes, State));
53681ad6265SDimitry Andric }
53781ad6265SDimitry Andric 
53881ad6265SDimitry Andric void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
53981ad6265SDimitry Andric                         LatticeTransferState &State) {
54081ad6265SDimitry Andric   assert(E->getNumArgs() > 0);
54181ad6265SDimitry Andric 
542cb14a3feSDimitry Andric   if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) {
543*0fca6ea1SDimitry Andric     setHasValue(*Loc, HasValueVal, State.Env);
54481ad6265SDimitry Andric 
54581ad6265SDimitry Andric     // Assign a storage location for the whole expression.
5465f757f3fSDimitry Andric     State.Env.setStorageLocation(*E, *Loc);
54706c3fb27SDimitry Andric   }
54881ad6265SDimitry Andric }
54981ad6265SDimitry Andric 
55081ad6265SDimitry Andric void transferValueOrConversionAssignment(
55181ad6265SDimitry Andric     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
55281ad6265SDimitry Andric     LatticeTransferState &State) {
55381ad6265SDimitry Andric   assert(E->getNumArgs() > 1);
554*0fca6ea1SDimitry Andric   transferAssignment(
555*0fca6ea1SDimitry Andric       E,
556*0fca6ea1SDimitry Andric       valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(),
55781ad6265SDimitry Andric                                 *E->getArg(1), MatchRes, State),
55881ad6265SDimitry Andric       State);
55981ad6265SDimitry Andric }
56081ad6265SDimitry Andric 
56181ad6265SDimitry Andric void transferNulloptAssignment(const CXXOperatorCallExpr *E,
56281ad6265SDimitry Andric                                const MatchFinder::MatchResult &,
56381ad6265SDimitry Andric                                LatticeTransferState &State) {
56481ad6265SDimitry Andric   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
56581ad6265SDimitry Andric }
56681ad6265SDimitry Andric 
5675f757f3fSDimitry Andric void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
5685f757f3fSDimitry Andric                   Environment &Env) {
56906c3fb27SDimitry Andric   // We account for cases where one or both of the optionals are not modeled,
57006c3fb27SDimitry Andric   // either lacking associated storage locations, or lacking values associated
57106c3fb27SDimitry Andric   // to such storage locations.
57281ad6265SDimitry Andric 
57306c3fb27SDimitry Andric   if (Loc1 == nullptr) {
57406c3fb27SDimitry Andric     if (Loc2 != nullptr)
575*0fca6ea1SDimitry Andric       setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env);
57606c3fb27SDimitry Andric     return;
57706c3fb27SDimitry Andric   }
57806c3fb27SDimitry Andric   if (Loc2 == nullptr) {
579*0fca6ea1SDimitry Andric     setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env);
58006c3fb27SDimitry Andric     return;
58106c3fb27SDimitry Andric   }
58281ad6265SDimitry Andric 
58306c3fb27SDimitry Andric   // Both expressions have locations, though they may not have corresponding
58406c3fb27SDimitry Andric   // values. In that case, we create a fresh value at this point. Note that if
58506c3fb27SDimitry Andric   // two branches both do this, they will not share the value, but it at least
58606c3fb27SDimitry Andric   // allows for local reasoning about the value. To avoid the above, we would
58706c3fb27SDimitry Andric   // need *lazy* value allocation.
58806c3fb27SDimitry Andric   // FIXME: allocate values lazily, instead of just creating a fresh value.
5895f757f3fSDimitry Andric   BoolValue *BoolVal1 = getHasValue(Env, Loc1);
59006c3fb27SDimitry Andric   if (BoolVal1 == nullptr)
59106c3fb27SDimitry Andric     BoolVal1 = &Env.makeAtomicBoolValue();
59206c3fb27SDimitry Andric 
5935f757f3fSDimitry Andric   BoolValue *BoolVal2 = getHasValue(Env, Loc2);
59406c3fb27SDimitry Andric   if (BoolVal2 == nullptr)
59506c3fb27SDimitry Andric     BoolVal2 = &Env.makeAtomicBoolValue();
59606c3fb27SDimitry Andric 
597*0fca6ea1SDimitry Andric   setHasValue(*Loc1, *BoolVal2, Env);
598*0fca6ea1SDimitry Andric   setHasValue(*Loc2, *BoolVal1, Env);
59981ad6265SDimitry Andric }
60081ad6265SDimitry Andric 
60181ad6265SDimitry Andric void transferSwapCall(const CXXMemberCallExpr *E,
60281ad6265SDimitry Andric                       const MatchFinder::MatchResult &,
60381ad6265SDimitry Andric                       LatticeTransferState &State) {
60481ad6265SDimitry Andric   assert(E->getNumArgs() == 1);
605cb14a3feSDimitry Andric   auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
60606c3fb27SDimitry Andric   transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
60781ad6265SDimitry Andric }
60881ad6265SDimitry Andric 
60981ad6265SDimitry Andric void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
61081ad6265SDimitry Andric                          LatticeTransferState &State) {
61181ad6265SDimitry Andric   assert(E->getNumArgs() == 2);
612cb14a3feSDimitry Andric   auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
613cb14a3feSDimitry Andric   auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1));
61406c3fb27SDimitry Andric   transferSwap(Arg0Loc, Arg1Loc, State.Env);
61581ad6265SDimitry Andric }
61681ad6265SDimitry Andric 
61706c3fb27SDimitry Andric void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
61806c3fb27SDimitry Andric                             LatticeTransferState &State) {
61906c3fb27SDimitry Andric   assert(E->getNumArgs() == 1);
62006c3fb27SDimitry Andric 
6215f757f3fSDimitry Andric   if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
6225f757f3fSDimitry Andric     State.Env.setStorageLocation(*E, *Loc);
62306c3fb27SDimitry Andric }
62406c3fb27SDimitry Andric 
62506c3fb27SDimitry Andric const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
62606c3fb27SDimitry Andric                                 const Formula &LHS, const Formula &RHS) {
627bdd1243dSDimitry Andric   // Logically, an optional<T> object is composed of two values - a `has_value`
628bdd1243dSDimitry Andric   // bit and a value of type T. Equality of optional objects compares both
629bdd1243dSDimitry Andric   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
630bdd1243dSDimitry Andric   // when two optional objects are engaged, the equality of their respective
631bdd1243dSDimitry Andric   // values of type T matters. Since we only track the `has_value` bits, we
632bdd1243dSDimitry Andric   // can't make any conclusions about equality when we know that two optional
633bdd1243dSDimitry Andric   // objects are engaged.
634bdd1243dSDimitry Andric   //
635bdd1243dSDimitry Andric   // We express this as two facts about the equality:
636bdd1243dSDimitry Andric   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
637bdd1243dSDimitry Andric   //    If they are equal, then either both are set or both are unset.
638bdd1243dSDimitry Andric   // b) (!LHS & !RHS) => EqVal
639bdd1243dSDimitry Andric   //    If neither is set, then they are equal.
640bdd1243dSDimitry Andric   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
64106c3fb27SDimitry Andric   return A.makeAnd(
64206c3fb27SDimitry Andric       A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
64306c3fb27SDimitry Andric                                     A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
64406c3fb27SDimitry Andric       A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
645bdd1243dSDimitry Andric }
646bdd1243dSDimitry Andric 
647bdd1243dSDimitry Andric void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
648bdd1243dSDimitry Andric                                     const MatchFinder::MatchResult &,
649bdd1243dSDimitry Andric                                     LatticeTransferState &State) {
650bdd1243dSDimitry Andric   Environment &Env = State.Env;
65106c3fb27SDimitry Andric   auto &A = Env.arena();
652bdd1243dSDimitry Andric   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
653cb14a3feSDimitry Andric   auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0));
6545f757f3fSDimitry Andric   if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {
655cb14a3feSDimitry Andric     auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1));
6565f757f3fSDimitry Andric     if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {
657bdd1243dSDimitry Andric       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
65806c3fb27SDimitry Andric         CmpValue = &A.makeNot(*CmpValue);
6595f757f3fSDimitry Andric       Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
66006c3fb27SDimitry Andric                                   RHasVal->formula()));
661bdd1243dSDimitry Andric     }
662bdd1243dSDimitry Andric   }
6635f757f3fSDimitry Andric }
664bdd1243dSDimitry Andric 
665bdd1243dSDimitry Andric void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
666bdd1243dSDimitry Andric                                  const clang::Expr *E, Environment &Env) {
66706c3fb27SDimitry Andric   auto &A = Env.arena();
668bdd1243dSDimitry Andric   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
669cb14a3feSDimitry Andric   auto *Loc = Env.get<RecordStorageLocation>(*E);
6705f757f3fSDimitry Andric   if (auto *HasVal = getHasValue(Env, Loc)) {
671bdd1243dSDimitry Andric     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
67206c3fb27SDimitry Andric       CmpValue = &A.makeNot(*CmpValue);
6735f757f3fSDimitry Andric     Env.assume(
67406c3fb27SDimitry Andric         evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
675bdd1243dSDimitry Andric   }
676bdd1243dSDimitry Andric }
677bdd1243dSDimitry Andric 
6785f757f3fSDimitry Andric void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,
6795f757f3fSDimitry Andric                                    const clang::Expr *E, Environment &Env) {
6805f757f3fSDimitry Andric   auto &A = Env.arena();
6815f757f3fSDimitry Andric   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
682cb14a3feSDimitry Andric   auto *Loc = Env.get<RecordStorageLocation>(*E);
6835f757f3fSDimitry Andric   if (auto *HasVal = getHasValue(Env, Loc)) {
6845f757f3fSDimitry Andric     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
6855f757f3fSDimitry Andric       CmpValue = &A.makeNot(*CmpValue);
6865f757f3fSDimitry Andric     Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
6875f757f3fSDimitry Andric                                 A.makeLiteral(false)));
6885f757f3fSDimitry Andric   }
6895f757f3fSDimitry Andric }
6905f757f3fSDimitry Andric 
691bdd1243dSDimitry Andric std::optional<StatementMatcher>
69281ad6265SDimitry Andric ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
693bdd1243dSDimitry Andric   if (Options.IgnoreSmartPointerDereference) {
694bdd1243dSDimitry Andric     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
695bdd1243dSDimitry Andric         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
696bdd1243dSDimitry Andric         unless(hasArgument(0, expr(hasOptionalType()))))));
697bdd1243dSDimitry Andric     return expr(
698bdd1243dSDimitry Andric         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
699bdd1243dSDimitry Andric   }
700bdd1243dSDimitry Andric   return std::nullopt;
70181ad6265SDimitry Andric }
70281ad6265SDimitry Andric 
70381ad6265SDimitry Andric StatementMatcher
704bdd1243dSDimitry Andric valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
70506c3fb27SDimitry Andric   return isOptionalMemberCallWithNameMatcher(hasName("value"),
70606c3fb27SDimitry Andric                                              IgnorableOptional);
70781ad6265SDimitry Andric }
70881ad6265SDimitry Andric 
70981ad6265SDimitry Andric StatementMatcher
710bdd1243dSDimitry Andric valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
71181ad6265SDimitry Andric   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
71281ad6265SDimitry Andric                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
71381ad6265SDimitry Andric }
71481ad6265SDimitry Andric 
715bdd1243dSDimitry Andric auto buildTransferMatchSwitch() {
71681ad6265SDimitry Andric   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
71781ad6265SDimitry Andric   // lot of duplicated work (e.g. string comparisons), consider providing APIs
71881ad6265SDimitry Andric   // that avoid it through memoization.
719bdd1243dSDimitry Andric   return CFGMatchSwitchBuilder<LatticeTransferState>()
72081ad6265SDimitry Andric       // make_optional
721bdd1243dSDimitry Andric       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
72281ad6265SDimitry Andric 
723bdd1243dSDimitry Andric       // optional::optional (in place)
724bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXConstructExpr>(
72581ad6265SDimitry Andric           isOptionalInPlaceConstructor(),
72681ad6265SDimitry Andric           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
72781ad6265SDimitry Andric              LatticeTransferState &State) {
72806c3fb27SDimitry Andric             constructOptionalValue(*E, State.Env,
729bdd1243dSDimitry Andric                                    State.Env.getBoolLiteralValue(true));
73081ad6265SDimitry Andric           })
731bdd1243dSDimitry Andric       // optional::optional(nullopt_t)
732bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXConstructExpr>(
73381ad6265SDimitry Andric           isOptionalNulloptConstructor(),
73481ad6265SDimitry Andric           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
73581ad6265SDimitry Andric              LatticeTransferState &State) {
73606c3fb27SDimitry Andric             constructOptionalValue(*E, State.Env,
73781ad6265SDimitry Andric                                    State.Env.getBoolLiteralValue(false));
73881ad6265SDimitry Andric           })
739bdd1243dSDimitry Andric       // optional::optional (value/conversion)
740bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
74181ad6265SDimitry Andric                                        transferValueOrConversionConstructor)
74281ad6265SDimitry Andric 
74381ad6265SDimitry Andric       // optional::operator=
744bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(
745bdd1243dSDimitry Andric           isOptionalValueOrConversionAssignment(),
74681ad6265SDimitry Andric           transferValueOrConversionAssignment)
747bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
74881ad6265SDimitry Andric                                           transferNulloptAssignment)
74981ad6265SDimitry Andric 
75081ad6265SDimitry Andric       // optional::value
751bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
752bdd1243dSDimitry Andric           valueCall(std::nullopt),
75381ad6265SDimitry Andric           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
75481ad6265SDimitry Andric              LatticeTransferState &State) {
75581ad6265SDimitry Andric             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
75681ad6265SDimitry Andric           })
75781ad6265SDimitry Andric 
75806c3fb27SDimitry Andric       // optional::operator*
75906c3fb27SDimitry Andric       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
760bdd1243dSDimitry Andric                                [](const CallExpr *E,
761bdd1243dSDimitry Andric                                   const MatchFinder::MatchResult &,
76281ad6265SDimitry Andric                                   LatticeTransferState &State) {
76381ad6265SDimitry Andric                                  transferUnwrapCall(E, E->getArg(0), State);
76481ad6265SDimitry Andric                                })
76581ad6265SDimitry Andric 
76606c3fb27SDimitry Andric       // optional::operator->
76706c3fb27SDimitry Andric       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
76806c3fb27SDimitry Andric                                [](const CallExpr *E,
76906c3fb27SDimitry Andric                                   const MatchFinder::MatchResult &,
77006c3fb27SDimitry Andric                                   LatticeTransferState &State) {
77106c3fb27SDimitry Andric                                  transferArrowOpCall(E, E->getArg(0), State);
77206c3fb27SDimitry Andric                                })
77306c3fb27SDimitry Andric 
77406c3fb27SDimitry Andric       // optional::has_value, optional::hasValue
77506c3fb27SDimitry Andric       // Of the supported optionals only folly::Optional uses hasValue, but this
77606c3fb27SDimitry Andric       // will also pass for other types
777bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
77806c3fb27SDimitry Andric           isOptionalMemberCallWithNameMatcher(
77906c3fb27SDimitry Andric               hasAnyName("has_value", "hasValue")),
78081ad6265SDimitry Andric           transferOptionalHasValueCall)
78181ad6265SDimitry Andric 
78281ad6265SDimitry Andric       // optional::operator bool
783bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
78406c3fb27SDimitry Andric           isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
78581ad6265SDimitry Andric           transferOptionalHasValueCall)
78681ad6265SDimitry Andric 
78781ad6265SDimitry Andric       // optional::emplace
788bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
78906c3fb27SDimitry Andric           isOptionalMemberCallWithNameMatcher(hasName("emplace")),
79081ad6265SDimitry Andric           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
79181ad6265SDimitry Andric              LatticeTransferState &State) {
7925f757f3fSDimitry Andric             if (RecordStorageLocation *Loc =
79306c3fb27SDimitry Andric                     getImplicitObjectLocation(*E, State.Env)) {
794*0fca6ea1SDimitry Andric               setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);
79506c3fb27SDimitry Andric             }
79681ad6265SDimitry Andric           })
79781ad6265SDimitry Andric 
79881ad6265SDimitry Andric       // optional::reset
799bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
80006c3fb27SDimitry Andric           isOptionalMemberCallWithNameMatcher(hasName("reset")),
80181ad6265SDimitry Andric           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
80281ad6265SDimitry Andric              LatticeTransferState &State) {
8035f757f3fSDimitry Andric             if (RecordStorageLocation *Loc =
80406c3fb27SDimitry Andric                     getImplicitObjectLocation(*E, State.Env)) {
805*0fca6ea1SDimitry Andric               setHasValue(*Loc, State.Env.getBoolLiteralValue(false),
80606c3fb27SDimitry Andric                           State.Env);
80706c3fb27SDimitry Andric             }
80881ad6265SDimitry Andric           })
80981ad6265SDimitry Andric 
81081ad6265SDimitry Andric       // optional::swap
81106c3fb27SDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
81206c3fb27SDimitry Andric           isOptionalMemberCallWithNameMatcher(hasName("swap")),
81381ad6265SDimitry Andric           transferSwapCall)
81481ad6265SDimitry Andric 
81581ad6265SDimitry Andric       // std::swap
816bdd1243dSDimitry Andric       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
81781ad6265SDimitry Andric 
81806c3fb27SDimitry Andric       // std::forward
81906c3fb27SDimitry Andric       .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
82006c3fb27SDimitry Andric 
82181ad6265SDimitry Andric       // opt.value_or("").empty()
822bdd1243dSDimitry Andric       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
823bdd1243dSDimitry Andric                            transferValueOrStringEmptyCall)
82481ad6265SDimitry Andric 
82581ad6265SDimitry Andric       // opt.value_or(X) != X
826bdd1243dSDimitry Andric       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
827bdd1243dSDimitry Andric 
828bdd1243dSDimitry Andric       // Comparisons (==, !=):
829bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(
8305f757f3fSDimitry Andric           isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
831bdd1243dSDimitry Andric           transferOptionalAndOptionalCmp)
832bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(
8335f757f3fSDimitry Andric           isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
8345f757f3fSDimitry Andric           [](const clang::CXXOperatorCallExpr *Cmp,
8355f757f3fSDimitry Andric              const MatchFinder::MatchResult &, LatticeTransferState &State) {
8365f757f3fSDimitry Andric             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);
8375f757f3fSDimitry Andric           })
8385f757f3fSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(
8395f757f3fSDimitry Andric           isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
8405f757f3fSDimitry Andric           [](const clang::CXXOperatorCallExpr *Cmp,
8415f757f3fSDimitry Andric              const MatchFinder::MatchResult &, LatticeTransferState &State) {
8425f757f3fSDimitry Andric             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);
8435f757f3fSDimitry Andric           })
8445f757f3fSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(
8455f757f3fSDimitry Andric           isComparisonOperatorCall(
8465f757f3fSDimitry Andric               hasOptionalType(),
8475f757f3fSDimitry Andric               unless(anyOf(hasOptionalType(), hasNulloptType()))),
848bdd1243dSDimitry Andric           [](const clang::CXXOperatorCallExpr *Cmp,
849bdd1243dSDimitry Andric              const MatchFinder::MatchResult &, LatticeTransferState &State) {
850bdd1243dSDimitry Andric             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
851bdd1243dSDimitry Andric           })
852bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(
8535f757f3fSDimitry Andric           isComparisonOperatorCall(
8545f757f3fSDimitry Andric               unless(anyOf(hasOptionalType(), hasNulloptType())),
855bdd1243dSDimitry Andric               hasOptionalType()),
856bdd1243dSDimitry Andric           [](const clang::CXXOperatorCallExpr *Cmp,
857bdd1243dSDimitry Andric              const MatchFinder::MatchResult &, LatticeTransferState &State) {
858bdd1243dSDimitry Andric             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
859bdd1243dSDimitry Andric           })
86081ad6265SDimitry Andric 
86181ad6265SDimitry Andric       // returns optional
862bdd1243dSDimitry Andric       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
86381ad6265SDimitry Andric                                transferCallReturningOptional)
86481ad6265SDimitry Andric 
86581ad6265SDimitry Andric       .Build();
86681ad6265SDimitry Andric }
86781ad6265SDimitry Andric 
8685f757f3fSDimitry Andric llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
86981ad6265SDimitry Andric                                                      const Environment &Env) {
8705f757f3fSDimitry Andric   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
8715f757f3fSDimitry Andric           getLocBehindPossiblePointer(*ObjectExpr, Env))) {
8725f757f3fSDimitry Andric     auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
87381ad6265SDimitry Andric     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
8745f757f3fSDimitry Andric       if (Env.proves(HasValueVal->formula()))
87581ad6265SDimitry Andric         return {};
87681ad6265SDimitry Andric     }
87781ad6265SDimitry Andric   }
87881ad6265SDimitry Andric 
87981ad6265SDimitry Andric   // Record that this unwrap is *not* provably safe.
88081ad6265SDimitry Andric   // FIXME: include either the name of the optional (if applicable) or a source
88181ad6265SDimitry Andric   // range of the access for easier interpretation of the result.
88281ad6265SDimitry Andric   return {ObjectExpr->getBeginLoc()};
88381ad6265SDimitry Andric }
88481ad6265SDimitry Andric 
88581ad6265SDimitry Andric auto buildDiagnoseMatchSwitch(
88681ad6265SDimitry Andric     const UncheckedOptionalAccessModelOptions &Options) {
88781ad6265SDimitry Andric   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
88881ad6265SDimitry Andric   // lot of duplicated work (e.g. string comparisons), consider providing APIs
88981ad6265SDimitry Andric   // that avoid it through memoization.
89081ad6265SDimitry Andric   auto IgnorableOptional = ignorableOptional(Options);
8915f757f3fSDimitry Andric   return CFGMatchSwitchBuilder<const Environment,
8925f757f3fSDimitry Andric                                llvm::SmallVector<SourceLocation>>()
89381ad6265SDimitry Andric       // optional::value
894bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
89581ad6265SDimitry Andric           valueCall(IgnorableOptional),
89681ad6265SDimitry Andric           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
89781ad6265SDimitry Andric              const Environment &Env) {
89806c3fb27SDimitry Andric             return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
89981ad6265SDimitry Andric           })
90081ad6265SDimitry Andric 
90181ad6265SDimitry Andric       // optional::operator*, optional::operator->
90206c3fb27SDimitry Andric       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
90306c3fb27SDimitry Andric                                [](const CallExpr *E,
90406c3fb27SDimitry Andric                                   const MatchFinder::MatchResult &,
90581ad6265SDimitry Andric                                   const Environment &Env) {
90606c3fb27SDimitry Andric                                  return diagnoseUnwrapCall(E->getArg(0), Env);
90781ad6265SDimitry Andric                                })
90881ad6265SDimitry Andric       .Build();
90981ad6265SDimitry Andric }
91081ad6265SDimitry Andric 
91181ad6265SDimitry Andric } // namespace
91281ad6265SDimitry Andric 
91381ad6265SDimitry Andric ast_matchers::DeclarationMatcher
91481ad6265SDimitry Andric UncheckedOptionalAccessModel::optionalClassDecl() {
915*0fca6ea1SDimitry Andric   return cxxRecordDecl(optionalClass());
9165f757f3fSDimitry Andric }
9175f757f3fSDimitry Andric 
9185f757f3fSDimitry Andric UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
9195f757f3fSDimitry Andric                                                            Environment &Env)
92081ad6265SDimitry Andric     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
9215f757f3fSDimitry Andric       TransferMatchSwitch(buildTransferMatchSwitch()) {
9225f757f3fSDimitry Andric   Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
9235f757f3fSDimitry Andric       [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
924*0fca6ea1SDimitry Andric         const CXXRecordDecl *Optional =
925*0fca6ea1SDimitry Andric             getOptionalBaseClass(Ty->getAsCXXRecordDecl());
926*0fca6ea1SDimitry Andric         if (Optional == nullptr)
9275f757f3fSDimitry Andric           return {};
928*0fca6ea1SDimitry Andric         return {{"value", valueTypeFromOptionalDecl(*Optional)},
9295f757f3fSDimitry Andric                 {"has_value", Ctx.BoolTy}};
9305f757f3fSDimitry Andric       });
9315f757f3fSDimitry Andric }
93281ad6265SDimitry Andric 
93306c3fb27SDimitry Andric void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
934bdd1243dSDimitry Andric                                             NoopLattice &L, Environment &Env) {
93581ad6265SDimitry Andric   LatticeTransferState State(L, Env);
93606c3fb27SDimitry Andric   TransferMatchSwitch(Elt, getASTContext(), State);
93781ad6265SDimitry Andric }
93881ad6265SDimitry Andric 
93981ad6265SDimitry Andric UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
94081ad6265SDimitry Andric     UncheckedOptionalAccessModelOptions Options)
94181ad6265SDimitry Andric     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
94281ad6265SDimitry Andric 
94381ad6265SDimitry Andric } // namespace dataflow
94481ad6265SDimitry Andric } // namespace clang
945