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" 217538b360SWei Yi Tee #include "clang/Analysis/CFG.h" 227538b360SWei Yi Tee #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h" 23af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 24cf1f978dSSam Estep #include "clang/Analysis/FlowSensitive/NoopLattice.h" 25af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h" 2658fe7f96SSam Estep #include "clang/Basic/SourceLocation.h" 27af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h" 28af98b0afSStanislav Gatev #include "llvm/Support/Casting.h" 29af98b0afSStanislav Gatev #include <cassert> 309e0fc676SStanislav Gatev #include <memory> 319e0fc676SStanislav Gatev #include <utility> 3258fe7f96SSam Estep #include <vector> 33af98b0afSStanislav Gatev 34af98b0afSStanislav Gatev namespace clang { 35af98b0afSStanislav Gatev namespace dataflow { 36af98b0afSStanislav Gatev namespace { 37af98b0afSStanislav Gatev 38af98b0afSStanislav Gatev using namespace ::clang::ast_matchers; 39cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>; 40af98b0afSStanislav Gatev 417e63a0d4SYitzhak Mandelbaum DeclarationMatcher optionalClass() { 42af98b0afSStanislav Gatev return classTemplateSpecializationDecl( 43af98b0afSStanislav Gatev anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"), 44af98b0afSStanislav Gatev hasName("__optional_destruct_base"), hasName("absl::optional"), 45af98b0afSStanislav Gatev hasName("base::Optional")), 46af98b0afSStanislav Gatev hasTemplateArgument(0, refersToType(type().bind("T")))); 47af98b0afSStanislav Gatev } 48af98b0afSStanislav Gatev 496adfc64eSYitzhak Mandelbaum auto optionalOrAliasType() { 5065e710c3SStanislav Gatev return hasUnqualifiedDesugaredType( 5165e710c3SStanislav Gatev recordType(hasDeclaration(optionalClass()))); 5265e710c3SStanislav Gatev } 5365e710c3SStanislav Gatev 546adfc64eSYitzhak Mandelbaum /// Matches any of the spellings of the optional types and sugar, aliases, etc. 556adfc64eSYitzhak Mandelbaum auto hasOptionalType() { return hasType(optionalOrAliasType()); } 566adfc64eSYitzhak Mandelbaum 57a184a0d8SYitzhak Mandelbaum auto isOptionalMemberCallWithName( 58a184a0d8SYitzhak Mandelbaum llvm::StringRef MemberName, 5934e0d057SKazu Hirata llvm::Optional<StatementMatcher> Ignorable = std::nullopt) { 60a184a0d8SYitzhak Mandelbaum auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr())) 61a184a0d8SYitzhak Mandelbaum : cxxThisExpr()); 62af98b0afSStanislav Gatev return cxxMemberCallExpr( 63a184a0d8SYitzhak Mandelbaum on(expr(Exception)), 64af98b0afSStanislav Gatev callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass())))); 65af98b0afSStanislav Gatev } 66af98b0afSStanislav Gatev 67a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName( 68a184a0d8SYitzhak Mandelbaum llvm::StringRef operator_name, 6934e0d057SKazu Hirata llvm::Optional<StatementMatcher> Ignorable = std::nullopt) { 70a184a0d8SYitzhak Mandelbaum return cxxOperatorCallExpr( 71a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName(operator_name), 72a184a0d8SYitzhak Mandelbaum callee(cxxMethodDecl(ofClass(optionalClass()))), 73a184a0d8SYitzhak Mandelbaum Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 74af98b0afSStanislav Gatev } 75af98b0afSStanislav Gatev 76092a530cSStanislav Gatev auto isMakeOptionalCall() { 779e0fc676SStanislav Gatev return callExpr( 789e0fc676SStanislav Gatev callee(functionDecl(hasAnyName( 799e0fc676SStanislav Gatev "std::make_optional", "base::make_optional", "absl::make_optional"))), 809e0fc676SStanislav Gatev hasOptionalType()); 819e0fc676SStanislav Gatev } 829e0fc676SStanislav Gatev 83*390029beSYitzhak Mandelbaum auto nulloptTypeDecl() { 84*390029beSYitzhak Mandelbaum return namedDecl( 85*390029beSYitzhak Mandelbaum hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")); 86092a530cSStanislav Gatev } 87092a530cSStanislav Gatev 88*390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); } 89*390029beSYitzhak Mandelbaum 90*390029beSYitzhak Mandelbaum // `optional` or `nullopt_t` 91*390029beSYitzhak Mandelbaum auto hasAnyOptionalType() { 92*390029beSYitzhak Mandelbaum return hasType(hasUnqualifiedDesugaredType( 93*390029beSYitzhak Mandelbaum recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass()))))); 94*390029beSYitzhak Mandelbaum } 95*390029beSYitzhak Mandelbaum 96*390029beSYitzhak Mandelbaum 97092a530cSStanislav Gatev auto inPlaceClass() { 98092a530cSStanislav Gatev return recordDecl( 99092a530cSStanislav Gatev hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t")); 100092a530cSStanislav Gatev } 101092a530cSStanislav Gatev 102092a530cSStanislav Gatev auto isOptionalNulloptConstructor() { 103092a530cSStanislav Gatev return cxxConstructExpr(hasOptionalType(), argumentCountIs(1), 104092a530cSStanislav Gatev hasArgument(0, hasNulloptType())); 105092a530cSStanislav Gatev } 106092a530cSStanislav Gatev 107092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() { 108092a530cSStanislav Gatev return cxxConstructExpr(hasOptionalType(), 109092a530cSStanislav Gatev hasArgument(0, hasType(inPlaceClass()))); 110092a530cSStanislav Gatev } 111092a530cSStanislav Gatev 112092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() { 113092a530cSStanislav Gatev return cxxConstructExpr( 114092a530cSStanislav Gatev hasOptionalType(), 115092a530cSStanislav Gatev unless(hasDeclaration( 116092a530cSStanislav Gatev cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 117092a530cSStanislav Gatev argumentCountIs(1), hasArgument(0, unless(hasNulloptType()))); 118092a530cSStanislav Gatev } 119092a530cSStanislav Gatev 120b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() { 121b000b770SStanislav Gatev return cxxOperatorCallExpr( 122b000b770SStanislav Gatev hasOverloadedOperatorName("="), 123b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 124b000b770SStanislav Gatev unless(hasDeclaration(cxxMethodDecl( 125b000b770SStanislav Gatev anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 126b000b770SStanislav Gatev argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 127b000b770SStanislav Gatev } 128b000b770SStanislav Gatev 129*390029beSYitzhak Mandelbaum auto isNulloptConstructor() { 130*390029beSYitzhak Mandelbaum return cxxConstructExpr(hasNulloptType(), argumentCountIs(1), 131*390029beSYitzhak Mandelbaum hasArgument(0, hasNulloptType())); 132*390029beSYitzhak Mandelbaum } 133*390029beSYitzhak Mandelbaum 134b000b770SStanislav Gatev auto isOptionalNulloptAssignment() { 135b000b770SStanislav Gatev return cxxOperatorCallExpr(hasOverloadedOperatorName("="), 136b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 137b000b770SStanislav Gatev argumentCountIs(2), 138b000b770SStanislav Gatev hasArgument(1, hasNulloptType())); 139b000b770SStanislav Gatev } 140b000b770SStanislav Gatev 1412ddd57aeSStanislav Gatev auto isStdSwapCall() { 1422ddd57aeSStanislav Gatev return callExpr(callee(functionDecl(hasName("std::swap"))), 1432ddd57aeSStanislav Gatev argumentCountIs(2), hasArgument(0, hasOptionalType()), 1442ddd57aeSStanislav Gatev hasArgument(1, hasOptionalType())); 1452ddd57aeSStanislav Gatev } 1462ddd57aeSStanislav Gatev 1477f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 1487f076004SYitzhak Mandelbaum 1497f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() { 1507f076004SYitzhak Mandelbaum // `opt.value_or("").empty()` 1517f076004SYitzhak Mandelbaum return cxxMemberCallExpr( 1527f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("empty"))), 1537f076004SYitzhak Mandelbaum onImplicitObjectArgument(ignoringImplicit( 1547f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 1557f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 1567f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 1577f076004SYitzhak Mandelbaum hasArgument(0, stringLiteral(hasSize(0)))) 1587f076004SYitzhak Mandelbaum .bind(ValueOrCallID)))); 1597f076004SYitzhak Mandelbaum } 1607f076004SYitzhak Mandelbaum 1617f076004SYitzhak Mandelbaum auto isValueOrNotEqX() { 1627f076004SYitzhak Mandelbaum auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 1637f076004SYitzhak Mandelbaum return hasOperands( 1647f076004SYitzhak Mandelbaum ignoringImplicit( 1657f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 1667f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 1677f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 1687f076004SYitzhak Mandelbaum hasArgument(0, Arg)) 1697f076004SYitzhak Mandelbaum .bind(ValueOrCallID)), 1707f076004SYitzhak Mandelbaum ignoringImplicit(Arg)); 1717f076004SYitzhak Mandelbaum }; 1727f076004SYitzhak Mandelbaum 1737f076004SYitzhak Mandelbaum // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 1747f076004SYitzhak Mandelbaum // support this pattern for any expression, but the AST does not have a 1757f076004SYitzhak Mandelbaum // generic expression comparison facility, so we specialize to common cases 1767f076004SYitzhak Mandelbaum // seen in practice. FIXME: define a matcher that compares values across 1777f076004SYitzhak Mandelbaum // nodes, which would let us generalize this to any `X`. 1787f076004SYitzhak Mandelbaum return binaryOperation(hasOperatorName("!="), 1797f076004SYitzhak Mandelbaum anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 1807f076004SYitzhak Mandelbaum ComparesToSame(stringLiteral(hasSize(0))), 1817f076004SYitzhak Mandelbaum ComparesToSame(integerLiteral(equals(0))))); 1827f076004SYitzhak Mandelbaum } 1837f076004SYitzhak Mandelbaum 18465e710c3SStanislav Gatev auto isCallReturningOptional() { 185cd0d5261SSam Estep return callExpr(hasType(qualType(anyOf( 186cd0d5261SSam Estep optionalOrAliasType(), referenceType(pointee(optionalOrAliasType())))))); 18765e710c3SStanislav Gatev } 18865e710c3SStanislav Gatev 189*390029beSYitzhak Mandelbaum template <typename L, typename R> 190*390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 191*390029beSYitzhak Mandelbaum return cxxOperatorCallExpr( 192*390029beSYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 193*390029beSYitzhak Mandelbaum argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 194*390029beSYitzhak Mandelbaum hasArgument(1, rhs_arg_matcher)); 195*390029beSYitzhak Mandelbaum } 196*390029beSYitzhak Mandelbaum 197*390029beSYitzhak Mandelbaum // Ensures that `Expr` is mapped to a `BoolValue` and returns it. 198*390029beSYitzhak Mandelbaum BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) { 199*390029beSYitzhak Mandelbaum auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None)); 200*390029beSYitzhak Mandelbaum if (Value != nullptr) 201*390029beSYitzhak Mandelbaum return *Value; 202*390029beSYitzhak Mandelbaum 203*390029beSYitzhak Mandelbaum auto &Loc = Env.createStorageLocation(Expr); 204*390029beSYitzhak Mandelbaum Value = &Env.makeAtomicBoolValue(); 205*390029beSYitzhak Mandelbaum Env.setValue(Loc, *Value); 206*390029beSYitzhak Mandelbaum Env.setStorageLocation(Expr, Loc); 207*390029beSYitzhak Mandelbaum return *Value; 208*390029beSYitzhak Mandelbaum } 209*390029beSYitzhak Mandelbaum 2108fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 2118fcdd625SStanislav Gatev /// property of the optional value `OptionalVal`. 2128fcdd625SStanislav Gatev void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) { 2138fcdd625SStanislav Gatev OptionalVal.setProperty("has_value", HasValueVal); 2148fcdd625SStanislav Gatev } 2158fcdd625SStanislav Gatev 2169e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the 2179e0fc676SStanislav Gatev /// symbolic value of its "has_value" property. 2189e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) { 2199e0fc676SStanislav Gatev auto OptionalVal = std::make_unique<StructValue>(); 2208fcdd625SStanislav Gatev setHasValue(*OptionalVal, HasValueVal); 2219e0fc676SStanislav Gatev return Env.takeOwnership(std::move(OptionalVal)); 2229e0fc676SStanislav Gatev } 2239e0fc676SStanislav Gatev 224af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the 22549ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null. 226dd38caf3SYitzhak Mandelbaum BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { 227dd38caf3SYitzhak Mandelbaum if (OptionalVal != nullptr) { 228dd38caf3SYitzhak Mandelbaum auto *HasValueVal = 229dd38caf3SYitzhak Mandelbaum cast_or_null<BoolValue>(OptionalVal->getProperty("has_value")); 230dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) { 231dd38caf3SYitzhak Mandelbaum HasValueVal = &Env.makeAtomicBoolValue(); 232dd38caf3SYitzhak Mandelbaum OptionalVal->setProperty("has_value", *HasValueVal); 233dd38caf3SYitzhak Mandelbaum } 234dd38caf3SYitzhak Mandelbaum return HasValueVal; 235af98b0afSStanislav Gatev } 236af98b0afSStanislav Gatev return nullptr; 237af98b0afSStanislav Gatev } 238af98b0afSStanislav Gatev 239092a530cSStanislav Gatev /// If `Type` is a reference type, returns the type of its pointee. Otherwise, 240092a530cSStanislav Gatev /// returns `Type` itself. 241092a530cSStanislav Gatev QualType stripReference(QualType Type) { 242092a530cSStanislav Gatev return Type->isReferenceType() ? Type->getPointeeType() : Type; 243092a530cSStanislav Gatev } 244092a530cSStanislav Gatev 245092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type. 246c0725865SYitzhak Mandelbaum bool isOptionalType(QualType Type) { 247092a530cSStanislav Gatev if (!Type->isRecordType()) 248092a530cSStanislav Gatev return false; 249092a530cSStanislav Gatev // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call. 250092a530cSStanislav Gatev auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString(); 251092a530cSStanislav Gatev return TypeName == "std::optional" || TypeName == "absl::optional" || 252092a530cSStanislav Gatev TypeName == "base::Optional"; 253092a530cSStanislav Gatev } 254092a530cSStanislav Gatev 255092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`. 256092a530cSStanislav Gatev /// 257092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this 258092a530cSStanislav Gatev /// function will be 2. 259092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 260c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 261092a530cSStanislav Gatev return 0; 262092a530cSStanislav Gatev return 1 + countOptionalWrappers( 263092a530cSStanislav Gatev ASTCtx, 264092a530cSStanislav Gatev cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) 265092a530cSStanislav Gatev ->getTemplateArgs() 266092a530cSStanislav Gatev .get(0) 267092a530cSStanislav Gatev .getAsType() 268092a530cSStanislav Gatev .getDesugaredType(ASTCtx)); 269092a530cSStanislav Gatev } 270092a530cSStanislav Gatev 271dd38caf3SYitzhak Mandelbaum /// Tries to initialize the `optional`'s value (that is, contents), and return 272dd38caf3SYitzhak Mandelbaum /// its location. Returns nullptr if the value can't be represented. 273dd38caf3SYitzhak Mandelbaum StorageLocation *maybeInitializeOptionalValueMember(QualType Q, 274dd38caf3SYitzhak Mandelbaum Value &OptionalVal, 275dd38caf3SYitzhak Mandelbaum Environment &Env) { 276dd38caf3SYitzhak Mandelbaum // The "value" property represents a synthetic field. As such, it needs 277dd38caf3SYitzhak Mandelbaum // `StorageLocation`, like normal fields (and other variables). So, we model 278dd38caf3SYitzhak Mandelbaum // it with a `ReferenceValue`, since that includes a storage location. Once 279dd38caf3SYitzhak Mandelbaum // the property is set, it will be shared by all environments that access the 280dd38caf3SYitzhak Mandelbaum // `Value` representing the optional (here, `OptionalVal`). 281dd38caf3SYitzhak Mandelbaum if (auto *ValueProp = OptionalVal.getProperty("value")) { 282dd38caf3SYitzhak Mandelbaum auto *ValueRef = clang::cast<ReferenceValue>(ValueProp); 28397d69cdaSWei Yi Tee auto &ValueLoc = ValueRef->getReferentLoc(); 284dd38caf3SYitzhak Mandelbaum if (Env.getValue(ValueLoc) == nullptr) { 285dd38caf3SYitzhak Mandelbaum // The property was previously set, but the value has been lost. This can 286dd38caf3SYitzhak Mandelbaum // happen, for example, because of an environment merge (where the two 287dd38caf3SYitzhak Mandelbaum // environments mapped the property to different values, which resulted in 288dd38caf3SYitzhak Mandelbaum // them both being discarded), or when two blocks in the CFG, with neither 289dd38caf3SYitzhak Mandelbaum // a dominator of the other, visit the same optional value, or even when a 290dd38caf3SYitzhak Mandelbaum // block is revisited during testing to collect per-statement state. 291dd38caf3SYitzhak Mandelbaum // FIXME: This situation means that the optional contents are not shared 292dd38caf3SYitzhak Mandelbaum // between branches and the like. Practically, this lack of sharing 293dd38caf3SYitzhak Mandelbaum // reduces the precision of the model when the contents are relevant to 294dd38caf3SYitzhak Mandelbaum // the check, like another optional or a boolean that influences control 295dd38caf3SYitzhak Mandelbaum // flow. 296dd38caf3SYitzhak Mandelbaum auto *ValueVal = Env.createValue(ValueLoc.getType()); 297dd38caf3SYitzhak Mandelbaum if (ValueVal == nullptr) 298dd38caf3SYitzhak Mandelbaum return nullptr; 299dd38caf3SYitzhak Mandelbaum Env.setValue(ValueLoc, *ValueVal); 300dd38caf3SYitzhak Mandelbaum } 301dd38caf3SYitzhak Mandelbaum return &ValueLoc; 302dd38caf3SYitzhak Mandelbaum } 303dd38caf3SYitzhak Mandelbaum 304dd38caf3SYitzhak Mandelbaum auto Ty = stripReference(Q); 305dd38caf3SYitzhak Mandelbaum auto *ValueVal = Env.createValue(Ty); 306dd38caf3SYitzhak Mandelbaum if (ValueVal == nullptr) 307dd38caf3SYitzhak Mandelbaum return nullptr; 308dd38caf3SYitzhak Mandelbaum auto &ValueLoc = Env.createStorageLocation(Ty); 309dd38caf3SYitzhak Mandelbaum Env.setValue(ValueLoc, *ValueVal); 310dd38caf3SYitzhak Mandelbaum auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc); 311dd38caf3SYitzhak Mandelbaum OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef))); 312dd38caf3SYitzhak Mandelbaum return &ValueLoc; 313dd38caf3SYitzhak Mandelbaum } 314dd38caf3SYitzhak Mandelbaum 315092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr, 316092a530cSStanislav Gatev const MatchFinder::MatchResult &, 317af98b0afSStanislav Gatev LatticeTransferState &State) { 31849ed5bf5SWei Yi Tee if (auto *OptionalVal = 31949ed5bf5SWei Yi Tee State.Env.getValue(*OptionalExpr, SkipPast::Reference)) { 320af98b0afSStanislav Gatev if (OptionalVal->getProperty("has_value") == nullptr) { 3218fcdd625SStanislav Gatev setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); 322af98b0afSStanislav Gatev } 323af98b0afSStanislav Gatev } 324af98b0afSStanislav Gatev } 325af98b0afSStanislav Gatev 3268fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be 3278fcdd625SStanislav Gatev /// empty in `Env. 3288fcdd625SStanislav Gatev bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { 3298fcdd625SStanislav Gatev auto *HasValueVal = 3308fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 3318fcdd625SStanislav Gatev return HasValueVal != nullptr && 3328fcdd625SStanislav Gatev Env.flowConditionImplies(Env.makeNot(*HasValueVal)); 3338fcdd625SStanislav Gatev } 3348fcdd625SStanislav Gatev 3358fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be 3368fcdd625SStanislav Gatev /// non-empty in `Env. 3378fcdd625SStanislav Gatev bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { 3388fcdd625SStanislav Gatev auto *HasValueVal = 3398fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 3408fcdd625SStanislav Gatev return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal); 3418fcdd625SStanislav Gatev } 3428fcdd625SStanislav Gatev 343092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 344af98b0afSStanislav Gatev LatticeTransferState &State) { 34549ed5bf5SWei Yi Tee if (auto *OptionalVal = 34649ed5bf5SWei Yi Tee State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 347dd38caf3SYitzhak Mandelbaum if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr) 348dd38caf3SYitzhak Mandelbaum if (auto *Loc = maybeInitializeOptionalValueMember( 349dd38caf3SYitzhak Mandelbaum UnwrapExpr->getType(), *OptionalVal, State.Env)) 350dd38caf3SYitzhak Mandelbaum State.Env.setStorageLocation(*UnwrapExpr, *Loc); 351af98b0afSStanislav Gatev } 352dd38caf3SYitzhak Mandelbaum } 353af98b0afSStanislav Gatev 354092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E, 355092a530cSStanislav Gatev const MatchFinder::MatchResult &, 356092a530cSStanislav Gatev LatticeTransferState &State) { 3579e0fc676SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E); 3589e0fc676SStanislav Gatev State.Env.setStorageLocation(*E, Loc); 3599e0fc676SStanislav Gatev State.Env.setValue( 3609e0fc676SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true))); 3619e0fc676SStanislav Gatev } 3629e0fc676SStanislav Gatev 363092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 364092a530cSStanislav Gatev const MatchFinder::MatchResult &, 365af98b0afSStanislav Gatev LatticeTransferState &State) { 366dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = getHasValue( 367dd38caf3SYitzhak Mandelbaum State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(), 368af98b0afSStanislav Gatev SkipPast::ReferenceThenPointer))) { 369af98b0afSStanislav Gatev auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr); 370af98b0afSStanislav Gatev State.Env.setValue(CallExprLoc, *HasValueVal); 371af98b0afSStanislav Gatev State.Env.setStorageLocation(*CallExpr, CallExprLoc); 372af98b0afSStanislav Gatev } 373af98b0afSStanislav Gatev } 374af98b0afSStanislav Gatev 3757f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in 3767f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property. 3777f076004SYitzhak Mandelbaum void transferValueOrImpl(const clang::Expr *ValueOrPredExpr, 3787f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 3797f076004SYitzhak Mandelbaum LatticeTransferState &State, 3807f076004SYitzhak Mandelbaum BoolValue &(*ModelPred)(Environment &Env, 3817f076004SYitzhak Mandelbaum BoolValue &ExprVal, 3827f076004SYitzhak Mandelbaum BoolValue &HasValueVal)) { 3837f076004SYitzhak Mandelbaum auto &Env = State.Env; 3847f076004SYitzhak Mandelbaum 3857f076004SYitzhak Mandelbaum const auto *ObjectArgumentExpr = 3867f076004SYitzhak Mandelbaum Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) 3877f076004SYitzhak Mandelbaum ->getImplicitObjectArgument(); 3887f076004SYitzhak Mandelbaum 389dd38caf3SYitzhak Mandelbaum auto *HasValueVal = getHasValue( 390dd38caf3SYitzhak Mandelbaum State.Env, 391dd38caf3SYitzhak Mandelbaum State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer)); 392dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) 3937f076004SYitzhak Mandelbaum return; 3947f076004SYitzhak Mandelbaum 395*390029beSYitzhak Mandelbaum Env.addToFlowCondition( 396*390029beSYitzhak Mandelbaum ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal)); 3977f076004SYitzhak Mandelbaum } 3987f076004SYitzhak Mandelbaum 3997f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 4007f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 4017f076004SYitzhak Mandelbaum LatticeTransferState &State) { 4027f076004SYitzhak Mandelbaum return transferValueOrImpl(ComparisonExpr, Result, State, 4037f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal, 4047f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & { 4057f076004SYitzhak Mandelbaum // If the result is *not* empty, then we know the 4067f076004SYitzhak Mandelbaum // optional must have been holding a value. If 4077f076004SYitzhak Mandelbaum // `ExprVal` is true, though, we don't learn 4087f076004SYitzhak Mandelbaum // anything definite about `has_value`, so we 4097f076004SYitzhak Mandelbaum // don't add any corresponding implications to 4107f076004SYitzhak Mandelbaum // the flow condition. 4117f076004SYitzhak Mandelbaum return Env.makeImplication(Env.makeNot(ExprVal), 4127f076004SYitzhak Mandelbaum HasValueVal); 4137f076004SYitzhak Mandelbaum }); 4147f076004SYitzhak Mandelbaum } 4157f076004SYitzhak Mandelbaum 4167f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr, 4177f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 4187f076004SYitzhak Mandelbaum LatticeTransferState &State) { 4197f076004SYitzhak Mandelbaum transferValueOrImpl(ComparisonExpr, Result, State, 4207f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal, 4217f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & { 4227f076004SYitzhak Mandelbaum // We know that if `(opt.value_or(X) != X)` then 4237f076004SYitzhak Mandelbaum // `opt.hasValue()`, even without knowing further 4247f076004SYitzhak Mandelbaum // details about the contents of `opt`. 4257f076004SYitzhak Mandelbaum return Env.makeImplication(ExprVal, HasValueVal); 4267f076004SYitzhak Mandelbaum }); 4277f076004SYitzhak Mandelbaum } 4287f076004SYitzhak Mandelbaum 42965e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E, 43065e710c3SStanislav Gatev const MatchFinder::MatchResult &Result, 43165e710c3SStanislav Gatev LatticeTransferState &State) { 43265e710c3SStanislav Gatev if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) 43365e710c3SStanislav Gatev return; 43465e710c3SStanislav Gatev 43565e710c3SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E); 43665e710c3SStanislav Gatev State.Env.setStorageLocation(*E, Loc); 43765e710c3SStanislav Gatev State.Env.setValue( 43865e710c3SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue())); 43965e710c3SStanislav Gatev } 44065e710c3SStanislav Gatev 441092a530cSStanislav Gatev void assignOptionalValue(const Expr &E, LatticeTransferState &State, 442092a530cSStanislav Gatev BoolValue &HasValueVal) { 443092a530cSStanislav Gatev if (auto *OptionalLoc = 444092a530cSStanislav Gatev State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) { 445092a530cSStanislav Gatev State.Env.setValue(*OptionalLoc, 446092a530cSStanislav Gatev createOptionalValue(State.Env, HasValueVal)); 4479e0fc676SStanislav Gatev } 4489e0fc676SStanislav Gatev } 4499e0fc676SStanislav Gatev 450b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>` 451b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>` 452b000b770SStanislav Gatev /// where `T` is constructible from `U`. 453*390029beSYitzhak Mandelbaum BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E, 454b000b770SStanislav Gatev const MatchFinder::MatchResult &MatchRes, 455b000b770SStanislav Gatev LatticeTransferState &State) { 456b000b770SStanislav Gatev assert(F.getTemplateSpecializationArgs()->size() > 0); 457b000b770SStanislav Gatev 458b000b770SStanislav Gatev const int TemplateParamOptionalWrappersCount = countOptionalWrappers( 459b000b770SStanislav Gatev *MatchRes.Context, 460b000b770SStanislav Gatev stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType())); 461b000b770SStanislav Gatev const int ArgTypeOptionalWrappersCount = 462b000b770SStanislav Gatev countOptionalWrappers(*MatchRes.Context, stripReference(E.getType())); 463b000b770SStanislav Gatev 464b000b770SStanislav Gatev // Check if this is a constructor/assignment call for `optional<T>` with 465b000b770SStanislav Gatev // argument of type `U` such that `T` is constructible from `U`. 466b000b770SStanislav Gatev if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) 467b000b770SStanislav Gatev return State.Env.getBoolLiteralValue(true); 468b000b770SStanislav Gatev 469b000b770SStanislav Gatev // This is a constructor/assignment call for `optional<T>` with argument of 470b000b770SStanislav Gatev // type `optional<U>` such that `T` is constructible from `U`. 471dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = 472dd38caf3SYitzhak Mandelbaum getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference))) 473dd38caf3SYitzhak Mandelbaum return *HasValueVal; 474b000b770SStanislav Gatev return State.Env.makeAtomicBoolValue(); 475b000b770SStanislav Gatev } 476b000b770SStanislav Gatev 477092a530cSStanislav Gatev void transferValueOrConversionConstructor( 478092a530cSStanislav Gatev const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 4799e0fc676SStanislav Gatev LatticeTransferState &State) { 480092a530cSStanislav Gatev assert(E->getNumArgs() > 0); 481092a530cSStanislav Gatev 482b000b770SStanislav Gatev assignOptionalValue(*E, State, 483*390029beSYitzhak Mandelbaum valueOrConversionHasValue(*E->getConstructor(), 484b000b770SStanislav Gatev *E->getArg(0), MatchRes, 485b000b770SStanislav Gatev State)); 486b000b770SStanislav Gatev } 487092a530cSStanislav Gatev 488b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 489b000b770SStanislav Gatev LatticeTransferState &State) { 490b000b770SStanislav Gatev assert(E->getNumArgs() > 0); 491b000b770SStanislav Gatev 492b000b770SStanislav Gatev auto *OptionalLoc = 493b000b770SStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 494a9ad689eSSam Estep if (OptionalLoc == nullptr) 495a9ad689eSSam Estep return; 496b000b770SStanislav Gatev 497b000b770SStanislav Gatev State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal)); 498b000b770SStanislav Gatev 499b000b770SStanislav Gatev // Assign a storage location for the whole expression. 500b000b770SStanislav Gatev State.Env.setStorageLocation(*E, *OptionalLoc); 501b000b770SStanislav Gatev } 502b000b770SStanislav Gatev 503b000b770SStanislav Gatev void transferValueOrConversionAssignment( 504b000b770SStanislav Gatev const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 505b000b770SStanislav Gatev LatticeTransferState &State) { 506b000b770SStanislav Gatev assert(E->getNumArgs() > 1); 507b000b770SStanislav Gatev transferAssignment(E, 508*390029beSYitzhak Mandelbaum valueOrConversionHasValue(*E->getDirectCallee(), 50906decd0bSKazu Hirata *E->getArg(1), MatchRes, State), 510b000b770SStanislav Gatev State); 511b000b770SStanislav Gatev } 512b000b770SStanislav Gatev 513b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E, 514b000b770SStanislav Gatev const MatchFinder::MatchResult &, 515b000b770SStanislav Gatev LatticeTransferState &State) { 516b000b770SStanislav Gatev transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 5179e0fc676SStanislav Gatev } 5189e0fc676SStanislav Gatev 5192ddd57aeSStanislav Gatev void transferSwap(const StorageLocation &OptionalLoc1, 5202ddd57aeSStanislav Gatev const StorageLocation &OptionalLoc2, 5212ddd57aeSStanislav Gatev LatticeTransferState &State) { 5222ddd57aeSStanislav Gatev auto *OptionalVal1 = State.Env.getValue(OptionalLoc1); 5232ddd57aeSStanislav Gatev assert(OptionalVal1 != nullptr); 5242ddd57aeSStanislav Gatev 5252ddd57aeSStanislav Gatev auto *OptionalVal2 = State.Env.getValue(OptionalLoc2); 5262ddd57aeSStanislav Gatev assert(OptionalVal2 != nullptr); 5272ddd57aeSStanislav Gatev 5282ddd57aeSStanislav Gatev State.Env.setValue(OptionalLoc1, *OptionalVal2); 5292ddd57aeSStanislav Gatev State.Env.setValue(OptionalLoc2, *OptionalVal1); 5302ddd57aeSStanislav Gatev } 5312ddd57aeSStanislav Gatev 5322ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E, 5332ddd57aeSStanislav Gatev const MatchFinder::MatchResult &, 5342ddd57aeSStanislav Gatev LatticeTransferState &State) { 5352ddd57aeSStanislav Gatev assert(E->getNumArgs() == 1); 5362ddd57aeSStanislav Gatev 5372ddd57aeSStanislav Gatev auto *OptionalLoc1 = State.Env.getStorageLocation( 5382ddd57aeSStanislav Gatev *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer); 5392ddd57aeSStanislav Gatev assert(OptionalLoc1 != nullptr); 5402ddd57aeSStanislav Gatev 5412ddd57aeSStanislav Gatev auto *OptionalLoc2 = 5422ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 5432ddd57aeSStanislav Gatev assert(OptionalLoc2 != nullptr); 5442ddd57aeSStanislav Gatev 5452ddd57aeSStanislav Gatev transferSwap(*OptionalLoc1, *OptionalLoc2, State); 5462ddd57aeSStanislav Gatev } 5472ddd57aeSStanislav Gatev 5482ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 5492ddd57aeSStanislav Gatev LatticeTransferState &State) { 5502ddd57aeSStanislav Gatev assert(E->getNumArgs() == 2); 5512ddd57aeSStanislav Gatev 5522ddd57aeSStanislav Gatev auto *OptionalLoc1 = 5532ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 5542ddd57aeSStanislav Gatev assert(OptionalLoc1 != nullptr); 5552ddd57aeSStanislav Gatev 5562ddd57aeSStanislav Gatev auto *OptionalLoc2 = 5572ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference); 5582ddd57aeSStanislav Gatev assert(OptionalLoc2 != nullptr); 5592ddd57aeSStanislav Gatev 5602ddd57aeSStanislav Gatev transferSwap(*OptionalLoc1, *OptionalLoc2, State); 5612ddd57aeSStanislav Gatev } 5622ddd57aeSStanislav Gatev 563*390029beSYitzhak Mandelbaum BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS, 564*390029beSYitzhak Mandelbaum BoolValue &RHS) { 565*390029beSYitzhak Mandelbaum // Logically, an optional<T> object is composed of two values - a `has_value` 566*390029beSYitzhak Mandelbaum // bit and a value of type T. Equality of optional objects compares both 567*390029beSYitzhak Mandelbaum // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 568*390029beSYitzhak Mandelbaum // when two optional objects are engaged, the equality of their respective 569*390029beSYitzhak Mandelbaum // values of type T matters. Since we only track the `has_value` bits, we 570*390029beSYitzhak Mandelbaum // can't make any conclusions about equality when we know that two optional 571*390029beSYitzhak Mandelbaum // objects are engaged. 572*390029beSYitzhak Mandelbaum // 573*390029beSYitzhak Mandelbaum // We express this as two facts about the equality: 574*390029beSYitzhak Mandelbaum // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 575*390029beSYitzhak Mandelbaum // If they are equal, then either both are set or both are unset. 576*390029beSYitzhak Mandelbaum // b) (!LHS & !RHS) => EqVal 577*390029beSYitzhak Mandelbaum // If neither is set, then they are equal. 578*390029beSYitzhak Mandelbaum // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 579*390029beSYitzhak Mandelbaum return Env.makeAnd( 580*390029beSYitzhak Mandelbaum Env.makeImplication( 581*390029beSYitzhak Mandelbaum EqVal, Env.makeOr(Env.makeAnd(LHS, RHS), 582*390029beSYitzhak Mandelbaum Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))), 583*390029beSYitzhak Mandelbaum Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS))); 584*390029beSYitzhak Mandelbaum } 585*390029beSYitzhak Mandelbaum 586*390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 587*390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, 588*390029beSYitzhak Mandelbaum LatticeTransferState &State) { 589*390029beSYitzhak Mandelbaum Environment &Env = State.Env; 590*390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 591*390029beSYitzhak Mandelbaum if (auto *LHasVal = getHasValue( 592*390029beSYitzhak Mandelbaum Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference))) 593*390029beSYitzhak Mandelbaum if (auto *RHasVal = getHasValue( 594*390029beSYitzhak Mandelbaum Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) { 595*390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 596*390029beSYitzhak Mandelbaum CmpValue = &State.Env.makeNot(*CmpValue); 597*390029beSYitzhak Mandelbaum Env.addToFlowCondition( 598*390029beSYitzhak Mandelbaum evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal)); 599*390029beSYitzhak Mandelbaum } 600*390029beSYitzhak Mandelbaum } 601*390029beSYitzhak Mandelbaum 602*390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 603*390029beSYitzhak Mandelbaum const clang::Expr *E, Environment &Env) { 604*390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 605*390029beSYitzhak Mandelbaum if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) { 606*390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 607*390029beSYitzhak Mandelbaum CmpValue = &Env.makeNot(*CmpValue); 608*390029beSYitzhak Mandelbaum Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal, 609*390029beSYitzhak Mandelbaum Env.getBoolLiteralValue(true))); 610*390029beSYitzhak Mandelbaum } 611*390029beSYitzhak Mandelbaum } 612*390029beSYitzhak Mandelbaum 613a184a0d8SYitzhak Mandelbaum llvm::Optional<StatementMatcher> 614a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 615a184a0d8SYitzhak Mandelbaum if (Options.IgnoreSmartPointerDereference) 616a184a0d8SYitzhak Mandelbaum return memberExpr(hasObjectExpression(ignoringParenImpCasts( 617a184a0d8SYitzhak Mandelbaum cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("->"), 618a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName("*")), 619a184a0d8SYitzhak Mandelbaum unless(hasArgument(0, expr(hasOptionalType()))))))); 62034e0d057SKazu Hirata return std::nullopt; 621a184a0d8SYitzhak Mandelbaum } 622a184a0d8SYitzhak Mandelbaum 62358fe7f96SSam Estep StatementMatcher 62458fe7f96SSam Estep valueCall(llvm::Optional<StatementMatcher> &IgnorableOptional) { 62558fe7f96SSam Estep return isOptionalMemberCallWithName("value", IgnorableOptional); 62658fe7f96SSam Estep } 62758fe7f96SSam Estep 62858fe7f96SSam Estep StatementMatcher 62958fe7f96SSam Estep valueOperatorCall(llvm::Optional<StatementMatcher> &IgnorableOptional) { 63058fe7f96SSam Estep return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 63158fe7f96SSam Estep isOptionalOperatorCallWithName("->", IgnorableOptional))); 63258fe7f96SSam Estep } 63358fe7f96SSam Estep 634a184a0d8SYitzhak Mandelbaum auto buildTransferMatchSwitch( 635a184a0d8SYitzhak Mandelbaum const UncheckedOptionalAccessModelOptions &Options) { 636b000b770SStanislav Gatev // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 637b000b770SStanislav Gatev // lot of duplicated work (e.g. string comparisons), consider providing APIs 638b000b770SStanislav Gatev // that avoid it through memoization. 639a184a0d8SYitzhak Mandelbaum auto IgnorableOptional = ignorableOptional(Options); 6407538b360SWei Yi Tee return CFGMatchSwitchBuilder<LatticeTransferState>() 641af98b0afSStanislav Gatev // Attach a symbolic "has_value" state to optional values that we see for 642af98b0afSStanislav Gatev // the first time. 6437538b360SWei Yi Tee .CaseOfCFGStmt<Expr>( 6446adfc64eSYitzhak Mandelbaum expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), 645af98b0afSStanislav Gatev initializeOptionalReference) 646af98b0afSStanislav Gatev 6479e0fc676SStanislav Gatev // make_optional 6487538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 649092a530cSStanislav Gatev 650b000b770SStanislav Gatev // optional::optional 6517538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 652092a530cSStanislav Gatev isOptionalInPlaceConstructor(), 653092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 654092a530cSStanislav Gatev LatticeTransferState &State) { 655092a530cSStanislav Gatev assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true)); 656092a530cSStanislav Gatev }) 6577538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 658*390029beSYitzhak Mandelbaum isNulloptConstructor(), 659092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 660092a530cSStanislav Gatev LatticeTransferState &State) { 661092a530cSStanislav Gatev assignOptionalValue(*E, State, 662092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false)); 663092a530cSStanislav Gatev }) 664*390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXConstructExpr>( 665*390029beSYitzhak Mandelbaum isOptionalNulloptConstructor(), 666*390029beSYitzhak Mandelbaum [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 667*390029beSYitzhak Mandelbaum LatticeTransferState &State) { 668*390029beSYitzhak Mandelbaum // Shares a temporary with the underlying `nullopt_t` instance. 669*390029beSYitzhak Mandelbaum if (auto *OptionalLoc = 670*390029beSYitzhak Mandelbaum State.Env.getStorageLocation(*E, SkipPast::None)) { 671*390029beSYitzhak Mandelbaum State.Env.setValue( 672*390029beSYitzhak Mandelbaum *OptionalLoc, 673*390029beSYitzhak Mandelbaum *State.Env.getValue(*E->getArg(0), SkipPast::None)); 674*390029beSYitzhak Mandelbaum } 675*390029beSYitzhak Mandelbaum }) 6767538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 677092a530cSStanislav Gatev transferValueOrConversionConstructor) 6789e0fc676SStanislav Gatev 679b000b770SStanislav Gatev // optional::operator= 6807538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>( 6817538b360SWei Yi Tee isOptionalValueOrConversionAssignment(), 682b000b770SStanislav Gatev transferValueOrConversionAssignment) 6837538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 684b000b770SStanislav Gatev transferNulloptAssignment) 685b000b770SStanislav Gatev 686af98b0afSStanislav Gatev // optional::value 6877538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 68858fe7f96SSam Estep valueCall(IgnorableOptional), 689092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 690092a530cSStanislav Gatev LatticeTransferState &State) { 691af98b0afSStanislav Gatev transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 692af98b0afSStanislav Gatev }) 693af98b0afSStanislav Gatev 694af98b0afSStanislav Gatev // optional::operator*, optional::operator-> 6957538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 6967538b360SWei Yi Tee [](const CallExpr *E, 6977538b360SWei Yi Tee const MatchFinder::MatchResult &, 698092a530cSStanislav Gatev LatticeTransferState &State) { 699af98b0afSStanislav Gatev transferUnwrapCall(E, E->getArg(0), State); 700af98b0afSStanislav Gatev }) 701af98b0afSStanislav Gatev 702af98b0afSStanislav Gatev // optional::has_value 7037538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 7047538b360SWei Yi Tee isOptionalMemberCallWithName("has_value"), 705af98b0afSStanislav Gatev transferOptionalHasValueCall) 706af98b0afSStanislav Gatev 7079e0fc676SStanislav Gatev // optional::operator bool 7087538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 7097538b360SWei Yi Tee isOptionalMemberCallWithName("operator bool"), 7109e0fc676SStanislav Gatev transferOptionalHasValueCall) 7119e0fc676SStanislav Gatev 7129e0fc676SStanislav Gatev // optional::emplace 7137538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 714092a530cSStanislav Gatev isOptionalMemberCallWithName("emplace"), 715092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 716092a530cSStanislav Gatev LatticeTransferState &State) { 717092a530cSStanislav Gatev assignOptionalValue(*E->getImplicitObjectArgument(), State, 718092a530cSStanislav Gatev State.Env.getBoolLiteralValue(true)); 719092a530cSStanislav Gatev }) 7209e0fc676SStanislav Gatev 7219e0fc676SStanislav Gatev // optional::reset 7227538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 723092a530cSStanislav Gatev isOptionalMemberCallWithName("reset"), 724092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 725092a530cSStanislav Gatev LatticeTransferState &State) { 726092a530cSStanislav Gatev assignOptionalValue(*E->getImplicitObjectArgument(), State, 727092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false)); 728092a530cSStanislav Gatev }) 7299e0fc676SStanislav Gatev 7302ddd57aeSStanislav Gatev // optional::swap 7317538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"), 7322ddd57aeSStanislav Gatev transferSwapCall) 7332ddd57aeSStanislav Gatev 7342ddd57aeSStanislav Gatev // std::swap 7357538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 7362ddd57aeSStanislav Gatev 7377f076004SYitzhak Mandelbaum // opt.value_or("").empty() 7387538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 7397538b360SWei Yi Tee transferValueOrStringEmptyCall) 7407f076004SYitzhak Mandelbaum 7417f076004SYitzhak Mandelbaum // opt.value_or(X) != X 7427538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 7437f076004SYitzhak Mandelbaum 744*390029beSYitzhak Mandelbaum // Comparisons (==, !=): 745*390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 746*390029beSYitzhak Mandelbaum isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()), 747*390029beSYitzhak Mandelbaum transferOptionalAndOptionalCmp) 748*390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 749*390029beSYitzhak Mandelbaum isComparisonOperatorCall(hasOptionalType(), 750*390029beSYitzhak Mandelbaum unless(hasAnyOptionalType())), 751*390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 752*390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 753*390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 754*390029beSYitzhak Mandelbaum }) 755*390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 756*390029beSYitzhak Mandelbaum isComparisonOperatorCall(unless(hasAnyOptionalType()), 757*390029beSYitzhak Mandelbaum hasOptionalType()), 758*390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 759*390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 760*390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 761*390029beSYitzhak Mandelbaum }) 762*390029beSYitzhak Mandelbaum 76365e710c3SStanislav Gatev // returns optional 7647538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 76565e710c3SStanislav Gatev transferCallReturningOptional) 76665e710c3SStanislav Gatev 767af98b0afSStanislav Gatev .Build(); 768af98b0afSStanislav Gatev } 769af98b0afSStanislav Gatev 77058fe7f96SSam Estep std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr, 77158fe7f96SSam Estep const Expr *ObjectExpr, 77258fe7f96SSam Estep const Environment &Env) { 77358fe7f96SSam Estep if (auto *OptionalVal = 77458fe7f96SSam Estep Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 77558fe7f96SSam Estep auto *Prop = OptionalVal->getProperty("has_value"); 77658fe7f96SSam Estep if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 77758fe7f96SSam Estep if (Env.flowConditionImplies(*HasValueVal)) 77858fe7f96SSam Estep return {}; 77958fe7f96SSam Estep } 78058fe7f96SSam Estep } 78158fe7f96SSam Estep 78258fe7f96SSam Estep // Record that this unwrap is *not* provably safe. 78358fe7f96SSam Estep // FIXME: include either the name of the optional (if applicable) or a source 78458fe7f96SSam Estep // range of the access for easier interpretation of the result. 78558fe7f96SSam Estep return {ObjectExpr->getBeginLoc()}; 78658fe7f96SSam Estep } 78758fe7f96SSam Estep 78858fe7f96SSam Estep auto buildDiagnoseMatchSwitch( 78958fe7f96SSam Estep const UncheckedOptionalAccessModelOptions &Options) { 79058fe7f96SSam Estep // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 79158fe7f96SSam Estep // lot of duplicated work (e.g. string comparisons), consider providing APIs 79258fe7f96SSam Estep // that avoid it through memoization. 79358fe7f96SSam Estep auto IgnorableOptional = ignorableOptional(Options); 7947538b360SWei Yi Tee return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>() 79558fe7f96SSam Estep // optional::value 7967538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 79758fe7f96SSam Estep valueCall(IgnorableOptional), 79858fe7f96SSam Estep [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 79958fe7f96SSam Estep const Environment &Env) { 80058fe7f96SSam Estep return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env); 80158fe7f96SSam Estep }) 80258fe7f96SSam Estep 80358fe7f96SSam Estep // optional::operator*, optional::operator-> 8047538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>( 80558fe7f96SSam Estep valueOperatorCall(IgnorableOptional), 80658fe7f96SSam Estep [](const CallExpr *E, const MatchFinder::MatchResult &, 80758fe7f96SSam Estep const Environment &Env) { 80858fe7f96SSam Estep return diagnoseUnwrapCall(E, E->getArg(0), Env); 80958fe7f96SSam Estep }) 81058fe7f96SSam Estep .Build(); 81158fe7f96SSam Estep } 81258fe7f96SSam Estep 813af98b0afSStanislav Gatev } // namespace 814af98b0afSStanislav Gatev 8157e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher 8167e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() { 8177e63a0d4SYitzhak Mandelbaum return optionalClass(); 8187e63a0d4SYitzhak Mandelbaum } 8197e63a0d4SYitzhak Mandelbaum 820a184a0d8SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel( 821a184a0d8SYitzhak Mandelbaum ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options) 822cf1f978dSSam Estep : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 823a184a0d8SYitzhak Mandelbaum TransferMatchSwitch(buildTransferMatchSwitch(Options)) {} 824af98b0afSStanislav Gatev 8257538b360SWei Yi Tee void UncheckedOptionalAccessModel::transfer(const CFGElement *Elt, 8267538b360SWei Yi Tee NoopLattice &L, Environment &Env) { 827af98b0afSStanislav Gatev LatticeTransferState State(L, Env); 8287538b360SWei Yi Tee TransferMatchSwitch(*Elt, getASTContext(), State); 829af98b0afSStanislav Gatev } 830af98b0afSStanislav Gatev 831c0725865SYitzhak Mandelbaum ComparisonResult UncheckedOptionalAccessModel::compare( 832c0725865SYitzhak Mandelbaum QualType Type, const Value &Val1, const Environment &Env1, 833c0725865SYitzhak Mandelbaum const Value &Val2, const Environment &Env2) { 834c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 835c0725865SYitzhak Mandelbaum return ComparisonResult::Unknown; 836c0725865SYitzhak Mandelbaum return isNonEmptyOptional(Val1, Env1) == isNonEmptyOptional(Val2, Env2) 837c0725865SYitzhak Mandelbaum ? ComparisonResult::Same 838c0725865SYitzhak Mandelbaum : ComparisonResult::Different; 8398fcdd625SStanislav Gatev } 8408fcdd625SStanislav Gatev 8418fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1, 8428fcdd625SStanislav Gatev const Environment &Env1, 8438fcdd625SStanislav Gatev const Value &Val2, 8448fcdd625SStanislav Gatev const Environment &Env2, 8458fcdd625SStanislav Gatev Value &MergedVal, 8468fcdd625SStanislav Gatev Environment &MergedEnv) { 847c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 8488fcdd625SStanislav Gatev return true; 8498fcdd625SStanislav Gatev 8508fcdd625SStanislav Gatev auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); 8518fcdd625SStanislav Gatev if (isNonEmptyOptional(Val1, Env1) && isNonEmptyOptional(Val2, Env2)) 8528fcdd625SStanislav Gatev MergedEnv.addToFlowCondition(HasValueVal); 8538fcdd625SStanislav Gatev else if (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)) 8548fcdd625SStanislav Gatev MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal)); 8558fcdd625SStanislav Gatev setHasValue(MergedVal, HasValueVal); 8568fcdd625SStanislav Gatev return true; 8578fcdd625SStanislav Gatev } 8588fcdd625SStanislav Gatev 85958fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 86058fe7f96SSam Estep UncheckedOptionalAccessModelOptions Options) 86158fe7f96SSam Estep : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 86258fe7f96SSam Estep 86358fe7f96SSam Estep std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose( 8647538b360SWei Yi Tee ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) { 8657538b360SWei Yi Tee return DiagnoseMatchSwitch(*Elt, Ctx, Env); 86658fe7f96SSam Estep } 86758fe7f96SSam Estep 868af98b0afSStanislav Gatev } // namespace dataflow 869af98b0afSStanislav Gatev } // namespace clang 870