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" 206761b24aSJan 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" 276761b24aSJan Voung #include "clang/Analysis/FlowSensitive/RecordOps.h" 28*72a28a3bSJan Voung #include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h" 290086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h" 30af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h" 31*72a28a3bSJan Voung #include "clang/Basic/OperatorKinds.h" 3258fe7f96SSam Estep #include "clang/Basic/SourceLocation.h" 33af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h" 34af98b0afSStanislav Gatev #include "llvm/Support/Casting.h" 35d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h" 36af98b0afSStanislav Gatev #include <cassert> 379e0fc676SStanislav Gatev #include <memory> 38a1580d7bSKazu Hirata #include <optional> 399e0fc676SStanislav Gatev #include <utility> 40af98b0afSStanislav Gatev 41af98b0afSStanislav Gatev namespace clang { 42af98b0afSStanislav Gatev namespace dataflow { 4309b462efSYitzhak Mandelbaum 4411c423f9SChris Cotter // Note: the Names appear in reverse order. E.g., to check 4511c423f9SChris Cotter // if NS is foo::bar::, call isFullyQualifiedNamespaceEqualTo(NS, "bar", "foo") 4611c423f9SChris Cotter template <class... NameTypes> 4711c423f9SChris Cotter static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS, 4811c423f9SChris Cotter llvm::StringRef Name, 4911c423f9SChris Cotter NameTypes... Names) { 5011c423f9SChris Cotter if (!(NS.getDeclName().isIdentifier() && NS.getName() == Name && 5111c423f9SChris Cotter NS.getParent() != nullptr)) 5211c423f9SChris Cotter return false; 5311c423f9SChris Cotter 5411c423f9SChris Cotter if constexpr (sizeof...(NameTypes) > 0) { 5511c423f9SChris Cotter if (NS.getParent()->isTranslationUnit()) 5611c423f9SChris Cotter return false; 5711c423f9SChris Cotter if (const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.getParent())) 5811c423f9SChris Cotter return isFullyQualifiedNamespaceEqualTo(*NextNS, Names...); 5911c423f9SChris Cotter return false; 6011c423f9SChris Cotter } else { 6111c423f9SChris Cotter return NS.getParent()->isTranslationUnit(); 6211c423f9SChris Cotter } 6309b462efSYitzhak Mandelbaum } 6409b462efSYitzhak Mandelbaum 6509b462efSYitzhak Mandelbaum static bool hasOptionalClassName(const CXXRecordDecl &RD) { 6609b462efSYitzhak Mandelbaum if (!RD.getDeclName().isIdentifier()) 6709b462efSYitzhak Mandelbaum return false; 6809b462efSYitzhak Mandelbaum 6909b462efSYitzhak Mandelbaum if (RD.getName() == "optional") { 7009b462efSYitzhak Mandelbaum if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext())) 7111c423f9SChris Cotter return N->isStdNamespace() || 7211c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "absl") || 7311c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "bsl"); 7409b462efSYitzhak Mandelbaum return false; 7509b462efSYitzhak Mandelbaum } 7609b462efSYitzhak Mandelbaum 7709b462efSYitzhak Mandelbaum if (RD.getName() == "Optional") { 782f0630f8SAnton Dukeman // Check whether namespace is "::base" or "::folly". 7909b462efSYitzhak Mandelbaum const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); 8011c423f9SChris Cotter return N != nullptr && (isFullyQualifiedNamespaceEqualTo(*N, "base") || 8111c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "folly")); 8211c423f9SChris Cotter } 8311c423f9SChris Cotter 8411c423f9SChris Cotter if (RD.getName() == "NullableValue") { 8511c423f9SChris Cotter const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); 8611c423f9SChris Cotter return N != nullptr && 8711c423f9SChris Cotter isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP"); 8809b462efSYitzhak Mandelbaum } 8909b462efSYitzhak Mandelbaum 9009b462efSYitzhak Mandelbaum return false; 9109b462efSYitzhak Mandelbaum } 9209b462efSYitzhak Mandelbaum 93d712c5edSmartinboehme static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) { 94d712c5edSmartinboehme if (RD == nullptr) 95d712c5edSmartinboehme return nullptr; 96d712c5edSmartinboehme if (hasOptionalClassName(*RD)) 97d712c5edSmartinboehme return RD; 98d712c5edSmartinboehme 99d712c5edSmartinboehme if (!RD->hasDefinition()) 100d712c5edSmartinboehme return nullptr; 101d712c5edSmartinboehme 102d712c5edSmartinboehme for (const CXXBaseSpecifier &Base : RD->bases()) 103d712c5edSmartinboehme if (const CXXRecordDecl *BaseClass = 104d712c5edSmartinboehme getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl())) 105d712c5edSmartinboehme return BaseClass; 106d712c5edSmartinboehme 107d712c5edSmartinboehme return nullptr; 108d712c5edSmartinboehme } 109d712c5edSmartinboehme 1106761b24aSJan Voung static bool isSupportedOptionalType(QualType Ty) { 1116761b24aSJan Voung const CXXRecordDecl *Optional = 1126761b24aSJan Voung getOptionalBaseClass(Ty->getAsCXXRecordDecl()); 1136761b24aSJan Voung return Optional != nullptr; 1146761b24aSJan Voung } 1156761b24aSJan Voung 116af98b0afSStanislav Gatev namespace { 117af98b0afSStanislav Gatev 118af98b0afSStanislav Gatev using namespace ::clang::ast_matchers; 1196761b24aSJan Voung 1206761b24aSJan Voung using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>; 121af98b0afSStanislav Gatev 122d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); } 123d712c5edSmartinboehme 124d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) { 125d712c5edSmartinboehme return getOptionalBaseClass(&Node) != nullptr; 12609b462efSYitzhak Mandelbaum } 12709b462efSYitzhak Mandelbaum 128d712c5edSmartinboehme auto desugarsToOptionalType() { 12965e710c3SStanislav Gatev return hasUnqualifiedDesugaredType( 130d712c5edSmartinboehme recordType(hasDeclaration(cxxRecordDecl(optionalClass())))); 13165e710c3SStanislav Gatev } 13265e710c3SStanislav Gatev 133d712c5edSmartinboehme auto desugarsToOptionalOrDerivedType() { 134d712c5edSmartinboehme return hasUnqualifiedDesugaredType( 135d712c5edSmartinboehme recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass())))); 136d712c5edSmartinboehme } 137d712c5edSmartinboehme 138d712c5edSmartinboehme auto hasOptionalType() { return hasType(desugarsToOptionalType()); } 139d712c5edSmartinboehme 140d712c5edSmartinboehme /// Matches any of the spellings of the optional types and sugar, aliases, 141d712c5edSmartinboehme /// derived classes, etc. 142d712c5edSmartinboehme auto hasOptionalOrDerivedType() { 143d712c5edSmartinboehme return hasType(desugarsToOptionalOrDerivedType()); 144d712c5edSmartinboehme } 145d712c5edSmartinboehme 146d712c5edSmartinboehme QualType getPublicType(const Expr *E) { 147d712c5edSmartinboehme auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens()); 148d712c5edSmartinboehme if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) { 149d712c5edSmartinboehme QualType Ty = E->getType(); 150d712c5edSmartinboehme if (Ty->isPointerType()) 151d712c5edSmartinboehme return Ty->getPointeeType(); 152d712c5edSmartinboehme return Ty; 153d712c5edSmartinboehme } 154d712c5edSmartinboehme 155d712c5edSmartinboehme // Is the derived type that we're casting from the type of `*this`? In this 156d712c5edSmartinboehme // special case, we can upcast to the base class even if the base is 157d712c5edSmartinboehme // non-public. 158d712c5edSmartinboehme bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr()); 159d712c5edSmartinboehme 160d712c5edSmartinboehme // Find the least-derived type in the path (i.e. the last entry in the list) 161d712c5edSmartinboehme // that we can access. 162d712c5edSmartinboehme const CXXBaseSpecifier *PublicBase = nullptr; 163d712c5edSmartinboehme for (const CXXBaseSpecifier *Base : Cast->path()) { 164d712c5edSmartinboehme if (Base->getAccessSpecifier() != AS_public && !CastingFromThis) 165d712c5edSmartinboehme break; 166d712c5edSmartinboehme PublicBase = Base; 167d712c5edSmartinboehme CastingFromThis = false; 168d712c5edSmartinboehme } 169d712c5edSmartinboehme 170d712c5edSmartinboehme if (PublicBase != nullptr) 171d712c5edSmartinboehme return PublicBase->getType(); 172d712c5edSmartinboehme 173d712c5edSmartinboehme // We didn't find any public type that we could cast to. There may be more 174d712c5edSmartinboehme // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this 175d712c5edSmartinboehme // will return the type of `getSubExpr()`.) 176d712c5edSmartinboehme return getPublicType(Cast->getSubExpr()); 177d712c5edSmartinboehme } 178d712c5edSmartinboehme 179d712c5edSmartinboehme // Returns the least-derived type for the receiver of `MCE` that 180d712c5edSmartinboehme // `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to. 181d712c5edSmartinboehme // Effectively, we upcast until we reach a non-public base class, unless that 182d712c5edSmartinboehme // base is a base of `*this`. 183d712c5edSmartinboehme // 184d712c5edSmartinboehme // This is needed to correctly match methods called on types derived from 185d712c5edSmartinboehme // `std::optional`. 186d712c5edSmartinboehme // 187d712c5edSmartinboehme // Say we have a `struct Derived : public std::optional<int> {} d;` For a call 188d712c5edSmartinboehme // `d.has_value()`, the `getImplicitObjectArgument()` looks like this: 189d712c5edSmartinboehme // 190d712c5edSmartinboehme // ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue 191d712c5edSmartinboehme // | <UncheckedDerivedToBase (optional -> __optional_storage_base)> 192d712c5edSmartinboehme // `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived' 193d712c5edSmartinboehme // 194d712c5edSmartinboehme // The type of the implicit object argument is `__optional_storage_base` 195d712c5edSmartinboehme // (since this is the internal type that `has_value()` is declared on). If we 196d712c5edSmartinboehme // call `IgnoreParenImpCasts()` on the implicit object argument, we get the 197d712c5edSmartinboehme // `DeclRefExpr`, which has type `Derived`. Neither of these types is 198d712c5edSmartinboehme // `optional`, and hence neither is sufficient for querying whether we are 199d712c5edSmartinboehme // calling a method on `optional`. 200d712c5edSmartinboehme // 201d712c5edSmartinboehme // Instead, starting with the most derived type, we need to follow the chain of 202d712c5edSmartinboehme // casts 203d712c5edSmartinboehme QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) { 204d712c5edSmartinboehme return getPublicType(MCE.getImplicitObjectArgument()); 205d712c5edSmartinboehme } 206d712c5edSmartinboehme 207d712c5edSmartinboehme AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType, 208d712c5edSmartinboehme ast_matchers::internal::Matcher<QualType>, InnerMatcher) { 209d712c5edSmartinboehme return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder); 210d712c5edSmartinboehme } 2116adfc64eSYitzhak Mandelbaum 2122f0630f8SAnton Dukeman auto isOptionalMemberCallWithNameMatcher( 2132f0630f8SAnton Dukeman ast_matchers::internal::Matcher<NamedDecl> matcher, 2146ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 215d712c5edSmartinboehme return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable))) 216d712c5edSmartinboehme : anything(), 217d712c5edSmartinboehme publicReceiverType(desugarsToOptionalType()), 2182f0630f8SAnton Dukeman callee(cxxMethodDecl(matcher))); 219af98b0afSStanislav Gatev } 220af98b0afSStanislav Gatev 221a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName( 222a184a0d8SYitzhak Mandelbaum llvm::StringRef operator_name, 2236ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 224a184a0d8SYitzhak Mandelbaum return cxxOperatorCallExpr( 225a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName(operator_name), 226a184a0d8SYitzhak Mandelbaum callee(cxxMethodDecl(ofClass(optionalClass()))), 227a184a0d8SYitzhak Mandelbaum Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 228af98b0afSStanislav Gatev } 229af98b0afSStanislav Gatev 230092a530cSStanislav Gatev auto isMakeOptionalCall() { 23111c423f9SChris Cotter return callExpr( 23211c423f9SChris Cotter callee(functionDecl(hasAnyName( 23311c423f9SChris Cotter "std::make_optional", "base::make_optional", "absl::make_optional", 23411c423f9SChris Cotter "folly::make_optional", "bsl::make_optional"))), 2359e0fc676SStanislav Gatev hasOptionalType()); 2369e0fc676SStanislav Gatev } 2379e0fc676SStanislav Gatev 238390029beSYitzhak Mandelbaum auto nulloptTypeDecl() { 2392f0630f8SAnton Dukeman return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t", 24011c423f9SChris Cotter "base::nullopt_t", "folly::None", 24111c423f9SChris Cotter "bsl::nullopt_t")); 242092a530cSStanislav Gatev } 243092a530cSStanislav Gatev 244390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); } 245390029beSYitzhak Mandelbaum 246092a530cSStanislav Gatev auto inPlaceClass() { 2472f0630f8SAnton Dukeman return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t", 24811c423f9SChris Cotter "base::in_place_t", "folly::in_place_t", 24911c423f9SChris Cotter "bsl::in_place_t")); 250092a530cSStanislav Gatev } 251092a530cSStanislav Gatev 252092a530cSStanislav Gatev auto isOptionalNulloptConstructor() { 2530086a355SYitzhak Mandelbaum return cxxConstructExpr( 2540086a355SYitzhak Mandelbaum hasDeclaration(cxxConstructorDecl(parameterCountIs(1), 255d712c5edSmartinboehme hasParameter(0, hasNulloptType()))), 256d712c5edSmartinboehme hasOptionalOrDerivedType()); 257092a530cSStanislav Gatev } 258092a530cSStanislav Gatev 259092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() { 260d712c5edSmartinboehme return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())), 261d712c5edSmartinboehme hasOptionalOrDerivedType()); 262092a530cSStanislav Gatev } 263092a530cSStanislav Gatev 264092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() { 265092a530cSStanislav Gatev return cxxConstructExpr( 266092a530cSStanislav Gatev unless(hasDeclaration( 267092a530cSStanislav Gatev cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 268d712c5edSmartinboehme argumentCountIs(1), hasArgument(0, unless(hasNulloptType())), 269d712c5edSmartinboehme hasOptionalOrDerivedType()); 270092a530cSStanislav Gatev } 271092a530cSStanislav Gatev 272b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() { 273b000b770SStanislav Gatev return cxxOperatorCallExpr( 274b000b770SStanislav Gatev hasOverloadedOperatorName("="), 275d712c5edSmartinboehme callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))), 276b000b770SStanislav Gatev unless(hasDeclaration(cxxMethodDecl( 277b000b770SStanislav Gatev anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 278b000b770SStanislav Gatev argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 279b000b770SStanislav Gatev } 280b000b770SStanislav Gatev 281b000b770SStanislav Gatev auto isOptionalNulloptAssignment() { 282d712c5edSmartinboehme return cxxOperatorCallExpr( 283d712c5edSmartinboehme hasOverloadedOperatorName("="), 284d712c5edSmartinboehme callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))), 285d712c5edSmartinboehme argumentCountIs(2), hasArgument(1, hasNulloptType())); 286b000b770SStanislav Gatev } 287b000b770SStanislav Gatev 2882ddd57aeSStanislav Gatev auto isStdSwapCall() { 2892ddd57aeSStanislav Gatev return callExpr(callee(functionDecl(hasName("std::swap"))), 290d712c5edSmartinboehme argumentCountIs(2), 291d712c5edSmartinboehme hasArgument(0, hasOptionalOrDerivedType()), 292d712c5edSmartinboehme hasArgument(1, hasOptionalOrDerivedType())); 2932ddd57aeSStanislav Gatev } 2942ddd57aeSStanislav Gatev 29525956d55SAMS21 auto isStdForwardCall() { 29625956d55SAMS21 return callExpr(callee(functionDecl(hasName("std::forward"))), 297d712c5edSmartinboehme argumentCountIs(1), 298d712c5edSmartinboehme hasArgument(0, hasOptionalOrDerivedType())); 29925956d55SAMS21 } 30025956d55SAMS21 3017f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 3027f076004SYitzhak Mandelbaum 3037f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() { 3047f076004SYitzhak Mandelbaum // `opt.value_or("").empty()` 3057f076004SYitzhak Mandelbaum return cxxMemberCallExpr( 3067f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("empty"))), 3077f076004SYitzhak Mandelbaum onImplicitObjectArgument(ignoringImplicit( 3087f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 3097f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 3107f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 3117f076004SYitzhak Mandelbaum hasArgument(0, stringLiteral(hasSize(0)))) 3127f076004SYitzhak Mandelbaum .bind(ValueOrCallID)))); 3137f076004SYitzhak Mandelbaum } 3147f076004SYitzhak Mandelbaum 3157f076004SYitzhak Mandelbaum auto isValueOrNotEqX() { 3167f076004SYitzhak Mandelbaum auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 3177f076004SYitzhak Mandelbaum return hasOperands( 3187f076004SYitzhak Mandelbaum ignoringImplicit( 3197f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 3207f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 3217f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 3227f076004SYitzhak Mandelbaum hasArgument(0, Arg)) 3237f076004SYitzhak Mandelbaum .bind(ValueOrCallID)), 3247f076004SYitzhak Mandelbaum ignoringImplicit(Arg)); 3257f076004SYitzhak Mandelbaum }; 3267f076004SYitzhak Mandelbaum 3277f076004SYitzhak Mandelbaum // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 3287f076004SYitzhak Mandelbaum // support this pattern for any expression, but the AST does not have a 3297f076004SYitzhak Mandelbaum // generic expression comparison facility, so we specialize to common cases 3307f076004SYitzhak Mandelbaum // seen in practice. FIXME: define a matcher that compares values across 3317f076004SYitzhak Mandelbaum // nodes, which would let us generalize this to any `X`. 3327f076004SYitzhak Mandelbaum return binaryOperation(hasOperatorName("!="), 3337f076004SYitzhak Mandelbaum anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 3347f076004SYitzhak Mandelbaum ComparesToSame(stringLiteral(hasSize(0))), 3357f076004SYitzhak Mandelbaum ComparesToSame(integerLiteral(equals(0))))); 3367f076004SYitzhak Mandelbaum } 3377f076004SYitzhak Mandelbaum 3386761b24aSJan Voung auto isZeroParamConstMemberCall() { 3396761b24aSJan Voung return cxxMemberCallExpr( 3406761b24aSJan Voung callee(cxxMethodDecl(parameterCountIs(0), isConst()))); 3416761b24aSJan Voung } 3426761b24aSJan Voung 34366bbbf2eSJan Voung auto isZeroParamConstMemberOperatorCall() { 34466bbbf2eSJan Voung return cxxOperatorCallExpr( 34566bbbf2eSJan Voung callee(cxxMethodDecl(parameterCountIs(0), isConst()))); 34666bbbf2eSJan Voung } 34766bbbf2eSJan Voung 3486761b24aSJan Voung auto isNonConstMemberCall() { 3496761b24aSJan Voung return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst())))); 3506761b24aSJan Voung } 3516761b24aSJan Voung 3526761b24aSJan Voung auto isNonConstMemberOperatorCall() { 3536761b24aSJan Voung return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst())))); 3546761b24aSJan Voung } 3556761b24aSJan Voung 35665e710c3SStanislav Gatev auto isCallReturningOptional() { 357d712c5edSmartinboehme return callExpr(hasType(qualType( 358d712c5edSmartinboehme anyOf(desugarsToOptionalOrDerivedType(), 359d712c5edSmartinboehme referenceType(pointee(desugarsToOptionalOrDerivedType())))))); 36065e710c3SStanislav Gatev } 36165e710c3SStanislav Gatev 362390029beSYitzhak Mandelbaum template <typename L, typename R> 363390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 364390029beSYitzhak Mandelbaum return cxxOperatorCallExpr( 365390029beSYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 366390029beSYitzhak Mandelbaum argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 367390029beSYitzhak Mandelbaum hasArgument(1, rhs_arg_matcher)); 368390029beSYitzhak Mandelbaum } 369390029beSYitzhak Mandelbaum 3706272226bSSam McCall /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula. 3716272226bSSam McCall const Formula &forceBoolValue(Environment &Env, const Expr &Expr) { 3722ee396b0Smartinboehme auto *Value = Env.get<BoolValue>(Expr); 373390029beSYitzhak Mandelbaum if (Value != nullptr) 3746272226bSSam McCall return Value->formula(); 375390029beSYitzhak Mandelbaum 376390029beSYitzhak Mandelbaum Value = &Env.makeAtomicBoolValue(); 377b244b6aeSMartin Braenne Env.setValue(Expr, *Value); 3786272226bSSam McCall return Value->formula(); 379390029beSYitzhak Mandelbaum } 380390029beSYitzhak Mandelbaum 38171f2ec2dSmartinboehme StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) { 38271f2ec2dSmartinboehme return OptionalLoc.getSyntheticField("has_value"); 38371f2ec2dSmartinboehme } 38471f2ec2dSmartinboehme 38571f2ec2dSmartinboehme StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) { 38671f2ec2dSmartinboehme return OptionalLoc.getSyntheticField("value"); 38771f2ec2dSmartinboehme } 38871f2ec2dSmartinboehme 3898fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 39071f2ec2dSmartinboehme /// property of the optional at `OptionalLoc`. 39171f2ec2dSmartinboehme void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal, 39271f2ec2dSmartinboehme Environment &Env) { 39371f2ec2dSmartinboehme Env.setValue(locForHasValue(OptionalLoc), HasValueVal); 3948fcdd625SStanislav Gatev } 3958fcdd625SStanislav Gatev 396af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the 39771f2ec2dSmartinboehme /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null. 39871f2ec2dSmartinboehme BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) { 39971f2ec2dSmartinboehme if (OptionalLoc == nullptr) 40071f2ec2dSmartinboehme return nullptr; 40171f2ec2dSmartinboehme StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc); 4022ee396b0Smartinboehme auto *HasValueVal = Env.get<BoolValue>(HasValueLoc); 403dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) { 404dd38caf3SYitzhak Mandelbaum HasValueVal = &Env.makeAtomicBoolValue(); 40571f2ec2dSmartinboehme Env.setValue(HasValueLoc, *HasValueVal); 406dd38caf3SYitzhak Mandelbaum } 407dd38caf3SYitzhak Mandelbaum return HasValueVal; 408af98b0afSStanislav Gatev } 409af98b0afSStanislav Gatev 410d712c5edSmartinboehme QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) { 411d712c5edSmartinboehme auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD); 412d712c5edSmartinboehme return CTSD.getTemplateArgs()[0].getAsType(); 413092a530cSStanislav Gatev } 414092a530cSStanislav Gatev 415092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`. 416092a530cSStanislav Gatev /// 417092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this 418092a530cSStanislav Gatev /// function will be 2. 419092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 420d712c5edSmartinboehme const CXXRecordDecl *Optional = 421d712c5edSmartinboehme getOptionalBaseClass(Type->getAsCXXRecordDecl()); 422d712c5edSmartinboehme if (Optional == nullptr) 423092a530cSStanislav Gatev return 0; 424092a530cSStanislav Gatev return 1 + countOptionalWrappers( 425092a530cSStanislav Gatev ASTCtx, 426d712c5edSmartinboehme valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx)); 427092a530cSStanislav Gatev } 428092a530cSStanislav Gatev 42971f2ec2dSmartinboehme StorageLocation *getLocBehindPossiblePointer(const Expr &E, 43071f2ec2dSmartinboehme const Environment &Env) { 43171f2ec2dSmartinboehme if (E.isPRValue()) { 43271f2ec2dSmartinboehme if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E))) 43371f2ec2dSmartinboehme return &PointerVal->getPointeeLoc(); 434dd38caf3SYitzhak Mandelbaum return nullptr; 435dd38caf3SYitzhak Mandelbaum } 43671f2ec2dSmartinboehme return Env.getStorageLocation(E); 43748bc7150SMartin Braenne } 43848bc7150SMartin Braenne 439092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 440af98b0afSStanislav Gatev LatticeTransferState &State) { 44171f2ec2dSmartinboehme if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 44271f2ec2dSmartinboehme getLocBehindPossiblePointer(*ObjectExpr, State.Env))) { 443b244b6aeSMartin Braenne if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr) 44471f2ec2dSmartinboehme State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc)); 445af98b0afSStanislav Gatev } 446dd38caf3SYitzhak Mandelbaum } 447af98b0afSStanislav Gatev 4483bc1ea5bSMartin Braenne void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 4493bc1ea5bSMartin Braenne LatticeTransferState &State) { 45071f2ec2dSmartinboehme if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 45171f2ec2dSmartinboehme getLocBehindPossiblePointer(*ObjectExpr, State.Env))) 45271f2ec2dSmartinboehme State.Env.setValue( 45371f2ec2dSmartinboehme *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc))); 4543bc1ea5bSMartin Braenne } 4553bc1ea5bSMartin Braenne 456092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E, 457092a530cSStanislav Gatev const MatchFinder::MatchResult &, 458092a530cSStanislav Gatev LatticeTransferState &State) { 459e8fce958Smartinboehme setHasValue(State.Env.getResultObjectLocation(*E), 460e8fce958Smartinboehme State.Env.getBoolLiteralValue(true), State.Env); 4619e0fc676SStanislav Gatev } 4629e0fc676SStanislav Gatev 463092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 464092a530cSStanislav Gatev const MatchFinder::MatchResult &, 465af98b0afSStanislav Gatev LatticeTransferState &State) { 466dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = getHasValue( 46771f2ec2dSmartinboehme State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 468b244b6aeSMartin Braenne State.Env.setValue(*CallExpr, *HasValueVal); 469af98b0afSStanislav Gatev } 470af98b0afSStanislav Gatev } 471af98b0afSStanislav Gatev 47211c423f9SChris Cotter void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr, 47311c423f9SChris Cotter const MatchFinder::MatchResult &, 47411c423f9SChris Cotter LatticeTransferState &State) { 47511c423f9SChris Cotter if (auto *HasValueVal = getHasValue( 47611c423f9SChris Cotter State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 47711c423f9SChris Cotter State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal)); 47811c423f9SChris Cotter } 47911c423f9SChris Cotter } 48011c423f9SChris Cotter 4817f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in 4827f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property. 4836272226bSSam McCall void transferValueOrImpl( 4846272226bSSam McCall const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result, 4857f076004SYitzhak Mandelbaum LatticeTransferState &State, 4866272226bSSam McCall const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal, 4876272226bSSam McCall const Formula &HasValueVal)) { 4887f076004SYitzhak Mandelbaum auto &Env = State.Env; 4897f076004SYitzhak Mandelbaum 49071f2ec2dSmartinboehme const auto *MCE = 49171f2ec2dSmartinboehme Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID); 4927f076004SYitzhak Mandelbaum 49371f2ec2dSmartinboehme auto *HasValueVal = 49471f2ec2dSmartinboehme getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env)); 495dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) 4967f076004SYitzhak Mandelbaum return; 4977f076004SYitzhak Mandelbaum 498526c9b7eSmartinboehme Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), 4996272226bSSam McCall HasValueVal->formula())); 5007f076004SYitzhak Mandelbaum } 5017f076004SYitzhak Mandelbaum 5027f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 5037f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 5047f076004SYitzhak Mandelbaum LatticeTransferState &State) { 5057f076004SYitzhak Mandelbaum return transferValueOrImpl(ComparisonExpr, Result, State, 5066272226bSSam McCall [](Environment &Env, const Formula &ExprVal, 5076272226bSSam McCall const Formula &HasValueVal) -> const Formula & { 5086272226bSSam McCall auto &A = Env.arena(); 5097f076004SYitzhak Mandelbaum // If the result is *not* empty, then we know the 5107f076004SYitzhak Mandelbaum // optional must have been holding a value. If 5117f076004SYitzhak Mandelbaum // `ExprVal` is true, though, we don't learn 5127f076004SYitzhak Mandelbaum // anything definite about `has_value`, so we 5137f076004SYitzhak Mandelbaum // don't add any corresponding implications to 5147f076004SYitzhak Mandelbaum // the flow condition. 5156272226bSSam McCall return A.makeImplies(A.makeNot(ExprVal), 5167f076004SYitzhak Mandelbaum HasValueVal); 5177f076004SYitzhak Mandelbaum }); 5187f076004SYitzhak Mandelbaum } 5197f076004SYitzhak Mandelbaum 5207f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr, 5217f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 5227f076004SYitzhak Mandelbaum LatticeTransferState &State) { 5237f076004SYitzhak Mandelbaum transferValueOrImpl(ComparisonExpr, Result, State, 5246272226bSSam McCall [](Environment &Env, const Formula &ExprVal, 5256272226bSSam McCall const Formula &HasValueVal) -> const Formula & { 5266272226bSSam McCall auto &A = Env.arena(); 5277f076004SYitzhak Mandelbaum // We know that if `(opt.value_or(X) != X)` then 5287f076004SYitzhak Mandelbaum // `opt.hasValue()`, even without knowing further 5297f076004SYitzhak Mandelbaum // details about the contents of `opt`. 5306272226bSSam McCall return A.makeImplies(ExprVal, HasValueVal); 5317f076004SYitzhak Mandelbaum }); 5327f076004SYitzhak Mandelbaum } 5337f076004SYitzhak Mandelbaum 53465e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E, 53565e710c3SStanislav Gatev const MatchFinder::MatchResult &Result, 53665e710c3SStanislav Gatev LatticeTransferState &State) { 5379ecdbe38SMartin Braenne RecordStorageLocation *Loc = nullptr; 53844f98d01SMartin Braenne if (E->isPRValue()) { 53944f98d01SMartin Braenne Loc = &State.Env.getResultObjectLocation(*E); 54044f98d01SMartin Braenne } else { 5412ee396b0Smartinboehme Loc = State.Env.get<RecordStorageLocation>(*E); 542f76f6674SMartin Braenne if (Loc == nullptr) { 5439ecdbe38SMartin Braenne Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E)); 544b244b6aeSMartin Braenne State.Env.setStorageLocation(*E, *Loc); 54544f98d01SMartin Braenne } 546f76f6674SMartin Braenne } 54744f98d01SMartin Braenne 548e8fce958Smartinboehme if (State.Env.getValue(locForHasValue(*Loc)) != nullptr) 549e8fce958Smartinboehme return; 550e8fce958Smartinboehme 551e8fce958Smartinboehme setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env); 55265e710c3SStanislav Gatev } 55365e710c3SStanislav Gatev 5546761b24aSJan Voung void handleConstMemberCall(const CallExpr *CE, 5556761b24aSJan Voung dataflow::RecordStorageLocation *RecordLoc, 5566761b24aSJan Voung const MatchFinder::MatchResult &Result, 5576761b24aSJan Voung LatticeTransferState &State) { 5586761b24aSJan Voung // If the const method returns an optional or reference to an optional. 5596761b24aSJan Voung if (RecordLoc != nullptr && isSupportedOptionalType(CE->getType())) { 560*72a28a3bSJan Voung const FunctionDecl *DirectCallee = CE->getDirectCallee(); 561*72a28a3bSJan Voung if (DirectCallee == nullptr) 562*72a28a3bSJan Voung return; 563*72a28a3bSJan Voung StorageLocation &Loc = 5646761b24aSJan Voung State.Lattice.getOrCreateConstMethodReturnStorageLocation( 565*72a28a3bSJan Voung *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) { 5666761b24aSJan Voung setHasValue(cast<RecordStorageLocation>(Loc), 5676761b24aSJan Voung State.Env.makeAtomicBoolValue(), State.Env); 5686761b24aSJan Voung }); 5696761b24aSJan Voung if (CE->isGLValue()) { 5706761b24aSJan Voung // If the call to the const method returns a reference to an optional, 5716761b24aSJan Voung // link the call expression to the cached StorageLocation. 572*72a28a3bSJan Voung State.Env.setStorageLocation(*CE, Loc); 5736761b24aSJan Voung } else { 5746761b24aSJan Voung // If the call to the const method returns an optional by value, we 5756761b24aSJan Voung // need to use CopyRecord to link the optional to the result object 5766761b24aSJan Voung // of the call expression. 5776761b24aSJan Voung auto &ResultLoc = State.Env.getResultObjectLocation(*CE); 578*72a28a3bSJan Voung copyRecord(cast<RecordStorageLocation>(Loc), ResultLoc, State.Env); 5796761b24aSJan Voung } 5806761b24aSJan Voung return; 5816761b24aSJan Voung } 5826761b24aSJan Voung 58366bbbf2eSJan Voung // Cache if the const method returns a boolean or pointer type. 5846761b24aSJan Voung // We may decide to cache other return types in the future. 58566bbbf2eSJan Voung if (RecordLoc != nullptr && 58666bbbf2eSJan Voung (CE->getType()->isBooleanType() || CE->getType()->isPointerType())) { 5876761b24aSJan Voung Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE, 5886761b24aSJan Voung State.Env); 5896761b24aSJan Voung if (Val == nullptr) 5906761b24aSJan Voung return; 5916761b24aSJan Voung State.Env.setValue(*CE, *Val); 5926761b24aSJan Voung return; 5936761b24aSJan Voung } 5946761b24aSJan Voung 5956761b24aSJan Voung // Perform default handling if the call returns an optional 5966761b24aSJan Voung // but wasn't handled above (if RecordLoc is nullptr). 5976761b24aSJan Voung if (isSupportedOptionalType(CE->getType())) { 5986761b24aSJan Voung transferCallReturningOptional(CE, Result, State); 5996761b24aSJan Voung } 6006761b24aSJan Voung } 6016761b24aSJan Voung 6026761b24aSJan Voung void transferValue_ConstMemberCall(const CXXMemberCallExpr *MCE, 6036761b24aSJan Voung const MatchFinder::MatchResult &Result, 6046761b24aSJan Voung LatticeTransferState &State) { 6056761b24aSJan Voung handleConstMemberCall( 6066761b24aSJan Voung MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State); 6076761b24aSJan Voung } 6086761b24aSJan Voung 60966bbbf2eSJan Voung void transferValue_ConstMemberOperatorCall( 61066bbbf2eSJan Voung const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result, 61166bbbf2eSJan Voung LatticeTransferState &State) { 61266bbbf2eSJan Voung auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>( 61366bbbf2eSJan Voung State.Env.getStorageLocation(*OCE->getArg(0))); 61466bbbf2eSJan Voung handleConstMemberCall(OCE, RecordLoc, Result, State); 61566bbbf2eSJan Voung } 61666bbbf2eSJan Voung 6176761b24aSJan Voung void handleNonConstMemberCall(const CallExpr *CE, 6186761b24aSJan Voung dataflow::RecordStorageLocation *RecordLoc, 6196761b24aSJan Voung const MatchFinder::MatchResult &Result, 6206761b24aSJan Voung LatticeTransferState &State) { 6216761b24aSJan Voung if (RecordLoc != nullptr) { 6221f6741c1SJan Voung // When a non-const member function is called, clear all (non-const) 6231f6741c1SJan Voung // optional fields of the receiver. Const-qualified fields can't be 6241f6741c1SJan Voung // changed (at least, not without UB). 6256761b24aSJan Voung for (const auto &[Field, FieldLoc] : RecordLoc->children()) { 6261f6741c1SJan Voung QualType FieldType = Field->getType(); 6271f6741c1SJan Voung if (!FieldType.isConstQualified() && 6281f6741c1SJan Voung isSupportedOptionalType(Field->getType())) { 6296761b24aSJan Voung auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc); 6306761b24aSJan Voung if (FieldRecordLoc) { 6316761b24aSJan Voung setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(), 6326761b24aSJan Voung State.Env); 6336761b24aSJan Voung } 6346761b24aSJan Voung } 6356761b24aSJan Voung } 6366761b24aSJan Voung State.Lattice.clearConstMethodReturnValues(*RecordLoc); 6376761b24aSJan Voung State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc); 6386761b24aSJan Voung } 6396761b24aSJan Voung 6406761b24aSJan Voung // Perform default handling if the call returns an optional. 6416761b24aSJan Voung if (isSupportedOptionalType(CE->getType())) { 6426761b24aSJan Voung transferCallReturningOptional(CE, Result, State); 6436761b24aSJan Voung } 6446761b24aSJan Voung } 6456761b24aSJan Voung 6466761b24aSJan Voung void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE, 6476761b24aSJan Voung const MatchFinder::MatchResult &Result, 6486761b24aSJan Voung LatticeTransferState &State) { 6496761b24aSJan Voung handleNonConstMemberCall( 6506761b24aSJan Voung MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State); 6516761b24aSJan Voung } 6526761b24aSJan Voung 6536761b24aSJan Voung void transferValue_NonConstMemberOperatorCall( 6546761b24aSJan Voung const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result, 6556761b24aSJan Voung LatticeTransferState &State) { 6566761b24aSJan Voung auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>( 6576761b24aSJan Voung State.Env.getStorageLocation(*OCE->getArg(0))); 6586761b24aSJan Voung handleNonConstMemberCall(OCE, RecordLoc, Result, State); 6596761b24aSJan Voung } 6606761b24aSJan Voung 661f653d140SMartin Braenne void constructOptionalValue(const Expr &E, Environment &Env, 662092a530cSStanislav Gatev BoolValue &HasValueVal) { 6639ecdbe38SMartin Braenne RecordStorageLocation &Loc = Env.getResultObjectLocation(E); 664e8fce958Smartinboehme setHasValue(Loc, HasValueVal, Env); 6659e0fc676SStanislav Gatev } 6669e0fc676SStanislav Gatev 667b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>` 668b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>` 669b000b770SStanislav Gatev /// where `T` is constructible from `U`. 670ae280281Smartinboehme BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E, 671b000b770SStanislav Gatev const MatchFinder::MatchResult &MatchRes, 672b000b770SStanislav Gatev LatticeTransferState &State) { 673ae280281Smartinboehme const int DestTypeOptionalWrappersCount = 674ae280281Smartinboehme countOptionalWrappers(*MatchRes.Context, DestType); 675c849843cSMartin Braenne const int ArgTypeOptionalWrappersCount = countOptionalWrappers( 676c849843cSMartin Braenne *MatchRes.Context, E.getType().getNonReferenceType()); 677b000b770SStanislav Gatev 678ae280281Smartinboehme // Is this an constructor of the form `template<class U> optional(U &&)` / 679ae280281Smartinboehme // assignment of the form `template<class U> optional& operator=(U &&)` 680ae280281Smartinboehme // (where `T` is assignable / constructible from `U`)? 681ae280281Smartinboehme // We recognize this because the number of optionals in the optional being 682ae280281Smartinboehme // assigned to is different from the function argument type. 683ae280281Smartinboehme if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount) 684b000b770SStanislav Gatev return State.Env.getBoolLiteralValue(true); 685b000b770SStanislav Gatev 686ae280281Smartinboehme // Otherwise, this must be a constructor of the form 687ae280281Smartinboehme // `template <class U> optional<optional<U> &&)` / assignment of the form 688ae280281Smartinboehme // `template <class U> optional& operator=(optional<U> &&) 689ae280281Smartinboehme // (where, again, `T` is assignable / constructible from `U`). 6902ee396b0Smartinboehme auto *Loc = State.Env.get<RecordStorageLocation>(E); 69171f2ec2dSmartinboehme if (auto *HasValueVal = getHasValue(State.Env, Loc)) 692dd38caf3SYitzhak Mandelbaum return *HasValueVal; 693b000b770SStanislav Gatev return State.Env.makeAtomicBoolValue(); 694b000b770SStanislav Gatev } 695b000b770SStanislav Gatev 696092a530cSStanislav Gatev void transferValueOrConversionConstructor( 697092a530cSStanislav Gatev const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 6989e0fc676SStanislav Gatev LatticeTransferState &State) { 699092a530cSStanislav Gatev assert(E->getNumArgs() > 0); 700092a530cSStanislav Gatev 701ae280281Smartinboehme constructOptionalValue( 702ae280281Smartinboehme *E, State.Env, 703ae280281Smartinboehme valueOrConversionHasValue( 704ae280281Smartinboehme E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0), 705ae280281Smartinboehme MatchRes, State)); 706b000b770SStanislav Gatev } 707092a530cSStanislav Gatev 708b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 709b000b770SStanislav Gatev LatticeTransferState &State) { 710b000b770SStanislav Gatev assert(E->getNumArgs() > 0); 711b000b770SStanislav Gatev 7122ee396b0Smartinboehme if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) { 713e8fce958Smartinboehme setHasValue(*Loc, HasValueVal, State.Env); 714b000b770SStanislav Gatev 715b000b770SStanislav Gatev // Assign a storage location for the whole expression. 716b244b6aeSMartin Braenne State.Env.setStorageLocation(*E, *Loc); 717f653d140SMartin Braenne } 718b000b770SStanislav Gatev } 719b000b770SStanislav Gatev 720b000b770SStanislav Gatev void transferValueOrConversionAssignment( 721b000b770SStanislav Gatev const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 722b000b770SStanislav Gatev LatticeTransferState &State) { 723b000b770SStanislav Gatev assert(E->getNumArgs() > 1); 724ae280281Smartinboehme transferAssignment( 725ae280281Smartinboehme E, 726ae280281Smartinboehme valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(), 72706decd0bSKazu Hirata *E->getArg(1), MatchRes, State), 728b000b770SStanislav Gatev State); 729b000b770SStanislav Gatev } 730b000b770SStanislav Gatev 731b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E, 732b000b770SStanislav Gatev const MatchFinder::MatchResult &, 733b000b770SStanislav Gatev LatticeTransferState &State) { 734b000b770SStanislav Gatev transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 7359e0fc676SStanislav Gatev } 7369e0fc676SStanislav Gatev 7379ecdbe38SMartin Braenne void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2, 7389ecdbe38SMartin Braenne Environment &Env) { 739d4fb829bSYitzhak Mandelbaum // We account for cases where one or both of the optionals are not modeled, 740d4fb829bSYitzhak Mandelbaum // either lacking associated storage locations, or lacking values associated 741d4fb829bSYitzhak Mandelbaum // to such storage locations. 7422ddd57aeSStanislav Gatev 743d4fb829bSYitzhak Mandelbaum if (Loc1 == nullptr) { 744d4fb829bSYitzhak Mandelbaum if (Loc2 != nullptr) 745e8fce958Smartinboehme setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env); 746d4fb829bSYitzhak Mandelbaum return; 747d4fb829bSYitzhak Mandelbaum } 748d4fb829bSYitzhak Mandelbaum if (Loc2 == nullptr) { 749e8fce958Smartinboehme setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env); 750d4fb829bSYitzhak Mandelbaum return; 751d4fb829bSYitzhak Mandelbaum } 7522ddd57aeSStanislav Gatev 753d4fb829bSYitzhak Mandelbaum // Both expressions have locations, though they may not have corresponding 754d4fb829bSYitzhak Mandelbaum // values. In that case, we create a fresh value at this point. Note that if 755d4fb829bSYitzhak Mandelbaum // two branches both do this, they will not share the value, but it at least 756d4fb829bSYitzhak Mandelbaum // allows for local reasoning about the value. To avoid the above, we would 757d4fb829bSYitzhak Mandelbaum // need *lazy* value allocation. 758d4fb829bSYitzhak Mandelbaum // FIXME: allocate values lazily, instead of just creating a fresh value. 75971f2ec2dSmartinboehme BoolValue *BoolVal1 = getHasValue(Env, Loc1); 760f653d140SMartin Braenne if (BoolVal1 == nullptr) 761f653d140SMartin Braenne BoolVal1 = &Env.makeAtomicBoolValue(); 762d4fb829bSYitzhak Mandelbaum 76371f2ec2dSmartinboehme BoolValue *BoolVal2 = getHasValue(Env, Loc2); 764f653d140SMartin Braenne if (BoolVal2 == nullptr) 765f653d140SMartin Braenne BoolVal2 = &Env.makeAtomicBoolValue(); 766d4fb829bSYitzhak Mandelbaum 767e8fce958Smartinboehme setHasValue(*Loc1, *BoolVal2, Env); 768e8fce958Smartinboehme setHasValue(*Loc2, *BoolVal1, Env); 7692ddd57aeSStanislav Gatev } 7702ddd57aeSStanislav Gatev 7712ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E, 7722ddd57aeSStanislav Gatev const MatchFinder::MatchResult &, 7732ddd57aeSStanislav Gatev LatticeTransferState &State) { 7742ddd57aeSStanislav Gatev assert(E->getNumArgs() == 1); 7752ee396b0Smartinboehme auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 776f653d140SMartin Braenne transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env); 7772ddd57aeSStanislav Gatev } 7782ddd57aeSStanislav Gatev 7792ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 7802ddd57aeSStanislav Gatev LatticeTransferState &State) { 7812ddd57aeSStanislav Gatev assert(E->getNumArgs() == 2); 7822ee396b0Smartinboehme auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 7832ee396b0Smartinboehme auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1)); 784f653d140SMartin Braenne transferSwap(Arg0Loc, Arg1Loc, State.Env); 7852ddd57aeSStanislav Gatev } 7862ddd57aeSStanislav Gatev 78725956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &, 78825956d55SAMS21 LatticeTransferState &State) { 78925956d55SAMS21 assert(E->getNumArgs() == 1); 79025956d55SAMS21 791b244b6aeSMartin Braenne if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0))) 792b244b6aeSMartin Braenne State.Env.setStorageLocation(*E, *Loc); 79325956d55SAMS21 } 79425956d55SAMS21 7956272226bSSam McCall const Formula &evaluateEquality(Arena &A, const Formula &EqVal, 7966272226bSSam McCall const Formula &LHS, const Formula &RHS) { 797390029beSYitzhak Mandelbaum // Logically, an optional<T> object is composed of two values - a `has_value` 798390029beSYitzhak Mandelbaum // bit and a value of type T. Equality of optional objects compares both 799390029beSYitzhak Mandelbaum // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 800390029beSYitzhak Mandelbaum // when two optional objects are engaged, the equality of their respective 801390029beSYitzhak Mandelbaum // values of type T matters. Since we only track the `has_value` bits, we 802390029beSYitzhak Mandelbaum // can't make any conclusions about equality when we know that two optional 803390029beSYitzhak Mandelbaum // objects are engaged. 804390029beSYitzhak Mandelbaum // 805390029beSYitzhak Mandelbaum // We express this as two facts about the equality: 806390029beSYitzhak Mandelbaum // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 807390029beSYitzhak Mandelbaum // If they are equal, then either both are set or both are unset. 808390029beSYitzhak Mandelbaum // b) (!LHS & !RHS) => EqVal 809390029beSYitzhak Mandelbaum // If neither is set, then they are equal. 810390029beSYitzhak Mandelbaum // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 8116272226bSSam McCall return A.makeAnd( 8126272226bSSam McCall A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS), 8136272226bSSam McCall A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))), 8146272226bSSam McCall A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS))); 815390029beSYitzhak Mandelbaum } 816390029beSYitzhak Mandelbaum 817390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 818390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, 819390029beSYitzhak Mandelbaum LatticeTransferState &State) { 820390029beSYitzhak Mandelbaum Environment &Env = State.Env; 8216272226bSSam McCall auto &A = Env.arena(); 822390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 8232ee396b0Smartinboehme auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0)); 82471f2ec2dSmartinboehme if (auto *LHasVal = getHasValue(Env, Arg0Loc)) { 8252ee396b0Smartinboehme auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1)); 82671f2ec2dSmartinboehme if (auto *RHasVal = getHasValue(Env, Arg1Loc)) { 827390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 8286272226bSSam McCall CmpValue = &A.makeNot(*CmpValue); 829526c9b7eSmartinboehme Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(), 8306272226bSSam McCall RHasVal->formula())); 831390029beSYitzhak Mandelbaum } 832390029beSYitzhak Mandelbaum } 83371f2ec2dSmartinboehme } 834390029beSYitzhak Mandelbaum 835390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 836390029beSYitzhak Mandelbaum const clang::Expr *E, Environment &Env) { 8376272226bSSam McCall auto &A = Env.arena(); 838390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 8392ee396b0Smartinboehme auto *Loc = Env.get<RecordStorageLocation>(*E); 84071f2ec2dSmartinboehme if (auto *HasVal = getHasValue(Env, Loc)) { 841390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 8426272226bSSam McCall CmpValue = &A.makeNot(*CmpValue); 843526c9b7eSmartinboehme Env.assume( 8446272226bSSam McCall evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true))); 845390029beSYitzhak Mandelbaum } 846390029beSYitzhak Mandelbaum } 847390029beSYitzhak Mandelbaum 84871f2ec2dSmartinboehme void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr, 84971f2ec2dSmartinboehme const clang::Expr *E, Environment &Env) { 85071f2ec2dSmartinboehme auto &A = Env.arena(); 85171f2ec2dSmartinboehme auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 8522ee396b0Smartinboehme auto *Loc = Env.get<RecordStorageLocation>(*E); 85371f2ec2dSmartinboehme if (auto *HasVal = getHasValue(Env, Loc)) { 85471f2ec2dSmartinboehme if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 85571f2ec2dSmartinboehme CmpValue = &A.makeNot(*CmpValue); 85671f2ec2dSmartinboehme Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(), 85771f2ec2dSmartinboehme A.makeLiteral(false))); 85871f2ec2dSmartinboehme } 85971f2ec2dSmartinboehme } 86071f2ec2dSmartinboehme 8616ad0788cSKazu Hirata std::optional<StatementMatcher> 862a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 8635d22d1f5SYitzhak Mandelbaum if (Options.IgnoreSmartPointerDereference) { 8645d22d1f5SYitzhak Mandelbaum auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 8655d22d1f5SYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 8665d22d1f5SYitzhak Mandelbaum unless(hasArgument(0, expr(hasOptionalType())))))); 8675d22d1f5SYitzhak Mandelbaum return expr( 8685d22d1f5SYitzhak Mandelbaum anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 8695d22d1f5SYitzhak Mandelbaum } 87034e0d057SKazu Hirata return std::nullopt; 871a184a0d8SYitzhak Mandelbaum } 872a184a0d8SYitzhak Mandelbaum 87358fe7f96SSam Estep StatementMatcher 8746ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 8752f0630f8SAnton Dukeman return isOptionalMemberCallWithNameMatcher(hasName("value"), 8762f0630f8SAnton Dukeman IgnorableOptional); 87758fe7f96SSam Estep } 87858fe7f96SSam Estep 87958fe7f96SSam Estep StatementMatcher 8806ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 88158fe7f96SSam Estep return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 88258fe7f96SSam Estep isOptionalOperatorCallWithName("->", IgnorableOptional))); 88358fe7f96SSam Estep } 88458fe7f96SSam Estep 8855d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() { 886b000b770SStanislav Gatev // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 887b000b770SStanislav Gatev // lot of duplicated work (e.g. string comparisons), consider providing APIs 888b000b770SStanislav Gatev // that avoid it through memoization. 8897538b360SWei Yi Tee return CFGMatchSwitchBuilder<LatticeTransferState>() 8909e0fc676SStanislav Gatev // make_optional 8917538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 892092a530cSStanislav Gatev 8930e8d4a6dSYitzhak Mandelbaum // optional::optional (in place) 8947538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 895092a530cSStanislav Gatev isOptionalInPlaceConstructor(), 896092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 897092a530cSStanislav Gatev LatticeTransferState &State) { 898f653d140SMartin Braenne constructOptionalValue(*E, State.Env, 8990e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(true)); 900092a530cSStanislav Gatev }) 9010e8d4a6dSYitzhak Mandelbaum // optional::optional(nullopt_t) 902390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXConstructExpr>( 903390029beSYitzhak Mandelbaum isOptionalNulloptConstructor(), 904390029beSYitzhak Mandelbaum [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 905390029beSYitzhak Mandelbaum LatticeTransferState &State) { 906f653d140SMartin Braenne constructOptionalValue(*E, State.Env, 9070e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(false)); 908390029beSYitzhak Mandelbaum }) 9090e8d4a6dSYitzhak Mandelbaum // optional::optional (value/conversion) 9107538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 911092a530cSStanislav Gatev transferValueOrConversionConstructor) 9129e0fc676SStanislav Gatev 913b000b770SStanislav Gatev // optional::operator= 9147538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>( 9157538b360SWei Yi Tee isOptionalValueOrConversionAssignment(), 916b000b770SStanislav Gatev transferValueOrConversionAssignment) 9177538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 918b000b770SStanislav Gatev transferNulloptAssignment) 919b000b770SStanislav Gatev 920af98b0afSStanislav Gatev // optional::value 9217538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 9225d22d1f5SYitzhak Mandelbaum valueCall(std::nullopt), 923092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 924092a530cSStanislav Gatev LatticeTransferState &State) { 925af98b0afSStanislav Gatev transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 926af98b0afSStanislav Gatev }) 927af98b0afSStanislav Gatev 9283bc1ea5bSMartin Braenne // optional::operator* 9293bc1ea5bSMartin Braenne .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"), 9307538b360SWei Yi Tee [](const CallExpr *E, 9317538b360SWei Yi Tee const MatchFinder::MatchResult &, 932092a530cSStanislav Gatev LatticeTransferState &State) { 933af98b0afSStanislav Gatev transferUnwrapCall(E, E->getArg(0), State); 934af98b0afSStanislav Gatev }) 935af98b0afSStanislav Gatev 9363bc1ea5bSMartin Braenne // optional::operator-> 9373bc1ea5bSMartin Braenne .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"), 9383bc1ea5bSMartin Braenne [](const CallExpr *E, 9393bc1ea5bSMartin Braenne const MatchFinder::MatchResult &, 9403bc1ea5bSMartin Braenne LatticeTransferState &State) { 9413bc1ea5bSMartin Braenne transferArrowOpCall(E, E->getArg(0), State); 9423bc1ea5bSMartin Braenne }) 9433bc1ea5bSMartin Braenne 9442f0630f8SAnton Dukeman // optional::has_value, optional::hasValue 9452f0630f8SAnton Dukeman // Of the supported optionals only folly::Optional uses hasValue, but this 9462f0630f8SAnton Dukeman // will also pass for other types 9477538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 9482f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher( 9492f0630f8SAnton Dukeman hasAnyName("has_value", "hasValue")), 950af98b0afSStanislav Gatev transferOptionalHasValueCall) 951af98b0afSStanislav Gatev 9529e0fc676SStanislav Gatev // optional::operator bool 9537538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 9542f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("operator bool")), 9559e0fc676SStanislav Gatev transferOptionalHasValueCall) 9569e0fc676SStanislav Gatev 95711c423f9SChris Cotter // NullableValue::isNull 95811c423f9SChris Cotter // Only NullableValue has isNull 95911c423f9SChris Cotter .CaseOfCFGStmt<CXXMemberCallExpr>( 96011c423f9SChris Cotter isOptionalMemberCallWithNameMatcher(hasName("isNull")), 96111c423f9SChris Cotter transferOptionalIsNullCall) 96211c423f9SChris Cotter 9639e0fc676SStanislav Gatev // optional::emplace 9647538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 9652f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("emplace")), 966092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 967092a530cSStanislav Gatev LatticeTransferState &State) { 9689ecdbe38SMartin Braenne if (RecordStorageLocation *Loc = 969f653d140SMartin Braenne getImplicitObjectLocation(*E, State.Env)) { 970e8fce958Smartinboehme setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env); 971f653d140SMartin Braenne } 972092a530cSStanislav Gatev }) 9739e0fc676SStanislav Gatev 9749e0fc676SStanislav Gatev // optional::reset 9757538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 9762f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("reset")), 977092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 978092a530cSStanislav Gatev LatticeTransferState &State) { 9799ecdbe38SMartin Braenne if (RecordStorageLocation *Loc = 980f653d140SMartin Braenne getImplicitObjectLocation(*E, State.Env)) { 981e8fce958Smartinboehme setHasValue(*Loc, State.Env.getBoolLiteralValue(false), 982f653d140SMartin Braenne State.Env); 983f653d140SMartin Braenne } 984092a530cSStanislav Gatev }) 9859e0fc676SStanislav Gatev 9862ddd57aeSStanislav Gatev // optional::swap 9872f0630f8SAnton Dukeman .CaseOfCFGStmt<CXXMemberCallExpr>( 9882f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("swap")), 9892ddd57aeSStanislav Gatev transferSwapCall) 9902ddd57aeSStanislav Gatev 9912ddd57aeSStanislav Gatev // std::swap 9927538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 9932ddd57aeSStanislav Gatev 99425956d55SAMS21 // std::forward 99525956d55SAMS21 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 99625956d55SAMS21 9977f076004SYitzhak Mandelbaum // opt.value_or("").empty() 9987538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 9997538b360SWei Yi Tee transferValueOrStringEmptyCall) 10007f076004SYitzhak Mandelbaum 10017f076004SYitzhak Mandelbaum // opt.value_or(X) != X 10027538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 10037f076004SYitzhak Mandelbaum 1004390029beSYitzhak Mandelbaum // Comparisons (==, !=): 1005390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 100671f2ec2dSmartinboehme isComparisonOperatorCall(hasOptionalType(), hasOptionalType()), 1007390029beSYitzhak Mandelbaum transferOptionalAndOptionalCmp) 1008390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 100971f2ec2dSmartinboehme isComparisonOperatorCall(hasOptionalType(), hasNulloptType()), 101071f2ec2dSmartinboehme [](const clang::CXXOperatorCallExpr *Cmp, 101171f2ec2dSmartinboehme const MatchFinder::MatchResult &, LatticeTransferState &State) { 101271f2ec2dSmartinboehme transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env); 101371f2ec2dSmartinboehme }) 101471f2ec2dSmartinboehme .CaseOfCFGStmt<CXXOperatorCallExpr>( 101571f2ec2dSmartinboehme isComparisonOperatorCall(hasNulloptType(), hasOptionalType()), 101671f2ec2dSmartinboehme [](const clang::CXXOperatorCallExpr *Cmp, 101771f2ec2dSmartinboehme const MatchFinder::MatchResult &, LatticeTransferState &State) { 101871f2ec2dSmartinboehme transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env); 101971f2ec2dSmartinboehme }) 102071f2ec2dSmartinboehme .CaseOfCFGStmt<CXXOperatorCallExpr>( 102171f2ec2dSmartinboehme isComparisonOperatorCall( 102271f2ec2dSmartinboehme hasOptionalType(), 102371f2ec2dSmartinboehme unless(anyOf(hasOptionalType(), hasNulloptType()))), 1024390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 1025390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 1026390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 1027390029beSYitzhak Mandelbaum }) 1028390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 102971f2ec2dSmartinboehme isComparisonOperatorCall( 103071f2ec2dSmartinboehme unless(anyOf(hasOptionalType(), hasNulloptType())), 1031390029beSYitzhak Mandelbaum hasOptionalType()), 1032390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 1033390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 1034390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 1035390029beSYitzhak Mandelbaum }) 1036390029beSYitzhak Mandelbaum 1037*72a28a3bSJan Voung // Smart-pointer-like operator* and operator-> calls that may look like 1038*72a28a3bSJan Voung // const accessors (below) but need special handling to allow mixing 1039*72a28a3bSJan Voung // the accessor calls. 1040*72a28a3bSJan Voung .CaseOfCFGStmt<CXXOperatorCallExpr>( 1041*72a28a3bSJan Voung isSmartPointerLikeOperatorStar(), 1042*72a28a3bSJan Voung [](const CXXOperatorCallExpr *E, 1043*72a28a3bSJan Voung const MatchFinder::MatchResult &Result, 1044*72a28a3bSJan Voung LatticeTransferState &State) { 1045*72a28a3bSJan Voung transferSmartPointerLikeCachedDeref( 1046*72a28a3bSJan Voung E, 1047*72a28a3bSJan Voung dyn_cast_or_null<RecordStorageLocation>( 1048*72a28a3bSJan Voung getLocBehindPossiblePointer(*E->getArg(0), State.Env)), 1049*72a28a3bSJan Voung State, [](StorageLocation &Loc) {}); 1050*72a28a3bSJan Voung }) 1051*72a28a3bSJan Voung .CaseOfCFGStmt<CXXOperatorCallExpr>( 1052*72a28a3bSJan Voung isSmartPointerLikeOperatorArrow(), 1053*72a28a3bSJan Voung [](const CXXOperatorCallExpr *E, 1054*72a28a3bSJan Voung const MatchFinder::MatchResult &Result, 1055*72a28a3bSJan Voung LatticeTransferState &State) { 1056*72a28a3bSJan Voung transferSmartPointerLikeCachedGet( 1057*72a28a3bSJan Voung E, 1058*72a28a3bSJan Voung dyn_cast_or_null<RecordStorageLocation>( 1059*72a28a3bSJan Voung getLocBehindPossiblePointer(*E->getArg(0), State.Env)), 1060*72a28a3bSJan Voung State, [](StorageLocation &Loc) {}); 1061*72a28a3bSJan Voung }) 1062*72a28a3bSJan Voung .CaseOfCFGStmt<CXXMemberCallExpr>( 1063*72a28a3bSJan Voung isSmartPointerLikeValueMethodCall(), 1064*72a28a3bSJan Voung [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result, 1065*72a28a3bSJan Voung LatticeTransferState &State) { 1066*72a28a3bSJan Voung transferSmartPointerLikeCachedDeref( 1067*72a28a3bSJan Voung E, getImplicitObjectLocation(*E, State.Env), State, 1068*72a28a3bSJan Voung [](StorageLocation &Loc) {}); 1069*72a28a3bSJan Voung }) 1070*72a28a3bSJan Voung .CaseOfCFGStmt<CXXMemberCallExpr>( 1071*72a28a3bSJan Voung isSmartPointerLikeGetMethodCall(), 1072*72a28a3bSJan Voung [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result, 1073*72a28a3bSJan Voung LatticeTransferState &State) { 1074*72a28a3bSJan Voung transferSmartPointerLikeCachedGet( 1075*72a28a3bSJan Voung E, getImplicitObjectLocation(*E, State.Env), State, 1076*72a28a3bSJan Voung [](StorageLocation &Loc) {}); 1077*72a28a3bSJan Voung }) 1078*72a28a3bSJan Voung 10796761b24aSJan Voung // const accessor calls 10806761b24aSJan Voung .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(), 10816761b24aSJan Voung transferValue_ConstMemberCall) 108266bbbf2eSJan Voung .CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(), 108366bbbf2eSJan Voung transferValue_ConstMemberOperatorCall) 10846761b24aSJan Voung // non-const member calls that may modify the state of an object. 10856761b24aSJan Voung .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(), 10866761b24aSJan Voung transferValue_NonConstMemberCall) 10876761b24aSJan Voung .CaseOfCFGStmt<CXXOperatorCallExpr>( 10886761b24aSJan Voung isNonConstMemberOperatorCall(), 10896761b24aSJan Voung transferValue_NonConstMemberOperatorCall) 10906761b24aSJan Voung 10916761b24aSJan Voung // other cases of returning optional 10927538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 109365e710c3SStanislav Gatev transferCallReturningOptional) 109465e710c3SStanislav Gatev 1095af98b0afSStanislav Gatev .Build(); 1096af98b0afSStanislav Gatev } 1097af98b0afSStanislav Gatev 1098004a7ceaSYitzhak Mandelbaum llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr, 109958fe7f96SSam Estep const Environment &Env) { 110071f2ec2dSmartinboehme if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 110171f2ec2dSmartinboehme getLocBehindPossiblePointer(*ObjectExpr, Env))) { 110271f2ec2dSmartinboehme auto *Prop = Env.getValue(locForHasValue(*OptionalLoc)); 110358fe7f96SSam Estep if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 1104526c9b7eSmartinboehme if (Env.proves(HasValueVal->formula())) 110558fe7f96SSam Estep return {}; 110658fe7f96SSam Estep } 110758fe7f96SSam Estep } 110858fe7f96SSam Estep 110958fe7f96SSam Estep // Record that this unwrap is *not* provably safe. 111058fe7f96SSam Estep // FIXME: include either the name of the optional (if applicable) or a source 111158fe7f96SSam Estep // range of the access for easier interpretation of the result. 111258fe7f96SSam Estep return {ObjectExpr->getBeginLoc()}; 111358fe7f96SSam Estep } 111458fe7f96SSam Estep 111558fe7f96SSam Estep auto buildDiagnoseMatchSwitch( 111658fe7f96SSam Estep const UncheckedOptionalAccessModelOptions &Options) { 111758fe7f96SSam Estep // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 111858fe7f96SSam Estep // lot of duplicated work (e.g. string comparisons), consider providing APIs 111958fe7f96SSam Estep // that avoid it through memoization. 112058fe7f96SSam Estep auto IgnorableOptional = ignorableOptional(Options); 1121004a7ceaSYitzhak Mandelbaum return CFGMatchSwitchBuilder<const Environment, 1122004a7ceaSYitzhak Mandelbaum llvm::SmallVector<SourceLocation>>() 112358fe7f96SSam Estep // optional::value 11247538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 112558fe7f96SSam Estep valueCall(IgnorableOptional), 112658fe7f96SSam Estep [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 112758fe7f96SSam Estep const Environment &Env) { 11286a81e694SMartin Braenne return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env); 112958fe7f96SSam Estep }) 113058fe7f96SSam Estep 113158fe7f96SSam Estep // optional::operator*, optional::operator-> 11326a81e694SMartin Braenne .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 11336a81e694SMartin Braenne [](const CallExpr *E, 11346a81e694SMartin Braenne const MatchFinder::MatchResult &, 113558fe7f96SSam Estep const Environment &Env) { 11366a81e694SMartin Braenne return diagnoseUnwrapCall(E->getArg(0), Env); 113758fe7f96SSam Estep }) 113858fe7f96SSam Estep .Build(); 113958fe7f96SSam Estep } 114058fe7f96SSam Estep 1141af98b0afSStanislav Gatev } // namespace 1142af98b0afSStanislav Gatev 11437e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher 11447e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() { 1145d712c5edSmartinboehme return cxxRecordDecl(optionalClass()); 114671f2ec2dSmartinboehme } 114771f2ec2dSmartinboehme 114871f2ec2dSmartinboehme UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, 114971f2ec2dSmartinboehme Environment &Env) 11506761b24aSJan Voung : DataflowAnalysis<UncheckedOptionalAccessModel, 11516761b24aSJan Voung UncheckedOptionalAccessLattice>(Ctx), 115271f2ec2dSmartinboehme TransferMatchSwitch(buildTransferMatchSwitch()) { 115371f2ec2dSmartinboehme Env.getDataflowAnalysisContext().setSyntheticFieldCallback( 115471f2ec2dSmartinboehme [&Ctx](QualType Ty) -> llvm::StringMap<QualType> { 1155d712c5edSmartinboehme const CXXRecordDecl *Optional = 1156d712c5edSmartinboehme getOptionalBaseClass(Ty->getAsCXXRecordDecl()); 1157d712c5edSmartinboehme if (Optional == nullptr) 115871f2ec2dSmartinboehme return {}; 1159d712c5edSmartinboehme return {{"value", valueTypeFromOptionalDecl(*Optional)}, 116071f2ec2dSmartinboehme {"has_value", Ctx.BoolTy}}; 116171f2ec2dSmartinboehme }); 116271f2ec2dSmartinboehme } 1163af98b0afSStanislav Gatev 11646b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 11656761b24aSJan Voung UncheckedOptionalAccessLattice &L, 11666761b24aSJan Voung Environment &Env) { 1167af98b0afSStanislav Gatev LatticeTransferState State(L, Env); 11686b991ba4SYitzhak Mandelbaum TransferMatchSwitch(Elt, getASTContext(), State); 1169af98b0afSStanislav Gatev } 1170af98b0afSStanislav Gatev 117158fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 117258fe7f96SSam Estep UncheckedOptionalAccessModelOptions Options) 117358fe7f96SSam Estep : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 117458fe7f96SSam Estep 1175af98b0afSStanislav Gatev } // namespace dataflow 1176af98b0afSStanislav Gatev } // namespace clang 1177