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, 59*34e0d057SKazu 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, 69*34e0d057SKazu 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 83092a530cSStanislav Gatev auto hasNulloptType() { 84092a530cSStanislav Gatev return hasType(namedDecl( 85092a530cSStanislav Gatev hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t"))); 86092a530cSStanislav Gatev } 87092a530cSStanislav Gatev 88092a530cSStanislav Gatev auto inPlaceClass() { 89092a530cSStanislav Gatev return recordDecl( 90092a530cSStanislav Gatev hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t")); 91092a530cSStanislav Gatev } 92092a530cSStanislav Gatev 93092a530cSStanislav Gatev auto isOptionalNulloptConstructor() { 94092a530cSStanislav Gatev return cxxConstructExpr(hasOptionalType(), argumentCountIs(1), 95092a530cSStanislav Gatev hasArgument(0, hasNulloptType())); 96092a530cSStanislav Gatev } 97092a530cSStanislav Gatev 98092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() { 99092a530cSStanislav Gatev return cxxConstructExpr(hasOptionalType(), 100092a530cSStanislav Gatev hasArgument(0, hasType(inPlaceClass()))); 101092a530cSStanislav Gatev } 102092a530cSStanislav Gatev 103092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() { 104092a530cSStanislav Gatev return cxxConstructExpr( 105092a530cSStanislav Gatev hasOptionalType(), 106092a530cSStanislav Gatev unless(hasDeclaration( 107092a530cSStanislav Gatev cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 108092a530cSStanislav Gatev argumentCountIs(1), hasArgument(0, unless(hasNulloptType()))); 109092a530cSStanislav Gatev } 110092a530cSStanislav Gatev 111b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() { 112b000b770SStanislav Gatev return cxxOperatorCallExpr( 113b000b770SStanislav Gatev hasOverloadedOperatorName("="), 114b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 115b000b770SStanislav Gatev unless(hasDeclaration(cxxMethodDecl( 116b000b770SStanislav Gatev anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 117b000b770SStanislav Gatev argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 118b000b770SStanislav Gatev } 119b000b770SStanislav Gatev 120b000b770SStanislav Gatev auto isOptionalNulloptAssignment() { 121b000b770SStanislav Gatev return cxxOperatorCallExpr(hasOverloadedOperatorName("="), 122b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 123b000b770SStanislav Gatev argumentCountIs(2), 124b000b770SStanislav Gatev hasArgument(1, hasNulloptType())); 125b000b770SStanislav Gatev } 126b000b770SStanislav Gatev 1272ddd57aeSStanislav Gatev auto isStdSwapCall() { 1282ddd57aeSStanislav Gatev return callExpr(callee(functionDecl(hasName("std::swap"))), 1292ddd57aeSStanislav Gatev argumentCountIs(2), hasArgument(0, hasOptionalType()), 1302ddd57aeSStanislav Gatev hasArgument(1, hasOptionalType())); 1312ddd57aeSStanislav Gatev } 1322ddd57aeSStanislav Gatev 1337f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 1347f076004SYitzhak Mandelbaum 1357f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() { 1367f076004SYitzhak Mandelbaum // `opt.value_or("").empty()` 1377f076004SYitzhak Mandelbaum return cxxMemberCallExpr( 1387f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("empty"))), 1397f076004SYitzhak Mandelbaum onImplicitObjectArgument(ignoringImplicit( 1407f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 1417f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 1427f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 1437f076004SYitzhak Mandelbaum hasArgument(0, stringLiteral(hasSize(0)))) 1447f076004SYitzhak Mandelbaum .bind(ValueOrCallID)))); 1457f076004SYitzhak Mandelbaum } 1467f076004SYitzhak Mandelbaum 1477f076004SYitzhak Mandelbaum auto isValueOrNotEqX() { 1487f076004SYitzhak Mandelbaum auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 1497f076004SYitzhak Mandelbaum return hasOperands( 1507f076004SYitzhak Mandelbaum ignoringImplicit( 1517f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 1527f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 1537f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 1547f076004SYitzhak Mandelbaum hasArgument(0, Arg)) 1557f076004SYitzhak Mandelbaum .bind(ValueOrCallID)), 1567f076004SYitzhak Mandelbaum ignoringImplicit(Arg)); 1577f076004SYitzhak Mandelbaum }; 1587f076004SYitzhak Mandelbaum 1597f076004SYitzhak Mandelbaum // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 1607f076004SYitzhak Mandelbaum // support this pattern for any expression, but the AST does not have a 1617f076004SYitzhak Mandelbaum // generic expression comparison facility, so we specialize to common cases 1627f076004SYitzhak Mandelbaum // seen in practice. FIXME: define a matcher that compares values across 1637f076004SYitzhak Mandelbaum // nodes, which would let us generalize this to any `X`. 1647f076004SYitzhak Mandelbaum return binaryOperation(hasOperatorName("!="), 1657f076004SYitzhak Mandelbaum anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 1667f076004SYitzhak Mandelbaum ComparesToSame(stringLiteral(hasSize(0))), 1677f076004SYitzhak Mandelbaum ComparesToSame(integerLiteral(equals(0))))); 1687f076004SYitzhak Mandelbaum } 1697f076004SYitzhak Mandelbaum 17065e710c3SStanislav Gatev auto isCallReturningOptional() { 171cd0d5261SSam Estep return callExpr(hasType(qualType(anyOf( 172cd0d5261SSam Estep optionalOrAliasType(), referenceType(pointee(optionalOrAliasType())))))); 17365e710c3SStanislav Gatev } 17465e710c3SStanislav Gatev 1758fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 1768fcdd625SStanislav Gatev /// property of the optional value `OptionalVal`. 1778fcdd625SStanislav Gatev void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) { 1788fcdd625SStanislav Gatev OptionalVal.setProperty("has_value", HasValueVal); 1798fcdd625SStanislav Gatev } 1808fcdd625SStanislav Gatev 1819e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the 1829e0fc676SStanislav Gatev /// symbolic value of its "has_value" property. 1839e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) { 1849e0fc676SStanislav Gatev auto OptionalVal = std::make_unique<StructValue>(); 1858fcdd625SStanislav Gatev setHasValue(*OptionalVal, HasValueVal); 1869e0fc676SStanislav Gatev return Env.takeOwnership(std::move(OptionalVal)); 1879e0fc676SStanislav Gatev } 1889e0fc676SStanislav Gatev 189af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the 19049ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null. 191dd38caf3SYitzhak Mandelbaum BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { 192dd38caf3SYitzhak Mandelbaum if (OptionalVal != nullptr) { 193dd38caf3SYitzhak Mandelbaum auto *HasValueVal = 194dd38caf3SYitzhak Mandelbaum cast_or_null<BoolValue>(OptionalVal->getProperty("has_value")); 195dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) { 196dd38caf3SYitzhak Mandelbaum HasValueVal = &Env.makeAtomicBoolValue(); 197dd38caf3SYitzhak Mandelbaum OptionalVal->setProperty("has_value", *HasValueVal); 198dd38caf3SYitzhak Mandelbaum } 199dd38caf3SYitzhak Mandelbaum return HasValueVal; 200af98b0afSStanislav Gatev } 201af98b0afSStanislav Gatev return nullptr; 202af98b0afSStanislav Gatev } 203af98b0afSStanislav Gatev 204092a530cSStanislav Gatev /// If `Type` is a reference type, returns the type of its pointee. Otherwise, 205092a530cSStanislav Gatev /// returns `Type` itself. 206092a530cSStanislav Gatev QualType stripReference(QualType Type) { 207092a530cSStanislav Gatev return Type->isReferenceType() ? Type->getPointeeType() : Type; 208092a530cSStanislav Gatev } 209092a530cSStanislav Gatev 210092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type. 211c0725865SYitzhak Mandelbaum bool isOptionalType(QualType Type) { 212092a530cSStanislav Gatev if (!Type->isRecordType()) 213092a530cSStanislav Gatev return false; 214092a530cSStanislav Gatev // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call. 215092a530cSStanislav Gatev auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString(); 216092a530cSStanislav Gatev return TypeName == "std::optional" || TypeName == "absl::optional" || 217092a530cSStanislav Gatev TypeName == "base::Optional"; 218092a530cSStanislav Gatev } 219092a530cSStanislav Gatev 220092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`. 221092a530cSStanislav Gatev /// 222092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this 223092a530cSStanislav Gatev /// function will be 2. 224092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 225c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 226092a530cSStanislav Gatev return 0; 227092a530cSStanislav Gatev return 1 + countOptionalWrappers( 228092a530cSStanislav Gatev ASTCtx, 229092a530cSStanislav Gatev cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) 230092a530cSStanislav Gatev ->getTemplateArgs() 231092a530cSStanislav Gatev .get(0) 232092a530cSStanislav Gatev .getAsType() 233092a530cSStanislav Gatev .getDesugaredType(ASTCtx)); 234092a530cSStanislav Gatev } 235092a530cSStanislav Gatev 236dd38caf3SYitzhak Mandelbaum /// Tries to initialize the `optional`'s value (that is, contents), and return 237dd38caf3SYitzhak Mandelbaum /// its location. Returns nullptr if the value can't be represented. 238dd38caf3SYitzhak Mandelbaum StorageLocation *maybeInitializeOptionalValueMember(QualType Q, 239dd38caf3SYitzhak Mandelbaum Value &OptionalVal, 240dd38caf3SYitzhak Mandelbaum Environment &Env) { 241dd38caf3SYitzhak Mandelbaum // The "value" property represents a synthetic field. As such, it needs 242dd38caf3SYitzhak Mandelbaum // `StorageLocation`, like normal fields (and other variables). So, we model 243dd38caf3SYitzhak Mandelbaum // it with a `ReferenceValue`, since that includes a storage location. Once 244dd38caf3SYitzhak Mandelbaum // the property is set, it will be shared by all environments that access the 245dd38caf3SYitzhak Mandelbaum // `Value` representing the optional (here, `OptionalVal`). 246dd38caf3SYitzhak Mandelbaum if (auto *ValueProp = OptionalVal.getProperty("value")) { 247dd38caf3SYitzhak Mandelbaum auto *ValueRef = clang::cast<ReferenceValue>(ValueProp); 24897d69cdaSWei Yi Tee auto &ValueLoc = ValueRef->getReferentLoc(); 249dd38caf3SYitzhak Mandelbaum if (Env.getValue(ValueLoc) == nullptr) { 250dd38caf3SYitzhak Mandelbaum // The property was previously set, but the value has been lost. This can 251dd38caf3SYitzhak Mandelbaum // happen, for example, because of an environment merge (where the two 252dd38caf3SYitzhak Mandelbaum // environments mapped the property to different values, which resulted in 253dd38caf3SYitzhak Mandelbaum // them both being discarded), or when two blocks in the CFG, with neither 254dd38caf3SYitzhak Mandelbaum // a dominator of the other, visit the same optional value, or even when a 255dd38caf3SYitzhak Mandelbaum // block is revisited during testing to collect per-statement state. 256dd38caf3SYitzhak Mandelbaum // FIXME: This situation means that the optional contents are not shared 257dd38caf3SYitzhak Mandelbaum // between branches and the like. Practically, this lack of sharing 258dd38caf3SYitzhak Mandelbaum // reduces the precision of the model when the contents are relevant to 259dd38caf3SYitzhak Mandelbaum // the check, like another optional or a boolean that influences control 260dd38caf3SYitzhak Mandelbaum // flow. 261dd38caf3SYitzhak Mandelbaum auto *ValueVal = Env.createValue(ValueLoc.getType()); 262dd38caf3SYitzhak Mandelbaum if (ValueVal == nullptr) 263dd38caf3SYitzhak Mandelbaum return nullptr; 264dd38caf3SYitzhak Mandelbaum Env.setValue(ValueLoc, *ValueVal); 265dd38caf3SYitzhak Mandelbaum } 266dd38caf3SYitzhak Mandelbaum return &ValueLoc; 267dd38caf3SYitzhak Mandelbaum } 268dd38caf3SYitzhak Mandelbaum 269dd38caf3SYitzhak Mandelbaum auto Ty = stripReference(Q); 270dd38caf3SYitzhak Mandelbaum auto *ValueVal = Env.createValue(Ty); 271dd38caf3SYitzhak Mandelbaum if (ValueVal == nullptr) 272dd38caf3SYitzhak Mandelbaum return nullptr; 273dd38caf3SYitzhak Mandelbaum auto &ValueLoc = Env.createStorageLocation(Ty); 274dd38caf3SYitzhak Mandelbaum Env.setValue(ValueLoc, *ValueVal); 275dd38caf3SYitzhak Mandelbaum auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc); 276dd38caf3SYitzhak Mandelbaum OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef))); 277dd38caf3SYitzhak Mandelbaum return &ValueLoc; 278dd38caf3SYitzhak Mandelbaum } 279dd38caf3SYitzhak Mandelbaum 280092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr, 281092a530cSStanislav Gatev const MatchFinder::MatchResult &, 282af98b0afSStanislav Gatev LatticeTransferState &State) { 28349ed5bf5SWei Yi Tee if (auto *OptionalVal = 28449ed5bf5SWei Yi Tee State.Env.getValue(*OptionalExpr, SkipPast::Reference)) { 285af98b0afSStanislav Gatev if (OptionalVal->getProperty("has_value") == nullptr) { 2868fcdd625SStanislav Gatev setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); 287af98b0afSStanislav Gatev } 288af98b0afSStanislav Gatev } 289af98b0afSStanislav Gatev } 290af98b0afSStanislav Gatev 2918fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be 2928fcdd625SStanislav Gatev /// empty in `Env. 2938fcdd625SStanislav Gatev bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { 2948fcdd625SStanislav Gatev auto *HasValueVal = 2958fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 2968fcdd625SStanislav Gatev return HasValueVal != nullptr && 2978fcdd625SStanislav Gatev Env.flowConditionImplies(Env.makeNot(*HasValueVal)); 2988fcdd625SStanislav Gatev } 2998fcdd625SStanislav Gatev 3008fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be 3018fcdd625SStanislav Gatev /// non-empty in `Env. 3028fcdd625SStanislav Gatev bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { 3038fcdd625SStanislav Gatev auto *HasValueVal = 3048fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 3058fcdd625SStanislav Gatev return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal); 3068fcdd625SStanislav Gatev } 3078fcdd625SStanislav Gatev 308092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 309af98b0afSStanislav Gatev LatticeTransferState &State) { 31049ed5bf5SWei Yi Tee if (auto *OptionalVal = 31149ed5bf5SWei Yi Tee State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 312dd38caf3SYitzhak Mandelbaum if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr) 313dd38caf3SYitzhak Mandelbaum if (auto *Loc = maybeInitializeOptionalValueMember( 314dd38caf3SYitzhak Mandelbaum UnwrapExpr->getType(), *OptionalVal, State.Env)) 315dd38caf3SYitzhak Mandelbaum State.Env.setStorageLocation(*UnwrapExpr, *Loc); 316af98b0afSStanislav Gatev } 317dd38caf3SYitzhak Mandelbaum } 318af98b0afSStanislav Gatev 319092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E, 320092a530cSStanislav Gatev const MatchFinder::MatchResult &, 321092a530cSStanislav Gatev LatticeTransferState &State) { 3229e0fc676SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E); 3239e0fc676SStanislav Gatev State.Env.setStorageLocation(*E, Loc); 3249e0fc676SStanislav Gatev State.Env.setValue( 3259e0fc676SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true))); 3269e0fc676SStanislav Gatev } 3279e0fc676SStanislav Gatev 328092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 329092a530cSStanislav Gatev const MatchFinder::MatchResult &, 330af98b0afSStanislav Gatev LatticeTransferState &State) { 331dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = getHasValue( 332dd38caf3SYitzhak Mandelbaum State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(), 333af98b0afSStanislav Gatev SkipPast::ReferenceThenPointer))) { 334af98b0afSStanislav Gatev auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr); 335af98b0afSStanislav Gatev State.Env.setValue(CallExprLoc, *HasValueVal); 336af98b0afSStanislav Gatev State.Env.setStorageLocation(*CallExpr, CallExprLoc); 337af98b0afSStanislav Gatev } 338af98b0afSStanislav Gatev } 339af98b0afSStanislav Gatev 3407f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in 3417f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property. 3427f076004SYitzhak Mandelbaum void transferValueOrImpl(const clang::Expr *ValueOrPredExpr, 3437f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 3447f076004SYitzhak Mandelbaum LatticeTransferState &State, 3457f076004SYitzhak Mandelbaum BoolValue &(*ModelPred)(Environment &Env, 3467f076004SYitzhak Mandelbaum BoolValue &ExprVal, 3477f076004SYitzhak Mandelbaum BoolValue &HasValueVal)) { 3487f076004SYitzhak Mandelbaum auto &Env = State.Env; 3497f076004SYitzhak Mandelbaum 3507f076004SYitzhak Mandelbaum const auto *ObjectArgumentExpr = 3517f076004SYitzhak Mandelbaum Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) 3527f076004SYitzhak Mandelbaum ->getImplicitObjectArgument(); 3537f076004SYitzhak Mandelbaum 354dd38caf3SYitzhak Mandelbaum auto *HasValueVal = getHasValue( 355dd38caf3SYitzhak Mandelbaum State.Env, 356dd38caf3SYitzhak Mandelbaum State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer)); 357dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) 3587f076004SYitzhak Mandelbaum return; 3597f076004SYitzhak Mandelbaum 3607f076004SYitzhak Mandelbaum auto *ExprValue = cast_or_null<BoolValue>( 3617f076004SYitzhak Mandelbaum State.Env.getValue(*ValueOrPredExpr, SkipPast::None)); 3627f076004SYitzhak Mandelbaum if (ExprValue == nullptr) { 3637f076004SYitzhak Mandelbaum auto &ExprLoc = State.Env.createStorageLocation(*ValueOrPredExpr); 3647f076004SYitzhak Mandelbaum ExprValue = &State.Env.makeAtomicBoolValue(); 3657f076004SYitzhak Mandelbaum State.Env.setValue(ExprLoc, *ExprValue); 3667f076004SYitzhak Mandelbaum State.Env.setStorageLocation(*ValueOrPredExpr, ExprLoc); 3677f076004SYitzhak Mandelbaum } 3687f076004SYitzhak Mandelbaum 3697f076004SYitzhak Mandelbaum Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal)); 3707f076004SYitzhak Mandelbaum } 3717f076004SYitzhak Mandelbaum 3727f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 3737f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 3747f076004SYitzhak Mandelbaum LatticeTransferState &State) { 3757f076004SYitzhak Mandelbaum return transferValueOrImpl(ComparisonExpr, Result, State, 3767f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal, 3777f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & { 3787f076004SYitzhak Mandelbaum // If the result is *not* empty, then we know the 3797f076004SYitzhak Mandelbaum // optional must have been holding a value. If 3807f076004SYitzhak Mandelbaum // `ExprVal` is true, though, we don't learn 3817f076004SYitzhak Mandelbaum // anything definite about `has_value`, so we 3827f076004SYitzhak Mandelbaum // don't add any corresponding implications to 3837f076004SYitzhak Mandelbaum // the flow condition. 3847f076004SYitzhak Mandelbaum return Env.makeImplication(Env.makeNot(ExprVal), 3857f076004SYitzhak Mandelbaum HasValueVal); 3867f076004SYitzhak Mandelbaum }); 3877f076004SYitzhak Mandelbaum } 3887f076004SYitzhak Mandelbaum 3897f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr, 3907f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 3917f076004SYitzhak Mandelbaum LatticeTransferState &State) { 3927f076004SYitzhak Mandelbaum transferValueOrImpl(ComparisonExpr, Result, State, 3937f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal, 3947f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & { 3957f076004SYitzhak Mandelbaum // We know that if `(opt.value_or(X) != X)` then 3967f076004SYitzhak Mandelbaum // `opt.hasValue()`, even without knowing further 3977f076004SYitzhak Mandelbaum // details about the contents of `opt`. 3987f076004SYitzhak Mandelbaum return Env.makeImplication(ExprVal, HasValueVal); 3997f076004SYitzhak Mandelbaum }); 4007f076004SYitzhak Mandelbaum } 4017f076004SYitzhak Mandelbaum 40265e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E, 40365e710c3SStanislav Gatev const MatchFinder::MatchResult &Result, 40465e710c3SStanislav Gatev LatticeTransferState &State) { 40565e710c3SStanislav Gatev if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) 40665e710c3SStanislav Gatev return; 40765e710c3SStanislav Gatev 40865e710c3SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E); 40965e710c3SStanislav Gatev State.Env.setStorageLocation(*E, Loc); 41065e710c3SStanislav Gatev State.Env.setValue( 41165e710c3SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue())); 41265e710c3SStanislav Gatev } 41365e710c3SStanislav Gatev 414092a530cSStanislav Gatev void assignOptionalValue(const Expr &E, LatticeTransferState &State, 415092a530cSStanislav Gatev BoolValue &HasValueVal) { 416092a530cSStanislav Gatev if (auto *OptionalLoc = 417092a530cSStanislav Gatev State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) { 418092a530cSStanislav Gatev State.Env.setValue(*OptionalLoc, 419092a530cSStanislav Gatev createOptionalValue(State.Env, HasValueVal)); 4209e0fc676SStanislav Gatev } 4219e0fc676SStanislav Gatev } 4229e0fc676SStanislav Gatev 423b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>` 424b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>` 425b000b770SStanislav Gatev /// where `T` is constructible from `U`. 42606decd0bSKazu Hirata BoolValue &value_orConversionHasValue(const FunctionDecl &F, const Expr &E, 427b000b770SStanislav Gatev const MatchFinder::MatchResult &MatchRes, 428b000b770SStanislav Gatev LatticeTransferState &State) { 429b000b770SStanislav Gatev assert(F.getTemplateSpecializationArgs()->size() > 0); 430b000b770SStanislav Gatev 431b000b770SStanislav Gatev const int TemplateParamOptionalWrappersCount = countOptionalWrappers( 432b000b770SStanislav Gatev *MatchRes.Context, 433b000b770SStanislav Gatev stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType())); 434b000b770SStanislav Gatev const int ArgTypeOptionalWrappersCount = 435b000b770SStanislav Gatev countOptionalWrappers(*MatchRes.Context, stripReference(E.getType())); 436b000b770SStanislav Gatev 437b000b770SStanislav Gatev // Check if this is a constructor/assignment call for `optional<T>` with 438b000b770SStanislav Gatev // argument of type `U` such that `T` is constructible from `U`. 439b000b770SStanislav Gatev if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) 440b000b770SStanislav Gatev return State.Env.getBoolLiteralValue(true); 441b000b770SStanislav Gatev 442b000b770SStanislav Gatev // This is a constructor/assignment call for `optional<T>` with argument of 443b000b770SStanislav Gatev // type `optional<U>` such that `T` is constructible from `U`. 444dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = 445dd38caf3SYitzhak Mandelbaum getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference))) 446dd38caf3SYitzhak Mandelbaum return *HasValueVal; 447b000b770SStanislav Gatev return State.Env.makeAtomicBoolValue(); 448b000b770SStanislav Gatev } 449b000b770SStanislav Gatev 450092a530cSStanislav Gatev void transferValueOrConversionConstructor( 451092a530cSStanislav Gatev const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 4529e0fc676SStanislav Gatev LatticeTransferState &State) { 453092a530cSStanislav Gatev assert(E->getNumArgs() > 0); 454092a530cSStanislav Gatev 455b000b770SStanislav Gatev assignOptionalValue(*E, State, 45606decd0bSKazu Hirata value_orConversionHasValue(*E->getConstructor(), 457b000b770SStanislav Gatev *E->getArg(0), MatchRes, 458b000b770SStanislav Gatev State)); 459b000b770SStanislav Gatev } 460092a530cSStanislav Gatev 461b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 462b000b770SStanislav Gatev LatticeTransferState &State) { 463b000b770SStanislav Gatev assert(E->getNumArgs() > 0); 464b000b770SStanislav Gatev 465b000b770SStanislav Gatev auto *OptionalLoc = 466b000b770SStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 467a9ad689eSSam Estep if (OptionalLoc == nullptr) 468a9ad689eSSam Estep return; 469b000b770SStanislav Gatev 470b000b770SStanislav Gatev State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal)); 471b000b770SStanislav Gatev 472b000b770SStanislav Gatev // Assign a storage location for the whole expression. 473b000b770SStanislav Gatev State.Env.setStorageLocation(*E, *OptionalLoc); 474b000b770SStanislav Gatev } 475b000b770SStanislav Gatev 476b000b770SStanislav Gatev void transferValueOrConversionAssignment( 477b000b770SStanislav Gatev const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 478b000b770SStanislav Gatev LatticeTransferState &State) { 479b000b770SStanislav Gatev assert(E->getNumArgs() > 1); 480b000b770SStanislav Gatev transferAssignment(E, 48106decd0bSKazu Hirata value_orConversionHasValue(*E->getDirectCallee(), 48206decd0bSKazu Hirata *E->getArg(1), MatchRes, State), 483b000b770SStanislav Gatev State); 484b000b770SStanislav Gatev } 485b000b770SStanislav Gatev 486b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E, 487b000b770SStanislav Gatev const MatchFinder::MatchResult &, 488b000b770SStanislav Gatev LatticeTransferState &State) { 489b000b770SStanislav Gatev transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 4909e0fc676SStanislav Gatev } 4919e0fc676SStanislav Gatev 4922ddd57aeSStanislav Gatev void transferSwap(const StorageLocation &OptionalLoc1, 4932ddd57aeSStanislav Gatev const StorageLocation &OptionalLoc2, 4942ddd57aeSStanislav Gatev LatticeTransferState &State) { 4952ddd57aeSStanislav Gatev auto *OptionalVal1 = State.Env.getValue(OptionalLoc1); 4962ddd57aeSStanislav Gatev assert(OptionalVal1 != nullptr); 4972ddd57aeSStanislav Gatev 4982ddd57aeSStanislav Gatev auto *OptionalVal2 = State.Env.getValue(OptionalLoc2); 4992ddd57aeSStanislav Gatev assert(OptionalVal2 != nullptr); 5002ddd57aeSStanislav Gatev 5012ddd57aeSStanislav Gatev State.Env.setValue(OptionalLoc1, *OptionalVal2); 5022ddd57aeSStanislav Gatev State.Env.setValue(OptionalLoc2, *OptionalVal1); 5032ddd57aeSStanislav Gatev } 5042ddd57aeSStanislav Gatev 5052ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E, 5062ddd57aeSStanislav Gatev const MatchFinder::MatchResult &, 5072ddd57aeSStanislav Gatev LatticeTransferState &State) { 5082ddd57aeSStanislav Gatev assert(E->getNumArgs() == 1); 5092ddd57aeSStanislav Gatev 5102ddd57aeSStanislav Gatev auto *OptionalLoc1 = State.Env.getStorageLocation( 5112ddd57aeSStanislav Gatev *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer); 5122ddd57aeSStanislav Gatev assert(OptionalLoc1 != nullptr); 5132ddd57aeSStanislav Gatev 5142ddd57aeSStanislav Gatev auto *OptionalLoc2 = 5152ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 5162ddd57aeSStanislav Gatev assert(OptionalLoc2 != nullptr); 5172ddd57aeSStanislav Gatev 5182ddd57aeSStanislav Gatev transferSwap(*OptionalLoc1, *OptionalLoc2, State); 5192ddd57aeSStanislav Gatev } 5202ddd57aeSStanislav Gatev 5212ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 5222ddd57aeSStanislav Gatev LatticeTransferState &State) { 5232ddd57aeSStanislav Gatev assert(E->getNumArgs() == 2); 5242ddd57aeSStanislav Gatev 5252ddd57aeSStanislav Gatev auto *OptionalLoc1 = 5262ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 5272ddd57aeSStanislav Gatev assert(OptionalLoc1 != nullptr); 5282ddd57aeSStanislav Gatev 5292ddd57aeSStanislav Gatev auto *OptionalLoc2 = 5302ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference); 5312ddd57aeSStanislav Gatev assert(OptionalLoc2 != nullptr); 5322ddd57aeSStanislav Gatev 5332ddd57aeSStanislav Gatev transferSwap(*OptionalLoc1, *OptionalLoc2, State); 5342ddd57aeSStanislav Gatev } 5352ddd57aeSStanislav Gatev 536a184a0d8SYitzhak Mandelbaum llvm::Optional<StatementMatcher> 537a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 538a184a0d8SYitzhak Mandelbaum if (Options.IgnoreSmartPointerDereference) 539a184a0d8SYitzhak Mandelbaum return memberExpr(hasObjectExpression(ignoringParenImpCasts( 540a184a0d8SYitzhak Mandelbaum cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("->"), 541a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName("*")), 542a184a0d8SYitzhak Mandelbaum unless(hasArgument(0, expr(hasOptionalType()))))))); 543*34e0d057SKazu Hirata return std::nullopt; 544a184a0d8SYitzhak Mandelbaum } 545a184a0d8SYitzhak Mandelbaum 54658fe7f96SSam Estep StatementMatcher 54758fe7f96SSam Estep valueCall(llvm::Optional<StatementMatcher> &IgnorableOptional) { 54858fe7f96SSam Estep return isOptionalMemberCallWithName("value", IgnorableOptional); 54958fe7f96SSam Estep } 55058fe7f96SSam Estep 55158fe7f96SSam Estep StatementMatcher 55258fe7f96SSam Estep valueOperatorCall(llvm::Optional<StatementMatcher> &IgnorableOptional) { 55358fe7f96SSam Estep return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 55458fe7f96SSam Estep isOptionalOperatorCallWithName("->", IgnorableOptional))); 55558fe7f96SSam Estep } 55658fe7f96SSam Estep 557a184a0d8SYitzhak Mandelbaum auto buildTransferMatchSwitch( 558a184a0d8SYitzhak Mandelbaum const UncheckedOptionalAccessModelOptions &Options) { 559b000b770SStanislav Gatev // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 560b000b770SStanislav Gatev // lot of duplicated work (e.g. string comparisons), consider providing APIs 561b000b770SStanislav Gatev // that avoid it through memoization. 562a184a0d8SYitzhak Mandelbaum auto IgnorableOptional = ignorableOptional(Options); 5637538b360SWei Yi Tee return CFGMatchSwitchBuilder<LatticeTransferState>() 564af98b0afSStanislav Gatev // Attach a symbolic "has_value" state to optional values that we see for 565af98b0afSStanislav Gatev // the first time. 5667538b360SWei Yi Tee .CaseOfCFGStmt<Expr>( 5676adfc64eSYitzhak Mandelbaum expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), 568af98b0afSStanislav Gatev initializeOptionalReference) 569af98b0afSStanislav Gatev 5709e0fc676SStanislav Gatev // make_optional 5717538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 572092a530cSStanislav Gatev 573b000b770SStanislav Gatev // optional::optional 5747538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 575092a530cSStanislav Gatev isOptionalInPlaceConstructor(), 576092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 577092a530cSStanislav Gatev LatticeTransferState &State) { 578092a530cSStanislav Gatev assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true)); 579092a530cSStanislav Gatev }) 5807538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 581092a530cSStanislav Gatev isOptionalNulloptConstructor(), 582092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 583092a530cSStanislav Gatev LatticeTransferState &State) { 584092a530cSStanislav Gatev assignOptionalValue(*E, State, 585092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false)); 586092a530cSStanislav Gatev }) 5877538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 588092a530cSStanislav Gatev transferValueOrConversionConstructor) 5899e0fc676SStanislav Gatev 590b000b770SStanislav Gatev // optional::operator= 5917538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>( 5927538b360SWei Yi Tee isOptionalValueOrConversionAssignment(), 593b000b770SStanislav Gatev transferValueOrConversionAssignment) 5947538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 595b000b770SStanislav Gatev transferNulloptAssignment) 596b000b770SStanislav Gatev 597af98b0afSStanislav Gatev // optional::value 5987538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 59958fe7f96SSam Estep valueCall(IgnorableOptional), 600092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 601092a530cSStanislav Gatev LatticeTransferState &State) { 602af98b0afSStanislav Gatev transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 603af98b0afSStanislav Gatev }) 604af98b0afSStanislav Gatev 605af98b0afSStanislav Gatev // optional::operator*, optional::operator-> 6067538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 6077538b360SWei Yi Tee [](const CallExpr *E, 6087538b360SWei Yi Tee const MatchFinder::MatchResult &, 609092a530cSStanislav Gatev LatticeTransferState &State) { 610af98b0afSStanislav Gatev transferUnwrapCall(E, E->getArg(0), State); 611af98b0afSStanislav Gatev }) 612af98b0afSStanislav Gatev 613af98b0afSStanislav Gatev // optional::has_value 6147538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 6157538b360SWei Yi Tee isOptionalMemberCallWithName("has_value"), 616af98b0afSStanislav Gatev transferOptionalHasValueCall) 617af98b0afSStanislav Gatev 6189e0fc676SStanislav Gatev // optional::operator bool 6197538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 6207538b360SWei Yi Tee isOptionalMemberCallWithName("operator bool"), 6219e0fc676SStanislav Gatev transferOptionalHasValueCall) 6229e0fc676SStanislav Gatev 6239e0fc676SStanislav Gatev // optional::emplace 6247538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 625092a530cSStanislav Gatev isOptionalMemberCallWithName("emplace"), 626092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 627092a530cSStanislav Gatev LatticeTransferState &State) { 628092a530cSStanislav Gatev assignOptionalValue(*E->getImplicitObjectArgument(), State, 629092a530cSStanislav Gatev State.Env.getBoolLiteralValue(true)); 630092a530cSStanislav Gatev }) 6319e0fc676SStanislav Gatev 6329e0fc676SStanislav Gatev // optional::reset 6337538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 634092a530cSStanislav Gatev isOptionalMemberCallWithName("reset"), 635092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 636092a530cSStanislav Gatev LatticeTransferState &State) { 637092a530cSStanislav Gatev assignOptionalValue(*E->getImplicitObjectArgument(), State, 638092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false)); 639092a530cSStanislav Gatev }) 6409e0fc676SStanislav Gatev 6412ddd57aeSStanislav Gatev // optional::swap 6427538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"), 6432ddd57aeSStanislav Gatev transferSwapCall) 6442ddd57aeSStanislav Gatev 6452ddd57aeSStanislav Gatev // std::swap 6467538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 6472ddd57aeSStanislav Gatev 6487f076004SYitzhak Mandelbaum // opt.value_or("").empty() 6497538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 6507538b360SWei Yi Tee transferValueOrStringEmptyCall) 6517f076004SYitzhak Mandelbaum 6527f076004SYitzhak Mandelbaum // opt.value_or(X) != X 6537538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 6547f076004SYitzhak Mandelbaum 65565e710c3SStanislav Gatev // returns optional 6567538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 65765e710c3SStanislav Gatev transferCallReturningOptional) 65865e710c3SStanislav Gatev 659af98b0afSStanislav Gatev .Build(); 660af98b0afSStanislav Gatev } 661af98b0afSStanislav Gatev 66258fe7f96SSam Estep std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr, 66358fe7f96SSam Estep const Expr *ObjectExpr, 66458fe7f96SSam Estep const Environment &Env) { 66558fe7f96SSam Estep if (auto *OptionalVal = 66658fe7f96SSam Estep Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 66758fe7f96SSam Estep auto *Prop = OptionalVal->getProperty("has_value"); 66858fe7f96SSam Estep if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 66958fe7f96SSam Estep if (Env.flowConditionImplies(*HasValueVal)) 67058fe7f96SSam Estep return {}; 67158fe7f96SSam Estep } 67258fe7f96SSam Estep } 67358fe7f96SSam Estep 67458fe7f96SSam Estep // Record that this unwrap is *not* provably safe. 67558fe7f96SSam Estep // FIXME: include either the name of the optional (if applicable) or a source 67658fe7f96SSam Estep // range of the access for easier interpretation of the result. 67758fe7f96SSam Estep return {ObjectExpr->getBeginLoc()}; 67858fe7f96SSam Estep } 67958fe7f96SSam Estep 68058fe7f96SSam Estep auto buildDiagnoseMatchSwitch( 68158fe7f96SSam Estep const UncheckedOptionalAccessModelOptions &Options) { 68258fe7f96SSam Estep // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 68358fe7f96SSam Estep // lot of duplicated work (e.g. string comparisons), consider providing APIs 68458fe7f96SSam Estep // that avoid it through memoization. 68558fe7f96SSam Estep auto IgnorableOptional = ignorableOptional(Options); 6867538b360SWei Yi Tee return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>() 68758fe7f96SSam Estep // optional::value 6887538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 68958fe7f96SSam Estep valueCall(IgnorableOptional), 69058fe7f96SSam Estep [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 69158fe7f96SSam Estep const Environment &Env) { 69258fe7f96SSam Estep return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env); 69358fe7f96SSam Estep }) 69458fe7f96SSam Estep 69558fe7f96SSam Estep // optional::operator*, optional::operator-> 6967538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>( 69758fe7f96SSam Estep valueOperatorCall(IgnorableOptional), 69858fe7f96SSam Estep [](const CallExpr *E, const MatchFinder::MatchResult &, 69958fe7f96SSam Estep const Environment &Env) { 70058fe7f96SSam Estep return diagnoseUnwrapCall(E, E->getArg(0), Env); 70158fe7f96SSam Estep }) 70258fe7f96SSam Estep .Build(); 70358fe7f96SSam Estep } 70458fe7f96SSam Estep 705af98b0afSStanislav Gatev } // namespace 706af98b0afSStanislav Gatev 7077e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher 7087e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() { 7097e63a0d4SYitzhak Mandelbaum return optionalClass(); 7107e63a0d4SYitzhak Mandelbaum } 7117e63a0d4SYitzhak Mandelbaum 712a184a0d8SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel( 713a184a0d8SYitzhak Mandelbaum ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options) 714cf1f978dSSam Estep : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 715a184a0d8SYitzhak Mandelbaum TransferMatchSwitch(buildTransferMatchSwitch(Options)) {} 716af98b0afSStanislav Gatev 7177538b360SWei Yi Tee void UncheckedOptionalAccessModel::transfer(const CFGElement *Elt, 7187538b360SWei Yi Tee NoopLattice &L, Environment &Env) { 719af98b0afSStanislav Gatev LatticeTransferState State(L, Env); 7207538b360SWei Yi Tee TransferMatchSwitch(*Elt, getASTContext(), State); 721af98b0afSStanislav Gatev } 722af98b0afSStanislav Gatev 723c0725865SYitzhak Mandelbaum ComparisonResult UncheckedOptionalAccessModel::compare( 724c0725865SYitzhak Mandelbaum QualType Type, const Value &Val1, const Environment &Env1, 725c0725865SYitzhak Mandelbaum const Value &Val2, const Environment &Env2) { 726c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 727c0725865SYitzhak Mandelbaum return ComparisonResult::Unknown; 728c0725865SYitzhak Mandelbaum return isNonEmptyOptional(Val1, Env1) == isNonEmptyOptional(Val2, Env2) 729c0725865SYitzhak Mandelbaum ? ComparisonResult::Same 730c0725865SYitzhak Mandelbaum : ComparisonResult::Different; 7318fcdd625SStanislav Gatev } 7328fcdd625SStanislav Gatev 7338fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1, 7348fcdd625SStanislav Gatev const Environment &Env1, 7358fcdd625SStanislav Gatev const Value &Val2, 7368fcdd625SStanislav Gatev const Environment &Env2, 7378fcdd625SStanislav Gatev Value &MergedVal, 7388fcdd625SStanislav Gatev Environment &MergedEnv) { 739c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 7408fcdd625SStanislav Gatev return true; 7418fcdd625SStanislav Gatev 7428fcdd625SStanislav Gatev auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); 7438fcdd625SStanislav Gatev if (isNonEmptyOptional(Val1, Env1) && isNonEmptyOptional(Val2, Env2)) 7448fcdd625SStanislav Gatev MergedEnv.addToFlowCondition(HasValueVal); 7458fcdd625SStanislav Gatev else if (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)) 7468fcdd625SStanislav Gatev MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal)); 7478fcdd625SStanislav Gatev setHasValue(MergedVal, HasValueVal); 7488fcdd625SStanislav Gatev return true; 7498fcdd625SStanislav Gatev } 7508fcdd625SStanislav Gatev 75158fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 75258fe7f96SSam Estep UncheckedOptionalAccessModelOptions Options) 75358fe7f96SSam Estep : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 75458fe7f96SSam Estep 75558fe7f96SSam Estep std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose( 7567538b360SWei Yi Tee ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) { 7577538b360SWei Yi Tee return DiagnoseMatchSwitch(*Elt, Ctx, Env); 75858fe7f96SSam Estep } 75958fe7f96SSam Estep 760af98b0afSStanislav Gatev } // namespace dataflow 761af98b0afSStanislav Gatev } // namespace clang 762