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" 20*6761b24aSJan Voung #include "clang/AST/Type.h" 21af98b0afSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h" 2209b462efSYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchersMacros.h" 237538b360SWei Yi Tee #include "clang/Analysis/CFG.h" 247538b360SWei Yi Tee #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h" 25af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 266272226bSSam McCall #include "clang/Analysis/FlowSensitive/Formula.h" 27*6761b24aSJan Voung #include "clang/Analysis/FlowSensitive/RecordOps.h" 280086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h" 29af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h" 3058fe7f96SSam Estep #include "clang/Basic/SourceLocation.h" 31af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h" 32af98b0afSStanislav Gatev #include "llvm/Support/Casting.h" 33d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h" 34af98b0afSStanislav Gatev #include <cassert> 359e0fc676SStanislav Gatev #include <memory> 36a1580d7bSKazu Hirata #include <optional> 379e0fc676SStanislav Gatev #include <utility> 38af98b0afSStanislav Gatev 39af98b0afSStanislav Gatev namespace clang { 40af98b0afSStanislav Gatev namespace dataflow { 4109b462efSYitzhak Mandelbaum 4211c423f9SChris Cotter // Note: the Names appear in reverse order. E.g., to check 4311c423f9SChris Cotter // if NS is foo::bar::, call isFullyQualifiedNamespaceEqualTo(NS, "bar", "foo") 4411c423f9SChris Cotter template <class... NameTypes> 4511c423f9SChris Cotter static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS, 4611c423f9SChris Cotter llvm::StringRef Name, 4711c423f9SChris Cotter NameTypes... Names) { 4811c423f9SChris Cotter if (!(NS.getDeclName().isIdentifier() && NS.getName() == Name && 4911c423f9SChris Cotter NS.getParent() != nullptr)) 5011c423f9SChris Cotter return false; 5111c423f9SChris Cotter 5211c423f9SChris Cotter if constexpr (sizeof...(NameTypes) > 0) { 5311c423f9SChris Cotter if (NS.getParent()->isTranslationUnit()) 5411c423f9SChris Cotter return false; 5511c423f9SChris Cotter if (const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.getParent())) 5611c423f9SChris Cotter return isFullyQualifiedNamespaceEqualTo(*NextNS, Names...); 5711c423f9SChris Cotter return false; 5811c423f9SChris Cotter } else { 5911c423f9SChris Cotter return NS.getParent()->isTranslationUnit(); 6011c423f9SChris Cotter } 6109b462efSYitzhak Mandelbaum } 6209b462efSYitzhak Mandelbaum 6309b462efSYitzhak Mandelbaum static bool hasOptionalClassName(const CXXRecordDecl &RD) { 6409b462efSYitzhak Mandelbaum if (!RD.getDeclName().isIdentifier()) 6509b462efSYitzhak Mandelbaum return false; 6609b462efSYitzhak Mandelbaum 6709b462efSYitzhak Mandelbaum if (RD.getName() == "optional") { 6809b462efSYitzhak Mandelbaum if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext())) 6911c423f9SChris Cotter return N->isStdNamespace() || 7011c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "absl") || 7111c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "bsl"); 7209b462efSYitzhak Mandelbaum return false; 7309b462efSYitzhak Mandelbaum } 7409b462efSYitzhak Mandelbaum 7509b462efSYitzhak Mandelbaum if (RD.getName() == "Optional") { 762f0630f8SAnton Dukeman // Check whether namespace is "::base" or "::folly". 7709b462efSYitzhak Mandelbaum const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); 7811c423f9SChris Cotter return N != nullptr && (isFullyQualifiedNamespaceEqualTo(*N, "base") || 7911c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "folly")); 8011c423f9SChris Cotter } 8111c423f9SChris Cotter 8211c423f9SChris Cotter if (RD.getName() == "NullableValue") { 8311c423f9SChris Cotter const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); 8411c423f9SChris Cotter return N != nullptr && 8511c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP"); 8609b462efSYitzhak Mandelbaum } 8709b462efSYitzhak Mandelbaum 8809b462efSYitzhak Mandelbaum return false; 8909b462efSYitzhak Mandelbaum } 9009b462efSYitzhak Mandelbaum 91d712c5edSmartinboehme static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) { 92d712c5edSmartinboehme if (RD == nullptr) 93d712c5edSmartinboehme return nullptr; 94d712c5edSmartinboehme if (hasOptionalClassName(*RD)) 95d712c5edSmartinboehme return RD; 96d712c5edSmartinboehme 97d712c5edSmartinboehme if (!RD->hasDefinition()) 98d712c5edSmartinboehme return nullptr; 99d712c5edSmartinboehme 100d712c5edSmartinboehme for (const CXXBaseSpecifier &Base : RD->bases()) 101d712c5edSmartinboehme if (const CXXRecordDecl *BaseClass = 102d712c5edSmartinboehme getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl())) 103d712c5edSmartinboehme return BaseClass; 104d712c5edSmartinboehme 105d712c5edSmartinboehme return nullptr; 106d712c5edSmartinboehme } 107d712c5edSmartinboehme 108*6761b24aSJan Voung static bool isSupportedOptionalType(QualType Ty) { 109*6761b24aSJan Voung const CXXRecordDecl *Optional = 110*6761b24aSJan Voung getOptionalBaseClass(Ty->getAsCXXRecordDecl()); 111*6761b24aSJan Voung return Optional != nullptr; 112*6761b24aSJan Voung } 113*6761b24aSJan Voung 114af98b0afSStanislav Gatev namespace { 115af98b0afSStanislav Gatev 116af98b0afSStanislav Gatev using namespace ::clang::ast_matchers; 117*6761b24aSJan Voung 118*6761b24aSJan Voung using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>; 119af98b0afSStanislav Gatev 120d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); } 121d712c5edSmartinboehme 122d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) { 123d712c5edSmartinboehme return getOptionalBaseClass(&Node) != nullptr; 12409b462efSYitzhak Mandelbaum } 12509b462efSYitzhak Mandelbaum 126d712c5edSmartinboehme auto desugarsToOptionalType() { 12765e710c3SStanislav Gatev return hasUnqualifiedDesugaredType( 128d712c5edSmartinboehme recordType(hasDeclaration(cxxRecordDecl(optionalClass())))); 12965e710c3SStanislav Gatev } 13065e710c3SStanislav Gatev 131d712c5edSmartinboehme auto desugarsToOptionalOrDerivedType() { 132d712c5edSmartinboehme return hasUnqualifiedDesugaredType( 133d712c5edSmartinboehme recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass())))); 134d712c5edSmartinboehme } 135d712c5edSmartinboehme 136d712c5edSmartinboehme auto hasOptionalType() { return hasType(desugarsToOptionalType()); } 137d712c5edSmartinboehme 138d712c5edSmartinboehme /// Matches any of the spellings of the optional types and sugar, aliases, 139d712c5edSmartinboehme /// derived classes, etc. 140d712c5edSmartinboehme auto hasOptionalOrDerivedType() { 141d712c5edSmartinboehme return hasType(desugarsToOptionalOrDerivedType()); 142d712c5edSmartinboehme } 143d712c5edSmartinboehme 144d712c5edSmartinboehme QualType getPublicType(const Expr *E) { 145d712c5edSmartinboehme auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens()); 146d712c5edSmartinboehme if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) { 147d712c5edSmartinboehme QualType Ty = E->getType(); 148d712c5edSmartinboehme if (Ty->isPointerType()) 149d712c5edSmartinboehme return Ty->getPointeeType(); 150d712c5edSmartinboehme return Ty; 151d712c5edSmartinboehme } 152d712c5edSmartinboehme 153d712c5edSmartinboehme // Is the derived type that we're casting from the type of `*this`? In this 154d712c5edSmartinboehme // special case, we can upcast to the base class even if the base is 155d712c5edSmartinboehme // non-public. 156d712c5edSmartinboehme bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr()); 157d712c5edSmartinboehme 158d712c5edSmartinboehme // Find the least-derived type in the path (i.e. the last entry in the list) 159d712c5edSmartinboehme // that we can access. 160d712c5edSmartinboehme const CXXBaseSpecifier *PublicBase = nullptr; 161d712c5edSmartinboehme for (const CXXBaseSpecifier *Base : Cast->path()) { 162d712c5edSmartinboehme if (Base->getAccessSpecifier() != AS_public && !CastingFromThis) 163d712c5edSmartinboehme break; 164d712c5edSmartinboehme PublicBase = Base; 165d712c5edSmartinboehme CastingFromThis = false; 166d712c5edSmartinboehme } 167d712c5edSmartinboehme 168d712c5edSmartinboehme if (PublicBase != nullptr) 169d712c5edSmartinboehme return PublicBase->getType(); 170d712c5edSmartinboehme 171d712c5edSmartinboehme // We didn't find any public type that we could cast to. There may be more 172d712c5edSmartinboehme // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this 173d712c5edSmartinboehme // will return the type of `getSubExpr()`.) 174d712c5edSmartinboehme return getPublicType(Cast->getSubExpr()); 175d712c5edSmartinboehme } 176d712c5edSmartinboehme 177d712c5edSmartinboehme // Returns the least-derived type for the receiver of `MCE` that 178d712c5edSmartinboehme // `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to. 179d712c5edSmartinboehme // Effectively, we upcast until we reach a non-public base class, unless that 180d712c5edSmartinboehme // base is a base of `*this`. 181d712c5edSmartinboehme // 182d712c5edSmartinboehme // This is needed to correctly match methods called on types derived from 183d712c5edSmartinboehme // `std::optional`. 184d712c5edSmartinboehme // 185d712c5edSmartinboehme // Say we have a `struct Derived : public std::optional<int> {} d;` For a call 186d712c5edSmartinboehme // `d.has_value()`, the `getImplicitObjectArgument()` looks like this: 187d712c5edSmartinboehme // 188d712c5edSmartinboehme // ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue 189d712c5edSmartinboehme // | <UncheckedDerivedToBase (optional -> __optional_storage_base)> 190d712c5edSmartinboehme // `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived' 191d712c5edSmartinboehme // 192d712c5edSmartinboehme // The type of the implicit object argument is `__optional_storage_base` 193d712c5edSmartinboehme // (since this is the internal type that `has_value()` is declared on). If we 194d712c5edSmartinboehme // call `IgnoreParenImpCasts()` on the implicit object argument, we get the 195d712c5edSmartinboehme // `DeclRefExpr`, which has type `Derived`. Neither of these types is 196d712c5edSmartinboehme // `optional`, and hence neither is sufficient for querying whether we are 197d712c5edSmartinboehme // calling a method on `optional`. 198d712c5edSmartinboehme // 199d712c5edSmartinboehme // Instead, starting with the most derived type, we need to follow the chain of 200d712c5edSmartinboehme // casts 201d712c5edSmartinboehme QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) { 202d712c5edSmartinboehme return getPublicType(MCE.getImplicitObjectArgument()); 203d712c5edSmartinboehme } 204d712c5edSmartinboehme 205d712c5edSmartinboehme AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType, 206d712c5edSmartinboehme ast_matchers::internal::Matcher<QualType>, InnerMatcher) { 207d712c5edSmartinboehme return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder); 208d712c5edSmartinboehme } 2096adfc64eSYitzhak Mandelbaum 2102f0630f8SAnton Dukeman auto isOptionalMemberCallWithNameMatcher( 2112f0630f8SAnton Dukeman ast_matchers::internal::Matcher<NamedDecl> matcher, 2126ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 213d712c5edSmartinboehme return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable))) 214d712c5edSmartinboehme : anything(), 215d712c5edSmartinboehme publicReceiverType(desugarsToOptionalType()), 2162f0630f8SAnton Dukeman callee(cxxMethodDecl(matcher))); 217af98b0afSStanislav Gatev } 218af98b0afSStanislav Gatev 219a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName( 220a184a0d8SYitzhak Mandelbaum llvm::StringRef operator_name, 2216ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 222a184a0d8SYitzhak Mandelbaum return cxxOperatorCallExpr( 223a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName(operator_name), 224a184a0d8SYitzhak Mandelbaum callee(cxxMethodDecl(ofClass(optionalClass()))), 225a184a0d8SYitzhak Mandelbaum Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 226af98b0afSStanislav Gatev } 227af98b0afSStanislav Gatev 228092a530cSStanislav Gatev auto isMakeOptionalCall() { 22911c423f9SChris Cotter return callExpr( 23011c423f9SChris Cotter callee(functionDecl(hasAnyName( 23111c423f9SChris Cotter "std::make_optional", "base::make_optional", "absl::make_optional", 23211c423f9SChris Cotter "folly::make_optional", "bsl::make_optional"))), 2339e0fc676SStanislav Gatev hasOptionalType()); 2349e0fc676SStanislav Gatev } 2359e0fc676SStanislav Gatev 236390029beSYitzhak Mandelbaum auto nulloptTypeDecl() { 2372f0630f8SAnton Dukeman return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t", 23811c423f9SChris Cotter "base::nullopt_t", "folly::None", 23911c423f9SChris Cotter "bsl::nullopt_t")); 240092a530cSStanislav Gatev } 241092a530cSStanislav Gatev 242390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); } 243390029beSYitzhak Mandelbaum 244092a530cSStanislav Gatev auto inPlaceClass() { 2452f0630f8SAnton Dukeman return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t", 24611c423f9SChris Cotter "base::in_place_t", "folly::in_place_t", 24711c423f9SChris Cotter "bsl::in_place_t")); 248092a530cSStanislav Gatev } 249092a530cSStanislav Gatev 250092a530cSStanislav Gatev auto isOptionalNulloptConstructor() { 2510086a355SYitzhak Mandelbaum return cxxConstructExpr( 2520086a355SYitzhak Mandelbaum hasDeclaration(cxxConstructorDecl(parameterCountIs(1), 253d712c5edSmartinboehme hasParameter(0, hasNulloptType()))), 254d712c5edSmartinboehme hasOptionalOrDerivedType()); 255092a530cSStanislav Gatev } 256092a530cSStanislav Gatev 257092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() { 258d712c5edSmartinboehme return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())), 259d712c5edSmartinboehme hasOptionalOrDerivedType()); 260092a530cSStanislav Gatev } 261092a530cSStanislav Gatev 262092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() { 263092a530cSStanislav Gatev return cxxConstructExpr( 264092a530cSStanislav Gatev unless(hasDeclaration( 265092a530cSStanislav Gatev cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 266d712c5edSmartinboehme argumentCountIs(1), hasArgument(0, unless(hasNulloptType())), 267d712c5edSmartinboehme hasOptionalOrDerivedType()); 268092a530cSStanislav Gatev } 269092a530cSStanislav Gatev 270b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() { 271b000b770SStanislav Gatev return cxxOperatorCallExpr( 272b000b770SStanislav Gatev hasOverloadedOperatorName("="), 273d712c5edSmartinboehme callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))), 274b000b770SStanislav Gatev unless(hasDeclaration(cxxMethodDecl( 275b000b770SStanislav Gatev anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 276b000b770SStanislav Gatev argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 277b000b770SStanislav Gatev } 278b000b770SStanislav Gatev 279b000b770SStanislav Gatev auto isOptionalNulloptAssignment() { 280d712c5edSmartinboehme return cxxOperatorCallExpr( 281d712c5edSmartinboehme hasOverloadedOperatorName("="), 282d712c5edSmartinboehme callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))), 283d712c5edSmartinboehme argumentCountIs(2), hasArgument(1, hasNulloptType())); 284b000b770SStanislav Gatev } 285b000b770SStanislav Gatev 2862ddd57aeSStanislav Gatev auto isStdSwapCall() { 2872ddd57aeSStanislav Gatev return callExpr(callee(functionDecl(hasName("std::swap"))), 288d712c5edSmartinboehme argumentCountIs(2), 289d712c5edSmartinboehme hasArgument(0, hasOptionalOrDerivedType()), 290d712c5edSmartinboehme hasArgument(1, hasOptionalOrDerivedType())); 2912ddd57aeSStanislav Gatev } 2922ddd57aeSStanislav Gatev 29325956d55SAMS21 auto isStdForwardCall() { 29425956d55SAMS21 return callExpr(callee(functionDecl(hasName("std::forward"))), 295d712c5edSmartinboehme argumentCountIs(1), 296d712c5edSmartinboehme hasArgument(0, hasOptionalOrDerivedType())); 29725956d55SAMS21 } 29825956d55SAMS21 2997f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 3007f076004SYitzhak Mandelbaum 3017f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() { 3027f076004SYitzhak Mandelbaum // `opt.value_or("").empty()` 3037f076004SYitzhak Mandelbaum return cxxMemberCallExpr( 3047f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("empty"))), 3057f076004SYitzhak Mandelbaum onImplicitObjectArgument(ignoringImplicit( 3067f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 3077f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 3087f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 3097f076004SYitzhak Mandelbaum hasArgument(0, stringLiteral(hasSize(0)))) 3107f076004SYitzhak Mandelbaum .bind(ValueOrCallID)))); 3117f076004SYitzhak Mandelbaum } 3127f076004SYitzhak Mandelbaum 3137f076004SYitzhak Mandelbaum auto isValueOrNotEqX() { 3147f076004SYitzhak Mandelbaum auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 3157f076004SYitzhak Mandelbaum return hasOperands( 3167f076004SYitzhak Mandelbaum ignoringImplicit( 3177f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 3187f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 3197f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 3207f076004SYitzhak Mandelbaum hasArgument(0, Arg)) 3217f076004SYitzhak Mandelbaum .bind(ValueOrCallID)), 3227f076004SYitzhak Mandelbaum ignoringImplicit(Arg)); 3237f076004SYitzhak Mandelbaum }; 3247f076004SYitzhak Mandelbaum 3257f076004SYitzhak Mandelbaum // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 3267f076004SYitzhak Mandelbaum // support this pattern for any expression, but the AST does not have a 3277f076004SYitzhak Mandelbaum // generic expression comparison facility, so we specialize to common cases 3287f076004SYitzhak Mandelbaum // seen in practice. FIXME: define a matcher that compares values across 3297f076004SYitzhak Mandelbaum // nodes, which would let us generalize this to any `X`. 3307f076004SYitzhak Mandelbaum return binaryOperation(hasOperatorName("!="), 3317f076004SYitzhak Mandelbaum anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 3327f076004SYitzhak Mandelbaum ComparesToSame(stringLiteral(hasSize(0))), 3337f076004SYitzhak Mandelbaum ComparesToSame(integerLiteral(equals(0))))); 3347f076004SYitzhak Mandelbaum } 3357f076004SYitzhak Mandelbaum 336*6761b24aSJan Voung auto isZeroParamConstMemberCall() { 337*6761b24aSJan Voung return cxxMemberCallExpr( 338*6761b24aSJan Voung callee(cxxMethodDecl(parameterCountIs(0), isConst()))); 339*6761b24aSJan Voung } 340*6761b24aSJan Voung 341*6761b24aSJan Voung auto isNonConstMemberCall() { 342*6761b24aSJan Voung return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst())))); 343*6761b24aSJan Voung } 344*6761b24aSJan Voung 345*6761b24aSJan Voung auto isNonConstMemberOperatorCall() { 346*6761b24aSJan Voung return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst())))); 347*6761b24aSJan Voung } 348*6761b24aSJan Voung 34965e710c3SStanislav Gatev auto isCallReturningOptional() { 350d712c5edSmartinboehme return callExpr(hasType(qualType( 351d712c5edSmartinboehme anyOf(desugarsToOptionalOrDerivedType(), 352d712c5edSmartinboehme referenceType(pointee(desugarsToOptionalOrDerivedType())))))); 35365e710c3SStanislav Gatev } 35465e710c3SStanislav Gatev 355390029beSYitzhak Mandelbaum template <typename L, typename R> 356390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 357390029beSYitzhak Mandelbaum return cxxOperatorCallExpr( 358390029beSYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 359390029beSYitzhak Mandelbaum argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 360390029beSYitzhak Mandelbaum hasArgument(1, rhs_arg_matcher)); 361390029beSYitzhak Mandelbaum } 362390029beSYitzhak Mandelbaum 3636272226bSSam McCall /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula. 3646272226bSSam McCall const Formula &forceBoolValue(Environment &Env, const Expr &Expr) { 3652ee396b0Smartinboehme auto *Value = Env.get<BoolValue>(Expr); 366390029beSYitzhak Mandelbaum if (Value != nullptr) 3676272226bSSam McCall return Value->formula(); 368390029beSYitzhak Mandelbaum 369390029beSYitzhak Mandelbaum Value = &Env.makeAtomicBoolValue(); 370b244b6aeSMartin Braenne Env.setValue(Expr, *Value); 3716272226bSSam McCall return Value->formula(); 372390029beSYitzhak Mandelbaum } 373390029beSYitzhak Mandelbaum 37471f2ec2dSmartinboehme StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) { 37571f2ec2dSmartinboehme return OptionalLoc.getSyntheticField("has_value"); 37671f2ec2dSmartinboehme } 37771f2ec2dSmartinboehme 37871f2ec2dSmartinboehme StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) { 37971f2ec2dSmartinboehme return OptionalLoc.getSyntheticField("value"); 38071f2ec2dSmartinboehme } 38171f2ec2dSmartinboehme 3828fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 38371f2ec2dSmartinboehme /// property of the optional at `OptionalLoc`. 38471f2ec2dSmartinboehme void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal, 38571f2ec2dSmartinboehme Environment &Env) { 38671f2ec2dSmartinboehme Env.setValue(locForHasValue(OptionalLoc), HasValueVal); 3878fcdd625SStanislav Gatev } 3888fcdd625SStanislav Gatev 389af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the 39071f2ec2dSmartinboehme /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null. 39171f2ec2dSmartinboehme BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) { 39271f2ec2dSmartinboehme if (OptionalLoc == nullptr) 39371f2ec2dSmartinboehme return nullptr; 39471f2ec2dSmartinboehme StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc); 3952ee396b0Smartinboehme auto *HasValueVal = Env.get<BoolValue>(HasValueLoc); 396dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) { 397dd38caf3SYitzhak Mandelbaum HasValueVal = &Env.makeAtomicBoolValue(); 39871f2ec2dSmartinboehme Env.setValue(HasValueLoc, *HasValueVal); 399dd38caf3SYitzhak Mandelbaum } 400dd38caf3SYitzhak Mandelbaum return HasValueVal; 401af98b0afSStanislav Gatev } 402af98b0afSStanislav Gatev 403d712c5edSmartinboehme QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) { 404d712c5edSmartinboehme auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD); 405d712c5edSmartinboehme return CTSD.getTemplateArgs()[0].getAsType(); 406092a530cSStanislav Gatev } 407092a530cSStanislav Gatev 408092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`. 409092a530cSStanislav Gatev /// 410092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this 411092a530cSStanislav Gatev /// function will be 2. 412092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 413d712c5edSmartinboehme const CXXRecordDecl *Optional = 414d712c5edSmartinboehme getOptionalBaseClass(Type->getAsCXXRecordDecl()); 415d712c5edSmartinboehme if (Optional == nullptr) 416092a530cSStanislav Gatev return 0; 417092a530cSStanislav Gatev return 1 + countOptionalWrappers( 418092a530cSStanislav Gatev ASTCtx, 419d712c5edSmartinboehme valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx)); 420092a530cSStanislav Gatev } 421092a530cSStanislav Gatev 42271f2ec2dSmartinboehme StorageLocation *getLocBehindPossiblePointer(const Expr &E, 42371f2ec2dSmartinboehme const Environment &Env) { 42471f2ec2dSmartinboehme if (E.isPRValue()) { 42571f2ec2dSmartinboehme if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E))) 42671f2ec2dSmartinboehme return &PointerVal->getPointeeLoc(); 427dd38caf3SYitzhak Mandelbaum return nullptr; 428dd38caf3SYitzhak Mandelbaum } 42971f2ec2dSmartinboehme return Env.getStorageLocation(E); 43048bc7150SMartin Braenne } 43148bc7150SMartin Braenne 432092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 433af98b0afSStanislav Gatev LatticeTransferState &State) { 43471f2ec2dSmartinboehme if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 43571f2ec2dSmartinboehme getLocBehindPossiblePointer(*ObjectExpr, State.Env))) { 436b244b6aeSMartin Braenne if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr) 43771f2ec2dSmartinboehme State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc)); 438af98b0afSStanislav Gatev } 439dd38caf3SYitzhak Mandelbaum } 440af98b0afSStanislav Gatev 4413bc1ea5bSMartin Braenne void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 4423bc1ea5bSMartin Braenne LatticeTransferState &State) { 44371f2ec2dSmartinboehme if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 44471f2ec2dSmartinboehme getLocBehindPossiblePointer(*ObjectExpr, State.Env))) 44571f2ec2dSmartinboehme State.Env.setValue( 44671f2ec2dSmartinboehme *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc))); 4473bc1ea5bSMartin Braenne } 4483bc1ea5bSMartin Braenne 449092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E, 450092a530cSStanislav Gatev const MatchFinder::MatchResult &, 451092a530cSStanislav Gatev LatticeTransferState &State) { 452e8fce958Smartinboehme setHasValue(State.Env.getResultObjectLocation(*E), 453e8fce958Smartinboehme State.Env.getBoolLiteralValue(true), State.Env); 4549e0fc676SStanislav Gatev } 4559e0fc676SStanislav Gatev 456092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 457092a530cSStanislav Gatev const MatchFinder::MatchResult &, 458af98b0afSStanislav Gatev LatticeTransferState &State) { 459dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = getHasValue( 46071f2ec2dSmartinboehme State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 461b244b6aeSMartin Braenne State.Env.setValue(*CallExpr, *HasValueVal); 462af98b0afSStanislav Gatev } 463af98b0afSStanislav Gatev } 464af98b0afSStanislav Gatev 46511c423f9SChris Cotter void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr, 46611c423f9SChris Cotter const MatchFinder::MatchResult &, 46711c423f9SChris Cotter LatticeTransferState &State) { 46811c423f9SChris Cotter if (auto *HasValueVal = getHasValue( 46911c423f9SChris Cotter State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 47011c423f9SChris Cotter State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal)); 47111c423f9SChris Cotter } 47211c423f9SChris Cotter } 47311c423f9SChris Cotter 4747f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in 4757f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property. 4766272226bSSam McCall void transferValueOrImpl( 4776272226bSSam McCall const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result, 4787f076004SYitzhak Mandelbaum LatticeTransferState &State, 4796272226bSSam McCall const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal, 4806272226bSSam McCall const Formula &HasValueVal)) { 4817f076004SYitzhak Mandelbaum auto &Env = State.Env; 4827f076004SYitzhak Mandelbaum 48371f2ec2dSmartinboehme const auto *MCE = 48471f2ec2dSmartinboehme Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID); 4857f076004SYitzhak Mandelbaum 48671f2ec2dSmartinboehme auto *HasValueVal = 48771f2ec2dSmartinboehme getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env)); 488dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) 4897f076004SYitzhak Mandelbaum return; 4907f076004SYitzhak Mandelbaum 491526c9b7eSmartinboehme Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), 4926272226bSSam McCall HasValueVal->formula())); 4937f076004SYitzhak Mandelbaum } 4947f076004SYitzhak Mandelbaum 4957f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 4967f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 4977f076004SYitzhak Mandelbaum LatticeTransferState &State) { 4987f076004SYitzhak Mandelbaum return transferValueOrImpl(ComparisonExpr, Result, State, 4996272226bSSam McCall [](Environment &Env, const Formula &ExprVal, 5006272226bSSam McCall const Formula &HasValueVal) -> const Formula & { 5016272226bSSam McCall auto &A = Env.arena(); 5027f076004SYitzhak Mandelbaum // If the result is *not* empty, then we know the 5037f076004SYitzhak Mandelbaum // optional must have been holding a value. If 5047f076004SYitzhak Mandelbaum // `ExprVal` is true, though, we don't learn 5057f076004SYitzhak Mandelbaum // anything definite about `has_value`, so we 5067f076004SYitzhak Mandelbaum // don't add any corresponding implications to 5077f076004SYitzhak Mandelbaum // the flow condition. 5086272226bSSam McCall return A.makeImplies(A.makeNot(ExprVal), 5097f076004SYitzhak Mandelbaum HasValueVal); 5107f076004SYitzhak Mandelbaum }); 5117f076004SYitzhak Mandelbaum } 5127f076004SYitzhak Mandelbaum 5137f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr, 5147f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 5157f076004SYitzhak Mandelbaum LatticeTransferState &State) { 5167f076004SYitzhak Mandelbaum transferValueOrImpl(ComparisonExpr, Result, State, 5176272226bSSam McCall [](Environment &Env, const Formula &ExprVal, 5186272226bSSam McCall const Formula &HasValueVal) -> const Formula & { 5196272226bSSam McCall auto &A = Env.arena(); 5207f076004SYitzhak Mandelbaum // We know that if `(opt.value_or(X) != X)` then 5217f076004SYitzhak Mandelbaum // `opt.hasValue()`, even without knowing further 5227f076004SYitzhak Mandelbaum // details about the contents of `opt`. 5236272226bSSam McCall return A.makeImplies(ExprVal, HasValueVal); 5247f076004SYitzhak Mandelbaum }); 5257f076004SYitzhak Mandelbaum } 5267f076004SYitzhak Mandelbaum 52765e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E, 52865e710c3SStanislav Gatev const MatchFinder::MatchResult &Result, 52965e710c3SStanislav Gatev LatticeTransferState &State) { 5309ecdbe38SMartin Braenne RecordStorageLocation *Loc = nullptr; 53144f98d01SMartin Braenne if (E->isPRValue()) { 53244f98d01SMartin Braenne Loc = &State.Env.getResultObjectLocation(*E); 53344f98d01SMartin Braenne } else { 5342ee396b0Smartinboehme Loc = State.Env.get<RecordStorageLocation>(*E); 535f76f6674SMartin Braenne if (Loc == nullptr) { 5369ecdbe38SMartin Braenne Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E)); 537b244b6aeSMartin Braenne State.Env.setStorageLocation(*E, *Loc); 53844f98d01SMartin Braenne } 539f76f6674SMartin Braenne } 54044f98d01SMartin Braenne 541e8fce958Smartinboehme if (State.Env.getValue(locForHasValue(*Loc)) != nullptr) 542e8fce958Smartinboehme return; 543e8fce958Smartinboehme 544e8fce958Smartinboehme setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env); 54565e710c3SStanislav Gatev } 54665e710c3SStanislav Gatev 547*6761b24aSJan Voung void handleConstMemberCall(const CallExpr *CE, 548*6761b24aSJan Voung dataflow::RecordStorageLocation *RecordLoc, 549*6761b24aSJan Voung const MatchFinder::MatchResult &Result, 550*6761b24aSJan Voung LatticeTransferState &State) { 551*6761b24aSJan Voung // If the const method returns an optional or reference to an optional. 552*6761b24aSJan Voung if (RecordLoc != nullptr && isSupportedOptionalType(CE->getType())) { 553*6761b24aSJan Voung StorageLocation *Loc = 554*6761b24aSJan Voung State.Lattice.getOrCreateConstMethodReturnStorageLocation( 555*6761b24aSJan Voung *RecordLoc, CE, State.Env, [&](StorageLocation &Loc) { 556*6761b24aSJan Voung setHasValue(cast<RecordStorageLocation>(Loc), 557*6761b24aSJan Voung State.Env.makeAtomicBoolValue(), State.Env); 558*6761b24aSJan Voung }); 559*6761b24aSJan Voung if (Loc == nullptr) 560*6761b24aSJan Voung return; 561*6761b24aSJan Voung if (CE->isGLValue()) { 562*6761b24aSJan Voung // If the call to the const method returns a reference to an optional, 563*6761b24aSJan Voung // link the call expression to the cached StorageLocation. 564*6761b24aSJan Voung State.Env.setStorageLocation(*CE, *Loc); 565*6761b24aSJan Voung } else { 566*6761b24aSJan Voung // If the call to the const method returns an optional by value, we 567*6761b24aSJan Voung // need to use CopyRecord to link the optional to the result object 568*6761b24aSJan Voung // of the call expression. 569*6761b24aSJan Voung auto &ResultLoc = State.Env.getResultObjectLocation(*CE); 570*6761b24aSJan Voung copyRecord(*cast<RecordStorageLocation>(Loc), ResultLoc, State.Env); 571*6761b24aSJan Voung } 572*6761b24aSJan Voung return; 573*6761b24aSJan Voung } 574*6761b24aSJan Voung 575*6761b24aSJan Voung // Cache if the const method returns a boolean type. 576*6761b24aSJan Voung // We may decide to cache other return types in the future. 577*6761b24aSJan Voung if (RecordLoc != nullptr && CE->getType()->isBooleanType()) { 578*6761b24aSJan Voung Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE, 579*6761b24aSJan Voung State.Env); 580*6761b24aSJan Voung if (Val == nullptr) 581*6761b24aSJan Voung return; 582*6761b24aSJan Voung State.Env.setValue(*CE, *Val); 583*6761b24aSJan Voung return; 584*6761b24aSJan Voung } 585*6761b24aSJan Voung 586*6761b24aSJan Voung // Perform default handling if the call returns an optional 587*6761b24aSJan Voung // but wasn't handled above (if RecordLoc is nullptr). 588*6761b24aSJan Voung if (isSupportedOptionalType(CE->getType())) { 589*6761b24aSJan Voung transferCallReturningOptional(CE, Result, State); 590*6761b24aSJan Voung } 591*6761b24aSJan Voung } 592*6761b24aSJan Voung 593*6761b24aSJan Voung void transferValue_ConstMemberCall(const CXXMemberCallExpr *MCE, 594*6761b24aSJan Voung const MatchFinder::MatchResult &Result, 595*6761b24aSJan Voung LatticeTransferState &State) { 596*6761b24aSJan Voung handleConstMemberCall( 597*6761b24aSJan Voung MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State); 598*6761b24aSJan Voung } 599*6761b24aSJan Voung 600*6761b24aSJan Voung void handleNonConstMemberCall(const CallExpr *CE, 601*6761b24aSJan Voung dataflow::RecordStorageLocation *RecordLoc, 602*6761b24aSJan Voung const MatchFinder::MatchResult &Result, 603*6761b24aSJan Voung LatticeTransferState &State) { 604*6761b24aSJan Voung // When a non-const member function is called, reset some state. 605*6761b24aSJan Voung if (RecordLoc != nullptr) { 606*6761b24aSJan Voung for (const auto &[Field, FieldLoc] : RecordLoc->children()) { 607*6761b24aSJan Voung if (isSupportedOptionalType(Field->getType())) { 608*6761b24aSJan Voung auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc); 609*6761b24aSJan Voung if (FieldRecordLoc) { 610*6761b24aSJan Voung setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(), 611*6761b24aSJan Voung State.Env); 612*6761b24aSJan Voung } 613*6761b24aSJan Voung } 614*6761b24aSJan Voung } 615*6761b24aSJan Voung State.Lattice.clearConstMethodReturnValues(*RecordLoc); 616*6761b24aSJan Voung State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc); 617*6761b24aSJan Voung } 618*6761b24aSJan Voung 619*6761b24aSJan Voung // Perform default handling if the call returns an optional. 620*6761b24aSJan Voung if (isSupportedOptionalType(CE->getType())) { 621*6761b24aSJan Voung transferCallReturningOptional(CE, Result, State); 622*6761b24aSJan Voung } 623*6761b24aSJan Voung } 624*6761b24aSJan Voung 625*6761b24aSJan Voung void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE, 626*6761b24aSJan Voung const MatchFinder::MatchResult &Result, 627*6761b24aSJan Voung LatticeTransferState &State) { 628*6761b24aSJan Voung handleNonConstMemberCall( 629*6761b24aSJan Voung MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State); 630*6761b24aSJan Voung } 631*6761b24aSJan Voung 632*6761b24aSJan Voung void transferValue_NonConstMemberOperatorCall( 633*6761b24aSJan Voung const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result, 634*6761b24aSJan Voung LatticeTransferState &State) { 635*6761b24aSJan Voung auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>( 636*6761b24aSJan Voung State.Env.getStorageLocation(*OCE->getArg(0))); 637*6761b24aSJan Voung handleNonConstMemberCall(OCE, RecordLoc, Result, State); 638*6761b24aSJan Voung } 639*6761b24aSJan Voung 640f653d140SMartin Braenne void constructOptionalValue(const Expr &E, Environment &Env, 641092a530cSStanislav Gatev BoolValue &HasValueVal) { 6429ecdbe38SMartin Braenne RecordStorageLocation &Loc = Env.getResultObjectLocation(E); 643e8fce958Smartinboehme setHasValue(Loc, HasValueVal, Env); 6449e0fc676SStanislav Gatev } 6459e0fc676SStanislav Gatev 646b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>` 647b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>` 648b000b770SStanislav Gatev /// where `T` is constructible from `U`. 649ae280281Smartinboehme BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E, 650b000b770SStanislav Gatev const MatchFinder::MatchResult &MatchRes, 651b000b770SStanislav Gatev LatticeTransferState &State) { 652ae280281Smartinboehme const int DestTypeOptionalWrappersCount = 653ae280281Smartinboehme countOptionalWrappers(*MatchRes.Context, DestType); 654c849843cSMartin Braenne const int ArgTypeOptionalWrappersCount = countOptionalWrappers( 655c849843cSMartin Braenne *MatchRes.Context, E.getType().getNonReferenceType()); 656b000b770SStanislav Gatev 657ae280281Smartinboehme // Is this an constructor of the form `template<class U> optional(U &&)` / 658ae280281Smartinboehme // assignment of the form `template<class U> optional& operator=(U &&)` 659ae280281Smartinboehme // (where `T` is assignable / constructible from `U`)? 660ae280281Smartinboehme // We recognize this because the number of optionals in the optional being 661ae280281Smartinboehme // assigned to is different from the function argument type. 662ae280281Smartinboehme if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount) 663b000b770SStanislav Gatev return State.Env.getBoolLiteralValue(true); 664b000b770SStanislav Gatev 665ae280281Smartinboehme // Otherwise, this must be a constructor of the form 666ae280281Smartinboehme // `template <class U> optional<optional<U> &&)` / assignment of the form 667ae280281Smartinboehme // `template <class U> optional& operator=(optional<U> &&) 668ae280281Smartinboehme // (where, again, `T` is assignable / constructible from `U`). 6692ee396b0Smartinboehme auto *Loc = State.Env.get<RecordStorageLocation>(E); 67071f2ec2dSmartinboehme if (auto *HasValueVal = getHasValue(State.Env, Loc)) 671dd38caf3SYitzhak Mandelbaum return *HasValueVal; 672b000b770SStanislav Gatev return State.Env.makeAtomicBoolValue(); 673b000b770SStanislav Gatev } 674b000b770SStanislav Gatev 675092a530cSStanislav Gatev void transferValueOrConversionConstructor( 676092a530cSStanislav Gatev const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 6779e0fc676SStanislav Gatev LatticeTransferState &State) { 678092a530cSStanislav Gatev assert(E->getNumArgs() > 0); 679092a530cSStanislav Gatev 680ae280281Smartinboehme constructOptionalValue( 681ae280281Smartinboehme *E, State.Env, 682ae280281Smartinboehme valueOrConversionHasValue( 683ae280281Smartinboehme E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0), 684ae280281Smartinboehme MatchRes, State)); 685b000b770SStanislav Gatev } 686092a530cSStanislav Gatev 687b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 688b000b770SStanislav Gatev LatticeTransferState &State) { 689b000b770SStanislav Gatev assert(E->getNumArgs() > 0); 690b000b770SStanislav Gatev 6912ee396b0Smartinboehme if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) { 692e8fce958Smartinboehme setHasValue(*Loc, HasValueVal, State.Env); 693b000b770SStanislav Gatev 694b000b770SStanislav Gatev // Assign a storage location for the whole expression. 695b244b6aeSMartin Braenne State.Env.setStorageLocation(*E, *Loc); 696f653d140SMartin Braenne } 697b000b770SStanislav Gatev } 698b000b770SStanislav Gatev 699b000b770SStanislav Gatev void transferValueOrConversionAssignment( 700b000b770SStanislav Gatev const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 701b000b770SStanislav Gatev LatticeTransferState &State) { 702b000b770SStanislav Gatev assert(E->getNumArgs() > 1); 703ae280281Smartinboehme transferAssignment( 704ae280281Smartinboehme E, 705ae280281Smartinboehme valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(), 70606decd0bSKazu Hirata *E->getArg(1), MatchRes, State), 707b000b770SStanislav Gatev State); 708b000b770SStanislav Gatev } 709b000b770SStanislav Gatev 710b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E, 711b000b770SStanislav Gatev const MatchFinder::MatchResult &, 712b000b770SStanislav Gatev LatticeTransferState &State) { 713b000b770SStanislav Gatev transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 7149e0fc676SStanislav Gatev } 7159e0fc676SStanislav Gatev 7169ecdbe38SMartin Braenne void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2, 7179ecdbe38SMartin Braenne Environment &Env) { 718d4fb829bSYitzhak Mandelbaum // We account for cases where one or both of the optionals are not modeled, 719d4fb829bSYitzhak Mandelbaum // either lacking associated storage locations, or lacking values associated 720d4fb829bSYitzhak Mandelbaum // to such storage locations. 7212ddd57aeSStanislav Gatev 722d4fb829bSYitzhak Mandelbaum if (Loc1 == nullptr) { 723d4fb829bSYitzhak Mandelbaum if (Loc2 != nullptr) 724e8fce958Smartinboehme setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env); 725d4fb829bSYitzhak Mandelbaum return; 726d4fb829bSYitzhak Mandelbaum } 727d4fb829bSYitzhak Mandelbaum if (Loc2 == nullptr) { 728e8fce958Smartinboehme setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env); 729d4fb829bSYitzhak Mandelbaum return; 730d4fb829bSYitzhak Mandelbaum } 7312ddd57aeSStanislav Gatev 732d4fb829bSYitzhak Mandelbaum // Both expressions have locations, though they may not have corresponding 733d4fb829bSYitzhak Mandelbaum // values. In that case, we create a fresh value at this point. Note that if 734d4fb829bSYitzhak Mandelbaum // two branches both do this, they will not share the value, but it at least 735d4fb829bSYitzhak Mandelbaum // allows for local reasoning about the value. To avoid the above, we would 736d4fb829bSYitzhak Mandelbaum // need *lazy* value allocation. 737d4fb829bSYitzhak Mandelbaum // FIXME: allocate values lazily, instead of just creating a fresh value. 73871f2ec2dSmartinboehme BoolValue *BoolVal1 = getHasValue(Env, Loc1); 739f653d140SMartin Braenne if (BoolVal1 == nullptr) 740f653d140SMartin Braenne BoolVal1 = &Env.makeAtomicBoolValue(); 741d4fb829bSYitzhak Mandelbaum 74271f2ec2dSmartinboehme BoolValue *BoolVal2 = getHasValue(Env, Loc2); 743f653d140SMartin Braenne if (BoolVal2 == nullptr) 744f653d140SMartin Braenne BoolVal2 = &Env.makeAtomicBoolValue(); 745d4fb829bSYitzhak Mandelbaum 746e8fce958Smartinboehme setHasValue(*Loc1, *BoolVal2, Env); 747e8fce958Smartinboehme setHasValue(*Loc2, *BoolVal1, Env); 7482ddd57aeSStanislav Gatev } 7492ddd57aeSStanislav Gatev 7502ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E, 7512ddd57aeSStanislav Gatev const MatchFinder::MatchResult &, 7522ddd57aeSStanislav Gatev LatticeTransferState &State) { 7532ddd57aeSStanislav Gatev assert(E->getNumArgs() == 1); 7542ee396b0Smartinboehme auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 755f653d140SMartin Braenne transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env); 7562ddd57aeSStanislav Gatev } 7572ddd57aeSStanislav Gatev 7582ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 7592ddd57aeSStanislav Gatev LatticeTransferState &State) { 7602ddd57aeSStanislav Gatev assert(E->getNumArgs() == 2); 7612ee396b0Smartinboehme auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 7622ee396b0Smartinboehme auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1)); 763f653d140SMartin Braenne transferSwap(Arg0Loc, Arg1Loc, State.Env); 7642ddd57aeSStanislav Gatev } 7652ddd57aeSStanislav Gatev 76625956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &, 76725956d55SAMS21 LatticeTransferState &State) { 76825956d55SAMS21 assert(E->getNumArgs() == 1); 76925956d55SAMS21 770b244b6aeSMartin Braenne if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0))) 771b244b6aeSMartin Braenne State.Env.setStorageLocation(*E, *Loc); 77225956d55SAMS21 } 77325956d55SAMS21 7746272226bSSam McCall const Formula &evaluateEquality(Arena &A, const Formula &EqVal, 7756272226bSSam McCall const Formula &LHS, const Formula &RHS) { 776390029beSYitzhak Mandelbaum // Logically, an optional<T> object is composed of two values - a `has_value` 777390029beSYitzhak Mandelbaum // bit and a value of type T. Equality of optional objects compares both 778390029beSYitzhak Mandelbaum // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 779390029beSYitzhak Mandelbaum // when two optional objects are engaged, the equality of their respective 780390029beSYitzhak Mandelbaum // values of type T matters. Since we only track the `has_value` bits, we 781390029beSYitzhak Mandelbaum // can't make any conclusions about equality when we know that two optional 782390029beSYitzhak Mandelbaum // objects are engaged. 783390029beSYitzhak Mandelbaum // 784390029beSYitzhak Mandelbaum // We express this as two facts about the equality: 785390029beSYitzhak Mandelbaum // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 786390029beSYitzhak Mandelbaum // If they are equal, then either both are set or both are unset. 787390029beSYitzhak Mandelbaum // b) (!LHS & !RHS) => EqVal 788390029beSYitzhak Mandelbaum // If neither is set, then they are equal. 789390029beSYitzhak Mandelbaum // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 7906272226bSSam McCall return A.makeAnd( 7916272226bSSam McCall A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS), 7926272226bSSam McCall A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))), 7936272226bSSam McCall A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS))); 794390029beSYitzhak Mandelbaum } 795390029beSYitzhak Mandelbaum 796390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 797390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, 798390029beSYitzhak Mandelbaum LatticeTransferState &State) { 799390029beSYitzhak Mandelbaum Environment &Env = State.Env; 8006272226bSSam McCall auto &A = Env.arena(); 801390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 8022ee396b0Smartinboehme auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0)); 80371f2ec2dSmartinboehme if (auto *LHasVal = getHasValue(Env, Arg0Loc)) { 8042ee396b0Smartinboehme auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1)); 80571f2ec2dSmartinboehme if (auto *RHasVal = getHasValue(Env, Arg1Loc)) { 806390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 8076272226bSSam McCall CmpValue = &A.makeNot(*CmpValue); 808526c9b7eSmartinboehme Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(), 8096272226bSSam McCall RHasVal->formula())); 810390029beSYitzhak Mandelbaum } 811390029beSYitzhak Mandelbaum } 81271f2ec2dSmartinboehme } 813390029beSYitzhak Mandelbaum 814390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 815390029beSYitzhak Mandelbaum const clang::Expr *E, Environment &Env) { 8166272226bSSam McCall auto &A = Env.arena(); 817390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 8182ee396b0Smartinboehme auto *Loc = Env.get<RecordStorageLocation>(*E); 81971f2ec2dSmartinboehme if (auto *HasVal = getHasValue(Env, Loc)) { 820390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 8216272226bSSam McCall CmpValue = &A.makeNot(*CmpValue); 822526c9b7eSmartinboehme Env.assume( 8236272226bSSam McCall evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true))); 824390029beSYitzhak Mandelbaum } 825390029beSYitzhak Mandelbaum } 826390029beSYitzhak Mandelbaum 82771f2ec2dSmartinboehme void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr, 82871f2ec2dSmartinboehme const clang::Expr *E, Environment &Env) { 82971f2ec2dSmartinboehme auto &A = Env.arena(); 83071f2ec2dSmartinboehme auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 8312ee396b0Smartinboehme auto *Loc = Env.get<RecordStorageLocation>(*E); 83271f2ec2dSmartinboehme if (auto *HasVal = getHasValue(Env, Loc)) { 83371f2ec2dSmartinboehme if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 83471f2ec2dSmartinboehme CmpValue = &A.makeNot(*CmpValue); 83571f2ec2dSmartinboehme Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(), 83671f2ec2dSmartinboehme A.makeLiteral(false))); 83771f2ec2dSmartinboehme } 83871f2ec2dSmartinboehme } 83971f2ec2dSmartinboehme 8406ad0788cSKazu Hirata std::optional<StatementMatcher> 841a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 8425d22d1f5SYitzhak Mandelbaum if (Options.IgnoreSmartPointerDereference) { 8435d22d1f5SYitzhak Mandelbaum auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 8445d22d1f5SYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 8455d22d1f5SYitzhak Mandelbaum unless(hasArgument(0, expr(hasOptionalType())))))); 8465d22d1f5SYitzhak Mandelbaum return expr( 8475d22d1f5SYitzhak Mandelbaum anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 8485d22d1f5SYitzhak Mandelbaum } 84934e0d057SKazu Hirata return std::nullopt; 850a184a0d8SYitzhak Mandelbaum } 851a184a0d8SYitzhak Mandelbaum 85258fe7f96SSam Estep StatementMatcher 8536ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 8542f0630f8SAnton Dukeman return isOptionalMemberCallWithNameMatcher(hasName("value"), 8552f0630f8SAnton Dukeman IgnorableOptional); 85658fe7f96SSam Estep } 85758fe7f96SSam Estep 85858fe7f96SSam Estep StatementMatcher 8596ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 86058fe7f96SSam Estep return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 86158fe7f96SSam Estep isOptionalOperatorCallWithName("->", IgnorableOptional))); 86258fe7f96SSam Estep } 86358fe7f96SSam Estep 8645d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() { 865b000b770SStanislav Gatev // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 866b000b770SStanislav Gatev // lot of duplicated work (e.g. string comparisons), consider providing APIs 867b000b770SStanislav Gatev // that avoid it through memoization. 8687538b360SWei Yi Tee return CFGMatchSwitchBuilder<LatticeTransferState>() 8699e0fc676SStanislav Gatev // make_optional 8707538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 871092a530cSStanislav Gatev 8720e8d4a6dSYitzhak Mandelbaum // optional::optional (in place) 8737538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 874092a530cSStanislav Gatev isOptionalInPlaceConstructor(), 875092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 876092a530cSStanislav Gatev LatticeTransferState &State) { 877f653d140SMartin Braenne constructOptionalValue(*E, State.Env, 8780e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(true)); 879092a530cSStanislav Gatev }) 8800e8d4a6dSYitzhak Mandelbaum // optional::optional(nullopt_t) 881390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXConstructExpr>( 882390029beSYitzhak Mandelbaum isOptionalNulloptConstructor(), 883390029beSYitzhak Mandelbaum [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 884390029beSYitzhak Mandelbaum LatticeTransferState &State) { 885f653d140SMartin Braenne constructOptionalValue(*E, State.Env, 8860e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(false)); 887390029beSYitzhak Mandelbaum }) 8880e8d4a6dSYitzhak Mandelbaum // optional::optional (value/conversion) 8897538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 890092a530cSStanislav Gatev transferValueOrConversionConstructor) 8919e0fc676SStanislav Gatev 892b000b770SStanislav Gatev // optional::operator= 8937538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>( 8947538b360SWei Yi Tee isOptionalValueOrConversionAssignment(), 895b000b770SStanislav Gatev transferValueOrConversionAssignment) 8967538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 897b000b770SStanislav Gatev transferNulloptAssignment) 898b000b770SStanislav Gatev 899af98b0afSStanislav Gatev // optional::value 9007538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 9015d22d1f5SYitzhak Mandelbaum valueCall(std::nullopt), 902092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 903092a530cSStanislav Gatev LatticeTransferState &State) { 904af98b0afSStanislav Gatev transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 905af98b0afSStanislav Gatev }) 906af98b0afSStanislav Gatev 9073bc1ea5bSMartin Braenne // optional::operator* 9083bc1ea5bSMartin Braenne .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"), 9097538b360SWei Yi Tee [](const CallExpr *E, 9107538b360SWei Yi Tee const MatchFinder::MatchResult &, 911092a530cSStanislav Gatev LatticeTransferState &State) { 912af98b0afSStanislav Gatev transferUnwrapCall(E, E->getArg(0), State); 913af98b0afSStanislav Gatev }) 914af98b0afSStanislav Gatev 9153bc1ea5bSMartin Braenne // optional::operator-> 9163bc1ea5bSMartin Braenne .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"), 9173bc1ea5bSMartin Braenne [](const CallExpr *E, 9183bc1ea5bSMartin Braenne const MatchFinder::MatchResult &, 9193bc1ea5bSMartin Braenne LatticeTransferState &State) { 9203bc1ea5bSMartin Braenne transferArrowOpCall(E, E->getArg(0), State); 9213bc1ea5bSMartin Braenne }) 9223bc1ea5bSMartin Braenne 9232f0630f8SAnton Dukeman // optional::has_value, optional::hasValue 9242f0630f8SAnton Dukeman // Of the supported optionals only folly::Optional uses hasValue, but this 9252f0630f8SAnton Dukeman // will also pass for other types 9267538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 9272f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher( 9282f0630f8SAnton Dukeman hasAnyName("has_value", "hasValue")), 929af98b0afSStanislav Gatev transferOptionalHasValueCall) 930af98b0afSStanislav Gatev 9319e0fc676SStanislav Gatev // optional::operator bool 9327538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 9332f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("operator bool")), 9349e0fc676SStanislav Gatev transferOptionalHasValueCall) 9359e0fc676SStanislav Gatev 93611c423f9SChris Cotter // NullableValue::isNull 93711c423f9SChris Cotter // Only NullableValue has isNull 93811c423f9SChris Cotter .CaseOfCFGStmt<CXXMemberCallExpr>( 93911c423f9SChris Cotter isOptionalMemberCallWithNameMatcher(hasName("isNull")), 94011c423f9SChris Cotter transferOptionalIsNullCall) 94111c423f9SChris Cotter 9429e0fc676SStanislav Gatev // optional::emplace 9437538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 9442f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("emplace")), 945092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 946092a530cSStanislav Gatev LatticeTransferState &State) { 9479ecdbe38SMartin Braenne if (RecordStorageLocation *Loc = 948f653d140SMartin Braenne getImplicitObjectLocation(*E, State.Env)) { 949e8fce958Smartinboehme setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env); 950f653d140SMartin Braenne } 951092a530cSStanislav Gatev }) 9529e0fc676SStanislav Gatev 9539e0fc676SStanislav Gatev // optional::reset 9547538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 9552f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("reset")), 956092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 957092a530cSStanislav Gatev LatticeTransferState &State) { 9589ecdbe38SMartin Braenne if (RecordStorageLocation *Loc = 959f653d140SMartin Braenne getImplicitObjectLocation(*E, State.Env)) { 960e8fce958Smartinboehme setHasValue(*Loc, State.Env.getBoolLiteralValue(false), 961f653d140SMartin Braenne State.Env); 962f653d140SMartin Braenne } 963092a530cSStanislav Gatev }) 9649e0fc676SStanislav Gatev 9652ddd57aeSStanislav Gatev // optional::swap 9662f0630f8SAnton Dukeman .CaseOfCFGStmt<CXXMemberCallExpr>( 9672f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("swap")), 9682ddd57aeSStanislav Gatev transferSwapCall) 9692ddd57aeSStanislav Gatev 9702ddd57aeSStanislav Gatev // std::swap 9717538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 9722ddd57aeSStanislav Gatev 97325956d55SAMS21 // std::forward 97425956d55SAMS21 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 97525956d55SAMS21 9767f076004SYitzhak Mandelbaum // opt.value_or("").empty() 9777538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 9787538b360SWei Yi Tee transferValueOrStringEmptyCall) 9797f076004SYitzhak Mandelbaum 9807f076004SYitzhak Mandelbaum // opt.value_or(X) != X 9817538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 9827f076004SYitzhak Mandelbaum 983390029beSYitzhak Mandelbaum // Comparisons (==, !=): 984390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 98571f2ec2dSmartinboehme isComparisonOperatorCall(hasOptionalType(), hasOptionalType()), 986390029beSYitzhak Mandelbaum transferOptionalAndOptionalCmp) 987390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 98871f2ec2dSmartinboehme isComparisonOperatorCall(hasOptionalType(), hasNulloptType()), 98971f2ec2dSmartinboehme [](const clang::CXXOperatorCallExpr *Cmp, 99071f2ec2dSmartinboehme const MatchFinder::MatchResult &, LatticeTransferState &State) { 99171f2ec2dSmartinboehme transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env); 99271f2ec2dSmartinboehme }) 99371f2ec2dSmartinboehme .CaseOfCFGStmt<CXXOperatorCallExpr>( 99471f2ec2dSmartinboehme isComparisonOperatorCall(hasNulloptType(), hasOptionalType()), 99571f2ec2dSmartinboehme [](const clang::CXXOperatorCallExpr *Cmp, 99671f2ec2dSmartinboehme const MatchFinder::MatchResult &, LatticeTransferState &State) { 99771f2ec2dSmartinboehme transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env); 99871f2ec2dSmartinboehme }) 99971f2ec2dSmartinboehme .CaseOfCFGStmt<CXXOperatorCallExpr>( 100071f2ec2dSmartinboehme isComparisonOperatorCall( 100171f2ec2dSmartinboehme hasOptionalType(), 100271f2ec2dSmartinboehme unless(anyOf(hasOptionalType(), hasNulloptType()))), 1003390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 1004390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 1005390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 1006390029beSYitzhak Mandelbaum }) 1007390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 100871f2ec2dSmartinboehme isComparisonOperatorCall( 100971f2ec2dSmartinboehme unless(anyOf(hasOptionalType(), hasNulloptType())), 1010390029beSYitzhak Mandelbaum hasOptionalType()), 1011390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 1012390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 1013390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 1014390029beSYitzhak Mandelbaum }) 1015390029beSYitzhak Mandelbaum 1016*6761b24aSJan Voung // const accessor calls 1017*6761b24aSJan Voung .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(), 1018*6761b24aSJan Voung transferValue_ConstMemberCall) 1019*6761b24aSJan Voung // non-const member calls that may modify the state of an object. 1020*6761b24aSJan Voung .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(), 1021*6761b24aSJan Voung transferValue_NonConstMemberCall) 1022*6761b24aSJan Voung .CaseOfCFGStmt<CXXOperatorCallExpr>( 1023*6761b24aSJan Voung isNonConstMemberOperatorCall(), 1024*6761b24aSJan Voung transferValue_NonConstMemberOperatorCall) 1025*6761b24aSJan Voung 1026*6761b24aSJan Voung // other cases of returning optional 10277538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 102865e710c3SStanislav Gatev transferCallReturningOptional) 102965e710c3SStanislav Gatev 1030af98b0afSStanislav Gatev .Build(); 1031af98b0afSStanislav Gatev } 1032af98b0afSStanislav Gatev 1033004a7ceaSYitzhak Mandelbaum llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr, 103458fe7f96SSam Estep const Environment &Env) { 103571f2ec2dSmartinboehme if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 103671f2ec2dSmartinboehme getLocBehindPossiblePointer(*ObjectExpr, Env))) { 103771f2ec2dSmartinboehme auto *Prop = Env.getValue(locForHasValue(*OptionalLoc)); 103858fe7f96SSam Estep if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 1039526c9b7eSmartinboehme if (Env.proves(HasValueVal->formula())) 104058fe7f96SSam Estep return {}; 104158fe7f96SSam Estep } 104258fe7f96SSam Estep } 104358fe7f96SSam Estep 104458fe7f96SSam Estep // Record that this unwrap is *not* provably safe. 104558fe7f96SSam Estep // FIXME: include either the name of the optional (if applicable) or a source 104658fe7f96SSam Estep // range of the access for easier interpretation of the result. 104758fe7f96SSam Estep return {ObjectExpr->getBeginLoc()}; 104858fe7f96SSam Estep } 104958fe7f96SSam Estep 105058fe7f96SSam Estep auto buildDiagnoseMatchSwitch( 105158fe7f96SSam Estep const UncheckedOptionalAccessModelOptions &Options) { 105258fe7f96SSam Estep // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 105358fe7f96SSam Estep // lot of duplicated work (e.g. string comparisons), consider providing APIs 105458fe7f96SSam Estep // that avoid it through memoization. 105558fe7f96SSam Estep auto IgnorableOptional = ignorableOptional(Options); 1056004a7ceaSYitzhak Mandelbaum return CFGMatchSwitchBuilder<const Environment, 1057004a7ceaSYitzhak Mandelbaum llvm::SmallVector<SourceLocation>>() 105858fe7f96SSam Estep // optional::value 10597538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 106058fe7f96SSam Estep valueCall(IgnorableOptional), 106158fe7f96SSam Estep [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 106258fe7f96SSam Estep const Environment &Env) { 10636a81e694SMartin Braenne return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env); 106458fe7f96SSam Estep }) 106558fe7f96SSam Estep 106658fe7f96SSam Estep // optional::operator*, optional::operator-> 10676a81e694SMartin Braenne .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 10686a81e694SMartin Braenne [](const CallExpr *E, 10696a81e694SMartin Braenne const MatchFinder::MatchResult &, 107058fe7f96SSam Estep const Environment &Env) { 10716a81e694SMartin Braenne return diagnoseUnwrapCall(E->getArg(0), Env); 107258fe7f96SSam Estep }) 107358fe7f96SSam Estep .Build(); 107458fe7f96SSam Estep } 107558fe7f96SSam Estep 1076af98b0afSStanislav Gatev } // namespace 1077af98b0afSStanislav Gatev 10787e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher 10797e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() { 1080d712c5edSmartinboehme return cxxRecordDecl(optionalClass()); 108171f2ec2dSmartinboehme } 108271f2ec2dSmartinboehme 108371f2ec2dSmartinboehme UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, 108471f2ec2dSmartinboehme Environment &Env) 1085*6761b24aSJan Voung : DataflowAnalysis<UncheckedOptionalAccessModel, 1086*6761b24aSJan Voung UncheckedOptionalAccessLattice>(Ctx), 108771f2ec2dSmartinboehme TransferMatchSwitch(buildTransferMatchSwitch()) { 108871f2ec2dSmartinboehme Env.getDataflowAnalysisContext().setSyntheticFieldCallback( 108971f2ec2dSmartinboehme [&Ctx](QualType Ty) -> llvm::StringMap<QualType> { 1090d712c5edSmartinboehme const CXXRecordDecl *Optional = 1091d712c5edSmartinboehme getOptionalBaseClass(Ty->getAsCXXRecordDecl()); 1092d712c5edSmartinboehme if (Optional == nullptr) 109371f2ec2dSmartinboehme return {}; 1094d712c5edSmartinboehme return {{"value", valueTypeFromOptionalDecl(*Optional)}, 109571f2ec2dSmartinboehme {"has_value", Ctx.BoolTy}}; 109671f2ec2dSmartinboehme }); 109771f2ec2dSmartinboehme } 1098af98b0afSStanislav Gatev 10996b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 1100*6761b24aSJan Voung UncheckedOptionalAccessLattice &L, 1101*6761b24aSJan Voung Environment &Env) { 1102af98b0afSStanislav Gatev LatticeTransferState State(L, Env); 11036b991ba4SYitzhak Mandelbaum TransferMatchSwitch(Elt, getASTContext(), State); 1104af98b0afSStanislav Gatev } 1105af98b0afSStanislav Gatev 110658fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 110758fe7f96SSam Estep UncheckedOptionalAccessModelOptions Options) 110858fe7f96SSam Estep : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 110958fe7f96SSam Estep 1110af98b0afSStanislav Gatev } // namespace dataflow 1111af98b0afSStanislav Gatev } // namespace clang 1112