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 41*11c423f9SChris Cotter // Note: the Names appear in reverse order. E.g., to check 42*11c423f9SChris Cotter // if NS is foo::bar::, call isFullyQualifiedNamespaceEqualTo(NS, "bar", "foo") 43*11c423f9SChris Cotter template <class... NameTypes> 44*11c423f9SChris Cotter static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS, 45*11c423f9SChris Cotter llvm::StringRef Name, 46*11c423f9SChris Cotter NameTypes... Names) { 47*11c423f9SChris Cotter if (!(NS.getDeclName().isIdentifier() && NS.getName() == Name && 48*11c423f9SChris Cotter NS.getParent() != nullptr)) 49*11c423f9SChris Cotter return false; 50*11c423f9SChris Cotter 51*11c423f9SChris Cotter if constexpr (sizeof...(NameTypes) > 0) { 52*11c423f9SChris Cotter if (NS.getParent()->isTranslationUnit()) 53*11c423f9SChris Cotter return false; 54*11c423f9SChris Cotter if (const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.getParent())) 55*11c423f9SChris Cotter return isFullyQualifiedNamespaceEqualTo(*NextNS, Names...); 56*11c423f9SChris Cotter return false; 57*11c423f9SChris Cotter } else { 58*11c423f9SChris Cotter return NS.getParent()->isTranslationUnit(); 59*11c423f9SChris Cotter } 6009b462efSYitzhak Mandelbaum } 6109b462efSYitzhak Mandelbaum 6209b462efSYitzhak Mandelbaum static bool hasOptionalClassName(const CXXRecordDecl &RD) { 6309b462efSYitzhak Mandelbaum if (!RD.getDeclName().isIdentifier()) 6409b462efSYitzhak Mandelbaum return false; 6509b462efSYitzhak Mandelbaum 6609b462efSYitzhak Mandelbaum if (RD.getName() == "optional") { 6709b462efSYitzhak Mandelbaum if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext())) 68*11c423f9SChris Cotter return N->isStdNamespace() || 69*11c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "absl") || 70*11c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "bsl"); 7109b462efSYitzhak Mandelbaum return false; 7209b462efSYitzhak Mandelbaum } 7309b462efSYitzhak Mandelbaum 7409b462efSYitzhak Mandelbaum if (RD.getName() == "Optional") { 752f0630f8SAnton Dukeman // Check whether namespace is "::base" or "::folly". 7609b462efSYitzhak Mandelbaum const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); 77*11c423f9SChris Cotter return N != nullptr && (isFullyQualifiedNamespaceEqualTo(*N, "base") || 78*11c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "folly")); 79*11c423f9SChris Cotter } 80*11c423f9SChris Cotter 81*11c423f9SChris Cotter if (RD.getName() == "NullableValue") { 82*11c423f9SChris Cotter const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); 83*11c423f9SChris Cotter return N != nullptr && 84*11c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP"); 8509b462efSYitzhak Mandelbaum } 8609b462efSYitzhak Mandelbaum 8709b462efSYitzhak Mandelbaum return false; 8809b462efSYitzhak Mandelbaum } 8909b462efSYitzhak Mandelbaum 90d712c5edSmartinboehme static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) { 91d712c5edSmartinboehme if (RD == nullptr) 92d712c5edSmartinboehme return nullptr; 93d712c5edSmartinboehme if (hasOptionalClassName(*RD)) 94d712c5edSmartinboehme return RD; 95d712c5edSmartinboehme 96d712c5edSmartinboehme if (!RD->hasDefinition()) 97d712c5edSmartinboehme return nullptr; 98d712c5edSmartinboehme 99d712c5edSmartinboehme for (const CXXBaseSpecifier &Base : RD->bases()) 100d712c5edSmartinboehme if (const CXXRecordDecl *BaseClass = 101d712c5edSmartinboehme getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl())) 102d712c5edSmartinboehme return BaseClass; 103d712c5edSmartinboehme 104d712c5edSmartinboehme return nullptr; 105d712c5edSmartinboehme } 106d712c5edSmartinboehme 107af98b0afSStanislav Gatev namespace { 108af98b0afSStanislav Gatev 109af98b0afSStanislav Gatev using namespace ::clang::ast_matchers; 110cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>; 111af98b0afSStanislav Gatev 112d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); } 113d712c5edSmartinboehme 114d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) { 115d712c5edSmartinboehme return getOptionalBaseClass(&Node) != nullptr; 11609b462efSYitzhak Mandelbaum } 11709b462efSYitzhak Mandelbaum 118d712c5edSmartinboehme auto desugarsToOptionalType() { 11965e710c3SStanislav Gatev return hasUnqualifiedDesugaredType( 120d712c5edSmartinboehme recordType(hasDeclaration(cxxRecordDecl(optionalClass())))); 12165e710c3SStanislav Gatev } 12265e710c3SStanislav Gatev 123d712c5edSmartinboehme auto desugarsToOptionalOrDerivedType() { 124d712c5edSmartinboehme return hasUnqualifiedDesugaredType( 125d712c5edSmartinboehme recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass())))); 126d712c5edSmartinboehme } 127d712c5edSmartinboehme 128d712c5edSmartinboehme auto hasOptionalType() { return hasType(desugarsToOptionalType()); } 129d712c5edSmartinboehme 130d712c5edSmartinboehme /// Matches any of the spellings of the optional types and sugar, aliases, 131d712c5edSmartinboehme /// derived classes, etc. 132d712c5edSmartinboehme auto hasOptionalOrDerivedType() { 133d712c5edSmartinboehme return hasType(desugarsToOptionalOrDerivedType()); 134d712c5edSmartinboehme } 135d712c5edSmartinboehme 136d712c5edSmartinboehme QualType getPublicType(const Expr *E) { 137d712c5edSmartinboehme auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens()); 138d712c5edSmartinboehme if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) { 139d712c5edSmartinboehme QualType Ty = E->getType(); 140d712c5edSmartinboehme if (Ty->isPointerType()) 141d712c5edSmartinboehme return Ty->getPointeeType(); 142d712c5edSmartinboehme return Ty; 143d712c5edSmartinboehme } 144d712c5edSmartinboehme 145d712c5edSmartinboehme // Is the derived type that we're casting from the type of `*this`? In this 146d712c5edSmartinboehme // special case, we can upcast to the base class even if the base is 147d712c5edSmartinboehme // non-public. 148d712c5edSmartinboehme bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr()); 149d712c5edSmartinboehme 150d712c5edSmartinboehme // Find the least-derived type in the path (i.e. the last entry in the list) 151d712c5edSmartinboehme // that we can access. 152d712c5edSmartinboehme const CXXBaseSpecifier *PublicBase = nullptr; 153d712c5edSmartinboehme for (const CXXBaseSpecifier *Base : Cast->path()) { 154d712c5edSmartinboehme if (Base->getAccessSpecifier() != AS_public && !CastingFromThis) 155d712c5edSmartinboehme break; 156d712c5edSmartinboehme PublicBase = Base; 157d712c5edSmartinboehme CastingFromThis = false; 158d712c5edSmartinboehme } 159d712c5edSmartinboehme 160d712c5edSmartinboehme if (PublicBase != nullptr) 161d712c5edSmartinboehme return PublicBase->getType(); 162d712c5edSmartinboehme 163d712c5edSmartinboehme // We didn't find any public type that we could cast to. There may be more 164d712c5edSmartinboehme // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this 165d712c5edSmartinboehme // will return the type of `getSubExpr()`.) 166d712c5edSmartinboehme return getPublicType(Cast->getSubExpr()); 167d712c5edSmartinboehme } 168d712c5edSmartinboehme 169d712c5edSmartinboehme // Returns the least-derived type for the receiver of `MCE` that 170d712c5edSmartinboehme // `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to. 171d712c5edSmartinboehme // Effectively, we upcast until we reach a non-public base class, unless that 172d712c5edSmartinboehme // base is a base of `*this`. 173d712c5edSmartinboehme // 174d712c5edSmartinboehme // This is needed to correctly match methods called on types derived from 175d712c5edSmartinboehme // `std::optional`. 176d712c5edSmartinboehme // 177d712c5edSmartinboehme // Say we have a `struct Derived : public std::optional<int> {} d;` For a call 178d712c5edSmartinboehme // `d.has_value()`, the `getImplicitObjectArgument()` looks like this: 179d712c5edSmartinboehme // 180d712c5edSmartinboehme // ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue 181d712c5edSmartinboehme // | <UncheckedDerivedToBase (optional -> __optional_storage_base)> 182d712c5edSmartinboehme // `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived' 183d712c5edSmartinboehme // 184d712c5edSmartinboehme // The type of the implicit object argument is `__optional_storage_base` 185d712c5edSmartinboehme // (since this is the internal type that `has_value()` is declared on). If we 186d712c5edSmartinboehme // call `IgnoreParenImpCasts()` on the implicit object argument, we get the 187d712c5edSmartinboehme // `DeclRefExpr`, which has type `Derived`. Neither of these types is 188d712c5edSmartinboehme // `optional`, and hence neither is sufficient for querying whether we are 189d712c5edSmartinboehme // calling a method on `optional`. 190d712c5edSmartinboehme // 191d712c5edSmartinboehme // Instead, starting with the most derived type, we need to follow the chain of 192d712c5edSmartinboehme // casts 193d712c5edSmartinboehme QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) { 194d712c5edSmartinboehme return getPublicType(MCE.getImplicitObjectArgument()); 195d712c5edSmartinboehme } 196d712c5edSmartinboehme 197d712c5edSmartinboehme AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType, 198d712c5edSmartinboehme ast_matchers::internal::Matcher<QualType>, InnerMatcher) { 199d712c5edSmartinboehme return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder); 200d712c5edSmartinboehme } 2016adfc64eSYitzhak Mandelbaum 2022f0630f8SAnton Dukeman auto isOptionalMemberCallWithNameMatcher( 2032f0630f8SAnton Dukeman ast_matchers::internal::Matcher<NamedDecl> matcher, 2046ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 205d712c5edSmartinboehme return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable))) 206d712c5edSmartinboehme : anything(), 207d712c5edSmartinboehme publicReceiverType(desugarsToOptionalType()), 2082f0630f8SAnton Dukeman callee(cxxMethodDecl(matcher))); 209af98b0afSStanislav Gatev } 210af98b0afSStanislav Gatev 211a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName( 212a184a0d8SYitzhak Mandelbaum llvm::StringRef operator_name, 2136ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 214a184a0d8SYitzhak Mandelbaum return cxxOperatorCallExpr( 215a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName(operator_name), 216a184a0d8SYitzhak Mandelbaum callee(cxxMethodDecl(ofClass(optionalClass()))), 217a184a0d8SYitzhak Mandelbaum Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 218af98b0afSStanislav Gatev } 219af98b0afSStanislav Gatev 220092a530cSStanislav Gatev auto isMakeOptionalCall() { 221*11c423f9SChris Cotter return callExpr( 222*11c423f9SChris Cotter callee(functionDecl(hasAnyName( 223*11c423f9SChris Cotter "std::make_optional", "base::make_optional", "absl::make_optional", 224*11c423f9SChris Cotter "folly::make_optional", "bsl::make_optional"))), 2259e0fc676SStanislav Gatev hasOptionalType()); 2269e0fc676SStanislav Gatev } 2279e0fc676SStanislav Gatev 228390029beSYitzhak Mandelbaum auto nulloptTypeDecl() { 2292f0630f8SAnton Dukeman return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t", 230*11c423f9SChris Cotter "base::nullopt_t", "folly::None", 231*11c423f9SChris Cotter "bsl::nullopt_t")); 232092a530cSStanislav Gatev } 233092a530cSStanislav Gatev 234390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); } 235390029beSYitzhak Mandelbaum 236092a530cSStanislav Gatev auto inPlaceClass() { 2372f0630f8SAnton Dukeman return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t", 238*11c423f9SChris Cotter "base::in_place_t", "folly::in_place_t", 239*11c423f9SChris Cotter "bsl::in_place_t")); 240092a530cSStanislav Gatev } 241092a530cSStanislav Gatev 242092a530cSStanislav Gatev auto isOptionalNulloptConstructor() { 2430086a355SYitzhak Mandelbaum return cxxConstructExpr( 2440086a355SYitzhak Mandelbaum hasDeclaration(cxxConstructorDecl(parameterCountIs(1), 245d712c5edSmartinboehme hasParameter(0, hasNulloptType()))), 246d712c5edSmartinboehme hasOptionalOrDerivedType()); 247092a530cSStanislav Gatev } 248092a530cSStanislav Gatev 249092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() { 250d712c5edSmartinboehme return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())), 251d712c5edSmartinboehme hasOptionalOrDerivedType()); 252092a530cSStanislav Gatev } 253092a530cSStanislav Gatev 254092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() { 255092a530cSStanislav Gatev return cxxConstructExpr( 256092a530cSStanislav Gatev unless(hasDeclaration( 257092a530cSStanislav Gatev cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 258d712c5edSmartinboehme argumentCountIs(1), hasArgument(0, unless(hasNulloptType())), 259d712c5edSmartinboehme hasOptionalOrDerivedType()); 260092a530cSStanislav Gatev } 261092a530cSStanislav Gatev 262b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() { 263b000b770SStanislav Gatev return cxxOperatorCallExpr( 264b000b770SStanislav Gatev hasOverloadedOperatorName("="), 265d712c5edSmartinboehme callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))), 266b000b770SStanislav Gatev unless(hasDeclaration(cxxMethodDecl( 267b000b770SStanislav Gatev anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 268b000b770SStanislav Gatev argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 269b000b770SStanislav Gatev } 270b000b770SStanislav Gatev 271b000b770SStanislav Gatev auto isOptionalNulloptAssignment() { 272d712c5edSmartinboehme return cxxOperatorCallExpr( 273d712c5edSmartinboehme hasOverloadedOperatorName("="), 274d712c5edSmartinboehme callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))), 275d712c5edSmartinboehme argumentCountIs(2), hasArgument(1, hasNulloptType())); 276b000b770SStanislav Gatev } 277b000b770SStanislav Gatev 2782ddd57aeSStanislav Gatev auto isStdSwapCall() { 2792ddd57aeSStanislav Gatev return callExpr(callee(functionDecl(hasName("std::swap"))), 280d712c5edSmartinboehme argumentCountIs(2), 281d712c5edSmartinboehme hasArgument(0, hasOptionalOrDerivedType()), 282d712c5edSmartinboehme hasArgument(1, hasOptionalOrDerivedType())); 2832ddd57aeSStanislav Gatev } 2842ddd57aeSStanislav Gatev 28525956d55SAMS21 auto isStdForwardCall() { 28625956d55SAMS21 return callExpr(callee(functionDecl(hasName("std::forward"))), 287d712c5edSmartinboehme argumentCountIs(1), 288d712c5edSmartinboehme hasArgument(0, hasOptionalOrDerivedType())); 28925956d55SAMS21 } 29025956d55SAMS21 2917f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 2927f076004SYitzhak Mandelbaum 2937f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() { 2947f076004SYitzhak Mandelbaum // `opt.value_or("").empty()` 2957f076004SYitzhak Mandelbaum return cxxMemberCallExpr( 2967f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("empty"))), 2977f076004SYitzhak Mandelbaum onImplicitObjectArgument(ignoringImplicit( 2987f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 2997f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 3007f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 3017f076004SYitzhak Mandelbaum hasArgument(0, stringLiteral(hasSize(0)))) 3027f076004SYitzhak Mandelbaum .bind(ValueOrCallID)))); 3037f076004SYitzhak Mandelbaum } 3047f076004SYitzhak Mandelbaum 3057f076004SYitzhak Mandelbaum auto isValueOrNotEqX() { 3067f076004SYitzhak Mandelbaum auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 3077f076004SYitzhak Mandelbaum return hasOperands( 3087f076004SYitzhak Mandelbaum ignoringImplicit( 3097f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 3107f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 3117f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 3127f076004SYitzhak Mandelbaum hasArgument(0, Arg)) 3137f076004SYitzhak Mandelbaum .bind(ValueOrCallID)), 3147f076004SYitzhak Mandelbaum ignoringImplicit(Arg)); 3157f076004SYitzhak Mandelbaum }; 3167f076004SYitzhak Mandelbaum 3177f076004SYitzhak Mandelbaum // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 3187f076004SYitzhak Mandelbaum // support this pattern for any expression, but the AST does not have a 3197f076004SYitzhak Mandelbaum // generic expression comparison facility, so we specialize to common cases 3207f076004SYitzhak Mandelbaum // seen in practice. FIXME: define a matcher that compares values across 3217f076004SYitzhak Mandelbaum // nodes, which would let us generalize this to any `X`. 3227f076004SYitzhak Mandelbaum return binaryOperation(hasOperatorName("!="), 3237f076004SYitzhak Mandelbaum anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 3247f076004SYitzhak Mandelbaum ComparesToSame(stringLiteral(hasSize(0))), 3257f076004SYitzhak Mandelbaum ComparesToSame(integerLiteral(equals(0))))); 3267f076004SYitzhak Mandelbaum } 3277f076004SYitzhak Mandelbaum 32865e710c3SStanislav Gatev auto isCallReturningOptional() { 329d712c5edSmartinboehme return callExpr(hasType(qualType( 330d712c5edSmartinboehme anyOf(desugarsToOptionalOrDerivedType(), 331d712c5edSmartinboehme referenceType(pointee(desugarsToOptionalOrDerivedType())))))); 33265e710c3SStanislav Gatev } 33365e710c3SStanislav Gatev 334390029beSYitzhak Mandelbaum template <typename L, typename R> 335390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 336390029beSYitzhak Mandelbaum return cxxOperatorCallExpr( 337390029beSYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 338390029beSYitzhak Mandelbaum argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 339390029beSYitzhak Mandelbaum hasArgument(1, rhs_arg_matcher)); 340390029beSYitzhak Mandelbaum } 341390029beSYitzhak Mandelbaum 3426272226bSSam McCall /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula. 3436272226bSSam McCall const Formula &forceBoolValue(Environment &Env, const Expr &Expr) { 3442ee396b0Smartinboehme auto *Value = Env.get<BoolValue>(Expr); 345390029beSYitzhak Mandelbaum if (Value != nullptr) 3466272226bSSam McCall return Value->formula(); 347390029beSYitzhak Mandelbaum 348390029beSYitzhak Mandelbaum Value = &Env.makeAtomicBoolValue(); 349b244b6aeSMartin Braenne Env.setValue(Expr, *Value); 3506272226bSSam McCall return Value->formula(); 351390029beSYitzhak Mandelbaum } 352390029beSYitzhak Mandelbaum 35371f2ec2dSmartinboehme StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) { 35471f2ec2dSmartinboehme return OptionalLoc.getSyntheticField("has_value"); 35571f2ec2dSmartinboehme } 35671f2ec2dSmartinboehme 35771f2ec2dSmartinboehme StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) { 35871f2ec2dSmartinboehme return OptionalLoc.getSyntheticField("value"); 35971f2ec2dSmartinboehme } 36071f2ec2dSmartinboehme 3618fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 36271f2ec2dSmartinboehme /// property of the optional at `OptionalLoc`. 36371f2ec2dSmartinboehme void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal, 36471f2ec2dSmartinboehme Environment &Env) { 36571f2ec2dSmartinboehme Env.setValue(locForHasValue(OptionalLoc), HasValueVal); 3668fcdd625SStanislav Gatev } 3678fcdd625SStanislav Gatev 368af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the 36971f2ec2dSmartinboehme /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null. 37071f2ec2dSmartinboehme BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) { 37171f2ec2dSmartinboehme if (OptionalLoc == nullptr) 37271f2ec2dSmartinboehme return nullptr; 37371f2ec2dSmartinboehme StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc); 3742ee396b0Smartinboehme auto *HasValueVal = Env.get<BoolValue>(HasValueLoc); 375dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) { 376dd38caf3SYitzhak Mandelbaum HasValueVal = &Env.makeAtomicBoolValue(); 37771f2ec2dSmartinboehme Env.setValue(HasValueLoc, *HasValueVal); 378dd38caf3SYitzhak Mandelbaum } 379dd38caf3SYitzhak Mandelbaum return HasValueVal; 380af98b0afSStanislav Gatev } 381af98b0afSStanislav Gatev 382d712c5edSmartinboehme QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) { 383d712c5edSmartinboehme auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD); 384d712c5edSmartinboehme return CTSD.getTemplateArgs()[0].getAsType(); 385092a530cSStanislav Gatev } 386092a530cSStanislav Gatev 387092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`. 388092a530cSStanislav Gatev /// 389092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this 390092a530cSStanislav Gatev /// function will be 2. 391092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 392d712c5edSmartinboehme const CXXRecordDecl *Optional = 393d712c5edSmartinboehme getOptionalBaseClass(Type->getAsCXXRecordDecl()); 394d712c5edSmartinboehme if (Optional == nullptr) 395092a530cSStanislav Gatev return 0; 396092a530cSStanislav Gatev return 1 + countOptionalWrappers( 397092a530cSStanislav Gatev ASTCtx, 398d712c5edSmartinboehme valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx)); 399092a530cSStanislav Gatev } 400092a530cSStanislav Gatev 40171f2ec2dSmartinboehme StorageLocation *getLocBehindPossiblePointer(const Expr &E, 40271f2ec2dSmartinboehme const Environment &Env) { 40371f2ec2dSmartinboehme if (E.isPRValue()) { 40471f2ec2dSmartinboehme if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E))) 40571f2ec2dSmartinboehme return &PointerVal->getPointeeLoc(); 406dd38caf3SYitzhak Mandelbaum return nullptr; 407dd38caf3SYitzhak Mandelbaum } 40871f2ec2dSmartinboehme return Env.getStorageLocation(E); 40948bc7150SMartin Braenne } 41048bc7150SMartin Braenne 411092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 412af98b0afSStanislav Gatev LatticeTransferState &State) { 41371f2ec2dSmartinboehme if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 41471f2ec2dSmartinboehme getLocBehindPossiblePointer(*ObjectExpr, State.Env))) { 415b244b6aeSMartin Braenne if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr) 41671f2ec2dSmartinboehme State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc)); 417af98b0afSStanislav Gatev } 418dd38caf3SYitzhak Mandelbaum } 419af98b0afSStanislav Gatev 4203bc1ea5bSMartin Braenne void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 4213bc1ea5bSMartin Braenne LatticeTransferState &State) { 42271f2ec2dSmartinboehme if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 42371f2ec2dSmartinboehme getLocBehindPossiblePointer(*ObjectExpr, State.Env))) 42471f2ec2dSmartinboehme State.Env.setValue( 42571f2ec2dSmartinboehme *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc))); 4263bc1ea5bSMartin Braenne } 4273bc1ea5bSMartin Braenne 428092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E, 429092a530cSStanislav Gatev const MatchFinder::MatchResult &, 430092a530cSStanislav Gatev LatticeTransferState &State) { 431e8fce958Smartinboehme setHasValue(State.Env.getResultObjectLocation(*E), 432e8fce958Smartinboehme State.Env.getBoolLiteralValue(true), State.Env); 4339e0fc676SStanislav Gatev } 4349e0fc676SStanislav Gatev 435092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 436092a530cSStanislav Gatev const MatchFinder::MatchResult &, 437af98b0afSStanislav Gatev LatticeTransferState &State) { 438dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = getHasValue( 43971f2ec2dSmartinboehme State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 440b244b6aeSMartin Braenne State.Env.setValue(*CallExpr, *HasValueVal); 441af98b0afSStanislav Gatev } 442af98b0afSStanislav Gatev } 443af98b0afSStanislav Gatev 444*11c423f9SChris Cotter void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr, 445*11c423f9SChris Cotter const MatchFinder::MatchResult &, 446*11c423f9SChris Cotter LatticeTransferState &State) { 447*11c423f9SChris Cotter if (auto *HasValueVal = getHasValue( 448*11c423f9SChris Cotter State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 449*11c423f9SChris Cotter State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal)); 450*11c423f9SChris Cotter } 451*11c423f9SChris Cotter } 452*11c423f9SChris Cotter 4537f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in 4547f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property. 4556272226bSSam McCall void transferValueOrImpl( 4566272226bSSam McCall const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result, 4577f076004SYitzhak Mandelbaum LatticeTransferState &State, 4586272226bSSam McCall const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal, 4596272226bSSam McCall const Formula &HasValueVal)) { 4607f076004SYitzhak Mandelbaum auto &Env = State.Env; 4617f076004SYitzhak Mandelbaum 46271f2ec2dSmartinboehme const auto *MCE = 46371f2ec2dSmartinboehme Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID); 4647f076004SYitzhak Mandelbaum 46571f2ec2dSmartinboehme auto *HasValueVal = 46671f2ec2dSmartinboehme getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env)); 467dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) 4687f076004SYitzhak Mandelbaum return; 4697f076004SYitzhak Mandelbaum 470526c9b7eSmartinboehme Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), 4716272226bSSam McCall HasValueVal->formula())); 4727f076004SYitzhak Mandelbaum } 4737f076004SYitzhak Mandelbaum 4747f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 4757f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 4767f076004SYitzhak Mandelbaum LatticeTransferState &State) { 4777f076004SYitzhak Mandelbaum return transferValueOrImpl(ComparisonExpr, Result, State, 4786272226bSSam McCall [](Environment &Env, const Formula &ExprVal, 4796272226bSSam McCall const Formula &HasValueVal) -> const Formula & { 4806272226bSSam McCall auto &A = Env.arena(); 4817f076004SYitzhak Mandelbaum // If the result is *not* empty, then we know the 4827f076004SYitzhak Mandelbaum // optional must have been holding a value. If 4837f076004SYitzhak Mandelbaum // `ExprVal` is true, though, we don't learn 4847f076004SYitzhak Mandelbaum // anything definite about `has_value`, so we 4857f076004SYitzhak Mandelbaum // don't add any corresponding implications to 4867f076004SYitzhak Mandelbaum // the flow condition. 4876272226bSSam McCall return A.makeImplies(A.makeNot(ExprVal), 4887f076004SYitzhak Mandelbaum HasValueVal); 4897f076004SYitzhak Mandelbaum }); 4907f076004SYitzhak Mandelbaum } 4917f076004SYitzhak Mandelbaum 4927f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr, 4937f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 4947f076004SYitzhak Mandelbaum LatticeTransferState &State) { 4957f076004SYitzhak Mandelbaum transferValueOrImpl(ComparisonExpr, Result, State, 4966272226bSSam McCall [](Environment &Env, const Formula &ExprVal, 4976272226bSSam McCall const Formula &HasValueVal) -> const Formula & { 4986272226bSSam McCall auto &A = Env.arena(); 4997f076004SYitzhak Mandelbaum // We know that if `(opt.value_or(X) != X)` then 5007f076004SYitzhak Mandelbaum // `opt.hasValue()`, even without knowing further 5017f076004SYitzhak Mandelbaum // details about the contents of `opt`. 5026272226bSSam McCall return A.makeImplies(ExprVal, HasValueVal); 5037f076004SYitzhak Mandelbaum }); 5047f076004SYitzhak Mandelbaum } 5057f076004SYitzhak Mandelbaum 50665e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E, 50765e710c3SStanislav Gatev const MatchFinder::MatchResult &Result, 50865e710c3SStanislav Gatev LatticeTransferState &State) { 5099ecdbe38SMartin Braenne RecordStorageLocation *Loc = nullptr; 51044f98d01SMartin Braenne if (E->isPRValue()) { 51144f98d01SMartin Braenne Loc = &State.Env.getResultObjectLocation(*E); 51244f98d01SMartin Braenne } else { 5132ee396b0Smartinboehme Loc = State.Env.get<RecordStorageLocation>(*E); 514f76f6674SMartin Braenne if (Loc == nullptr) { 5159ecdbe38SMartin Braenne Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E)); 516b244b6aeSMartin Braenne State.Env.setStorageLocation(*E, *Loc); 51744f98d01SMartin Braenne } 518f76f6674SMartin Braenne } 51944f98d01SMartin Braenne 520e8fce958Smartinboehme if (State.Env.getValue(locForHasValue(*Loc)) != nullptr) 521e8fce958Smartinboehme return; 522e8fce958Smartinboehme 523e8fce958Smartinboehme setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env); 52465e710c3SStanislav Gatev } 52565e710c3SStanislav Gatev 526f653d140SMartin Braenne void constructOptionalValue(const Expr &E, Environment &Env, 527092a530cSStanislav Gatev BoolValue &HasValueVal) { 5289ecdbe38SMartin Braenne RecordStorageLocation &Loc = Env.getResultObjectLocation(E); 529e8fce958Smartinboehme setHasValue(Loc, HasValueVal, Env); 5309e0fc676SStanislav Gatev } 5319e0fc676SStanislav Gatev 532b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>` 533b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>` 534b000b770SStanislav Gatev /// where `T` is constructible from `U`. 535ae280281Smartinboehme BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E, 536b000b770SStanislav Gatev const MatchFinder::MatchResult &MatchRes, 537b000b770SStanislav Gatev LatticeTransferState &State) { 538ae280281Smartinboehme const int DestTypeOptionalWrappersCount = 539ae280281Smartinboehme countOptionalWrappers(*MatchRes.Context, DestType); 540c849843cSMartin Braenne const int ArgTypeOptionalWrappersCount = countOptionalWrappers( 541c849843cSMartin Braenne *MatchRes.Context, E.getType().getNonReferenceType()); 542b000b770SStanislav Gatev 543ae280281Smartinboehme // Is this an constructor of the form `template<class U> optional(U &&)` / 544ae280281Smartinboehme // assignment of the form `template<class U> optional& operator=(U &&)` 545ae280281Smartinboehme // (where `T` is assignable / constructible from `U`)? 546ae280281Smartinboehme // We recognize this because the number of optionals in the optional being 547ae280281Smartinboehme // assigned to is different from the function argument type. 548ae280281Smartinboehme if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount) 549b000b770SStanislav Gatev return State.Env.getBoolLiteralValue(true); 550b000b770SStanislav Gatev 551ae280281Smartinboehme // Otherwise, this must be a constructor of the form 552ae280281Smartinboehme // `template <class U> optional<optional<U> &&)` / assignment of the form 553ae280281Smartinboehme // `template <class U> optional& operator=(optional<U> &&) 554ae280281Smartinboehme // (where, again, `T` is assignable / constructible from `U`). 5552ee396b0Smartinboehme auto *Loc = State.Env.get<RecordStorageLocation>(E); 55671f2ec2dSmartinboehme if (auto *HasValueVal = getHasValue(State.Env, Loc)) 557dd38caf3SYitzhak Mandelbaum return *HasValueVal; 558b000b770SStanislav Gatev return State.Env.makeAtomicBoolValue(); 559b000b770SStanislav Gatev } 560b000b770SStanislav Gatev 561092a530cSStanislav Gatev void transferValueOrConversionConstructor( 562092a530cSStanislav Gatev const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 5639e0fc676SStanislav Gatev LatticeTransferState &State) { 564092a530cSStanislav Gatev assert(E->getNumArgs() > 0); 565092a530cSStanislav Gatev 566ae280281Smartinboehme constructOptionalValue( 567ae280281Smartinboehme *E, State.Env, 568ae280281Smartinboehme valueOrConversionHasValue( 569ae280281Smartinboehme E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0), 570ae280281Smartinboehme MatchRes, State)); 571b000b770SStanislav Gatev } 572092a530cSStanislav Gatev 573b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 574b000b770SStanislav Gatev LatticeTransferState &State) { 575b000b770SStanislav Gatev assert(E->getNumArgs() > 0); 576b000b770SStanislav Gatev 5772ee396b0Smartinboehme if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) { 578e8fce958Smartinboehme setHasValue(*Loc, HasValueVal, State.Env); 579b000b770SStanislav Gatev 580b000b770SStanislav Gatev // Assign a storage location for the whole expression. 581b244b6aeSMartin Braenne State.Env.setStorageLocation(*E, *Loc); 582f653d140SMartin Braenne } 583b000b770SStanislav Gatev } 584b000b770SStanislav Gatev 585b000b770SStanislav Gatev void transferValueOrConversionAssignment( 586b000b770SStanislav Gatev const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 587b000b770SStanislav Gatev LatticeTransferState &State) { 588b000b770SStanislav Gatev assert(E->getNumArgs() > 1); 589ae280281Smartinboehme transferAssignment( 590ae280281Smartinboehme E, 591ae280281Smartinboehme valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(), 59206decd0bSKazu Hirata *E->getArg(1), MatchRes, State), 593b000b770SStanislav Gatev State); 594b000b770SStanislav Gatev } 595b000b770SStanislav Gatev 596b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E, 597b000b770SStanislav Gatev const MatchFinder::MatchResult &, 598b000b770SStanislav Gatev LatticeTransferState &State) { 599b000b770SStanislav Gatev transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 6009e0fc676SStanislav Gatev } 6019e0fc676SStanislav Gatev 6029ecdbe38SMartin Braenne void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2, 6039ecdbe38SMartin Braenne Environment &Env) { 604d4fb829bSYitzhak Mandelbaum // We account for cases where one or both of the optionals are not modeled, 605d4fb829bSYitzhak Mandelbaum // either lacking associated storage locations, or lacking values associated 606d4fb829bSYitzhak Mandelbaum // to such storage locations. 6072ddd57aeSStanislav Gatev 608d4fb829bSYitzhak Mandelbaum if (Loc1 == nullptr) { 609d4fb829bSYitzhak Mandelbaum if (Loc2 != nullptr) 610e8fce958Smartinboehme setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env); 611d4fb829bSYitzhak Mandelbaum return; 612d4fb829bSYitzhak Mandelbaum } 613d4fb829bSYitzhak Mandelbaum if (Loc2 == nullptr) { 614e8fce958Smartinboehme setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env); 615d4fb829bSYitzhak Mandelbaum return; 616d4fb829bSYitzhak Mandelbaum } 6172ddd57aeSStanislav Gatev 618d4fb829bSYitzhak Mandelbaum // Both expressions have locations, though they may not have corresponding 619d4fb829bSYitzhak Mandelbaum // values. In that case, we create a fresh value at this point. Note that if 620d4fb829bSYitzhak Mandelbaum // two branches both do this, they will not share the value, but it at least 621d4fb829bSYitzhak Mandelbaum // allows for local reasoning about the value. To avoid the above, we would 622d4fb829bSYitzhak Mandelbaum // need *lazy* value allocation. 623d4fb829bSYitzhak Mandelbaum // FIXME: allocate values lazily, instead of just creating a fresh value. 62471f2ec2dSmartinboehme BoolValue *BoolVal1 = getHasValue(Env, Loc1); 625f653d140SMartin Braenne if (BoolVal1 == nullptr) 626f653d140SMartin Braenne BoolVal1 = &Env.makeAtomicBoolValue(); 627d4fb829bSYitzhak Mandelbaum 62871f2ec2dSmartinboehme BoolValue *BoolVal2 = getHasValue(Env, Loc2); 629f653d140SMartin Braenne if (BoolVal2 == nullptr) 630f653d140SMartin Braenne BoolVal2 = &Env.makeAtomicBoolValue(); 631d4fb829bSYitzhak Mandelbaum 632e8fce958Smartinboehme setHasValue(*Loc1, *BoolVal2, Env); 633e8fce958Smartinboehme setHasValue(*Loc2, *BoolVal1, Env); 6342ddd57aeSStanislav Gatev } 6352ddd57aeSStanislav Gatev 6362ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E, 6372ddd57aeSStanislav Gatev const MatchFinder::MatchResult &, 6382ddd57aeSStanislav Gatev LatticeTransferState &State) { 6392ddd57aeSStanislav Gatev assert(E->getNumArgs() == 1); 6402ee396b0Smartinboehme auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 641f653d140SMartin Braenne transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env); 6422ddd57aeSStanislav Gatev } 6432ddd57aeSStanislav Gatev 6442ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 6452ddd57aeSStanislav Gatev LatticeTransferState &State) { 6462ddd57aeSStanislav Gatev assert(E->getNumArgs() == 2); 6472ee396b0Smartinboehme auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 6482ee396b0Smartinboehme auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1)); 649f653d140SMartin Braenne transferSwap(Arg0Loc, Arg1Loc, State.Env); 6502ddd57aeSStanislav Gatev } 6512ddd57aeSStanislav Gatev 65225956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &, 65325956d55SAMS21 LatticeTransferState &State) { 65425956d55SAMS21 assert(E->getNumArgs() == 1); 65525956d55SAMS21 656b244b6aeSMartin Braenne if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0))) 657b244b6aeSMartin Braenne State.Env.setStorageLocation(*E, *Loc); 65825956d55SAMS21 } 65925956d55SAMS21 6606272226bSSam McCall const Formula &evaluateEquality(Arena &A, const Formula &EqVal, 6616272226bSSam McCall const Formula &LHS, const Formula &RHS) { 662390029beSYitzhak Mandelbaum // Logically, an optional<T> object is composed of two values - a `has_value` 663390029beSYitzhak Mandelbaum // bit and a value of type T. Equality of optional objects compares both 664390029beSYitzhak Mandelbaum // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 665390029beSYitzhak Mandelbaum // when two optional objects are engaged, the equality of their respective 666390029beSYitzhak Mandelbaum // values of type T matters. Since we only track the `has_value` bits, we 667390029beSYitzhak Mandelbaum // can't make any conclusions about equality when we know that two optional 668390029beSYitzhak Mandelbaum // objects are engaged. 669390029beSYitzhak Mandelbaum // 670390029beSYitzhak Mandelbaum // We express this as two facts about the equality: 671390029beSYitzhak Mandelbaum // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 672390029beSYitzhak Mandelbaum // If they are equal, then either both are set or both are unset. 673390029beSYitzhak Mandelbaum // b) (!LHS & !RHS) => EqVal 674390029beSYitzhak Mandelbaum // If neither is set, then they are equal. 675390029beSYitzhak Mandelbaum // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 6766272226bSSam McCall return A.makeAnd( 6776272226bSSam McCall A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS), 6786272226bSSam McCall A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))), 6796272226bSSam McCall A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS))); 680390029beSYitzhak Mandelbaum } 681390029beSYitzhak Mandelbaum 682390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 683390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, 684390029beSYitzhak Mandelbaum LatticeTransferState &State) { 685390029beSYitzhak Mandelbaum Environment &Env = State.Env; 6866272226bSSam McCall auto &A = Env.arena(); 687390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 6882ee396b0Smartinboehme auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0)); 68971f2ec2dSmartinboehme if (auto *LHasVal = getHasValue(Env, Arg0Loc)) { 6902ee396b0Smartinboehme auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1)); 69171f2ec2dSmartinboehme if (auto *RHasVal = getHasValue(Env, Arg1Loc)) { 692390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 6936272226bSSam McCall CmpValue = &A.makeNot(*CmpValue); 694526c9b7eSmartinboehme Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(), 6956272226bSSam McCall RHasVal->formula())); 696390029beSYitzhak Mandelbaum } 697390029beSYitzhak Mandelbaum } 69871f2ec2dSmartinboehme } 699390029beSYitzhak Mandelbaum 700390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 701390029beSYitzhak Mandelbaum const clang::Expr *E, Environment &Env) { 7026272226bSSam McCall auto &A = Env.arena(); 703390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 7042ee396b0Smartinboehme auto *Loc = Env.get<RecordStorageLocation>(*E); 70571f2ec2dSmartinboehme if (auto *HasVal = getHasValue(Env, Loc)) { 706390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 7076272226bSSam McCall CmpValue = &A.makeNot(*CmpValue); 708526c9b7eSmartinboehme Env.assume( 7096272226bSSam McCall evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true))); 710390029beSYitzhak Mandelbaum } 711390029beSYitzhak Mandelbaum } 712390029beSYitzhak Mandelbaum 71371f2ec2dSmartinboehme void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr, 71471f2ec2dSmartinboehme const clang::Expr *E, Environment &Env) { 71571f2ec2dSmartinboehme auto &A = Env.arena(); 71671f2ec2dSmartinboehme auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 7172ee396b0Smartinboehme auto *Loc = Env.get<RecordStorageLocation>(*E); 71871f2ec2dSmartinboehme if (auto *HasVal = getHasValue(Env, Loc)) { 71971f2ec2dSmartinboehme if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 72071f2ec2dSmartinboehme CmpValue = &A.makeNot(*CmpValue); 72171f2ec2dSmartinboehme Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(), 72271f2ec2dSmartinboehme A.makeLiteral(false))); 72371f2ec2dSmartinboehme } 72471f2ec2dSmartinboehme } 72571f2ec2dSmartinboehme 7266ad0788cSKazu Hirata std::optional<StatementMatcher> 727a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 7285d22d1f5SYitzhak Mandelbaum if (Options.IgnoreSmartPointerDereference) { 7295d22d1f5SYitzhak Mandelbaum auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 7305d22d1f5SYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 7315d22d1f5SYitzhak Mandelbaum unless(hasArgument(0, expr(hasOptionalType())))))); 7325d22d1f5SYitzhak Mandelbaum return expr( 7335d22d1f5SYitzhak Mandelbaum anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 7345d22d1f5SYitzhak Mandelbaum } 73534e0d057SKazu Hirata return std::nullopt; 736a184a0d8SYitzhak Mandelbaum } 737a184a0d8SYitzhak Mandelbaum 73858fe7f96SSam Estep StatementMatcher 7396ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 7402f0630f8SAnton Dukeman return isOptionalMemberCallWithNameMatcher(hasName("value"), 7412f0630f8SAnton Dukeman IgnorableOptional); 74258fe7f96SSam Estep } 74358fe7f96SSam Estep 74458fe7f96SSam Estep StatementMatcher 7456ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 74658fe7f96SSam Estep return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 74758fe7f96SSam Estep isOptionalOperatorCallWithName("->", IgnorableOptional))); 74858fe7f96SSam Estep } 74958fe7f96SSam Estep 7505d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() { 751b000b770SStanislav Gatev // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 752b000b770SStanislav Gatev // lot of duplicated work (e.g. string comparisons), consider providing APIs 753b000b770SStanislav Gatev // that avoid it through memoization. 7547538b360SWei Yi Tee return CFGMatchSwitchBuilder<LatticeTransferState>() 7559e0fc676SStanislav Gatev // make_optional 7567538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 757092a530cSStanislav Gatev 7580e8d4a6dSYitzhak Mandelbaum // optional::optional (in place) 7597538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 760092a530cSStanislav Gatev isOptionalInPlaceConstructor(), 761092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 762092a530cSStanislav Gatev LatticeTransferState &State) { 763f653d140SMartin Braenne constructOptionalValue(*E, State.Env, 7640e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(true)); 765092a530cSStanislav Gatev }) 7660e8d4a6dSYitzhak Mandelbaum // optional::optional(nullopt_t) 767390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXConstructExpr>( 768390029beSYitzhak Mandelbaum isOptionalNulloptConstructor(), 769390029beSYitzhak Mandelbaum [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 770390029beSYitzhak Mandelbaum LatticeTransferState &State) { 771f653d140SMartin Braenne constructOptionalValue(*E, State.Env, 7720e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(false)); 773390029beSYitzhak Mandelbaum }) 7740e8d4a6dSYitzhak Mandelbaum // optional::optional (value/conversion) 7757538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 776092a530cSStanislav Gatev transferValueOrConversionConstructor) 7779e0fc676SStanislav Gatev 778b000b770SStanislav Gatev // optional::operator= 7797538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>( 7807538b360SWei Yi Tee isOptionalValueOrConversionAssignment(), 781b000b770SStanislav Gatev transferValueOrConversionAssignment) 7827538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 783b000b770SStanislav Gatev transferNulloptAssignment) 784b000b770SStanislav Gatev 785af98b0afSStanislav Gatev // optional::value 7867538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 7875d22d1f5SYitzhak Mandelbaum valueCall(std::nullopt), 788092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 789092a530cSStanislav Gatev LatticeTransferState &State) { 790af98b0afSStanislav Gatev transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 791af98b0afSStanislav Gatev }) 792af98b0afSStanislav Gatev 7933bc1ea5bSMartin Braenne // optional::operator* 7943bc1ea5bSMartin Braenne .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"), 7957538b360SWei Yi Tee [](const CallExpr *E, 7967538b360SWei Yi Tee const MatchFinder::MatchResult &, 797092a530cSStanislav Gatev LatticeTransferState &State) { 798af98b0afSStanislav Gatev transferUnwrapCall(E, E->getArg(0), State); 799af98b0afSStanislav Gatev }) 800af98b0afSStanislav Gatev 8013bc1ea5bSMartin Braenne // optional::operator-> 8023bc1ea5bSMartin Braenne .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"), 8033bc1ea5bSMartin Braenne [](const CallExpr *E, 8043bc1ea5bSMartin Braenne const MatchFinder::MatchResult &, 8053bc1ea5bSMartin Braenne LatticeTransferState &State) { 8063bc1ea5bSMartin Braenne transferArrowOpCall(E, E->getArg(0), State); 8073bc1ea5bSMartin Braenne }) 8083bc1ea5bSMartin Braenne 8092f0630f8SAnton Dukeman // optional::has_value, optional::hasValue 8102f0630f8SAnton Dukeman // Of the supported optionals only folly::Optional uses hasValue, but this 8112f0630f8SAnton Dukeman // will also pass for other types 8127538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8132f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher( 8142f0630f8SAnton Dukeman hasAnyName("has_value", "hasValue")), 815af98b0afSStanislav Gatev transferOptionalHasValueCall) 816af98b0afSStanislav Gatev 8179e0fc676SStanislav Gatev // optional::operator bool 8187538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8192f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("operator bool")), 8209e0fc676SStanislav Gatev transferOptionalHasValueCall) 8219e0fc676SStanislav Gatev 822*11c423f9SChris Cotter // NullableValue::isNull 823*11c423f9SChris Cotter // Only NullableValue has isNull 824*11c423f9SChris Cotter .CaseOfCFGStmt<CXXMemberCallExpr>( 825*11c423f9SChris Cotter isOptionalMemberCallWithNameMatcher(hasName("isNull")), 826*11c423f9SChris Cotter transferOptionalIsNullCall) 827*11c423f9SChris Cotter 8289e0fc676SStanislav Gatev // optional::emplace 8297538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8302f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("emplace")), 831092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 832092a530cSStanislav Gatev LatticeTransferState &State) { 8339ecdbe38SMartin Braenne if (RecordStorageLocation *Loc = 834f653d140SMartin Braenne getImplicitObjectLocation(*E, State.Env)) { 835e8fce958Smartinboehme setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env); 836f653d140SMartin Braenne } 837092a530cSStanislav Gatev }) 8389e0fc676SStanislav Gatev 8399e0fc676SStanislav Gatev // optional::reset 8407538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8412f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("reset")), 842092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 843092a530cSStanislav Gatev LatticeTransferState &State) { 8449ecdbe38SMartin Braenne if (RecordStorageLocation *Loc = 845f653d140SMartin Braenne getImplicitObjectLocation(*E, State.Env)) { 846e8fce958Smartinboehme setHasValue(*Loc, State.Env.getBoolLiteralValue(false), 847f653d140SMartin Braenne State.Env); 848f653d140SMartin Braenne } 849092a530cSStanislav Gatev }) 8509e0fc676SStanislav Gatev 8512ddd57aeSStanislav Gatev // optional::swap 8522f0630f8SAnton Dukeman .CaseOfCFGStmt<CXXMemberCallExpr>( 8532f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("swap")), 8542ddd57aeSStanislav Gatev transferSwapCall) 8552ddd57aeSStanislav Gatev 8562ddd57aeSStanislav Gatev // std::swap 8577538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 8582ddd57aeSStanislav Gatev 85925956d55SAMS21 // std::forward 86025956d55SAMS21 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 86125956d55SAMS21 8627f076004SYitzhak Mandelbaum // opt.value_or("").empty() 8637538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 8647538b360SWei Yi Tee transferValueOrStringEmptyCall) 8657f076004SYitzhak Mandelbaum 8667f076004SYitzhak Mandelbaum // opt.value_or(X) != X 8677538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 8687f076004SYitzhak Mandelbaum 869390029beSYitzhak Mandelbaum // Comparisons (==, !=): 870390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 87171f2ec2dSmartinboehme isComparisonOperatorCall(hasOptionalType(), hasOptionalType()), 872390029beSYitzhak Mandelbaum transferOptionalAndOptionalCmp) 873390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 87471f2ec2dSmartinboehme isComparisonOperatorCall(hasOptionalType(), hasNulloptType()), 87571f2ec2dSmartinboehme [](const clang::CXXOperatorCallExpr *Cmp, 87671f2ec2dSmartinboehme const MatchFinder::MatchResult &, LatticeTransferState &State) { 87771f2ec2dSmartinboehme transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env); 87871f2ec2dSmartinboehme }) 87971f2ec2dSmartinboehme .CaseOfCFGStmt<CXXOperatorCallExpr>( 88071f2ec2dSmartinboehme isComparisonOperatorCall(hasNulloptType(), hasOptionalType()), 88171f2ec2dSmartinboehme [](const clang::CXXOperatorCallExpr *Cmp, 88271f2ec2dSmartinboehme const MatchFinder::MatchResult &, LatticeTransferState &State) { 88371f2ec2dSmartinboehme transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env); 88471f2ec2dSmartinboehme }) 88571f2ec2dSmartinboehme .CaseOfCFGStmt<CXXOperatorCallExpr>( 88671f2ec2dSmartinboehme isComparisonOperatorCall( 88771f2ec2dSmartinboehme hasOptionalType(), 88871f2ec2dSmartinboehme unless(anyOf(hasOptionalType(), hasNulloptType()))), 889390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 890390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 891390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 892390029beSYitzhak Mandelbaum }) 893390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 89471f2ec2dSmartinboehme isComparisonOperatorCall( 89571f2ec2dSmartinboehme unless(anyOf(hasOptionalType(), hasNulloptType())), 896390029beSYitzhak Mandelbaum hasOptionalType()), 897390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 898390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 899390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 900390029beSYitzhak Mandelbaum }) 901390029beSYitzhak Mandelbaum 90265e710c3SStanislav Gatev // returns optional 9037538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 90465e710c3SStanislav Gatev transferCallReturningOptional) 90565e710c3SStanislav Gatev 906af98b0afSStanislav Gatev .Build(); 907af98b0afSStanislav Gatev } 908af98b0afSStanislav Gatev 909004a7ceaSYitzhak Mandelbaum llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr, 91058fe7f96SSam Estep const Environment &Env) { 91171f2ec2dSmartinboehme if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 91271f2ec2dSmartinboehme getLocBehindPossiblePointer(*ObjectExpr, Env))) { 91371f2ec2dSmartinboehme auto *Prop = Env.getValue(locForHasValue(*OptionalLoc)); 91458fe7f96SSam Estep if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 915526c9b7eSmartinboehme if (Env.proves(HasValueVal->formula())) 91658fe7f96SSam Estep return {}; 91758fe7f96SSam Estep } 91858fe7f96SSam Estep } 91958fe7f96SSam Estep 92058fe7f96SSam Estep // Record that this unwrap is *not* provably safe. 92158fe7f96SSam Estep // FIXME: include either the name of the optional (if applicable) or a source 92258fe7f96SSam Estep // range of the access for easier interpretation of the result. 92358fe7f96SSam Estep return {ObjectExpr->getBeginLoc()}; 92458fe7f96SSam Estep } 92558fe7f96SSam Estep 92658fe7f96SSam Estep auto buildDiagnoseMatchSwitch( 92758fe7f96SSam Estep const UncheckedOptionalAccessModelOptions &Options) { 92858fe7f96SSam Estep // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 92958fe7f96SSam Estep // lot of duplicated work (e.g. string comparisons), consider providing APIs 93058fe7f96SSam Estep // that avoid it through memoization. 93158fe7f96SSam Estep auto IgnorableOptional = ignorableOptional(Options); 932004a7ceaSYitzhak Mandelbaum return CFGMatchSwitchBuilder<const Environment, 933004a7ceaSYitzhak Mandelbaum llvm::SmallVector<SourceLocation>>() 93458fe7f96SSam Estep // optional::value 9357538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 93658fe7f96SSam Estep valueCall(IgnorableOptional), 93758fe7f96SSam Estep [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 93858fe7f96SSam Estep const Environment &Env) { 9396a81e694SMartin Braenne return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env); 94058fe7f96SSam Estep }) 94158fe7f96SSam Estep 94258fe7f96SSam Estep // optional::operator*, optional::operator-> 9436a81e694SMartin Braenne .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 9446a81e694SMartin Braenne [](const CallExpr *E, 9456a81e694SMartin Braenne const MatchFinder::MatchResult &, 94658fe7f96SSam Estep const Environment &Env) { 9476a81e694SMartin Braenne return diagnoseUnwrapCall(E->getArg(0), Env); 94858fe7f96SSam Estep }) 94958fe7f96SSam Estep .Build(); 95058fe7f96SSam Estep } 95158fe7f96SSam Estep 952af98b0afSStanislav Gatev } // namespace 953af98b0afSStanislav Gatev 9547e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher 9557e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() { 956d712c5edSmartinboehme return cxxRecordDecl(optionalClass()); 95771f2ec2dSmartinboehme } 95871f2ec2dSmartinboehme 95971f2ec2dSmartinboehme UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, 96071f2ec2dSmartinboehme Environment &Env) 961cf1f978dSSam Estep : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 96271f2ec2dSmartinboehme TransferMatchSwitch(buildTransferMatchSwitch()) { 96371f2ec2dSmartinboehme Env.getDataflowAnalysisContext().setSyntheticFieldCallback( 96471f2ec2dSmartinboehme [&Ctx](QualType Ty) -> llvm::StringMap<QualType> { 965d712c5edSmartinboehme const CXXRecordDecl *Optional = 966d712c5edSmartinboehme getOptionalBaseClass(Ty->getAsCXXRecordDecl()); 967d712c5edSmartinboehme if (Optional == nullptr) 96871f2ec2dSmartinboehme return {}; 969d712c5edSmartinboehme return {{"value", valueTypeFromOptionalDecl(*Optional)}, 97071f2ec2dSmartinboehme {"has_value", Ctx.BoolTy}}; 97171f2ec2dSmartinboehme }); 97271f2ec2dSmartinboehme } 973af98b0afSStanislav Gatev 9746b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 9757538b360SWei Yi Tee NoopLattice &L, Environment &Env) { 976af98b0afSStanislav Gatev LatticeTransferState State(L, Env); 9776b991ba4SYitzhak Mandelbaum TransferMatchSwitch(Elt, getASTContext(), State); 978af98b0afSStanislav Gatev } 979af98b0afSStanislav Gatev 98058fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 98158fe7f96SSam Estep UncheckedOptionalAccessModelOptions Options) 98258fe7f96SSam Estep : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 98358fe7f96SSam Estep 984af98b0afSStanislav Gatev } // namespace dataflow 985af98b0afSStanislav Gatev } // namespace clang 986