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