1b000b770SStanislav Gatev //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===// 2b000b770SStanislav Gatev // 3b000b770SStanislav Gatev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4b000b770SStanislav Gatev // See https://llvm.org/LICENSE.txt for license information. 5b000b770SStanislav Gatev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6b000b770SStanislav Gatev // 7b000b770SStanislav Gatev //===----------------------------------------------------------------------===// 8b000b770SStanislav Gatev // 9b000b770SStanislav Gatev // This file defines a dataflow analysis that detects unsafe uses of optional 10b000b770SStanislav Gatev // values. 11b000b770SStanislav Gatev // 12b000b770SStanislav Gatev //===----------------------------------------------------------------------===// 13b000b770SStanislav Gatev 14af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h" 15af98b0afSStanislav Gatev #include "clang/AST/ASTContext.h" 167e63a0d4SYitzhak Mandelbaum #include "clang/AST/DeclCXX.h" 17af98b0afSStanislav Gatev #include "clang/AST/Expr.h" 18af98b0afSStanislav Gatev #include "clang/AST/ExprCXX.h" 19af98b0afSStanislav Gatev #include "clang/AST/Stmt.h" 20af98b0afSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h" 21af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 22af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/MatchSwitch.h" 23af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h" 24af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h" 25af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h" 26af98b0afSStanislav Gatev #include "llvm/Support/Casting.h" 27af98b0afSStanislav Gatev #include <cassert> 289e0fc676SStanislav Gatev #include <memory> 299e0fc676SStanislav Gatev #include <utility> 30af98b0afSStanislav Gatev 31af98b0afSStanislav Gatev namespace clang { 32af98b0afSStanislav Gatev namespace dataflow { 33af98b0afSStanislav Gatev namespace { 34af98b0afSStanislav Gatev 35af98b0afSStanislav Gatev using namespace ::clang::ast_matchers; 36af98b0afSStanislav Gatev using LatticeTransferState = TransferState<SourceLocationsLattice>; 37af98b0afSStanislav Gatev 387e63a0d4SYitzhak Mandelbaum DeclarationMatcher optionalClass() { 39af98b0afSStanislav Gatev return classTemplateSpecializationDecl( 40af98b0afSStanislav Gatev anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"), 41af98b0afSStanislav Gatev hasName("__optional_destruct_base"), hasName("absl::optional"), 42af98b0afSStanislav Gatev hasName("base::Optional")), 43af98b0afSStanislav Gatev hasTemplateArgument(0, refersToType(type().bind("T")))); 44af98b0afSStanislav Gatev } 45af98b0afSStanislav Gatev 466adfc64eSYitzhak Mandelbaum auto optionalOrAliasType() { 4765e710c3SStanislav Gatev return hasUnqualifiedDesugaredType( 4865e710c3SStanislav Gatev recordType(hasDeclaration(optionalClass()))); 4965e710c3SStanislav Gatev } 5065e710c3SStanislav Gatev 516adfc64eSYitzhak Mandelbaum /// Matches any of the spellings of the optional types and sugar, aliases, etc. 526adfc64eSYitzhak Mandelbaum auto hasOptionalType() { return hasType(optionalOrAliasType()); } 536adfc64eSYitzhak Mandelbaum 54a184a0d8SYitzhak Mandelbaum auto isOptionalMemberCallWithName( 55a184a0d8SYitzhak Mandelbaum llvm::StringRef MemberName, 56a184a0d8SYitzhak Mandelbaum llvm::Optional<StatementMatcher> Ignorable = llvm::None) { 57a184a0d8SYitzhak Mandelbaum auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr())) 58a184a0d8SYitzhak Mandelbaum : cxxThisExpr()); 59af98b0afSStanislav Gatev return cxxMemberCallExpr( 60a184a0d8SYitzhak Mandelbaum on(expr(Exception)), 61af98b0afSStanislav Gatev callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass())))); 62af98b0afSStanislav Gatev } 63af98b0afSStanislav Gatev 64a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName( 65a184a0d8SYitzhak Mandelbaum llvm::StringRef operator_name, 66a184a0d8SYitzhak Mandelbaum llvm::Optional<StatementMatcher> Ignorable = llvm::None) { 67a184a0d8SYitzhak Mandelbaum return cxxOperatorCallExpr( 68a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName(operator_name), 69a184a0d8SYitzhak Mandelbaum callee(cxxMethodDecl(ofClass(optionalClass()))), 70a184a0d8SYitzhak Mandelbaum Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 71af98b0afSStanislav Gatev } 72af98b0afSStanislav Gatev 73092a530cSStanislav Gatev auto isMakeOptionalCall() { 749e0fc676SStanislav Gatev return callExpr( 759e0fc676SStanislav Gatev callee(functionDecl(hasAnyName( 769e0fc676SStanislav Gatev "std::make_optional", "base::make_optional", "absl::make_optional"))), 779e0fc676SStanislav Gatev hasOptionalType()); 789e0fc676SStanislav Gatev } 799e0fc676SStanislav Gatev 80092a530cSStanislav Gatev auto hasNulloptType() { 81092a530cSStanislav Gatev return hasType(namedDecl( 82092a530cSStanislav Gatev hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t"))); 83092a530cSStanislav Gatev } 84092a530cSStanislav Gatev 85092a530cSStanislav Gatev auto inPlaceClass() { 86092a530cSStanislav Gatev return recordDecl( 87092a530cSStanislav Gatev hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t")); 88092a530cSStanislav Gatev } 89092a530cSStanislav Gatev 90092a530cSStanislav Gatev auto isOptionalNulloptConstructor() { 91092a530cSStanislav Gatev return cxxConstructExpr(hasOptionalType(), argumentCountIs(1), 92092a530cSStanislav Gatev hasArgument(0, hasNulloptType())); 93092a530cSStanislav Gatev } 94092a530cSStanislav Gatev 95092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() { 96092a530cSStanislav Gatev return cxxConstructExpr(hasOptionalType(), 97092a530cSStanislav Gatev hasArgument(0, hasType(inPlaceClass()))); 98092a530cSStanislav Gatev } 99092a530cSStanislav Gatev 100092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() { 101092a530cSStanislav Gatev return cxxConstructExpr( 102092a530cSStanislav Gatev hasOptionalType(), 103092a530cSStanislav Gatev unless(hasDeclaration( 104092a530cSStanislav Gatev cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 105092a530cSStanislav Gatev argumentCountIs(1), hasArgument(0, unless(hasNulloptType()))); 106092a530cSStanislav Gatev } 107092a530cSStanislav Gatev 108b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() { 109b000b770SStanislav Gatev return cxxOperatorCallExpr( 110b000b770SStanislav Gatev hasOverloadedOperatorName("="), 111b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 112b000b770SStanislav Gatev unless(hasDeclaration(cxxMethodDecl( 113b000b770SStanislav Gatev anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 114b000b770SStanislav Gatev argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 115b000b770SStanislav Gatev } 116b000b770SStanislav Gatev 117b000b770SStanislav Gatev auto isOptionalNulloptAssignment() { 118b000b770SStanislav Gatev return cxxOperatorCallExpr(hasOverloadedOperatorName("="), 119b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 120b000b770SStanislav Gatev argumentCountIs(2), 121b000b770SStanislav Gatev hasArgument(1, hasNulloptType())); 122b000b770SStanislav Gatev } 123b000b770SStanislav Gatev 1242ddd57aeSStanislav Gatev auto isStdSwapCall() { 1252ddd57aeSStanislav Gatev return callExpr(callee(functionDecl(hasName("std::swap"))), 1262ddd57aeSStanislav Gatev argumentCountIs(2), hasArgument(0, hasOptionalType()), 1272ddd57aeSStanislav Gatev hasArgument(1, hasOptionalType())); 1282ddd57aeSStanislav Gatev } 1292ddd57aeSStanislav Gatev 1307f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 1317f076004SYitzhak Mandelbaum 1327f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() { 1337f076004SYitzhak Mandelbaum // `opt.value_or("").empty()` 1347f076004SYitzhak Mandelbaum return cxxMemberCallExpr( 1357f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("empty"))), 1367f076004SYitzhak Mandelbaum onImplicitObjectArgument(ignoringImplicit( 1377f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 1387f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 1397f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 1407f076004SYitzhak Mandelbaum hasArgument(0, stringLiteral(hasSize(0)))) 1417f076004SYitzhak Mandelbaum .bind(ValueOrCallID)))); 1427f076004SYitzhak Mandelbaum } 1437f076004SYitzhak Mandelbaum 1447f076004SYitzhak Mandelbaum auto isValueOrNotEqX() { 1457f076004SYitzhak Mandelbaum auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 1467f076004SYitzhak Mandelbaum return hasOperands( 1477f076004SYitzhak Mandelbaum ignoringImplicit( 1487f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 1497f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 1507f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 1517f076004SYitzhak Mandelbaum hasArgument(0, Arg)) 1527f076004SYitzhak Mandelbaum .bind(ValueOrCallID)), 1537f076004SYitzhak Mandelbaum ignoringImplicit(Arg)); 1547f076004SYitzhak Mandelbaum }; 1557f076004SYitzhak Mandelbaum 1567f076004SYitzhak Mandelbaum // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 1577f076004SYitzhak Mandelbaum // support this pattern for any expression, but the AST does not have a 1587f076004SYitzhak Mandelbaum // generic expression comparison facility, so we specialize to common cases 1597f076004SYitzhak Mandelbaum // seen in practice. FIXME: define a matcher that compares values across 1607f076004SYitzhak Mandelbaum // nodes, which would let us generalize this to any `X`. 1617f076004SYitzhak Mandelbaum return binaryOperation(hasOperatorName("!="), 1627f076004SYitzhak Mandelbaum anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 1637f076004SYitzhak Mandelbaum ComparesToSame(stringLiteral(hasSize(0))), 1647f076004SYitzhak Mandelbaum ComparesToSame(integerLiteral(equals(0))))); 1657f076004SYitzhak Mandelbaum } 1667f076004SYitzhak Mandelbaum 16765e710c3SStanislav Gatev auto isCallReturningOptional() { 1686adfc64eSYitzhak Mandelbaum return callExpr(callee(functionDecl(returns(anyOf( 1696adfc64eSYitzhak Mandelbaum optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))))); 17065e710c3SStanislav Gatev } 17165e710c3SStanislav Gatev 1729e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the 1739e0fc676SStanislav Gatev /// symbolic value of its "has_value" property. 1749e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) { 1759e0fc676SStanislav Gatev auto OptionalVal = std::make_unique<StructValue>(); 1769e0fc676SStanislav Gatev OptionalVal->setProperty("has_value", HasValueVal); 1779e0fc676SStanislav Gatev return Env.takeOwnership(std::move(OptionalVal)); 1789e0fc676SStanislav Gatev } 1799e0fc676SStanislav Gatev 180af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the 181*49ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null. 182*49ed5bf5SWei Yi Tee BoolValue *getHasValue(Value *OptionalVal) { 183*49ed5bf5SWei Yi Tee if (OptionalVal) { 184af98b0afSStanislav Gatev return cast<BoolValue>(OptionalVal->getProperty("has_value")); 185af98b0afSStanislav Gatev } 186af98b0afSStanislav Gatev return nullptr; 187af98b0afSStanislav Gatev } 188af98b0afSStanislav Gatev 189092a530cSStanislav Gatev /// If `Type` is a reference type, returns the type of its pointee. Otherwise, 190092a530cSStanislav Gatev /// returns `Type` itself. 191092a530cSStanislav Gatev QualType stripReference(QualType Type) { 192092a530cSStanislav Gatev return Type->isReferenceType() ? Type->getPointeeType() : Type; 193092a530cSStanislav Gatev } 194092a530cSStanislav Gatev 195092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type. 196092a530cSStanislav Gatev bool IsOptionalType(QualType Type) { 197092a530cSStanislav Gatev if (!Type->isRecordType()) 198092a530cSStanislav Gatev return false; 199092a530cSStanislav Gatev // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call. 200092a530cSStanislav Gatev auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString(); 201092a530cSStanislav Gatev return TypeName == "std::optional" || TypeName == "absl::optional" || 202092a530cSStanislav Gatev TypeName == "base::Optional"; 203092a530cSStanislav Gatev } 204092a530cSStanislav Gatev 205092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`. 206092a530cSStanislav Gatev /// 207092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this 208092a530cSStanislav Gatev /// function will be 2. 209092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 210092a530cSStanislav Gatev if (!IsOptionalType(Type)) 211092a530cSStanislav Gatev return 0; 212092a530cSStanislav Gatev return 1 + countOptionalWrappers( 213092a530cSStanislav Gatev ASTCtx, 214092a530cSStanislav Gatev cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) 215092a530cSStanislav Gatev ->getTemplateArgs() 216092a530cSStanislav Gatev .get(0) 217092a530cSStanislav Gatev .getAsType() 218092a530cSStanislav Gatev .getDesugaredType(ASTCtx)); 219092a530cSStanislav Gatev } 220092a530cSStanislav Gatev 221092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr, 222092a530cSStanislav Gatev const MatchFinder::MatchResult &, 223af98b0afSStanislav Gatev LatticeTransferState &State) { 224*49ed5bf5SWei Yi Tee if (auto *OptionalVal = 225*49ed5bf5SWei Yi Tee State.Env.getValue(*OptionalExpr, SkipPast::Reference)) { 226af98b0afSStanislav Gatev if (OptionalVal->getProperty("has_value") == nullptr) { 227af98b0afSStanislav Gatev OptionalVal->setProperty("has_value", State.Env.makeAtomicBoolValue()); 228af98b0afSStanislav Gatev } 229af98b0afSStanislav Gatev } 230af98b0afSStanislav Gatev } 231af98b0afSStanislav Gatev 232092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 233af98b0afSStanislav Gatev LatticeTransferState &State) { 234*49ed5bf5SWei Yi Tee if (auto *OptionalVal = 235*49ed5bf5SWei Yi Tee State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 236af98b0afSStanislav Gatev auto *HasValueVal = getHasValue(OptionalVal); 237af98b0afSStanislav Gatev assert(HasValueVal != nullptr); 238af98b0afSStanislav Gatev 239af98b0afSStanislav Gatev if (State.Env.flowConditionImplies(*HasValueVal)) 240af98b0afSStanislav Gatev return; 241af98b0afSStanislav Gatev } 242af98b0afSStanislav Gatev 243af98b0afSStanislav Gatev // Record that this unwrap is *not* provably safe. 2447e63a0d4SYitzhak Mandelbaum // FIXME: include either the name of the optional (if applicable) or a source 2457e63a0d4SYitzhak Mandelbaum // range of the access for easier interpretation of the result. 246af98b0afSStanislav Gatev State.Lattice.getSourceLocations().insert(ObjectExpr->getBeginLoc()); 247af98b0afSStanislav Gatev } 248af98b0afSStanislav Gatev 249092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E, 250092a530cSStanislav Gatev const MatchFinder::MatchResult &, 251092a530cSStanislav Gatev LatticeTransferState &State) { 2529e0fc676SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E); 2539e0fc676SStanislav Gatev State.Env.setStorageLocation(*E, Loc); 2549e0fc676SStanislav Gatev State.Env.setValue( 2559e0fc676SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true))); 2569e0fc676SStanislav Gatev } 2579e0fc676SStanislav Gatev 258092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 259092a530cSStanislav Gatev const MatchFinder::MatchResult &, 260af98b0afSStanislav Gatev LatticeTransferState &State) { 261af98b0afSStanislav Gatev if (auto *OptionalVal = cast_or_null<StructValue>( 262af98b0afSStanislav Gatev State.Env.getValue(*CallExpr->getImplicitObjectArgument(), 263af98b0afSStanislav Gatev SkipPast::ReferenceThenPointer))) { 264af98b0afSStanislav Gatev auto *HasValueVal = getHasValue(OptionalVal); 265af98b0afSStanislav Gatev assert(HasValueVal != nullptr); 266af98b0afSStanislav Gatev 267af98b0afSStanislav Gatev auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr); 268af98b0afSStanislav Gatev State.Env.setValue(CallExprLoc, *HasValueVal); 269af98b0afSStanislav Gatev State.Env.setStorageLocation(*CallExpr, CallExprLoc); 270af98b0afSStanislav Gatev } 271af98b0afSStanislav Gatev } 272af98b0afSStanislav Gatev 2737f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in 2747f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property. 2757f076004SYitzhak Mandelbaum void transferValueOrImpl(const clang::Expr *ValueOrPredExpr, 2767f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 2777f076004SYitzhak Mandelbaum LatticeTransferState &State, 2787f076004SYitzhak Mandelbaum BoolValue &(*ModelPred)(Environment &Env, 2797f076004SYitzhak Mandelbaum BoolValue &ExprVal, 2807f076004SYitzhak Mandelbaum BoolValue &HasValueVal)) { 2817f076004SYitzhak Mandelbaum auto &Env = State.Env; 2827f076004SYitzhak Mandelbaum 2837f076004SYitzhak Mandelbaum const auto *ObjectArgumentExpr = 2847f076004SYitzhak Mandelbaum Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) 2857f076004SYitzhak Mandelbaum ->getImplicitObjectArgument(); 2867f076004SYitzhak Mandelbaum 2877f076004SYitzhak Mandelbaum auto *OptionalVal = cast_or_null<StructValue>( 2887f076004SYitzhak Mandelbaum Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer)); 2897f076004SYitzhak Mandelbaum if (OptionalVal == nullptr) 2907f076004SYitzhak Mandelbaum return; 2917f076004SYitzhak Mandelbaum auto *HasValueVal = getHasValue(OptionalVal); 2927f076004SYitzhak Mandelbaum assert(HasValueVal != nullptr); 2937f076004SYitzhak Mandelbaum 2947f076004SYitzhak Mandelbaum auto *ExprValue = cast_or_null<BoolValue>( 2957f076004SYitzhak Mandelbaum State.Env.getValue(*ValueOrPredExpr, SkipPast::None)); 2967f076004SYitzhak Mandelbaum if (ExprValue == nullptr) { 2977f076004SYitzhak Mandelbaum auto &ExprLoc = State.Env.createStorageLocation(*ValueOrPredExpr); 2987f076004SYitzhak Mandelbaum ExprValue = &State.Env.makeAtomicBoolValue(); 2997f076004SYitzhak Mandelbaum State.Env.setValue(ExprLoc, *ExprValue); 3007f076004SYitzhak Mandelbaum State.Env.setStorageLocation(*ValueOrPredExpr, ExprLoc); 3017f076004SYitzhak Mandelbaum } 3027f076004SYitzhak Mandelbaum 3037f076004SYitzhak Mandelbaum Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal)); 3047f076004SYitzhak Mandelbaum } 3057f076004SYitzhak Mandelbaum 3067f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 3077f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 3087f076004SYitzhak Mandelbaum LatticeTransferState &State) { 3097f076004SYitzhak Mandelbaum return transferValueOrImpl(ComparisonExpr, Result, State, 3107f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal, 3117f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & { 3127f076004SYitzhak Mandelbaum // If the result is *not* empty, then we know the 3137f076004SYitzhak Mandelbaum // optional must have been holding a value. If 3147f076004SYitzhak Mandelbaum // `ExprVal` is true, though, we don't learn 3157f076004SYitzhak Mandelbaum // anything definite about `has_value`, so we 3167f076004SYitzhak Mandelbaum // don't add any corresponding implications to 3177f076004SYitzhak Mandelbaum // the flow condition. 3187f076004SYitzhak Mandelbaum return Env.makeImplication(Env.makeNot(ExprVal), 3197f076004SYitzhak Mandelbaum HasValueVal); 3207f076004SYitzhak Mandelbaum }); 3217f076004SYitzhak Mandelbaum } 3227f076004SYitzhak Mandelbaum 3237f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr, 3247f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 3257f076004SYitzhak Mandelbaum LatticeTransferState &State) { 3267f076004SYitzhak Mandelbaum transferValueOrImpl(ComparisonExpr, Result, State, 3277f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal, 3287f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & { 3297f076004SYitzhak Mandelbaum // We know that if `(opt.value_or(X) != X)` then 3307f076004SYitzhak Mandelbaum // `opt.hasValue()`, even without knowing further 3317f076004SYitzhak Mandelbaum // details about the contents of `opt`. 3327f076004SYitzhak Mandelbaum return Env.makeImplication(ExprVal, HasValueVal); 3337f076004SYitzhak Mandelbaum }); 3347f076004SYitzhak Mandelbaum } 3357f076004SYitzhak Mandelbaum 33665e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E, 33765e710c3SStanislav Gatev const MatchFinder::MatchResult &Result, 33865e710c3SStanislav Gatev LatticeTransferState &State) { 33965e710c3SStanislav Gatev if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) 34065e710c3SStanislav Gatev return; 34165e710c3SStanislav Gatev 34265e710c3SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E); 34365e710c3SStanislav Gatev State.Env.setStorageLocation(*E, Loc); 34465e710c3SStanislav Gatev State.Env.setValue( 34565e710c3SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue())); 34665e710c3SStanislav Gatev } 34765e710c3SStanislav Gatev 348092a530cSStanislav Gatev void assignOptionalValue(const Expr &E, LatticeTransferState &State, 349092a530cSStanislav Gatev BoolValue &HasValueVal) { 350092a530cSStanislav Gatev if (auto *OptionalLoc = 351092a530cSStanislav Gatev State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) { 352092a530cSStanislav Gatev State.Env.setValue(*OptionalLoc, 353092a530cSStanislav Gatev createOptionalValue(State.Env, HasValueVal)); 3549e0fc676SStanislav Gatev } 3559e0fc676SStanislav Gatev } 3569e0fc676SStanislav Gatev 357b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>` 358b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>` 359b000b770SStanislav Gatev /// where `T` is constructible from `U`. 360b000b770SStanislav Gatev BoolValue & 361b000b770SStanislav Gatev getValueOrConversionHasValue(const FunctionDecl &F, const Expr &E, 362b000b770SStanislav Gatev const MatchFinder::MatchResult &MatchRes, 363b000b770SStanislav Gatev LatticeTransferState &State) { 364b000b770SStanislav Gatev assert(F.getTemplateSpecializationArgs()->size() > 0); 365b000b770SStanislav Gatev 366b000b770SStanislav Gatev const int TemplateParamOptionalWrappersCount = countOptionalWrappers( 367b000b770SStanislav Gatev *MatchRes.Context, 368b000b770SStanislav Gatev stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType())); 369b000b770SStanislav Gatev const int ArgTypeOptionalWrappersCount = 370b000b770SStanislav Gatev countOptionalWrappers(*MatchRes.Context, stripReference(E.getType())); 371b000b770SStanislav Gatev 372b000b770SStanislav Gatev // Check if this is a constructor/assignment call for `optional<T>` with 373b000b770SStanislav Gatev // argument of type `U` such that `T` is constructible from `U`. 374b000b770SStanislav Gatev if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) 375b000b770SStanislav Gatev return State.Env.getBoolLiteralValue(true); 376b000b770SStanislav Gatev 377b000b770SStanislav Gatev // This is a constructor/assignment call for `optional<T>` with argument of 378b000b770SStanislav Gatev // type `optional<U>` such that `T` is constructible from `U`. 379b000b770SStanislav Gatev if (BoolValue *Val = getHasValue(State.Env.getValue(E, SkipPast::Reference))) 380b000b770SStanislav Gatev return *Val; 381b000b770SStanislav Gatev return State.Env.makeAtomicBoolValue(); 382b000b770SStanislav Gatev } 383b000b770SStanislav Gatev 384092a530cSStanislav Gatev void transferValueOrConversionConstructor( 385092a530cSStanislav Gatev const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 3869e0fc676SStanislav Gatev LatticeTransferState &State) { 387092a530cSStanislav Gatev assert(E->getNumArgs() > 0); 388092a530cSStanislav Gatev 389b000b770SStanislav Gatev assignOptionalValue(*E, State, 390b000b770SStanislav Gatev getValueOrConversionHasValue(*E->getConstructor(), 391b000b770SStanislav Gatev *E->getArg(0), MatchRes, 392b000b770SStanislav Gatev State)); 393b000b770SStanislav Gatev } 394092a530cSStanislav Gatev 395b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 396b000b770SStanislav Gatev LatticeTransferState &State) { 397b000b770SStanislav Gatev assert(E->getNumArgs() > 0); 398b000b770SStanislav Gatev 399b000b770SStanislav Gatev auto *OptionalLoc = 400b000b770SStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 401b000b770SStanislav Gatev assert(OptionalLoc != nullptr); 402b000b770SStanislav Gatev 403b000b770SStanislav Gatev State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal)); 404b000b770SStanislav Gatev 405b000b770SStanislav Gatev // Assign a storage location for the whole expression. 406b000b770SStanislav Gatev State.Env.setStorageLocation(*E, *OptionalLoc); 407b000b770SStanislav Gatev } 408b000b770SStanislav Gatev 409b000b770SStanislav Gatev void transferValueOrConversionAssignment( 410b000b770SStanislav Gatev const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 411b000b770SStanislav Gatev LatticeTransferState &State) { 412b000b770SStanislav Gatev assert(E->getNumArgs() > 1); 413b000b770SStanislav Gatev transferAssignment(E, 414b000b770SStanislav Gatev getValueOrConversionHasValue( 415b000b770SStanislav Gatev *E->getDirectCallee(), *E->getArg(1), MatchRes, State), 416b000b770SStanislav Gatev State); 417b000b770SStanislav Gatev } 418b000b770SStanislav Gatev 419b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E, 420b000b770SStanislav Gatev const MatchFinder::MatchResult &, 421b000b770SStanislav Gatev LatticeTransferState &State) { 422b000b770SStanislav Gatev transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 4239e0fc676SStanislav Gatev } 4249e0fc676SStanislav Gatev 4252ddd57aeSStanislav Gatev void transferSwap(const StorageLocation &OptionalLoc1, 4262ddd57aeSStanislav Gatev const StorageLocation &OptionalLoc2, 4272ddd57aeSStanislav Gatev LatticeTransferState &State) { 4282ddd57aeSStanislav Gatev auto *OptionalVal1 = State.Env.getValue(OptionalLoc1); 4292ddd57aeSStanislav Gatev assert(OptionalVal1 != nullptr); 4302ddd57aeSStanislav Gatev 4312ddd57aeSStanislav Gatev auto *OptionalVal2 = State.Env.getValue(OptionalLoc2); 4322ddd57aeSStanislav Gatev assert(OptionalVal2 != nullptr); 4332ddd57aeSStanislav Gatev 4342ddd57aeSStanislav Gatev State.Env.setValue(OptionalLoc1, *OptionalVal2); 4352ddd57aeSStanislav Gatev State.Env.setValue(OptionalLoc2, *OptionalVal1); 4362ddd57aeSStanislav Gatev } 4372ddd57aeSStanislav Gatev 4382ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E, 4392ddd57aeSStanislav Gatev const MatchFinder::MatchResult &, 4402ddd57aeSStanislav Gatev LatticeTransferState &State) { 4412ddd57aeSStanislav Gatev assert(E->getNumArgs() == 1); 4422ddd57aeSStanislav Gatev 4432ddd57aeSStanislav Gatev auto *OptionalLoc1 = State.Env.getStorageLocation( 4442ddd57aeSStanislav Gatev *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer); 4452ddd57aeSStanislav Gatev assert(OptionalLoc1 != nullptr); 4462ddd57aeSStanislav Gatev 4472ddd57aeSStanislav Gatev auto *OptionalLoc2 = 4482ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 4492ddd57aeSStanislav Gatev assert(OptionalLoc2 != nullptr); 4502ddd57aeSStanislav Gatev 4512ddd57aeSStanislav Gatev transferSwap(*OptionalLoc1, *OptionalLoc2, State); 4522ddd57aeSStanislav Gatev } 4532ddd57aeSStanislav Gatev 4542ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 4552ddd57aeSStanislav Gatev LatticeTransferState &State) { 4562ddd57aeSStanislav Gatev assert(E->getNumArgs() == 2); 4572ddd57aeSStanislav Gatev 4582ddd57aeSStanislav Gatev auto *OptionalLoc1 = 4592ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 4602ddd57aeSStanislav Gatev assert(OptionalLoc1 != nullptr); 4612ddd57aeSStanislav Gatev 4622ddd57aeSStanislav Gatev auto *OptionalLoc2 = 4632ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference); 4642ddd57aeSStanislav Gatev assert(OptionalLoc2 != nullptr); 4652ddd57aeSStanislav Gatev 4662ddd57aeSStanislav Gatev transferSwap(*OptionalLoc1, *OptionalLoc2, State); 4672ddd57aeSStanislav Gatev } 4682ddd57aeSStanislav Gatev 469a184a0d8SYitzhak Mandelbaum llvm::Optional<StatementMatcher> 470a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 471a184a0d8SYitzhak Mandelbaum if (Options.IgnoreSmartPointerDereference) 472a184a0d8SYitzhak Mandelbaum return memberExpr(hasObjectExpression(ignoringParenImpCasts( 473a184a0d8SYitzhak Mandelbaum cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("->"), 474a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName("*")), 475a184a0d8SYitzhak Mandelbaum unless(hasArgument(0, expr(hasOptionalType()))))))); 476a184a0d8SYitzhak Mandelbaum return llvm::None; 477a184a0d8SYitzhak Mandelbaum } 478a184a0d8SYitzhak Mandelbaum 479a184a0d8SYitzhak Mandelbaum auto buildTransferMatchSwitch( 480a184a0d8SYitzhak Mandelbaum const UncheckedOptionalAccessModelOptions &Options) { 481b000b770SStanislav Gatev // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 482b000b770SStanislav Gatev // lot of duplicated work (e.g. string comparisons), consider providing APIs 483b000b770SStanislav Gatev // that avoid it through memoization. 484a184a0d8SYitzhak Mandelbaum auto IgnorableOptional = ignorableOptional(Options); 485af98b0afSStanislav Gatev return MatchSwitchBuilder<LatticeTransferState>() 486af98b0afSStanislav Gatev // Attach a symbolic "has_value" state to optional values that we see for 487af98b0afSStanislav Gatev // the first time. 4886adfc64eSYitzhak Mandelbaum .CaseOf<Expr>( 4896adfc64eSYitzhak Mandelbaum expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), 490af98b0afSStanislav Gatev initializeOptionalReference) 491af98b0afSStanislav Gatev 4929e0fc676SStanislav Gatev // make_optional 493092a530cSStanislav Gatev .CaseOf<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 494092a530cSStanislav Gatev 495b000b770SStanislav Gatev // optional::optional 496092a530cSStanislav Gatev .CaseOf<CXXConstructExpr>( 497092a530cSStanislav Gatev isOptionalInPlaceConstructor(), 498092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 499092a530cSStanislav Gatev LatticeTransferState &State) { 500092a530cSStanislav Gatev assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true)); 501092a530cSStanislav Gatev }) 502092a530cSStanislav Gatev .CaseOf<CXXConstructExpr>( 503092a530cSStanislav Gatev isOptionalNulloptConstructor(), 504092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 505092a530cSStanislav Gatev LatticeTransferState &State) { 506092a530cSStanislav Gatev assignOptionalValue(*E, State, 507092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false)); 508092a530cSStanislav Gatev }) 509092a530cSStanislav Gatev .CaseOf<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 510092a530cSStanislav Gatev transferValueOrConversionConstructor) 5119e0fc676SStanislav Gatev 512b000b770SStanislav Gatev // optional::operator= 513b000b770SStanislav Gatev .CaseOf<CXXOperatorCallExpr>(isOptionalValueOrConversionAssignment(), 514b000b770SStanislav Gatev transferValueOrConversionAssignment) 515b000b770SStanislav Gatev .CaseOf<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 516b000b770SStanislav Gatev transferNulloptAssignment) 517b000b770SStanislav Gatev 518af98b0afSStanislav Gatev // optional::value 519092a530cSStanislav Gatev .CaseOf<CXXMemberCallExpr>( 520a184a0d8SYitzhak Mandelbaum isOptionalMemberCallWithName("value", IgnorableOptional), 521092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 522092a530cSStanislav Gatev LatticeTransferState &State) { 523af98b0afSStanislav Gatev transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 524af98b0afSStanislav Gatev }) 525af98b0afSStanislav Gatev 526af98b0afSStanislav Gatev // optional::operator*, optional::operator-> 527a184a0d8SYitzhak Mandelbaum .CaseOf<CallExpr>( 528a184a0d8SYitzhak Mandelbaum expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 529a184a0d8SYitzhak Mandelbaum isOptionalOperatorCallWithName("->", IgnorableOptional))), 530092a530cSStanislav Gatev [](const CallExpr *E, const MatchFinder::MatchResult &, 531092a530cSStanislav Gatev LatticeTransferState &State) { 532af98b0afSStanislav Gatev transferUnwrapCall(E, E->getArg(0), State); 533af98b0afSStanislav Gatev }) 534af98b0afSStanislav Gatev 535af98b0afSStanislav Gatev // optional::has_value 536092a530cSStanislav Gatev .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("has_value"), 537af98b0afSStanislav Gatev transferOptionalHasValueCall) 538af98b0afSStanislav Gatev 5399e0fc676SStanislav Gatev // optional::operator bool 540092a530cSStanislav Gatev .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("operator bool"), 5419e0fc676SStanislav Gatev transferOptionalHasValueCall) 5429e0fc676SStanislav Gatev 5439e0fc676SStanislav Gatev // optional::emplace 544092a530cSStanislav Gatev .CaseOf<CXXMemberCallExpr>( 545092a530cSStanislav Gatev isOptionalMemberCallWithName("emplace"), 546092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 547092a530cSStanislav Gatev LatticeTransferState &State) { 548092a530cSStanislav Gatev assignOptionalValue(*E->getImplicitObjectArgument(), State, 549092a530cSStanislav Gatev State.Env.getBoolLiteralValue(true)); 550092a530cSStanislav Gatev }) 5519e0fc676SStanislav Gatev 5529e0fc676SStanislav Gatev // optional::reset 553092a530cSStanislav Gatev .CaseOf<CXXMemberCallExpr>( 554092a530cSStanislav Gatev isOptionalMemberCallWithName("reset"), 555092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 556092a530cSStanislav Gatev LatticeTransferState &State) { 557092a530cSStanislav Gatev assignOptionalValue(*E->getImplicitObjectArgument(), State, 558092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false)); 559092a530cSStanislav Gatev }) 5609e0fc676SStanislav Gatev 5612ddd57aeSStanislav Gatev // optional::swap 5622ddd57aeSStanislav Gatev .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"), 5632ddd57aeSStanislav Gatev transferSwapCall) 5642ddd57aeSStanislav Gatev 5652ddd57aeSStanislav Gatev // std::swap 5662ddd57aeSStanislav Gatev .CaseOf<CallExpr>(isStdSwapCall(), transferStdSwapCall) 5672ddd57aeSStanislav Gatev 5687f076004SYitzhak Mandelbaum // opt.value_or("").empty() 5697f076004SYitzhak Mandelbaum .CaseOf<Expr>(isValueOrStringEmptyCall(), transferValueOrStringEmptyCall) 5707f076004SYitzhak Mandelbaum 5717f076004SYitzhak Mandelbaum // opt.value_or(X) != X 5727f076004SYitzhak Mandelbaum .CaseOf<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 5737f076004SYitzhak Mandelbaum 57465e710c3SStanislav Gatev // returns optional 57565e710c3SStanislav Gatev .CaseOf<CallExpr>(isCallReturningOptional(), 57665e710c3SStanislav Gatev transferCallReturningOptional) 57765e710c3SStanislav Gatev 578af98b0afSStanislav Gatev .Build(); 579af98b0afSStanislav Gatev } 580af98b0afSStanislav Gatev 581af98b0afSStanislav Gatev } // namespace 582af98b0afSStanislav Gatev 5837e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher 5847e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() { 5857e63a0d4SYitzhak Mandelbaum return optionalClass(); 5867e63a0d4SYitzhak Mandelbaum } 5877e63a0d4SYitzhak Mandelbaum 588a184a0d8SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel( 589a184a0d8SYitzhak Mandelbaum ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options) 590af98b0afSStanislav Gatev : DataflowAnalysis<UncheckedOptionalAccessModel, SourceLocationsLattice>( 591af98b0afSStanislav Gatev Ctx), 592a184a0d8SYitzhak Mandelbaum TransferMatchSwitch(buildTransferMatchSwitch(Options)) {} 593af98b0afSStanislav Gatev 594af98b0afSStanislav Gatev void UncheckedOptionalAccessModel::transfer(const Stmt *S, 595af98b0afSStanislav Gatev SourceLocationsLattice &L, 596af98b0afSStanislav Gatev Environment &Env) { 597af98b0afSStanislav Gatev LatticeTransferState State(L, Env); 598af98b0afSStanislav Gatev TransferMatchSwitch(*S, getASTContext(), State); 599af98b0afSStanislav Gatev } 600af98b0afSStanislav Gatev 601af98b0afSStanislav Gatev } // namespace dataflow 602af98b0afSStanislav Gatev } // namespace clang 603