181ad6265SDimitry Andric //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric // 981ad6265SDimitry Andric // This file defines a dataflow analysis that detects unsafe uses of optional 1081ad6265SDimitry Andric // values. 1181ad6265SDimitry Andric // 1281ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1381ad6265SDimitry Andric 1481ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h" 1581ad6265SDimitry Andric #include "clang/AST/ASTContext.h" 1681ad6265SDimitry Andric #include "clang/AST/DeclCXX.h" 1781ad6265SDimitry Andric #include "clang/AST/Expr.h" 1881ad6265SDimitry Andric #include "clang/AST/ExprCXX.h" 1981ad6265SDimitry Andric #include "clang/AST/Stmt.h" 2081ad6265SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h" 21*bdd1243dSDimitry Andric #include "clang/Analysis/CFG.h" 22*bdd1243dSDimitry Andric #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h" 2381ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 2481ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/NoopLattice.h" 25*bdd1243dSDimitry Andric #include "clang/Analysis/FlowSensitive/StorageLocation.h" 2681ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/Value.h" 2781ad6265SDimitry Andric #include "clang/Basic/SourceLocation.h" 2881ad6265SDimitry Andric #include "llvm/ADT/StringRef.h" 2981ad6265SDimitry Andric #include "llvm/Support/Casting.h" 30*bdd1243dSDimitry Andric #include "llvm/Support/ErrorHandling.h" 3181ad6265SDimitry Andric #include <cassert> 3281ad6265SDimitry Andric #include <memory> 33*bdd1243dSDimitry Andric #include <optional> 3481ad6265SDimitry Andric #include <utility> 3581ad6265SDimitry Andric #include <vector> 3681ad6265SDimitry Andric 3781ad6265SDimitry Andric namespace clang { 3881ad6265SDimitry Andric namespace dataflow { 3981ad6265SDimitry Andric namespace { 4081ad6265SDimitry Andric 4181ad6265SDimitry Andric using namespace ::clang::ast_matchers; 4281ad6265SDimitry Andric using LatticeTransferState = TransferState<NoopLattice>; 4381ad6265SDimitry Andric 4481ad6265SDimitry Andric DeclarationMatcher optionalClass() { 4581ad6265SDimitry Andric return classTemplateSpecializationDecl( 4681ad6265SDimitry Andric anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"), 4781ad6265SDimitry Andric hasName("__optional_destruct_base"), hasName("absl::optional"), 4881ad6265SDimitry Andric hasName("base::Optional")), 4981ad6265SDimitry Andric hasTemplateArgument(0, refersToType(type().bind("T")))); 5081ad6265SDimitry Andric } 5181ad6265SDimitry Andric 5281ad6265SDimitry Andric auto optionalOrAliasType() { 5381ad6265SDimitry Andric return hasUnqualifiedDesugaredType( 5481ad6265SDimitry Andric recordType(hasDeclaration(optionalClass()))); 5581ad6265SDimitry Andric } 5681ad6265SDimitry Andric 5781ad6265SDimitry Andric /// Matches any of the spellings of the optional types and sugar, aliases, etc. 5881ad6265SDimitry Andric auto hasOptionalType() { return hasType(optionalOrAliasType()); } 5981ad6265SDimitry Andric 6081ad6265SDimitry Andric auto isOptionalMemberCallWithName( 6181ad6265SDimitry Andric llvm::StringRef MemberName, 62*bdd1243dSDimitry Andric const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 6381ad6265SDimitry Andric auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr())) 6481ad6265SDimitry Andric : cxxThisExpr()); 6581ad6265SDimitry Andric return cxxMemberCallExpr( 6681ad6265SDimitry Andric on(expr(Exception)), 6781ad6265SDimitry Andric callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass())))); 6881ad6265SDimitry Andric } 6981ad6265SDimitry Andric 7081ad6265SDimitry Andric auto isOptionalOperatorCallWithName( 7181ad6265SDimitry Andric llvm::StringRef operator_name, 72*bdd1243dSDimitry Andric const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 7381ad6265SDimitry Andric return cxxOperatorCallExpr( 7481ad6265SDimitry Andric hasOverloadedOperatorName(operator_name), 7581ad6265SDimitry Andric callee(cxxMethodDecl(ofClass(optionalClass()))), 7681ad6265SDimitry Andric Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 7781ad6265SDimitry Andric } 7881ad6265SDimitry Andric 7981ad6265SDimitry Andric auto isMakeOptionalCall() { 8081ad6265SDimitry Andric return callExpr( 8181ad6265SDimitry Andric callee(functionDecl(hasAnyName( 8281ad6265SDimitry Andric "std::make_optional", "base::make_optional", "absl::make_optional"))), 8381ad6265SDimitry Andric hasOptionalType()); 8481ad6265SDimitry Andric } 8581ad6265SDimitry Andric 86*bdd1243dSDimitry Andric auto nulloptTypeDecl() { 87*bdd1243dSDimitry Andric return namedDecl( 88*bdd1243dSDimitry Andric hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")); 8981ad6265SDimitry Andric } 9081ad6265SDimitry Andric 91*bdd1243dSDimitry Andric auto hasNulloptType() { return hasType(nulloptTypeDecl()); } 92*bdd1243dSDimitry Andric 93*bdd1243dSDimitry Andric // `optional` or `nullopt_t` 94*bdd1243dSDimitry Andric auto hasAnyOptionalType() { 95*bdd1243dSDimitry Andric return hasType(hasUnqualifiedDesugaredType( 96*bdd1243dSDimitry Andric recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass()))))); 97*bdd1243dSDimitry Andric } 98*bdd1243dSDimitry Andric 99*bdd1243dSDimitry Andric 10081ad6265SDimitry Andric auto inPlaceClass() { 10181ad6265SDimitry Andric return recordDecl( 10281ad6265SDimitry Andric hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t")); 10381ad6265SDimitry Andric } 10481ad6265SDimitry Andric 10581ad6265SDimitry Andric auto isOptionalNulloptConstructor() { 106*bdd1243dSDimitry Andric return cxxConstructExpr( 107*bdd1243dSDimitry Andric hasOptionalType(), 108*bdd1243dSDimitry Andric hasDeclaration(cxxConstructorDecl(parameterCountIs(1), 109*bdd1243dSDimitry Andric hasParameter(0, hasNulloptType())))); 11081ad6265SDimitry Andric } 11181ad6265SDimitry Andric 11281ad6265SDimitry Andric auto isOptionalInPlaceConstructor() { 11381ad6265SDimitry Andric return cxxConstructExpr(hasOptionalType(), 11481ad6265SDimitry Andric hasArgument(0, hasType(inPlaceClass()))); 11581ad6265SDimitry Andric } 11681ad6265SDimitry Andric 11781ad6265SDimitry Andric auto isOptionalValueOrConversionConstructor() { 11881ad6265SDimitry Andric return cxxConstructExpr( 11981ad6265SDimitry Andric hasOptionalType(), 12081ad6265SDimitry Andric unless(hasDeclaration( 12181ad6265SDimitry Andric cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 12281ad6265SDimitry Andric argumentCountIs(1), hasArgument(0, unless(hasNulloptType()))); 12381ad6265SDimitry Andric } 12481ad6265SDimitry Andric 12581ad6265SDimitry Andric auto isOptionalValueOrConversionAssignment() { 12681ad6265SDimitry Andric return cxxOperatorCallExpr( 12781ad6265SDimitry Andric hasOverloadedOperatorName("="), 12881ad6265SDimitry Andric callee(cxxMethodDecl(ofClass(optionalClass()))), 12981ad6265SDimitry Andric unless(hasDeclaration(cxxMethodDecl( 13081ad6265SDimitry Andric anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 13181ad6265SDimitry Andric argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 13281ad6265SDimitry Andric } 13381ad6265SDimitry Andric 134*bdd1243dSDimitry Andric auto isNulloptConstructor() { 135*bdd1243dSDimitry Andric return cxxConstructExpr(hasNulloptType(), argumentCountIs(1), 136*bdd1243dSDimitry Andric hasArgument(0, hasNulloptType())); 137*bdd1243dSDimitry Andric } 138*bdd1243dSDimitry Andric 13981ad6265SDimitry Andric auto isOptionalNulloptAssignment() { 14081ad6265SDimitry Andric return cxxOperatorCallExpr(hasOverloadedOperatorName("="), 14181ad6265SDimitry Andric callee(cxxMethodDecl(ofClass(optionalClass()))), 14281ad6265SDimitry Andric argumentCountIs(2), 14381ad6265SDimitry Andric hasArgument(1, hasNulloptType())); 14481ad6265SDimitry Andric } 14581ad6265SDimitry Andric 14681ad6265SDimitry Andric auto isStdSwapCall() { 14781ad6265SDimitry Andric return callExpr(callee(functionDecl(hasName("std::swap"))), 14881ad6265SDimitry Andric argumentCountIs(2), hasArgument(0, hasOptionalType()), 14981ad6265SDimitry Andric hasArgument(1, hasOptionalType())); 15081ad6265SDimitry Andric } 15181ad6265SDimitry Andric 15281ad6265SDimitry Andric constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 15381ad6265SDimitry Andric 15481ad6265SDimitry Andric auto isValueOrStringEmptyCall() { 15581ad6265SDimitry Andric // `opt.value_or("").empty()` 15681ad6265SDimitry Andric return cxxMemberCallExpr( 15781ad6265SDimitry Andric callee(cxxMethodDecl(hasName("empty"))), 15881ad6265SDimitry Andric onImplicitObjectArgument(ignoringImplicit( 15981ad6265SDimitry Andric cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 16081ad6265SDimitry Andric callee(cxxMethodDecl(hasName("value_or"), 16181ad6265SDimitry Andric ofClass(optionalClass()))), 16281ad6265SDimitry Andric hasArgument(0, stringLiteral(hasSize(0)))) 16381ad6265SDimitry Andric .bind(ValueOrCallID)))); 16481ad6265SDimitry Andric } 16581ad6265SDimitry Andric 16681ad6265SDimitry Andric auto isValueOrNotEqX() { 16781ad6265SDimitry Andric auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 16881ad6265SDimitry Andric return hasOperands( 16981ad6265SDimitry Andric ignoringImplicit( 17081ad6265SDimitry Andric cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 17181ad6265SDimitry Andric callee(cxxMethodDecl(hasName("value_or"), 17281ad6265SDimitry Andric ofClass(optionalClass()))), 17381ad6265SDimitry Andric hasArgument(0, Arg)) 17481ad6265SDimitry Andric .bind(ValueOrCallID)), 17581ad6265SDimitry Andric ignoringImplicit(Arg)); 17681ad6265SDimitry Andric }; 17781ad6265SDimitry Andric 17881ad6265SDimitry Andric // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 17981ad6265SDimitry Andric // support this pattern for any expression, but the AST does not have a 18081ad6265SDimitry Andric // generic expression comparison facility, so we specialize to common cases 18181ad6265SDimitry Andric // seen in practice. FIXME: define a matcher that compares values across 18281ad6265SDimitry Andric // nodes, which would let us generalize this to any `X`. 18381ad6265SDimitry Andric return binaryOperation(hasOperatorName("!="), 18481ad6265SDimitry Andric anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 18581ad6265SDimitry Andric ComparesToSame(stringLiteral(hasSize(0))), 18681ad6265SDimitry Andric ComparesToSame(integerLiteral(equals(0))))); 18781ad6265SDimitry Andric } 18881ad6265SDimitry Andric 18981ad6265SDimitry Andric auto isCallReturningOptional() { 19081ad6265SDimitry Andric return callExpr(hasType(qualType(anyOf( 19181ad6265SDimitry Andric optionalOrAliasType(), referenceType(pointee(optionalOrAliasType())))))); 19281ad6265SDimitry Andric } 19381ad6265SDimitry Andric 194*bdd1243dSDimitry Andric template <typename L, typename R> 195*bdd1243dSDimitry Andric auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 196*bdd1243dSDimitry Andric return cxxOperatorCallExpr( 197*bdd1243dSDimitry Andric anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 198*bdd1243dSDimitry Andric argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 199*bdd1243dSDimitry Andric hasArgument(1, rhs_arg_matcher)); 200*bdd1243dSDimitry Andric } 201*bdd1243dSDimitry Andric 202*bdd1243dSDimitry Andric // Ensures that `Expr` is mapped to a `BoolValue` and returns it. 203*bdd1243dSDimitry Andric BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) { 204*bdd1243dSDimitry Andric auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None)); 205*bdd1243dSDimitry Andric if (Value != nullptr) 206*bdd1243dSDimitry Andric return *Value; 207*bdd1243dSDimitry Andric 208*bdd1243dSDimitry Andric auto &Loc = Env.createStorageLocation(Expr); 209*bdd1243dSDimitry Andric Value = &Env.makeAtomicBoolValue(); 210*bdd1243dSDimitry Andric Env.setValue(Loc, *Value); 211*bdd1243dSDimitry Andric Env.setStorageLocation(Expr, Loc); 212*bdd1243dSDimitry Andric return *Value; 213*bdd1243dSDimitry Andric } 214*bdd1243dSDimitry Andric 21581ad6265SDimitry Andric /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 21681ad6265SDimitry Andric /// property of the optional value `OptionalVal`. 21781ad6265SDimitry Andric void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) { 21881ad6265SDimitry Andric OptionalVal.setProperty("has_value", HasValueVal); 21981ad6265SDimitry Andric } 22081ad6265SDimitry Andric 22181ad6265SDimitry Andric /// Creates a symbolic value for an `optional` value using `HasValueVal` as the 22281ad6265SDimitry Andric /// symbolic value of its "has_value" property. 22381ad6265SDimitry Andric StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) { 22481ad6265SDimitry Andric auto OptionalVal = std::make_unique<StructValue>(); 22581ad6265SDimitry Andric setHasValue(*OptionalVal, HasValueVal); 22681ad6265SDimitry Andric return Env.takeOwnership(std::move(OptionalVal)); 22781ad6265SDimitry Andric } 22881ad6265SDimitry Andric 22981ad6265SDimitry Andric /// Returns the symbolic value that represents the "has_value" property of the 23081ad6265SDimitry Andric /// optional value `OptionalVal`. Returns null if `OptionalVal` is null. 23181ad6265SDimitry Andric BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { 23281ad6265SDimitry Andric if (OptionalVal != nullptr) { 23381ad6265SDimitry Andric auto *HasValueVal = 23481ad6265SDimitry Andric cast_or_null<BoolValue>(OptionalVal->getProperty("has_value")); 23581ad6265SDimitry Andric if (HasValueVal == nullptr) { 23681ad6265SDimitry Andric HasValueVal = &Env.makeAtomicBoolValue(); 23781ad6265SDimitry Andric OptionalVal->setProperty("has_value", *HasValueVal); 23881ad6265SDimitry Andric } 23981ad6265SDimitry Andric return HasValueVal; 24081ad6265SDimitry Andric } 24181ad6265SDimitry Andric return nullptr; 24281ad6265SDimitry Andric } 24381ad6265SDimitry Andric 24481ad6265SDimitry Andric /// If `Type` is a reference type, returns the type of its pointee. Otherwise, 24581ad6265SDimitry Andric /// returns `Type` itself. 24681ad6265SDimitry Andric QualType stripReference(QualType Type) { 24781ad6265SDimitry Andric return Type->isReferenceType() ? Type->getPointeeType() : Type; 24881ad6265SDimitry Andric } 24981ad6265SDimitry Andric 25081ad6265SDimitry Andric /// Returns true if and only if `Type` is an optional type. 251*bdd1243dSDimitry Andric bool isOptionalType(QualType Type) { 25281ad6265SDimitry Andric if (!Type->isRecordType()) 25381ad6265SDimitry Andric return false; 25481ad6265SDimitry Andric // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call. 25581ad6265SDimitry Andric auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString(); 25681ad6265SDimitry Andric return TypeName == "std::optional" || TypeName == "absl::optional" || 25781ad6265SDimitry Andric TypeName == "base::Optional"; 25881ad6265SDimitry Andric } 25981ad6265SDimitry Andric 26081ad6265SDimitry Andric /// Returns the number of optional wrappers in `Type`. 26181ad6265SDimitry Andric /// 26281ad6265SDimitry Andric /// For example, if `Type` is `optional<optional<int>>`, the result of this 26381ad6265SDimitry Andric /// function will be 2. 26481ad6265SDimitry Andric int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 265*bdd1243dSDimitry Andric if (!isOptionalType(Type)) 26681ad6265SDimitry Andric return 0; 26781ad6265SDimitry Andric return 1 + countOptionalWrappers( 26881ad6265SDimitry Andric ASTCtx, 26981ad6265SDimitry Andric cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) 27081ad6265SDimitry Andric ->getTemplateArgs() 27181ad6265SDimitry Andric .get(0) 27281ad6265SDimitry Andric .getAsType() 27381ad6265SDimitry Andric .getDesugaredType(ASTCtx)); 27481ad6265SDimitry Andric } 27581ad6265SDimitry Andric 27681ad6265SDimitry Andric /// Tries to initialize the `optional`'s value (that is, contents), and return 27781ad6265SDimitry Andric /// its location. Returns nullptr if the value can't be represented. 27881ad6265SDimitry Andric StorageLocation *maybeInitializeOptionalValueMember(QualType Q, 27981ad6265SDimitry Andric Value &OptionalVal, 28081ad6265SDimitry Andric Environment &Env) { 28181ad6265SDimitry Andric // The "value" property represents a synthetic field. As such, it needs 28281ad6265SDimitry Andric // `StorageLocation`, like normal fields (and other variables). So, we model 28381ad6265SDimitry Andric // it with a `ReferenceValue`, since that includes a storage location. Once 28481ad6265SDimitry Andric // the property is set, it will be shared by all environments that access the 28581ad6265SDimitry Andric // `Value` representing the optional (here, `OptionalVal`). 28681ad6265SDimitry Andric if (auto *ValueProp = OptionalVal.getProperty("value")) { 28781ad6265SDimitry Andric auto *ValueRef = clang::cast<ReferenceValue>(ValueProp); 28881ad6265SDimitry Andric auto &ValueLoc = ValueRef->getReferentLoc(); 28981ad6265SDimitry Andric if (Env.getValue(ValueLoc) == nullptr) { 29081ad6265SDimitry Andric // The property was previously set, but the value has been lost. This can 29181ad6265SDimitry Andric // happen, for example, because of an environment merge (where the two 29281ad6265SDimitry Andric // environments mapped the property to different values, which resulted in 29381ad6265SDimitry Andric // them both being discarded), or when two blocks in the CFG, with neither 29481ad6265SDimitry Andric // a dominator of the other, visit the same optional value, or even when a 29581ad6265SDimitry Andric // block is revisited during testing to collect per-statement state. 29681ad6265SDimitry Andric // FIXME: This situation means that the optional contents are not shared 29781ad6265SDimitry Andric // between branches and the like. Practically, this lack of sharing 29881ad6265SDimitry Andric // reduces the precision of the model when the contents are relevant to 29981ad6265SDimitry Andric // the check, like another optional or a boolean that influences control 30081ad6265SDimitry Andric // flow. 30181ad6265SDimitry Andric auto *ValueVal = Env.createValue(ValueLoc.getType()); 30281ad6265SDimitry Andric if (ValueVal == nullptr) 30381ad6265SDimitry Andric return nullptr; 30481ad6265SDimitry Andric Env.setValue(ValueLoc, *ValueVal); 30581ad6265SDimitry Andric } 30681ad6265SDimitry Andric return &ValueLoc; 30781ad6265SDimitry Andric } 30881ad6265SDimitry Andric 30981ad6265SDimitry Andric auto Ty = stripReference(Q); 31081ad6265SDimitry Andric auto *ValueVal = Env.createValue(Ty); 31181ad6265SDimitry Andric if (ValueVal == nullptr) 31281ad6265SDimitry Andric return nullptr; 31381ad6265SDimitry Andric auto &ValueLoc = Env.createStorageLocation(Ty); 31481ad6265SDimitry Andric Env.setValue(ValueLoc, *ValueVal); 31581ad6265SDimitry Andric auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc); 31681ad6265SDimitry Andric OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef))); 31781ad6265SDimitry Andric return &ValueLoc; 31881ad6265SDimitry Andric } 31981ad6265SDimitry Andric 32081ad6265SDimitry Andric void initializeOptionalReference(const Expr *OptionalExpr, 32181ad6265SDimitry Andric const MatchFinder::MatchResult &, 32281ad6265SDimitry Andric LatticeTransferState &State) { 32381ad6265SDimitry Andric if (auto *OptionalVal = 32481ad6265SDimitry Andric State.Env.getValue(*OptionalExpr, SkipPast::Reference)) { 32581ad6265SDimitry Andric if (OptionalVal->getProperty("has_value") == nullptr) { 32681ad6265SDimitry Andric setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); 32781ad6265SDimitry Andric } 32881ad6265SDimitry Andric } 32981ad6265SDimitry Andric } 33081ad6265SDimitry Andric 33181ad6265SDimitry Andric /// Returns true if and only if `OptionalVal` is initialized and known to be 33281ad6265SDimitry Andric /// empty in `Env. 33381ad6265SDimitry Andric bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { 33481ad6265SDimitry Andric auto *HasValueVal = 33581ad6265SDimitry Andric cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 33681ad6265SDimitry Andric return HasValueVal != nullptr && 33781ad6265SDimitry Andric Env.flowConditionImplies(Env.makeNot(*HasValueVal)); 33881ad6265SDimitry Andric } 33981ad6265SDimitry Andric 34081ad6265SDimitry Andric /// Returns true if and only if `OptionalVal` is initialized and known to be 34181ad6265SDimitry Andric /// non-empty in `Env. 34281ad6265SDimitry Andric bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { 34381ad6265SDimitry Andric auto *HasValueVal = 34481ad6265SDimitry Andric cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 34581ad6265SDimitry Andric return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal); 34681ad6265SDimitry Andric } 34781ad6265SDimitry Andric 34881ad6265SDimitry Andric void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 34981ad6265SDimitry Andric LatticeTransferState &State) { 35081ad6265SDimitry Andric if (auto *OptionalVal = 35181ad6265SDimitry Andric State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 35281ad6265SDimitry Andric if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr) 35381ad6265SDimitry Andric if (auto *Loc = maybeInitializeOptionalValueMember( 35481ad6265SDimitry Andric UnwrapExpr->getType(), *OptionalVal, State.Env)) 35581ad6265SDimitry Andric State.Env.setStorageLocation(*UnwrapExpr, *Loc); 35681ad6265SDimitry Andric } 35781ad6265SDimitry Andric } 35881ad6265SDimitry Andric 35981ad6265SDimitry Andric void transferMakeOptionalCall(const CallExpr *E, 36081ad6265SDimitry Andric const MatchFinder::MatchResult &, 36181ad6265SDimitry Andric LatticeTransferState &State) { 36281ad6265SDimitry Andric auto &Loc = State.Env.createStorageLocation(*E); 36381ad6265SDimitry Andric State.Env.setStorageLocation(*E, Loc); 36481ad6265SDimitry Andric State.Env.setValue( 36581ad6265SDimitry Andric Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true))); 36681ad6265SDimitry Andric } 36781ad6265SDimitry Andric 36881ad6265SDimitry Andric void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 36981ad6265SDimitry Andric const MatchFinder::MatchResult &, 37081ad6265SDimitry Andric LatticeTransferState &State) { 37181ad6265SDimitry Andric if (auto *HasValueVal = getHasValue( 37281ad6265SDimitry Andric State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(), 37381ad6265SDimitry Andric SkipPast::ReferenceThenPointer))) { 37481ad6265SDimitry Andric auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr); 37581ad6265SDimitry Andric State.Env.setValue(CallExprLoc, *HasValueVal); 37681ad6265SDimitry Andric State.Env.setStorageLocation(*CallExpr, CallExprLoc); 37781ad6265SDimitry Andric } 37881ad6265SDimitry Andric } 37981ad6265SDimitry Andric 38081ad6265SDimitry Andric /// `ModelPred` builds a logical formula relating the predicate in 38181ad6265SDimitry Andric /// `ValueOrPredExpr` to the optional's `has_value` property. 38281ad6265SDimitry Andric void transferValueOrImpl(const clang::Expr *ValueOrPredExpr, 38381ad6265SDimitry Andric const MatchFinder::MatchResult &Result, 38481ad6265SDimitry Andric LatticeTransferState &State, 38581ad6265SDimitry Andric BoolValue &(*ModelPred)(Environment &Env, 38681ad6265SDimitry Andric BoolValue &ExprVal, 38781ad6265SDimitry Andric BoolValue &HasValueVal)) { 38881ad6265SDimitry Andric auto &Env = State.Env; 38981ad6265SDimitry Andric 39081ad6265SDimitry Andric const auto *ObjectArgumentExpr = 39181ad6265SDimitry Andric Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) 39281ad6265SDimitry Andric ->getImplicitObjectArgument(); 39381ad6265SDimitry Andric 39481ad6265SDimitry Andric auto *HasValueVal = getHasValue( 39581ad6265SDimitry Andric State.Env, 39681ad6265SDimitry Andric State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer)); 39781ad6265SDimitry Andric if (HasValueVal == nullptr) 39881ad6265SDimitry Andric return; 39981ad6265SDimitry Andric 400*bdd1243dSDimitry Andric Env.addToFlowCondition( 401*bdd1243dSDimitry Andric ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal)); 40281ad6265SDimitry Andric } 40381ad6265SDimitry Andric 40481ad6265SDimitry Andric void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 40581ad6265SDimitry Andric const MatchFinder::MatchResult &Result, 40681ad6265SDimitry Andric LatticeTransferState &State) { 40781ad6265SDimitry Andric return transferValueOrImpl(ComparisonExpr, Result, State, 40881ad6265SDimitry Andric [](Environment &Env, BoolValue &ExprVal, 40981ad6265SDimitry Andric BoolValue &HasValueVal) -> BoolValue & { 41081ad6265SDimitry Andric // If the result is *not* empty, then we know the 41181ad6265SDimitry Andric // optional must have been holding a value. If 41281ad6265SDimitry Andric // `ExprVal` is true, though, we don't learn 41381ad6265SDimitry Andric // anything definite about `has_value`, so we 41481ad6265SDimitry Andric // don't add any corresponding implications to 41581ad6265SDimitry Andric // the flow condition. 41681ad6265SDimitry Andric return Env.makeImplication(Env.makeNot(ExprVal), 41781ad6265SDimitry Andric HasValueVal); 41881ad6265SDimitry Andric }); 41981ad6265SDimitry Andric } 42081ad6265SDimitry Andric 42181ad6265SDimitry Andric void transferValueOrNotEqX(const Expr *ComparisonExpr, 42281ad6265SDimitry Andric const MatchFinder::MatchResult &Result, 42381ad6265SDimitry Andric LatticeTransferState &State) { 42481ad6265SDimitry Andric transferValueOrImpl(ComparisonExpr, Result, State, 42581ad6265SDimitry Andric [](Environment &Env, BoolValue &ExprVal, 42681ad6265SDimitry Andric BoolValue &HasValueVal) -> BoolValue & { 42781ad6265SDimitry Andric // We know that if `(opt.value_or(X) != X)` then 42881ad6265SDimitry Andric // `opt.hasValue()`, even without knowing further 42981ad6265SDimitry Andric // details about the contents of `opt`. 43081ad6265SDimitry Andric return Env.makeImplication(ExprVal, HasValueVal); 43181ad6265SDimitry Andric }); 43281ad6265SDimitry Andric } 43381ad6265SDimitry Andric 43481ad6265SDimitry Andric void transferCallReturningOptional(const CallExpr *E, 43581ad6265SDimitry Andric const MatchFinder::MatchResult &Result, 43681ad6265SDimitry Andric LatticeTransferState &State) { 43781ad6265SDimitry Andric if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) 43881ad6265SDimitry Andric return; 43981ad6265SDimitry Andric 44081ad6265SDimitry Andric auto &Loc = State.Env.createStorageLocation(*E); 44181ad6265SDimitry Andric State.Env.setStorageLocation(*E, Loc); 44281ad6265SDimitry Andric State.Env.setValue( 44381ad6265SDimitry Andric Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue())); 44481ad6265SDimitry Andric } 44581ad6265SDimitry Andric 446*bdd1243dSDimitry Andric void assignOptionalValue(const Expr &E, Environment &Env, 44781ad6265SDimitry Andric BoolValue &HasValueVal) { 44881ad6265SDimitry Andric if (auto *OptionalLoc = 449*bdd1243dSDimitry Andric Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) { 450*bdd1243dSDimitry Andric Env.setValue(*OptionalLoc, createOptionalValue(Env, HasValueVal)); 45181ad6265SDimitry Andric } 45281ad6265SDimitry Andric } 45381ad6265SDimitry Andric 45481ad6265SDimitry Andric /// Returns a symbolic value for the "has_value" property of an `optional<T>` 45581ad6265SDimitry Andric /// value that is constructed/assigned from a value of type `U` or `optional<U>` 45681ad6265SDimitry Andric /// where `T` is constructible from `U`. 457*bdd1243dSDimitry Andric BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E, 45881ad6265SDimitry Andric const MatchFinder::MatchResult &MatchRes, 45981ad6265SDimitry Andric LatticeTransferState &State) { 460*bdd1243dSDimitry Andric assert(F.getTemplateSpecializationArgs() != nullptr); 46181ad6265SDimitry Andric assert(F.getTemplateSpecializationArgs()->size() > 0); 46281ad6265SDimitry Andric 46381ad6265SDimitry Andric const int TemplateParamOptionalWrappersCount = countOptionalWrappers( 46481ad6265SDimitry Andric *MatchRes.Context, 46581ad6265SDimitry Andric stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType())); 46681ad6265SDimitry Andric const int ArgTypeOptionalWrappersCount = 46781ad6265SDimitry Andric countOptionalWrappers(*MatchRes.Context, stripReference(E.getType())); 46881ad6265SDimitry Andric 46981ad6265SDimitry Andric // Check if this is a constructor/assignment call for `optional<T>` with 47081ad6265SDimitry Andric // argument of type `U` such that `T` is constructible from `U`. 47181ad6265SDimitry Andric if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) 47281ad6265SDimitry Andric return State.Env.getBoolLiteralValue(true); 47381ad6265SDimitry Andric 47481ad6265SDimitry Andric // This is a constructor/assignment call for `optional<T>` with argument of 47581ad6265SDimitry Andric // type `optional<U>` such that `T` is constructible from `U`. 47681ad6265SDimitry Andric if (auto *HasValueVal = 47781ad6265SDimitry Andric getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference))) 47881ad6265SDimitry Andric return *HasValueVal; 47981ad6265SDimitry Andric return State.Env.makeAtomicBoolValue(); 48081ad6265SDimitry Andric } 48181ad6265SDimitry Andric 48281ad6265SDimitry Andric void transferValueOrConversionConstructor( 48381ad6265SDimitry Andric const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 48481ad6265SDimitry Andric LatticeTransferState &State) { 48581ad6265SDimitry Andric assert(E->getNumArgs() > 0); 48681ad6265SDimitry Andric 487*bdd1243dSDimitry Andric assignOptionalValue(*E, State.Env, 488*bdd1243dSDimitry Andric valueOrConversionHasValue(*E->getConstructor(), 48981ad6265SDimitry Andric *E->getArg(0), MatchRes, 49081ad6265SDimitry Andric State)); 49181ad6265SDimitry Andric } 49281ad6265SDimitry Andric 49381ad6265SDimitry Andric void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 49481ad6265SDimitry Andric LatticeTransferState &State) { 49581ad6265SDimitry Andric assert(E->getNumArgs() > 0); 49681ad6265SDimitry Andric 49781ad6265SDimitry Andric auto *OptionalLoc = 49881ad6265SDimitry Andric State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 49981ad6265SDimitry Andric if (OptionalLoc == nullptr) 50081ad6265SDimitry Andric return; 50181ad6265SDimitry Andric 50281ad6265SDimitry Andric State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal)); 50381ad6265SDimitry Andric 50481ad6265SDimitry Andric // Assign a storage location for the whole expression. 50581ad6265SDimitry Andric State.Env.setStorageLocation(*E, *OptionalLoc); 50681ad6265SDimitry Andric } 50781ad6265SDimitry Andric 50881ad6265SDimitry Andric void transferValueOrConversionAssignment( 50981ad6265SDimitry Andric const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 51081ad6265SDimitry Andric LatticeTransferState &State) { 51181ad6265SDimitry Andric assert(E->getNumArgs() > 1); 51281ad6265SDimitry Andric transferAssignment(E, 513*bdd1243dSDimitry Andric valueOrConversionHasValue(*E->getDirectCallee(), 51481ad6265SDimitry Andric *E->getArg(1), MatchRes, State), 51581ad6265SDimitry Andric State); 51681ad6265SDimitry Andric } 51781ad6265SDimitry Andric 51881ad6265SDimitry Andric void transferNulloptAssignment(const CXXOperatorCallExpr *E, 51981ad6265SDimitry Andric const MatchFinder::MatchResult &, 52081ad6265SDimitry Andric LatticeTransferState &State) { 52181ad6265SDimitry Andric transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 52281ad6265SDimitry Andric } 52381ad6265SDimitry Andric 52481ad6265SDimitry Andric void transferSwap(const StorageLocation &OptionalLoc1, 52581ad6265SDimitry Andric const StorageLocation &OptionalLoc2, 52681ad6265SDimitry Andric LatticeTransferState &State) { 52781ad6265SDimitry Andric auto *OptionalVal1 = State.Env.getValue(OptionalLoc1); 52881ad6265SDimitry Andric assert(OptionalVal1 != nullptr); 52981ad6265SDimitry Andric 53081ad6265SDimitry Andric auto *OptionalVal2 = State.Env.getValue(OptionalLoc2); 53181ad6265SDimitry Andric assert(OptionalVal2 != nullptr); 53281ad6265SDimitry Andric 53381ad6265SDimitry Andric State.Env.setValue(OptionalLoc1, *OptionalVal2); 53481ad6265SDimitry Andric State.Env.setValue(OptionalLoc2, *OptionalVal1); 53581ad6265SDimitry Andric } 53681ad6265SDimitry Andric 53781ad6265SDimitry Andric void transferSwapCall(const CXXMemberCallExpr *E, 53881ad6265SDimitry Andric const MatchFinder::MatchResult &, 53981ad6265SDimitry Andric LatticeTransferState &State) { 54081ad6265SDimitry Andric assert(E->getNumArgs() == 1); 54181ad6265SDimitry Andric 54281ad6265SDimitry Andric auto *OptionalLoc1 = State.Env.getStorageLocation( 54381ad6265SDimitry Andric *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer); 54481ad6265SDimitry Andric assert(OptionalLoc1 != nullptr); 54581ad6265SDimitry Andric 54681ad6265SDimitry Andric auto *OptionalLoc2 = 54781ad6265SDimitry Andric State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 54881ad6265SDimitry Andric assert(OptionalLoc2 != nullptr); 54981ad6265SDimitry Andric 55081ad6265SDimitry Andric transferSwap(*OptionalLoc1, *OptionalLoc2, State); 55181ad6265SDimitry Andric } 55281ad6265SDimitry Andric 55381ad6265SDimitry Andric void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 55481ad6265SDimitry Andric LatticeTransferState &State) { 55581ad6265SDimitry Andric assert(E->getNumArgs() == 2); 55681ad6265SDimitry Andric 55781ad6265SDimitry Andric auto *OptionalLoc1 = 55881ad6265SDimitry Andric State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 55981ad6265SDimitry Andric assert(OptionalLoc1 != nullptr); 56081ad6265SDimitry Andric 56181ad6265SDimitry Andric auto *OptionalLoc2 = 56281ad6265SDimitry Andric State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference); 56381ad6265SDimitry Andric assert(OptionalLoc2 != nullptr); 56481ad6265SDimitry Andric 56581ad6265SDimitry Andric transferSwap(*OptionalLoc1, *OptionalLoc2, State); 56681ad6265SDimitry Andric } 56781ad6265SDimitry Andric 568*bdd1243dSDimitry Andric BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS, 569*bdd1243dSDimitry Andric BoolValue &RHS) { 570*bdd1243dSDimitry Andric // Logically, an optional<T> object is composed of two values - a `has_value` 571*bdd1243dSDimitry Andric // bit and a value of type T. Equality of optional objects compares both 572*bdd1243dSDimitry Andric // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 573*bdd1243dSDimitry Andric // when two optional objects are engaged, the equality of their respective 574*bdd1243dSDimitry Andric // values of type T matters. Since we only track the `has_value` bits, we 575*bdd1243dSDimitry Andric // can't make any conclusions about equality when we know that two optional 576*bdd1243dSDimitry Andric // objects are engaged. 577*bdd1243dSDimitry Andric // 578*bdd1243dSDimitry Andric // We express this as two facts about the equality: 579*bdd1243dSDimitry Andric // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 580*bdd1243dSDimitry Andric // If they are equal, then either both are set or both are unset. 581*bdd1243dSDimitry Andric // b) (!LHS & !RHS) => EqVal 582*bdd1243dSDimitry Andric // If neither is set, then they are equal. 583*bdd1243dSDimitry Andric // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 584*bdd1243dSDimitry Andric return Env.makeAnd( 585*bdd1243dSDimitry Andric Env.makeImplication( 586*bdd1243dSDimitry Andric EqVal, Env.makeOr(Env.makeAnd(LHS, RHS), 587*bdd1243dSDimitry Andric Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))), 588*bdd1243dSDimitry Andric Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS))); 589*bdd1243dSDimitry Andric } 590*bdd1243dSDimitry Andric 591*bdd1243dSDimitry Andric void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 592*bdd1243dSDimitry Andric const MatchFinder::MatchResult &, 593*bdd1243dSDimitry Andric LatticeTransferState &State) { 594*bdd1243dSDimitry Andric Environment &Env = State.Env; 595*bdd1243dSDimitry Andric auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 596*bdd1243dSDimitry Andric if (auto *LHasVal = getHasValue( 597*bdd1243dSDimitry Andric Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference))) 598*bdd1243dSDimitry Andric if (auto *RHasVal = getHasValue( 599*bdd1243dSDimitry Andric Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) { 600*bdd1243dSDimitry Andric if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 601*bdd1243dSDimitry Andric CmpValue = &State.Env.makeNot(*CmpValue); 602*bdd1243dSDimitry Andric Env.addToFlowCondition( 603*bdd1243dSDimitry Andric evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal)); 604*bdd1243dSDimitry Andric } 605*bdd1243dSDimitry Andric } 606*bdd1243dSDimitry Andric 607*bdd1243dSDimitry Andric void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 608*bdd1243dSDimitry Andric const clang::Expr *E, Environment &Env) { 609*bdd1243dSDimitry Andric auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 610*bdd1243dSDimitry Andric if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) { 611*bdd1243dSDimitry Andric if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 612*bdd1243dSDimitry Andric CmpValue = &Env.makeNot(*CmpValue); 613*bdd1243dSDimitry Andric Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal, 614*bdd1243dSDimitry Andric Env.getBoolLiteralValue(true))); 615*bdd1243dSDimitry Andric } 616*bdd1243dSDimitry Andric } 617*bdd1243dSDimitry Andric 618*bdd1243dSDimitry Andric std::optional<StatementMatcher> 61981ad6265SDimitry Andric ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 620*bdd1243dSDimitry Andric if (Options.IgnoreSmartPointerDereference) { 621*bdd1243dSDimitry Andric auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 622*bdd1243dSDimitry Andric anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 623*bdd1243dSDimitry Andric unless(hasArgument(0, expr(hasOptionalType())))))); 624*bdd1243dSDimitry Andric return expr( 625*bdd1243dSDimitry Andric anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 626*bdd1243dSDimitry Andric } 627*bdd1243dSDimitry Andric return std::nullopt; 62881ad6265SDimitry Andric } 62981ad6265SDimitry Andric 63081ad6265SDimitry Andric StatementMatcher 631*bdd1243dSDimitry Andric valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 63281ad6265SDimitry Andric return isOptionalMemberCallWithName("value", IgnorableOptional); 63381ad6265SDimitry Andric } 63481ad6265SDimitry Andric 63581ad6265SDimitry Andric StatementMatcher 636*bdd1243dSDimitry Andric valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 63781ad6265SDimitry Andric return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 63881ad6265SDimitry Andric isOptionalOperatorCallWithName("->", IgnorableOptional))); 63981ad6265SDimitry Andric } 64081ad6265SDimitry Andric 641*bdd1243dSDimitry Andric auto buildTransferMatchSwitch() { 64281ad6265SDimitry Andric // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 64381ad6265SDimitry Andric // lot of duplicated work (e.g. string comparisons), consider providing APIs 64481ad6265SDimitry Andric // that avoid it through memoization. 645*bdd1243dSDimitry Andric return CFGMatchSwitchBuilder<LatticeTransferState>() 64681ad6265SDimitry Andric // Attach a symbolic "has_value" state to optional values that we see for 64781ad6265SDimitry Andric // the first time. 648*bdd1243dSDimitry Andric .CaseOfCFGStmt<Expr>( 64981ad6265SDimitry Andric expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), 65081ad6265SDimitry Andric initializeOptionalReference) 65181ad6265SDimitry Andric 65281ad6265SDimitry Andric // make_optional 653*bdd1243dSDimitry Andric .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 65481ad6265SDimitry Andric 655*bdd1243dSDimitry Andric // optional::optional (in place) 656*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXConstructExpr>( 65781ad6265SDimitry Andric isOptionalInPlaceConstructor(), 65881ad6265SDimitry Andric [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 65981ad6265SDimitry Andric LatticeTransferState &State) { 660*bdd1243dSDimitry Andric assignOptionalValue(*E, State.Env, 661*bdd1243dSDimitry Andric State.Env.getBoolLiteralValue(true)); 66281ad6265SDimitry Andric }) 663*bdd1243dSDimitry Andric // nullopt_t::nullopt_t 664*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXConstructExpr>( 665*bdd1243dSDimitry Andric isNulloptConstructor(), 666*bdd1243dSDimitry Andric [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 667*bdd1243dSDimitry Andric LatticeTransferState &State) { 668*bdd1243dSDimitry Andric assignOptionalValue(*E, State.Env, 669*bdd1243dSDimitry Andric State.Env.getBoolLiteralValue(false)); 670*bdd1243dSDimitry Andric }) 671*bdd1243dSDimitry Andric // optional::optional(nullopt_t) 672*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXConstructExpr>( 67381ad6265SDimitry Andric isOptionalNulloptConstructor(), 67481ad6265SDimitry Andric [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 67581ad6265SDimitry Andric LatticeTransferState &State) { 676*bdd1243dSDimitry Andric assignOptionalValue(*E, State.Env, 67781ad6265SDimitry Andric State.Env.getBoolLiteralValue(false)); 67881ad6265SDimitry Andric }) 679*bdd1243dSDimitry Andric // optional::optional (value/conversion) 680*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 68181ad6265SDimitry Andric transferValueOrConversionConstructor) 68281ad6265SDimitry Andric 683*bdd1243dSDimitry Andric 68481ad6265SDimitry Andric // optional::operator= 685*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXOperatorCallExpr>( 686*bdd1243dSDimitry Andric isOptionalValueOrConversionAssignment(), 68781ad6265SDimitry Andric transferValueOrConversionAssignment) 688*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 68981ad6265SDimitry Andric transferNulloptAssignment) 69081ad6265SDimitry Andric 69181ad6265SDimitry Andric // optional::value 692*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXMemberCallExpr>( 693*bdd1243dSDimitry Andric valueCall(std::nullopt), 69481ad6265SDimitry Andric [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 69581ad6265SDimitry Andric LatticeTransferState &State) { 69681ad6265SDimitry Andric transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 69781ad6265SDimitry Andric }) 69881ad6265SDimitry Andric 69981ad6265SDimitry Andric // optional::operator*, optional::operator-> 700*bdd1243dSDimitry Andric .CaseOfCFGStmt<CallExpr>(valueOperatorCall(std::nullopt), 701*bdd1243dSDimitry Andric [](const CallExpr *E, 702*bdd1243dSDimitry Andric const MatchFinder::MatchResult &, 70381ad6265SDimitry Andric LatticeTransferState &State) { 70481ad6265SDimitry Andric transferUnwrapCall(E, E->getArg(0), State); 70581ad6265SDimitry Andric }) 70681ad6265SDimitry Andric 70781ad6265SDimitry Andric // optional::has_value 708*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXMemberCallExpr>( 709*bdd1243dSDimitry Andric isOptionalMemberCallWithName("has_value"), 71081ad6265SDimitry Andric transferOptionalHasValueCall) 71181ad6265SDimitry Andric 71281ad6265SDimitry Andric // optional::operator bool 713*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXMemberCallExpr>( 714*bdd1243dSDimitry Andric isOptionalMemberCallWithName("operator bool"), 71581ad6265SDimitry Andric transferOptionalHasValueCall) 71681ad6265SDimitry Andric 71781ad6265SDimitry Andric // optional::emplace 718*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXMemberCallExpr>( 71981ad6265SDimitry Andric isOptionalMemberCallWithName("emplace"), 72081ad6265SDimitry Andric [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 72181ad6265SDimitry Andric LatticeTransferState &State) { 722*bdd1243dSDimitry Andric assignOptionalValue(*E->getImplicitObjectArgument(), State.Env, 72381ad6265SDimitry Andric State.Env.getBoolLiteralValue(true)); 72481ad6265SDimitry Andric }) 72581ad6265SDimitry Andric 72681ad6265SDimitry Andric // optional::reset 727*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXMemberCallExpr>( 72881ad6265SDimitry Andric isOptionalMemberCallWithName("reset"), 72981ad6265SDimitry Andric [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 73081ad6265SDimitry Andric LatticeTransferState &State) { 731*bdd1243dSDimitry Andric assignOptionalValue(*E->getImplicitObjectArgument(), State.Env, 73281ad6265SDimitry Andric State.Env.getBoolLiteralValue(false)); 73381ad6265SDimitry Andric }) 73481ad6265SDimitry Andric 73581ad6265SDimitry Andric // optional::swap 736*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"), 73781ad6265SDimitry Andric transferSwapCall) 73881ad6265SDimitry Andric 73981ad6265SDimitry Andric // std::swap 740*bdd1243dSDimitry Andric .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 74181ad6265SDimitry Andric 74281ad6265SDimitry Andric // opt.value_or("").empty() 743*bdd1243dSDimitry Andric .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 744*bdd1243dSDimitry Andric transferValueOrStringEmptyCall) 74581ad6265SDimitry Andric 74681ad6265SDimitry Andric // opt.value_or(X) != X 747*bdd1243dSDimitry Andric .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 748*bdd1243dSDimitry Andric 749*bdd1243dSDimitry Andric // Comparisons (==, !=): 750*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXOperatorCallExpr>( 751*bdd1243dSDimitry Andric isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()), 752*bdd1243dSDimitry Andric transferOptionalAndOptionalCmp) 753*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXOperatorCallExpr>( 754*bdd1243dSDimitry Andric isComparisonOperatorCall(hasOptionalType(), 755*bdd1243dSDimitry Andric unless(hasAnyOptionalType())), 756*bdd1243dSDimitry Andric [](const clang::CXXOperatorCallExpr *Cmp, 757*bdd1243dSDimitry Andric const MatchFinder::MatchResult &, LatticeTransferState &State) { 758*bdd1243dSDimitry Andric transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 759*bdd1243dSDimitry Andric }) 760*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXOperatorCallExpr>( 761*bdd1243dSDimitry Andric isComparisonOperatorCall(unless(hasAnyOptionalType()), 762*bdd1243dSDimitry Andric hasOptionalType()), 763*bdd1243dSDimitry Andric [](const clang::CXXOperatorCallExpr *Cmp, 764*bdd1243dSDimitry Andric const MatchFinder::MatchResult &, LatticeTransferState &State) { 765*bdd1243dSDimitry Andric transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 766*bdd1243dSDimitry Andric }) 76781ad6265SDimitry Andric 76881ad6265SDimitry Andric // returns optional 769*bdd1243dSDimitry Andric .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 77081ad6265SDimitry Andric transferCallReturningOptional) 77181ad6265SDimitry Andric 77281ad6265SDimitry Andric .Build(); 77381ad6265SDimitry Andric } 77481ad6265SDimitry Andric 77581ad6265SDimitry Andric std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr, 77681ad6265SDimitry Andric const Expr *ObjectExpr, 77781ad6265SDimitry Andric const Environment &Env) { 77881ad6265SDimitry Andric if (auto *OptionalVal = 77981ad6265SDimitry Andric Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 78081ad6265SDimitry Andric auto *Prop = OptionalVal->getProperty("has_value"); 78181ad6265SDimitry Andric if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 78281ad6265SDimitry Andric if (Env.flowConditionImplies(*HasValueVal)) 78381ad6265SDimitry Andric return {}; 78481ad6265SDimitry Andric } 78581ad6265SDimitry Andric } 78681ad6265SDimitry Andric 78781ad6265SDimitry Andric // Record that this unwrap is *not* provably safe. 78881ad6265SDimitry Andric // FIXME: include either the name of the optional (if applicable) or a source 78981ad6265SDimitry Andric // range of the access for easier interpretation of the result. 79081ad6265SDimitry Andric return {ObjectExpr->getBeginLoc()}; 79181ad6265SDimitry Andric } 79281ad6265SDimitry Andric 79381ad6265SDimitry Andric auto buildDiagnoseMatchSwitch( 79481ad6265SDimitry Andric const UncheckedOptionalAccessModelOptions &Options) { 79581ad6265SDimitry Andric // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 79681ad6265SDimitry Andric // lot of duplicated work (e.g. string comparisons), consider providing APIs 79781ad6265SDimitry Andric // that avoid it through memoization. 79881ad6265SDimitry Andric auto IgnorableOptional = ignorableOptional(Options); 799*bdd1243dSDimitry Andric return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>() 80081ad6265SDimitry Andric // optional::value 801*bdd1243dSDimitry Andric .CaseOfCFGStmt<CXXMemberCallExpr>( 80281ad6265SDimitry Andric valueCall(IgnorableOptional), 80381ad6265SDimitry Andric [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 80481ad6265SDimitry Andric const Environment &Env) { 80581ad6265SDimitry Andric return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env); 80681ad6265SDimitry Andric }) 80781ad6265SDimitry Andric 80881ad6265SDimitry Andric // optional::operator*, optional::operator-> 809*bdd1243dSDimitry Andric .CaseOfCFGStmt<CallExpr>( 81081ad6265SDimitry Andric valueOperatorCall(IgnorableOptional), 81181ad6265SDimitry Andric [](const CallExpr *E, const MatchFinder::MatchResult &, 81281ad6265SDimitry Andric const Environment &Env) { 81381ad6265SDimitry Andric return diagnoseUnwrapCall(E, E->getArg(0), Env); 81481ad6265SDimitry Andric }) 81581ad6265SDimitry Andric .Build(); 81681ad6265SDimitry Andric } 81781ad6265SDimitry Andric 81881ad6265SDimitry Andric } // namespace 81981ad6265SDimitry Andric 82081ad6265SDimitry Andric ast_matchers::DeclarationMatcher 82181ad6265SDimitry Andric UncheckedOptionalAccessModel::optionalClassDecl() { 82281ad6265SDimitry Andric return optionalClass(); 82381ad6265SDimitry Andric } 82481ad6265SDimitry Andric 825*bdd1243dSDimitry Andric UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx) 82681ad6265SDimitry Andric : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 827*bdd1243dSDimitry Andric TransferMatchSwitch(buildTransferMatchSwitch()) {} 82881ad6265SDimitry Andric 829*bdd1243dSDimitry Andric void UncheckedOptionalAccessModel::transfer(const CFGElement *Elt, 830*bdd1243dSDimitry Andric NoopLattice &L, Environment &Env) { 83181ad6265SDimitry Andric LatticeTransferState State(L, Env); 832*bdd1243dSDimitry Andric TransferMatchSwitch(*Elt, getASTContext(), State); 83381ad6265SDimitry Andric } 83481ad6265SDimitry Andric 835*bdd1243dSDimitry Andric ComparisonResult UncheckedOptionalAccessModel::compare( 836*bdd1243dSDimitry Andric QualType Type, const Value &Val1, const Environment &Env1, 837*bdd1243dSDimitry Andric const Value &Val2, const Environment &Env2) { 838*bdd1243dSDimitry Andric if (!isOptionalType(Type)) 839*bdd1243dSDimitry Andric return ComparisonResult::Unknown; 840*bdd1243dSDimitry Andric bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 841*bdd1243dSDimitry Andric bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 842*bdd1243dSDimitry Andric if (MustNonEmpty1 && MustNonEmpty2) return ComparisonResult::Same; 843*bdd1243dSDimitry Andric // If exactly one is true, then they're different, no reason to check whether 844*bdd1243dSDimitry Andric // they're definitely empty. 845*bdd1243dSDimitry Andric if (MustNonEmpty1 || MustNonEmpty2) return ComparisonResult::Different; 846*bdd1243dSDimitry Andric // Check if they're both definitely empty. 847*bdd1243dSDimitry Andric return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)) 848*bdd1243dSDimitry Andric ? ComparisonResult::Same 849*bdd1243dSDimitry Andric : ComparisonResult::Different; 85081ad6265SDimitry Andric } 85181ad6265SDimitry Andric 85281ad6265SDimitry Andric bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1, 85381ad6265SDimitry Andric const Environment &Env1, 85481ad6265SDimitry Andric const Value &Val2, 85581ad6265SDimitry Andric const Environment &Env2, 85681ad6265SDimitry Andric Value &MergedVal, 85781ad6265SDimitry Andric Environment &MergedEnv) { 858*bdd1243dSDimitry Andric if (!isOptionalType(Type)) 85981ad6265SDimitry Andric return true; 860*bdd1243dSDimitry Andric // FIXME: uses same approach as join for `BoolValues`. Requires non-const 861*bdd1243dSDimitry Andric // values, though, so will require updating the interface. 86281ad6265SDimitry Andric auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); 863*bdd1243dSDimitry Andric bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 864*bdd1243dSDimitry Andric bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 865*bdd1243dSDimitry Andric if (MustNonEmpty1 && MustNonEmpty2) 86681ad6265SDimitry Andric MergedEnv.addToFlowCondition(HasValueVal); 867*bdd1243dSDimitry Andric else if ( 868*bdd1243dSDimitry Andric // Only make the costly calls to `isEmptyOptional` if we got "unknown" 869*bdd1243dSDimitry Andric // (false) for both calls to `isNonEmptyOptional`. 870*bdd1243dSDimitry Andric !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) && 871*bdd1243dSDimitry Andric isEmptyOptional(Val2, Env2)) 87281ad6265SDimitry Andric MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal)); 87381ad6265SDimitry Andric setHasValue(MergedVal, HasValueVal); 87481ad6265SDimitry Andric return true; 87581ad6265SDimitry Andric } 87681ad6265SDimitry Andric 877*bdd1243dSDimitry Andric Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev, 878*bdd1243dSDimitry Andric const Environment &PrevEnv, 879*bdd1243dSDimitry Andric Value &Current, 880*bdd1243dSDimitry Andric Environment &CurrentEnv) { 881*bdd1243dSDimitry Andric switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) { 882*bdd1243dSDimitry Andric case ComparisonResult::Same: 883*bdd1243dSDimitry Andric return &Prev; 884*bdd1243dSDimitry Andric case ComparisonResult::Different: 885*bdd1243dSDimitry Andric if (auto *PrevHasVal = 886*bdd1243dSDimitry Andric cast_or_null<BoolValue>(Prev.getProperty("has_value"))) { 887*bdd1243dSDimitry Andric if (isa<TopBoolValue>(PrevHasVal)) 888*bdd1243dSDimitry Andric return &Prev; 889*bdd1243dSDimitry Andric } 890*bdd1243dSDimitry Andric if (auto *CurrentHasVal = 891*bdd1243dSDimitry Andric cast_or_null<BoolValue>(Current.getProperty("has_value"))) { 892*bdd1243dSDimitry Andric if (isa<TopBoolValue>(CurrentHasVal)) 893*bdd1243dSDimitry Andric return &Current; 894*bdd1243dSDimitry Andric } 895*bdd1243dSDimitry Andric return &createOptionalValue(CurrentEnv, CurrentEnv.makeTopBoolValue()); 896*bdd1243dSDimitry Andric case ComparisonResult::Unknown: 897*bdd1243dSDimitry Andric return nullptr; 898*bdd1243dSDimitry Andric } 899*bdd1243dSDimitry Andric llvm_unreachable("all cases covered in switch"); 900*bdd1243dSDimitry Andric } 901*bdd1243dSDimitry Andric 90281ad6265SDimitry Andric UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 90381ad6265SDimitry Andric UncheckedOptionalAccessModelOptions Options) 90481ad6265SDimitry Andric : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 90581ad6265SDimitry Andric 90681ad6265SDimitry Andric std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose( 907*bdd1243dSDimitry Andric ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) { 908*bdd1243dSDimitry Andric return DiagnoseMatchSwitch(*Elt, Ctx, Env); 90981ad6265SDimitry Andric } 91081ad6265SDimitry Andric 91181ad6265SDimitry Andric } // namespace dataflow 91281ad6265SDimitry Andric } // namespace clang 913