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