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" 2109b462efSYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchersMacros.h" 227538b360SWei Yi Tee #include "clang/Analysis/CFG.h" 237538b360SWei Yi Tee #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h" 24af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 25cf1f978dSSam Estep #include "clang/Analysis/FlowSensitive/NoopLattice.h" 260086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h" 27af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h" 2858fe7f96SSam Estep #include "clang/Basic/SourceLocation.h" 29af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h" 30af98b0afSStanislav Gatev #include "llvm/Support/Casting.h" 31d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h" 32af98b0afSStanislav Gatev #include <cassert> 339e0fc676SStanislav Gatev #include <memory> 34a1580d7bSKazu Hirata #include <optional> 359e0fc676SStanislav Gatev #include <utility> 3658fe7f96SSam Estep #include <vector> 37af98b0afSStanislav Gatev 38af98b0afSStanislav Gatev namespace clang { 39af98b0afSStanislav Gatev namespace dataflow { 4009b462efSYitzhak Mandelbaum 4109b462efSYitzhak Mandelbaum static bool isTopLevelNamespaceWithName(const NamespaceDecl &NS, 4209b462efSYitzhak Mandelbaum llvm::StringRef Name) { 4309b462efSYitzhak Mandelbaum return NS.getDeclName().isIdentifier() && NS.getName() == Name && 4409b462efSYitzhak Mandelbaum NS.getParent() != nullptr && NS.getParent()->isTranslationUnit(); 4509b462efSYitzhak Mandelbaum } 4609b462efSYitzhak Mandelbaum 4709b462efSYitzhak Mandelbaum static bool hasOptionalClassName(const CXXRecordDecl &RD) { 4809b462efSYitzhak Mandelbaum if (!RD.getDeclName().isIdentifier()) 4909b462efSYitzhak Mandelbaum return false; 5009b462efSYitzhak Mandelbaum 5109b462efSYitzhak Mandelbaum if (RD.getName() == "optional") { 5209b462efSYitzhak Mandelbaum if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext())) 5309b462efSYitzhak Mandelbaum return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl"); 5409b462efSYitzhak Mandelbaum return false; 5509b462efSYitzhak Mandelbaum } 5609b462efSYitzhak Mandelbaum 5709b462efSYitzhak Mandelbaum if (RD.getName() == "Optional") { 5809b462efSYitzhak Mandelbaum // Check whether namespace is "::base". 5909b462efSYitzhak Mandelbaum const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); 6009b462efSYitzhak Mandelbaum return N != nullptr && isTopLevelNamespaceWithName(*N, "base"); 6109b462efSYitzhak Mandelbaum } 6209b462efSYitzhak Mandelbaum 6309b462efSYitzhak Mandelbaum return false; 6409b462efSYitzhak Mandelbaum } 6509b462efSYitzhak Mandelbaum 66af98b0afSStanislav Gatev namespace { 67af98b0afSStanislav Gatev 68af98b0afSStanislav Gatev using namespace ::clang::ast_matchers; 69cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>; 70af98b0afSStanislav Gatev 7109b462efSYitzhak Mandelbaum AST_MATCHER(CXXRecordDecl, hasOptionalClassNameMatcher) { 7209b462efSYitzhak Mandelbaum return hasOptionalClassName(Node); 7309b462efSYitzhak Mandelbaum } 7409b462efSYitzhak Mandelbaum 757e63a0d4SYitzhak Mandelbaum DeclarationMatcher optionalClass() { 76af98b0afSStanislav Gatev return classTemplateSpecializationDecl( 7709b462efSYitzhak Mandelbaum hasOptionalClassNameMatcher(), 78af98b0afSStanislav Gatev hasTemplateArgument(0, refersToType(type().bind("T")))); 79af98b0afSStanislav Gatev } 80af98b0afSStanislav Gatev 816adfc64eSYitzhak Mandelbaum auto optionalOrAliasType() { 8265e710c3SStanislav Gatev return hasUnqualifiedDesugaredType( 8365e710c3SStanislav Gatev recordType(hasDeclaration(optionalClass()))); 8465e710c3SStanislav Gatev } 8565e710c3SStanislav Gatev 866adfc64eSYitzhak Mandelbaum /// Matches any of the spellings of the optional types and sugar, aliases, etc. 876adfc64eSYitzhak Mandelbaum auto hasOptionalType() { return hasType(optionalOrAliasType()); } 886adfc64eSYitzhak Mandelbaum 89a184a0d8SYitzhak Mandelbaum auto isOptionalMemberCallWithName( 90a184a0d8SYitzhak Mandelbaum llvm::StringRef MemberName, 916ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 92a184a0d8SYitzhak Mandelbaum auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr())) 93a184a0d8SYitzhak Mandelbaum : cxxThisExpr()); 94af98b0afSStanislav Gatev return cxxMemberCallExpr( 9509b462efSYitzhak Mandelbaum on(expr(Exception, 9609b462efSYitzhak Mandelbaum anyOf(hasOptionalType(), 9709b462efSYitzhak Mandelbaum hasType(pointerType(pointee(optionalOrAliasType())))))), 9809b462efSYitzhak Mandelbaum callee(cxxMethodDecl(hasName(MemberName)))); 99af98b0afSStanislav Gatev } 100af98b0afSStanislav Gatev 101a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName( 102a184a0d8SYitzhak Mandelbaum llvm::StringRef operator_name, 1036ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 104a184a0d8SYitzhak Mandelbaum return cxxOperatorCallExpr( 105a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName(operator_name), 106a184a0d8SYitzhak Mandelbaum callee(cxxMethodDecl(ofClass(optionalClass()))), 107a184a0d8SYitzhak Mandelbaum Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 108af98b0afSStanislav Gatev } 109af98b0afSStanislav Gatev 110092a530cSStanislav Gatev auto isMakeOptionalCall() { 1119e0fc676SStanislav Gatev return callExpr( 1129e0fc676SStanislav Gatev callee(functionDecl(hasAnyName( 1139e0fc676SStanislav Gatev "std::make_optional", "base::make_optional", "absl::make_optional"))), 1149e0fc676SStanislav Gatev hasOptionalType()); 1159e0fc676SStanislav Gatev } 1169e0fc676SStanislav Gatev 117390029beSYitzhak Mandelbaum auto nulloptTypeDecl() { 118390029beSYitzhak Mandelbaum return namedDecl( 119390029beSYitzhak Mandelbaum hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")); 120092a530cSStanislav Gatev } 121092a530cSStanislav Gatev 122390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); } 123390029beSYitzhak Mandelbaum 124390029beSYitzhak Mandelbaum // `optional` or `nullopt_t` 125390029beSYitzhak Mandelbaum auto hasAnyOptionalType() { 126390029beSYitzhak Mandelbaum return hasType(hasUnqualifiedDesugaredType( 127390029beSYitzhak Mandelbaum recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass()))))); 128390029beSYitzhak Mandelbaum } 129390029beSYitzhak Mandelbaum 130092a530cSStanislav Gatev auto inPlaceClass() { 131092a530cSStanislav Gatev return recordDecl( 132092a530cSStanislav Gatev hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t")); 133092a530cSStanislav Gatev } 134092a530cSStanislav Gatev 135092a530cSStanislav Gatev auto isOptionalNulloptConstructor() { 1360086a355SYitzhak Mandelbaum return cxxConstructExpr( 1370086a355SYitzhak Mandelbaum hasOptionalType(), 1380086a355SYitzhak Mandelbaum hasDeclaration(cxxConstructorDecl(parameterCountIs(1), 1390086a355SYitzhak Mandelbaum hasParameter(0, hasNulloptType())))); 140092a530cSStanislav Gatev } 141092a530cSStanislav Gatev 142092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() { 143092a530cSStanislav Gatev return cxxConstructExpr(hasOptionalType(), 144092a530cSStanislav Gatev hasArgument(0, hasType(inPlaceClass()))); 145092a530cSStanislav Gatev } 146092a530cSStanislav Gatev 147092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() { 148092a530cSStanislav Gatev return cxxConstructExpr( 149092a530cSStanislav Gatev hasOptionalType(), 150092a530cSStanislav Gatev unless(hasDeclaration( 151092a530cSStanislav Gatev cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 152092a530cSStanislav Gatev argumentCountIs(1), hasArgument(0, unless(hasNulloptType()))); 153092a530cSStanislav Gatev } 154092a530cSStanislav Gatev 155b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() { 156b000b770SStanislav Gatev return cxxOperatorCallExpr( 157b000b770SStanislav Gatev hasOverloadedOperatorName("="), 158b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 159b000b770SStanislav Gatev unless(hasDeclaration(cxxMethodDecl( 160b000b770SStanislav Gatev anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 161b000b770SStanislav Gatev argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 162b000b770SStanislav Gatev } 163b000b770SStanislav Gatev 164390029beSYitzhak Mandelbaum auto isNulloptConstructor() { 165390029beSYitzhak Mandelbaum return cxxConstructExpr(hasNulloptType(), argumentCountIs(1), 166390029beSYitzhak Mandelbaum hasArgument(0, hasNulloptType())); 167390029beSYitzhak Mandelbaum } 168390029beSYitzhak Mandelbaum 169b000b770SStanislav Gatev auto isOptionalNulloptAssignment() { 170b000b770SStanislav Gatev return cxxOperatorCallExpr(hasOverloadedOperatorName("="), 171b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 172b000b770SStanislav Gatev argumentCountIs(2), 173b000b770SStanislav Gatev hasArgument(1, hasNulloptType())); 174b000b770SStanislav Gatev } 175b000b770SStanislav Gatev 1762ddd57aeSStanislav Gatev auto isStdSwapCall() { 1772ddd57aeSStanislav Gatev return callExpr(callee(functionDecl(hasName("std::swap"))), 1782ddd57aeSStanislav Gatev argumentCountIs(2), hasArgument(0, hasOptionalType()), 1792ddd57aeSStanislav Gatev hasArgument(1, hasOptionalType())); 1802ddd57aeSStanislav Gatev } 1812ddd57aeSStanislav Gatev 18225956d55SAMS21 auto isStdForwardCall() { 18325956d55SAMS21 return callExpr(callee(functionDecl(hasName("std::forward"))), 18425956d55SAMS21 argumentCountIs(1), hasArgument(0, hasOptionalType())); 18525956d55SAMS21 } 18625956d55SAMS21 1877f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 1887f076004SYitzhak Mandelbaum 1897f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() { 1907f076004SYitzhak Mandelbaum // `opt.value_or("").empty()` 1917f076004SYitzhak Mandelbaum return cxxMemberCallExpr( 1927f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("empty"))), 1937f076004SYitzhak Mandelbaum onImplicitObjectArgument(ignoringImplicit( 1947f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 1957f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 1967f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 1977f076004SYitzhak Mandelbaum hasArgument(0, stringLiteral(hasSize(0)))) 1987f076004SYitzhak Mandelbaum .bind(ValueOrCallID)))); 1997f076004SYitzhak Mandelbaum } 2007f076004SYitzhak Mandelbaum 2017f076004SYitzhak Mandelbaum auto isValueOrNotEqX() { 2027f076004SYitzhak Mandelbaum auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 2037f076004SYitzhak Mandelbaum return hasOperands( 2047f076004SYitzhak Mandelbaum ignoringImplicit( 2057f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 2067f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 2077f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 2087f076004SYitzhak Mandelbaum hasArgument(0, Arg)) 2097f076004SYitzhak Mandelbaum .bind(ValueOrCallID)), 2107f076004SYitzhak Mandelbaum ignoringImplicit(Arg)); 2117f076004SYitzhak Mandelbaum }; 2127f076004SYitzhak Mandelbaum 2137f076004SYitzhak Mandelbaum // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 2147f076004SYitzhak Mandelbaum // support this pattern for any expression, but the AST does not have a 2157f076004SYitzhak Mandelbaum // generic expression comparison facility, so we specialize to common cases 2167f076004SYitzhak Mandelbaum // seen in practice. FIXME: define a matcher that compares values across 2177f076004SYitzhak Mandelbaum // nodes, which would let us generalize this to any `X`. 2187f076004SYitzhak Mandelbaum return binaryOperation(hasOperatorName("!="), 2197f076004SYitzhak Mandelbaum anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 2207f076004SYitzhak Mandelbaum ComparesToSame(stringLiteral(hasSize(0))), 2217f076004SYitzhak Mandelbaum ComparesToSame(integerLiteral(equals(0))))); 2227f076004SYitzhak Mandelbaum } 2237f076004SYitzhak Mandelbaum 22465e710c3SStanislav Gatev auto isCallReturningOptional() { 225cd0d5261SSam Estep return callExpr(hasType(qualType(anyOf( 226cd0d5261SSam Estep optionalOrAliasType(), referenceType(pointee(optionalOrAliasType())))))); 22765e710c3SStanislav Gatev } 22865e710c3SStanislav Gatev 229390029beSYitzhak Mandelbaum template <typename L, typename R> 230390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 231390029beSYitzhak Mandelbaum return cxxOperatorCallExpr( 232390029beSYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 233390029beSYitzhak Mandelbaum argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 234390029beSYitzhak Mandelbaum hasArgument(1, rhs_arg_matcher)); 235390029beSYitzhak Mandelbaum } 236390029beSYitzhak Mandelbaum 237ffb4f4dbSDmitri Gribenko /// Ensures that `Expr` is mapped to a `BoolValue` and returns it. 238390029beSYitzhak Mandelbaum BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) { 239390029beSYitzhak Mandelbaum auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None)); 240390029beSYitzhak Mandelbaum if (Value != nullptr) 241390029beSYitzhak Mandelbaum return *Value; 242390029beSYitzhak Mandelbaum 243390029beSYitzhak Mandelbaum auto &Loc = Env.createStorageLocation(Expr); 244390029beSYitzhak Mandelbaum Value = &Env.makeAtomicBoolValue(); 245390029beSYitzhak Mandelbaum Env.setValue(Loc, *Value); 246390029beSYitzhak Mandelbaum Env.setStorageLocation(Expr, Loc); 247390029beSYitzhak Mandelbaum return *Value; 248390029beSYitzhak Mandelbaum } 249390029beSYitzhak Mandelbaum 2508fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 2518fcdd625SStanislav Gatev /// property of the optional value `OptionalVal`. 2528fcdd625SStanislav Gatev void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) { 2538fcdd625SStanislav Gatev OptionalVal.setProperty("has_value", HasValueVal); 2548fcdd625SStanislav Gatev } 2558fcdd625SStanislav Gatev 2569e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the 2579e0fc676SStanislav Gatev /// symbolic value of its "has_value" property. 2589e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) { 259745a957fSMartin Braenne auto &OptionalVal = Env.create<StructValue>(); 260745a957fSMartin Braenne setHasValue(OptionalVal, HasValueVal); 261745a957fSMartin Braenne return OptionalVal; 2629e0fc676SStanislav Gatev } 2639e0fc676SStanislav Gatev 264af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the 26549ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null. 266dd38caf3SYitzhak Mandelbaum BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { 267dd38caf3SYitzhak Mandelbaum if (OptionalVal != nullptr) { 268dd38caf3SYitzhak Mandelbaum auto *HasValueVal = 269dd38caf3SYitzhak Mandelbaum cast_or_null<BoolValue>(OptionalVal->getProperty("has_value")); 270dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) { 271dd38caf3SYitzhak Mandelbaum HasValueVal = &Env.makeAtomicBoolValue(); 272dd38caf3SYitzhak Mandelbaum OptionalVal->setProperty("has_value", *HasValueVal); 273dd38caf3SYitzhak Mandelbaum } 274dd38caf3SYitzhak Mandelbaum return HasValueVal; 275af98b0afSStanislav Gatev } 276af98b0afSStanislav Gatev return nullptr; 277af98b0afSStanislav Gatev } 278af98b0afSStanislav Gatev 279092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type. 280c0725865SYitzhak Mandelbaum bool isOptionalType(QualType Type) { 281092a530cSStanislav Gatev if (!Type->isRecordType()) 282092a530cSStanislav Gatev return false; 283cd22e0dcSYitzhak Mandelbaum const CXXRecordDecl *D = Type->getAsCXXRecordDecl(); 28409b462efSYitzhak Mandelbaum return D != nullptr && hasOptionalClassName(*D); 285092a530cSStanislav Gatev } 286092a530cSStanislav Gatev 287092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`. 288092a530cSStanislav Gatev /// 289092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this 290092a530cSStanislav Gatev /// function will be 2. 291092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 292c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 293092a530cSStanislav Gatev return 0; 294092a530cSStanislav Gatev return 1 + countOptionalWrappers( 295092a530cSStanislav Gatev ASTCtx, 296092a530cSStanislav Gatev cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) 297092a530cSStanislav Gatev ->getTemplateArgs() 298092a530cSStanislav Gatev .get(0) 299092a530cSStanislav Gatev .getAsType() 300092a530cSStanislav Gatev .getDesugaredType(ASTCtx)); 301092a530cSStanislav Gatev } 302092a530cSStanislav Gatev 303dd38caf3SYitzhak Mandelbaum /// Tries to initialize the `optional`'s value (that is, contents), and return 304dd38caf3SYitzhak Mandelbaum /// its location. Returns nullptr if the value can't be represented. 305dd38caf3SYitzhak Mandelbaum StorageLocation *maybeInitializeOptionalValueMember(QualType Q, 306dd38caf3SYitzhak Mandelbaum Value &OptionalVal, 307dd38caf3SYitzhak Mandelbaum Environment &Env) { 308dd38caf3SYitzhak Mandelbaum // The "value" property represents a synthetic field. As such, it needs 309dd38caf3SYitzhak Mandelbaum // `StorageLocation`, like normal fields (and other variables). So, we model 310af22be39SMartin Braenne // it with a `PointerValue`, since that includes a storage location. Once 311dd38caf3SYitzhak Mandelbaum // the property is set, it will be shared by all environments that access the 312dd38caf3SYitzhak Mandelbaum // `Value` representing the optional (here, `OptionalVal`). 313dd38caf3SYitzhak Mandelbaum if (auto *ValueProp = OptionalVal.getProperty("value")) { 314af22be39SMartin Braenne auto *ValuePtr = clang::cast<PointerValue>(ValueProp); 315af22be39SMartin Braenne auto &ValueLoc = ValuePtr->getPointeeLoc(); 316dd38caf3SYitzhak Mandelbaum if (Env.getValue(ValueLoc) == nullptr) { 317dd38caf3SYitzhak Mandelbaum // The property was previously set, but the value has been lost. This can 318*bbeda830SMartin Braenne // happen in various situations, for example: 319*bbeda830SMartin Braenne // - Because of an environment merge (where the two environments mapped 320*bbeda830SMartin Braenne // the property to different values, which resulted in them both being 321*bbeda830SMartin Braenne // discarded). 322*bbeda830SMartin Braenne // - When two blocks in the CFG, with neither a dominator of the other, 323*bbeda830SMartin Braenne // visit the same optional value. (FIXME: This is something we can and 324*bbeda830SMartin Braenne // should fix -- see also the lengthy FIXME below.) 325*bbeda830SMartin Braenne // - Or even when a block is revisited during testing to collect 326*bbeda830SMartin Braenne // per-statement state. 327*bbeda830SMartin Braenne // 328dd38caf3SYitzhak Mandelbaum // FIXME: This situation means that the optional contents are not shared 329dd38caf3SYitzhak Mandelbaum // between branches and the like. Practically, this lack of sharing 330dd38caf3SYitzhak Mandelbaum // reduces the precision of the model when the contents are relevant to 331dd38caf3SYitzhak Mandelbaum // the check, like another optional or a boolean that influences control 332dd38caf3SYitzhak Mandelbaum // flow. 333dd38caf3SYitzhak Mandelbaum auto *ValueVal = Env.createValue(ValueLoc.getType()); 334dd38caf3SYitzhak Mandelbaum if (ValueVal == nullptr) 335dd38caf3SYitzhak Mandelbaum return nullptr; 336dd38caf3SYitzhak Mandelbaum Env.setValue(ValueLoc, *ValueVal); 337dd38caf3SYitzhak Mandelbaum } 338dd38caf3SYitzhak Mandelbaum return &ValueLoc; 339dd38caf3SYitzhak Mandelbaum } 340dd38caf3SYitzhak Mandelbaum 341c849843cSMartin Braenne auto Ty = Q.getNonReferenceType(); 342dd38caf3SYitzhak Mandelbaum auto *ValueVal = Env.createValue(Ty); 343dd38caf3SYitzhak Mandelbaum if (ValueVal == nullptr) 344dd38caf3SYitzhak Mandelbaum return nullptr; 345dd38caf3SYitzhak Mandelbaum auto &ValueLoc = Env.createStorageLocation(Ty); 346dd38caf3SYitzhak Mandelbaum Env.setValue(ValueLoc, *ValueVal); 347af22be39SMartin Braenne auto &ValuePtr = Env.create<PointerValue>(ValueLoc); 348*bbeda830SMartin Braenne // FIXME: 349*bbeda830SMartin Braenne // The change we make to the `value` property below may become visible to 350*bbeda830SMartin Braenne // other blocks that aren't successors of the current block and therefore 351*bbeda830SMartin Braenne // don't see the change we made above mapping `ValueLoc` to `ValueVal`. For 352*bbeda830SMartin Braenne // example: 353*bbeda830SMartin Braenne // 354*bbeda830SMartin Braenne // void target(optional<int> oo, bool b) { 355*bbeda830SMartin Braenne // // `oo` is associated with a `StructValue` here, which we will call 356*bbeda830SMartin Braenne // // `OptionalVal`. 357*bbeda830SMartin Braenne // 358*bbeda830SMartin Braenne // // The `has_value` property is set on `OptionalVal` (but not the 359*bbeda830SMartin Braenne // // `value` property yet). 360*bbeda830SMartin Braenne // if (!oo.has_value()) return; 361*bbeda830SMartin Braenne // 362*bbeda830SMartin Braenne // if (b) { 363*bbeda830SMartin Braenne // // Let's assume we transfer the `if` branch first. 364*bbeda830SMartin Braenne // // 365*bbeda830SMartin Braenne // // This causes us to call `maybeInitializeOptionalValueMember()`, 366*bbeda830SMartin Braenne // // which causes us to set the `value` property on `OptionalVal` 367*bbeda830SMartin Braenne // // (which had not been set until this point). This `value` property 368*bbeda830SMartin Braenne // // refers to a `PointerValue`, which in turn refers to a 369*bbeda830SMartin Braenne // // StorageLocation` that is associated to an `IntegerValue`. 370*bbeda830SMartin Braenne // oo.value(); 371*bbeda830SMartin Braenne // } else { 372*bbeda830SMartin Braenne // // Let's assume we transfer the `else` branch after the `if` branch. 373*bbeda830SMartin Braenne // // 374*bbeda830SMartin Braenne // // We see the `value` property that the `if` branch set on 375*bbeda830SMartin Braenne // // `OptionalVal`, but in the environment for this block, the 376*bbeda830SMartin Braenne // // `StorageLocation` in the `PointerValue` is not associated with any 377*bbeda830SMartin Braenne // // `Value`. 378*bbeda830SMartin Braenne // oo.value(); 379*bbeda830SMartin Braenne // } 380*bbeda830SMartin Braenne // } 381*bbeda830SMartin Braenne // 382*bbeda830SMartin Braenne // This situation is currently "saved" by the code above that checks whether 383*bbeda830SMartin Braenne // the `value` property is already set, and if, the `ValueLoc` is not 384*bbeda830SMartin Braenne // associated with a `ValueVal`, creates a new `ValueVal`. 385*bbeda830SMartin Braenne // 386*bbeda830SMartin Braenne // However, what we should really do is to make sure that the change to the 387*bbeda830SMartin Braenne // `value` property does not "leak" to other blocks that are not successors 388*bbeda830SMartin Braenne // of this block. To do this, instead of simply setting the `value` property 389*bbeda830SMartin Braenne // on the existing `OptionalVal`, we should create a new `Value` for the 390*bbeda830SMartin Braenne // optional, set the property on that, and associate the storage location that 391*bbeda830SMartin Braenne // is currently associated with the existing `OptionalVal` with the newly 392*bbeda830SMartin Braenne // created `Value` instead. 393af22be39SMartin Braenne OptionalVal.setProperty("value", ValuePtr); 394dd38caf3SYitzhak Mandelbaum return &ValueLoc; 395dd38caf3SYitzhak Mandelbaum } 396dd38caf3SYitzhak Mandelbaum 397092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr, 398092a530cSStanislav Gatev const MatchFinder::MatchResult &, 399af98b0afSStanislav Gatev LatticeTransferState &State) { 40049ed5bf5SWei Yi Tee if (auto *OptionalVal = 40149ed5bf5SWei Yi Tee State.Env.getValue(*OptionalExpr, SkipPast::Reference)) { 402af98b0afSStanislav Gatev if (OptionalVal->getProperty("has_value") == nullptr) { 4038fcdd625SStanislav Gatev setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); 404af98b0afSStanislav Gatev } 405af98b0afSStanislav Gatev } 406af98b0afSStanislav Gatev } 407af98b0afSStanislav Gatev 4088fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be 409ffb4f4dbSDmitri Gribenko /// empty in `Env`. 4108fcdd625SStanislav Gatev bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { 4118fcdd625SStanislav Gatev auto *HasValueVal = 4128fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 4138fcdd625SStanislav Gatev return HasValueVal != nullptr && 4148fcdd625SStanislav Gatev Env.flowConditionImplies(Env.makeNot(*HasValueVal)); 4158fcdd625SStanislav Gatev } 4168fcdd625SStanislav Gatev 4178fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be 418ffb4f4dbSDmitri Gribenko /// non-empty in `Env`. 4198fcdd625SStanislav Gatev bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { 4208fcdd625SStanislav Gatev auto *HasValueVal = 4218fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 4228fcdd625SStanislav Gatev return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal); 4238fcdd625SStanislav Gatev } 4248fcdd625SStanislav Gatev 42548bc7150SMartin Braenne StorageLocation *maybeSkipPointer(StorageLocation *Loc, 42648bc7150SMartin Braenne const Environment &Env) { 42748bc7150SMartin Braenne if (Loc == nullptr) 42848bc7150SMartin Braenne return nullptr; 42948bc7150SMartin Braenne if (auto *Val = dyn_cast_or_null<PointerValue>(Env.getValue(*Loc))) 43048bc7150SMartin Braenne return &Val->getPointeeLoc(); 43148bc7150SMartin Braenne return Loc; 43248bc7150SMartin Braenne } 43348bc7150SMartin Braenne 43448bc7150SMartin Braenne Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) { 43548bc7150SMartin Braenne Value *Val = Env.getValue(E, SkipPast::Reference); 43648bc7150SMartin Braenne if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Val)) 43748bc7150SMartin Braenne return Env.getValue(PointerVal->getPointeeLoc()); 43848bc7150SMartin Braenne return Val; 43948bc7150SMartin Braenne } 44048bc7150SMartin Braenne 441092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 442af98b0afSStanislav Gatev LatticeTransferState &State) { 44349ed5bf5SWei Yi Tee if (auto *OptionalVal = 44448bc7150SMartin Braenne getValueBehindPossiblePointer(*ObjectExpr, State.Env)) { 445dd38caf3SYitzhak Mandelbaum if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr) 446dd38caf3SYitzhak Mandelbaum if (auto *Loc = maybeInitializeOptionalValueMember( 447dd38caf3SYitzhak Mandelbaum UnwrapExpr->getType(), *OptionalVal, State.Env)) 448dd38caf3SYitzhak Mandelbaum State.Env.setStorageLocation(*UnwrapExpr, *Loc); 449af98b0afSStanislav Gatev } 450dd38caf3SYitzhak Mandelbaum } 451af98b0afSStanislav Gatev 4523bc1ea5bSMartin Braenne void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 4533bc1ea5bSMartin Braenne LatticeTransferState &State) { 4543bc1ea5bSMartin Braenne if (auto *OptionalVal = 4553bc1ea5bSMartin Braenne getValueBehindPossiblePointer(*ObjectExpr, State.Env)) { 4563bc1ea5bSMartin Braenne if (auto *Loc = maybeInitializeOptionalValueMember( 4573bc1ea5bSMartin Braenne UnwrapExpr->getType()->getPointeeType(), *OptionalVal, State.Env)) { 4583bc1ea5bSMartin Braenne State.Env.setValueStrict(*UnwrapExpr, 4593bc1ea5bSMartin Braenne State.Env.create<PointerValue>(*Loc)); 4603bc1ea5bSMartin Braenne } 4613bc1ea5bSMartin Braenne } 4623bc1ea5bSMartin Braenne } 4633bc1ea5bSMartin Braenne 464092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E, 465092a530cSStanislav Gatev const MatchFinder::MatchResult &, 466092a530cSStanislav Gatev LatticeTransferState &State) { 4679e0fc676SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E); 4689e0fc676SStanislav Gatev State.Env.setStorageLocation(*E, Loc); 4699e0fc676SStanislav Gatev State.Env.setValue( 4709e0fc676SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true))); 4719e0fc676SStanislav Gatev } 4729e0fc676SStanislav Gatev 473092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 474092a530cSStanislav Gatev const MatchFinder::MatchResult &, 475af98b0afSStanislav Gatev LatticeTransferState &State) { 476dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = getHasValue( 47748bc7150SMartin Braenne State.Env, getValueBehindPossiblePointer( 47848bc7150SMartin Braenne *CallExpr->getImplicitObjectArgument(), State.Env))) { 479af98b0afSStanislav Gatev auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr); 480af98b0afSStanislav Gatev State.Env.setValue(CallExprLoc, *HasValueVal); 481af98b0afSStanislav Gatev State.Env.setStorageLocation(*CallExpr, CallExprLoc); 482af98b0afSStanislav Gatev } 483af98b0afSStanislav Gatev } 484af98b0afSStanislav Gatev 4857f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in 4867f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property. 4877f076004SYitzhak Mandelbaum void transferValueOrImpl(const clang::Expr *ValueOrPredExpr, 4887f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 4897f076004SYitzhak Mandelbaum LatticeTransferState &State, 4907f076004SYitzhak Mandelbaum BoolValue &(*ModelPred)(Environment &Env, 4917f076004SYitzhak Mandelbaum BoolValue &ExprVal, 4927f076004SYitzhak Mandelbaum BoolValue &HasValueVal)) { 4937f076004SYitzhak Mandelbaum auto &Env = State.Env; 4947f076004SYitzhak Mandelbaum 4957f076004SYitzhak Mandelbaum const auto *ObjectArgumentExpr = 4967f076004SYitzhak Mandelbaum Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) 4977f076004SYitzhak Mandelbaum ->getImplicitObjectArgument(); 4987f076004SYitzhak Mandelbaum 499dd38caf3SYitzhak Mandelbaum auto *HasValueVal = getHasValue( 50048bc7150SMartin Braenne State.Env, getValueBehindPossiblePointer(*ObjectArgumentExpr, State.Env)); 501dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) 5027f076004SYitzhak Mandelbaum return; 5037f076004SYitzhak Mandelbaum 504390029beSYitzhak Mandelbaum Env.addToFlowCondition( 505390029beSYitzhak Mandelbaum ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal)); 5067f076004SYitzhak Mandelbaum } 5077f076004SYitzhak Mandelbaum 5087f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 5097f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 5107f076004SYitzhak Mandelbaum LatticeTransferState &State) { 5117f076004SYitzhak Mandelbaum return transferValueOrImpl(ComparisonExpr, Result, State, 5127f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal, 5137f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & { 5147f076004SYitzhak Mandelbaum // If the result is *not* empty, then we know the 5157f076004SYitzhak Mandelbaum // optional must have been holding a value. If 5167f076004SYitzhak Mandelbaum // `ExprVal` is true, though, we don't learn 5177f076004SYitzhak Mandelbaum // anything definite about `has_value`, so we 5187f076004SYitzhak Mandelbaum // don't add any corresponding implications to 5197f076004SYitzhak Mandelbaum // the flow condition. 5207f076004SYitzhak Mandelbaum return Env.makeImplication(Env.makeNot(ExprVal), 5217f076004SYitzhak Mandelbaum HasValueVal); 5227f076004SYitzhak Mandelbaum }); 5237f076004SYitzhak Mandelbaum } 5247f076004SYitzhak Mandelbaum 5257f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr, 5267f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 5277f076004SYitzhak Mandelbaum LatticeTransferState &State) { 5287f076004SYitzhak Mandelbaum transferValueOrImpl(ComparisonExpr, Result, State, 5297f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal, 5307f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & { 5317f076004SYitzhak Mandelbaum // We know that if `(opt.value_or(X) != X)` then 5327f076004SYitzhak Mandelbaum // `opt.hasValue()`, even without knowing further 5337f076004SYitzhak Mandelbaum // details about the contents of `opt`. 5347f076004SYitzhak Mandelbaum return Env.makeImplication(ExprVal, HasValueVal); 5357f076004SYitzhak Mandelbaum }); 5367f076004SYitzhak Mandelbaum } 5377f076004SYitzhak Mandelbaum 53865e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E, 53965e710c3SStanislav Gatev const MatchFinder::MatchResult &Result, 54065e710c3SStanislav Gatev LatticeTransferState &State) { 54165e710c3SStanislav Gatev if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) 54265e710c3SStanislav Gatev return; 54365e710c3SStanislav Gatev 54465e710c3SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E); 54565e710c3SStanislav Gatev State.Env.setStorageLocation(*E, Loc); 54665e710c3SStanislav Gatev State.Env.setValue( 54765e710c3SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue())); 54865e710c3SStanislav Gatev } 54965e710c3SStanislav Gatev 5500e8d4a6dSYitzhak Mandelbaum void assignOptionalValue(const Expr &E, Environment &Env, 551092a530cSStanislav Gatev BoolValue &HasValueVal) { 55248bc7150SMartin Braenne if (auto *OptionalLoc = maybeSkipPointer( 55348bc7150SMartin Braenne Env.getStorageLocation(E, SkipPast::Reference), Env)) { 5540e8d4a6dSYitzhak Mandelbaum Env.setValue(*OptionalLoc, createOptionalValue(Env, HasValueVal)); 5559e0fc676SStanislav Gatev } 5569e0fc676SStanislav Gatev } 5579e0fc676SStanislav Gatev 558b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>` 559b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>` 560b000b770SStanislav Gatev /// where `T` is constructible from `U`. 561390029beSYitzhak Mandelbaum BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E, 562b000b770SStanislav Gatev const MatchFinder::MatchResult &MatchRes, 563b000b770SStanislav Gatev LatticeTransferState &State) { 5640086a355SYitzhak Mandelbaum assert(F.getTemplateSpecializationArgs() != nullptr); 565b000b770SStanislav Gatev assert(F.getTemplateSpecializationArgs()->size() > 0); 566b000b770SStanislav Gatev 567c849843cSMartin Braenne const int TemplateParamOptionalWrappersCount = 568c849843cSMartin Braenne countOptionalWrappers(*MatchRes.Context, F.getTemplateSpecializationArgs() 569c849843cSMartin Braenne ->get(0) 570c849843cSMartin Braenne .getAsType() 571c849843cSMartin Braenne .getNonReferenceType()); 572c849843cSMartin Braenne const int ArgTypeOptionalWrappersCount = countOptionalWrappers( 573c849843cSMartin Braenne *MatchRes.Context, E.getType().getNonReferenceType()); 574b000b770SStanislav Gatev 575b000b770SStanislav Gatev // Check if this is a constructor/assignment call for `optional<T>` with 576b000b770SStanislav Gatev // argument of type `U` such that `T` is constructible from `U`. 577b000b770SStanislav Gatev if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) 578b000b770SStanislav Gatev return State.Env.getBoolLiteralValue(true); 579b000b770SStanislav Gatev 580b000b770SStanislav Gatev // This is a constructor/assignment call for `optional<T>` with argument of 581b000b770SStanislav Gatev // type `optional<U>` such that `T` is constructible from `U`. 582dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = 583dd38caf3SYitzhak Mandelbaum getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference))) 584dd38caf3SYitzhak Mandelbaum return *HasValueVal; 585b000b770SStanislav Gatev return State.Env.makeAtomicBoolValue(); 586b000b770SStanislav Gatev } 587b000b770SStanislav Gatev 588092a530cSStanislav Gatev void transferValueOrConversionConstructor( 589092a530cSStanislav Gatev const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 5909e0fc676SStanislav Gatev LatticeTransferState &State) { 591092a530cSStanislav Gatev assert(E->getNumArgs() > 0); 592092a530cSStanislav Gatev 5930e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E, State.Env, 594390029beSYitzhak Mandelbaum valueOrConversionHasValue(*E->getConstructor(), 595b000b770SStanislav Gatev *E->getArg(0), MatchRes, 596b000b770SStanislav Gatev State)); 597b000b770SStanislav Gatev } 598092a530cSStanislav Gatev 599b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 600b000b770SStanislav Gatev LatticeTransferState &State) { 601b000b770SStanislav Gatev assert(E->getNumArgs() > 0); 602b000b770SStanislav Gatev 603b000b770SStanislav Gatev auto *OptionalLoc = 604b000b770SStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 605a9ad689eSSam Estep if (OptionalLoc == nullptr) 606a9ad689eSSam Estep return; 607b000b770SStanislav Gatev 608b000b770SStanislav Gatev State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal)); 609b000b770SStanislav Gatev 610b000b770SStanislav Gatev // Assign a storage location for the whole expression. 611b000b770SStanislav Gatev State.Env.setStorageLocation(*E, *OptionalLoc); 612b000b770SStanislav Gatev } 613b000b770SStanislav Gatev 614b000b770SStanislav Gatev void transferValueOrConversionAssignment( 615b000b770SStanislav Gatev const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 616b000b770SStanislav Gatev LatticeTransferState &State) { 617b000b770SStanislav Gatev assert(E->getNumArgs() > 1); 618b000b770SStanislav Gatev transferAssignment(E, 619390029beSYitzhak Mandelbaum valueOrConversionHasValue(*E->getDirectCallee(), 62006decd0bSKazu Hirata *E->getArg(1), MatchRes, State), 621b000b770SStanislav Gatev State); 622b000b770SStanislav Gatev } 623b000b770SStanislav Gatev 624b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E, 625b000b770SStanislav Gatev const MatchFinder::MatchResult &, 626b000b770SStanislav Gatev LatticeTransferState &State) { 627b000b770SStanislav Gatev transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 6289e0fc676SStanislav Gatev } 6299e0fc676SStanislav Gatev 63048bc7150SMartin Braenne void transferSwap(StorageLocation *Loc1, StorageLocation *Loc2, 631d4fb829bSYitzhak Mandelbaum Environment &Env) { 632d4fb829bSYitzhak Mandelbaum // We account for cases where one or both of the optionals are not modeled, 633d4fb829bSYitzhak Mandelbaum // either lacking associated storage locations, or lacking values associated 634d4fb829bSYitzhak Mandelbaum // to such storage locations. 6352ddd57aeSStanislav Gatev 636d4fb829bSYitzhak Mandelbaum if (Loc1 == nullptr) { 637d4fb829bSYitzhak Mandelbaum if (Loc2 != nullptr) 638d4fb829bSYitzhak Mandelbaum Env.setValue(*Loc2, createOptionalValue(Env, Env.makeAtomicBoolValue())); 639d4fb829bSYitzhak Mandelbaum return; 640d4fb829bSYitzhak Mandelbaum } 641d4fb829bSYitzhak Mandelbaum if (Loc2 == nullptr) { 642d4fb829bSYitzhak Mandelbaum Env.setValue(*Loc1, createOptionalValue(Env, Env.makeAtomicBoolValue())); 643d4fb829bSYitzhak Mandelbaum return; 644d4fb829bSYitzhak Mandelbaum } 6452ddd57aeSStanislav Gatev 646d4fb829bSYitzhak Mandelbaum // Both expressions have locations, though they may not have corresponding 647d4fb829bSYitzhak Mandelbaum // values. In that case, we create a fresh value at this point. Note that if 648d4fb829bSYitzhak Mandelbaum // two branches both do this, they will not share the value, but it at least 649d4fb829bSYitzhak Mandelbaum // allows for local reasoning about the value. To avoid the above, we would 650d4fb829bSYitzhak Mandelbaum // need *lazy* value allocation. 651d4fb829bSYitzhak Mandelbaum // FIXME: allocate values lazily, instead of just creating a fresh value. 652d4fb829bSYitzhak Mandelbaum auto *Val1 = Env.getValue(*Loc1); 653d4fb829bSYitzhak Mandelbaum if (Val1 == nullptr) 654d4fb829bSYitzhak Mandelbaum Val1 = &createOptionalValue(Env, Env.makeAtomicBoolValue()); 655d4fb829bSYitzhak Mandelbaum 656d4fb829bSYitzhak Mandelbaum auto *Val2 = Env.getValue(*Loc2); 657d4fb829bSYitzhak Mandelbaum if (Val2 == nullptr) 658d4fb829bSYitzhak Mandelbaum Val2 = &createOptionalValue(Env, Env.makeAtomicBoolValue()); 659d4fb829bSYitzhak Mandelbaum 660d4fb829bSYitzhak Mandelbaum Env.setValue(*Loc1, *Val2); 661d4fb829bSYitzhak Mandelbaum Env.setValue(*Loc2, *Val1); 6622ddd57aeSStanislav Gatev } 6632ddd57aeSStanislav Gatev 6642ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E, 6652ddd57aeSStanislav Gatev const MatchFinder::MatchResult &, 6662ddd57aeSStanislav Gatev LatticeTransferState &State) { 6672ddd57aeSStanislav Gatev assert(E->getNumArgs() == 1); 66848bc7150SMartin Braenne transferSwap(maybeSkipPointer( 66948bc7150SMartin Braenne State.Env.getStorageLocation(*E->getImplicitObjectArgument(), 67048bc7150SMartin Braenne SkipPast::Reference), 67148bc7150SMartin Braenne State.Env), 67248bc7150SMartin Braenne State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference), 67348bc7150SMartin Braenne State.Env); 6742ddd57aeSStanislav Gatev } 6752ddd57aeSStanislav Gatev 6762ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 6772ddd57aeSStanislav Gatev LatticeTransferState &State) { 6782ddd57aeSStanislav Gatev assert(E->getNumArgs() == 2); 67948bc7150SMartin Braenne transferSwap(State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference), 68048bc7150SMartin Braenne State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference), 68148bc7150SMartin Braenne State.Env); 6822ddd57aeSStanislav Gatev } 6832ddd57aeSStanislav Gatev 68425956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &, 68525956d55SAMS21 LatticeTransferState &State) { 68625956d55SAMS21 assert(E->getNumArgs() == 1); 68725956d55SAMS21 68825956d55SAMS21 StorageLocation *LocRet = State.Env.getStorageLocation(*E, SkipPast::None); 68925956d55SAMS21 if (LocRet != nullptr) 69025956d55SAMS21 return; 69125956d55SAMS21 69225956d55SAMS21 StorageLocation *LocArg = 69325956d55SAMS21 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 69425956d55SAMS21 69525956d55SAMS21 if (LocArg == nullptr) 69625956d55SAMS21 return; 69725956d55SAMS21 69825956d55SAMS21 Value *ValArg = State.Env.getValue(*LocArg); 69925956d55SAMS21 if (ValArg == nullptr) 70025956d55SAMS21 ValArg = &createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()); 70125956d55SAMS21 70225956d55SAMS21 // Create a new storage location 70325956d55SAMS21 LocRet = &State.Env.createStorageLocation(*E); 70425956d55SAMS21 State.Env.setStorageLocation(*E, *LocRet); 70525956d55SAMS21 70625956d55SAMS21 State.Env.setValue(*LocRet, *ValArg); 70725956d55SAMS21 } 70825956d55SAMS21 709390029beSYitzhak Mandelbaum BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS, 710390029beSYitzhak Mandelbaum BoolValue &RHS) { 711390029beSYitzhak Mandelbaum // Logically, an optional<T> object is composed of two values - a `has_value` 712390029beSYitzhak Mandelbaum // bit and a value of type T. Equality of optional objects compares both 713390029beSYitzhak Mandelbaum // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 714390029beSYitzhak Mandelbaum // when two optional objects are engaged, the equality of their respective 715390029beSYitzhak Mandelbaum // values of type T matters. Since we only track the `has_value` bits, we 716390029beSYitzhak Mandelbaum // can't make any conclusions about equality when we know that two optional 717390029beSYitzhak Mandelbaum // objects are engaged. 718390029beSYitzhak Mandelbaum // 719390029beSYitzhak Mandelbaum // We express this as two facts about the equality: 720390029beSYitzhak Mandelbaum // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 721390029beSYitzhak Mandelbaum // If they are equal, then either both are set or both are unset. 722390029beSYitzhak Mandelbaum // b) (!LHS & !RHS) => EqVal 723390029beSYitzhak Mandelbaum // If neither is set, then they are equal. 724390029beSYitzhak Mandelbaum // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 725390029beSYitzhak Mandelbaum return Env.makeAnd( 726390029beSYitzhak Mandelbaum Env.makeImplication( 727390029beSYitzhak Mandelbaum EqVal, Env.makeOr(Env.makeAnd(LHS, RHS), 728390029beSYitzhak Mandelbaum Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))), 729390029beSYitzhak Mandelbaum Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS))); 730390029beSYitzhak Mandelbaum } 731390029beSYitzhak Mandelbaum 732390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 733390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, 734390029beSYitzhak Mandelbaum LatticeTransferState &State) { 735390029beSYitzhak Mandelbaum Environment &Env = State.Env; 736390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 737390029beSYitzhak Mandelbaum if (auto *LHasVal = getHasValue( 738390029beSYitzhak Mandelbaum Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference))) 739390029beSYitzhak Mandelbaum if (auto *RHasVal = getHasValue( 740390029beSYitzhak Mandelbaum Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) { 741390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 742390029beSYitzhak Mandelbaum CmpValue = &State.Env.makeNot(*CmpValue); 743390029beSYitzhak Mandelbaum Env.addToFlowCondition( 744390029beSYitzhak Mandelbaum evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal)); 745390029beSYitzhak Mandelbaum } 746390029beSYitzhak Mandelbaum } 747390029beSYitzhak Mandelbaum 748390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 749390029beSYitzhak Mandelbaum const clang::Expr *E, Environment &Env) { 750390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 751390029beSYitzhak Mandelbaum if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) { 752390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 753390029beSYitzhak Mandelbaum CmpValue = &Env.makeNot(*CmpValue); 754390029beSYitzhak Mandelbaum Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal, 755390029beSYitzhak Mandelbaum Env.getBoolLiteralValue(true))); 756390029beSYitzhak Mandelbaum } 757390029beSYitzhak Mandelbaum } 758390029beSYitzhak Mandelbaum 7596ad0788cSKazu Hirata std::optional<StatementMatcher> 760a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 7615d22d1f5SYitzhak Mandelbaum if (Options.IgnoreSmartPointerDereference) { 7625d22d1f5SYitzhak Mandelbaum auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 7635d22d1f5SYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 7645d22d1f5SYitzhak Mandelbaum unless(hasArgument(0, expr(hasOptionalType())))))); 7655d22d1f5SYitzhak Mandelbaum return expr( 7665d22d1f5SYitzhak Mandelbaum anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 7675d22d1f5SYitzhak Mandelbaum } 76834e0d057SKazu Hirata return std::nullopt; 769a184a0d8SYitzhak Mandelbaum } 770a184a0d8SYitzhak Mandelbaum 77158fe7f96SSam Estep StatementMatcher 7726ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 77358fe7f96SSam Estep return isOptionalMemberCallWithName("value", IgnorableOptional); 77458fe7f96SSam Estep } 77558fe7f96SSam Estep 77658fe7f96SSam Estep StatementMatcher 7776ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 77858fe7f96SSam Estep return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 77958fe7f96SSam Estep isOptionalOperatorCallWithName("->", IgnorableOptional))); 78058fe7f96SSam Estep } 78158fe7f96SSam Estep 7825d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() { 783b000b770SStanislav Gatev // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 784b000b770SStanislav Gatev // lot of duplicated work (e.g. string comparisons), consider providing APIs 785b000b770SStanislav Gatev // that avoid it through memoization. 7867538b360SWei Yi Tee return CFGMatchSwitchBuilder<LatticeTransferState>() 787af98b0afSStanislav Gatev // Attach a symbolic "has_value" state to optional values that we see for 788af98b0afSStanislav Gatev // the first time. 7897538b360SWei Yi Tee .CaseOfCFGStmt<Expr>( 7906adfc64eSYitzhak Mandelbaum expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), 791af98b0afSStanislav Gatev initializeOptionalReference) 792af98b0afSStanislav Gatev 7939e0fc676SStanislav Gatev // make_optional 7947538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 795092a530cSStanislav Gatev 7960e8d4a6dSYitzhak Mandelbaum // optional::optional (in place) 7977538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 798092a530cSStanislav Gatev isOptionalInPlaceConstructor(), 799092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 800092a530cSStanislav Gatev LatticeTransferState &State) { 8010e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E, State.Env, 8020e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(true)); 803092a530cSStanislav Gatev }) 8040e8d4a6dSYitzhak Mandelbaum // nullopt_t::nullopt_t 8057538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 806390029beSYitzhak Mandelbaum isNulloptConstructor(), 807092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 808092a530cSStanislav Gatev LatticeTransferState &State) { 8090e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E, State.Env, 810092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false)); 811092a530cSStanislav Gatev }) 8120e8d4a6dSYitzhak Mandelbaum // optional::optional(nullopt_t) 813390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXConstructExpr>( 814390029beSYitzhak Mandelbaum isOptionalNulloptConstructor(), 815390029beSYitzhak Mandelbaum [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 816390029beSYitzhak Mandelbaum LatticeTransferState &State) { 8170e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E, State.Env, 8180e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(false)); 819390029beSYitzhak Mandelbaum }) 8200e8d4a6dSYitzhak Mandelbaum // optional::optional (value/conversion) 8217538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 822092a530cSStanislav Gatev transferValueOrConversionConstructor) 8239e0fc676SStanislav Gatev 824b000b770SStanislav Gatev // optional::operator= 8257538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>( 8267538b360SWei Yi Tee isOptionalValueOrConversionAssignment(), 827b000b770SStanislav Gatev transferValueOrConversionAssignment) 8287538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 829b000b770SStanislav Gatev transferNulloptAssignment) 830b000b770SStanislav Gatev 831af98b0afSStanislav Gatev // optional::value 8327538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8335d22d1f5SYitzhak Mandelbaum valueCall(std::nullopt), 834092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 835092a530cSStanislav Gatev LatticeTransferState &State) { 836af98b0afSStanislav Gatev transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 837af98b0afSStanislav Gatev }) 838af98b0afSStanislav Gatev 8393bc1ea5bSMartin Braenne // optional::operator* 8403bc1ea5bSMartin Braenne .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"), 8417538b360SWei Yi Tee [](const CallExpr *E, 8427538b360SWei Yi Tee const MatchFinder::MatchResult &, 843092a530cSStanislav Gatev LatticeTransferState &State) { 844af98b0afSStanislav Gatev transferUnwrapCall(E, E->getArg(0), State); 845af98b0afSStanislav Gatev }) 846af98b0afSStanislav Gatev 8473bc1ea5bSMartin Braenne // optional::operator-> 8483bc1ea5bSMartin Braenne .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"), 8493bc1ea5bSMartin Braenne [](const CallExpr *E, 8503bc1ea5bSMartin Braenne const MatchFinder::MatchResult &, 8513bc1ea5bSMartin Braenne LatticeTransferState &State) { 8523bc1ea5bSMartin Braenne transferArrowOpCall(E, E->getArg(0), State); 8533bc1ea5bSMartin Braenne }) 8543bc1ea5bSMartin Braenne 855af98b0afSStanislav Gatev // optional::has_value 8567538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8577538b360SWei Yi Tee isOptionalMemberCallWithName("has_value"), 858af98b0afSStanislav Gatev transferOptionalHasValueCall) 859af98b0afSStanislav Gatev 8609e0fc676SStanislav Gatev // optional::operator bool 8617538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8627538b360SWei Yi Tee isOptionalMemberCallWithName("operator bool"), 8639e0fc676SStanislav Gatev transferOptionalHasValueCall) 8649e0fc676SStanislav Gatev 8659e0fc676SStanislav Gatev // optional::emplace 8667538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 867092a530cSStanislav Gatev isOptionalMemberCallWithName("emplace"), 868092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 869092a530cSStanislav Gatev LatticeTransferState &State) { 8700e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E->getImplicitObjectArgument(), State.Env, 871092a530cSStanislav Gatev State.Env.getBoolLiteralValue(true)); 872092a530cSStanislav Gatev }) 8739e0fc676SStanislav Gatev 8749e0fc676SStanislav Gatev // optional::reset 8757538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 876092a530cSStanislav Gatev isOptionalMemberCallWithName("reset"), 877092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 878092a530cSStanislav Gatev LatticeTransferState &State) { 8790e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E->getImplicitObjectArgument(), State.Env, 880092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false)); 881092a530cSStanislav Gatev }) 8829e0fc676SStanislav Gatev 8832ddd57aeSStanislav Gatev // optional::swap 8847538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"), 8852ddd57aeSStanislav Gatev transferSwapCall) 8862ddd57aeSStanislav Gatev 8872ddd57aeSStanislav Gatev // std::swap 8887538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 8892ddd57aeSStanislav Gatev 89025956d55SAMS21 // std::forward 89125956d55SAMS21 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 89225956d55SAMS21 8937f076004SYitzhak Mandelbaum // opt.value_or("").empty() 8947538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 8957538b360SWei Yi Tee transferValueOrStringEmptyCall) 8967f076004SYitzhak Mandelbaum 8977f076004SYitzhak Mandelbaum // opt.value_or(X) != X 8987538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 8997f076004SYitzhak Mandelbaum 900390029beSYitzhak Mandelbaum // Comparisons (==, !=): 901390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 902390029beSYitzhak Mandelbaum isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()), 903390029beSYitzhak Mandelbaum transferOptionalAndOptionalCmp) 904390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 905390029beSYitzhak Mandelbaum isComparisonOperatorCall(hasOptionalType(), 906390029beSYitzhak Mandelbaum unless(hasAnyOptionalType())), 907390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 908390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 909390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 910390029beSYitzhak Mandelbaum }) 911390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 912390029beSYitzhak Mandelbaum isComparisonOperatorCall(unless(hasAnyOptionalType()), 913390029beSYitzhak Mandelbaum hasOptionalType()), 914390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 915390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 916390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 917390029beSYitzhak Mandelbaum }) 918390029beSYitzhak Mandelbaum 91965e710c3SStanislav Gatev // returns optional 9207538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 92165e710c3SStanislav Gatev transferCallReturningOptional) 92265e710c3SStanislav Gatev 923af98b0afSStanislav Gatev .Build(); 924af98b0afSStanislav Gatev } 925af98b0afSStanislav Gatev 9266a81e694SMartin Braenne std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr, 92758fe7f96SSam Estep const Environment &Env) { 92848bc7150SMartin Braenne if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) { 92958fe7f96SSam Estep auto *Prop = OptionalVal->getProperty("has_value"); 93058fe7f96SSam Estep if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 93158fe7f96SSam Estep if (Env.flowConditionImplies(*HasValueVal)) 93258fe7f96SSam Estep return {}; 93358fe7f96SSam Estep } 93458fe7f96SSam Estep } 93558fe7f96SSam Estep 93658fe7f96SSam Estep // Record that this unwrap is *not* provably safe. 93758fe7f96SSam Estep // FIXME: include either the name of the optional (if applicable) or a source 93858fe7f96SSam Estep // range of the access for easier interpretation of the result. 93958fe7f96SSam Estep return {ObjectExpr->getBeginLoc()}; 94058fe7f96SSam Estep } 94158fe7f96SSam Estep 94258fe7f96SSam Estep auto buildDiagnoseMatchSwitch( 94358fe7f96SSam Estep const UncheckedOptionalAccessModelOptions &Options) { 94458fe7f96SSam Estep // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 94558fe7f96SSam Estep // lot of duplicated work (e.g. string comparisons), consider providing APIs 94658fe7f96SSam Estep // that avoid it through memoization. 94758fe7f96SSam Estep auto IgnorableOptional = ignorableOptional(Options); 9487538b360SWei Yi Tee return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>() 94958fe7f96SSam Estep // optional::value 9507538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 95158fe7f96SSam Estep valueCall(IgnorableOptional), 95258fe7f96SSam Estep [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 95358fe7f96SSam Estep const Environment &Env) { 9546a81e694SMartin Braenne return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env); 95558fe7f96SSam Estep }) 95658fe7f96SSam Estep 95758fe7f96SSam Estep // optional::operator*, optional::operator-> 9586a81e694SMartin Braenne .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 9596a81e694SMartin Braenne [](const CallExpr *E, 9606a81e694SMartin Braenne const MatchFinder::MatchResult &, 96158fe7f96SSam Estep const Environment &Env) { 9626a81e694SMartin Braenne return diagnoseUnwrapCall(E->getArg(0), Env); 96358fe7f96SSam Estep }) 96458fe7f96SSam Estep .Build(); 96558fe7f96SSam Estep } 96658fe7f96SSam Estep 967af98b0afSStanislav Gatev } // namespace 968af98b0afSStanislav Gatev 9697e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher 9707e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() { 9717e63a0d4SYitzhak Mandelbaum return optionalClass(); 9727e63a0d4SYitzhak Mandelbaum } 9737e63a0d4SYitzhak Mandelbaum 9745d22d1f5SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx) 975cf1f978dSSam Estep : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 9765d22d1f5SYitzhak Mandelbaum TransferMatchSwitch(buildTransferMatchSwitch()) {} 977af98b0afSStanislav Gatev 9786b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 9797538b360SWei Yi Tee NoopLattice &L, Environment &Env) { 980af98b0afSStanislav Gatev LatticeTransferState State(L, Env); 9816b991ba4SYitzhak Mandelbaum TransferMatchSwitch(Elt, getASTContext(), State); 982af98b0afSStanislav Gatev } 983af98b0afSStanislav Gatev 984c0725865SYitzhak Mandelbaum ComparisonResult UncheckedOptionalAccessModel::compare( 985c0725865SYitzhak Mandelbaum QualType Type, const Value &Val1, const Environment &Env1, 986c0725865SYitzhak Mandelbaum const Value &Val2, const Environment &Env2) { 987c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 988c0725865SYitzhak Mandelbaum return ComparisonResult::Unknown; 989d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 990d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 99125956d55SAMS21 if (MustNonEmpty1 && MustNonEmpty2) 99225956d55SAMS21 return ComparisonResult::Same; 993d34fbf2dSYitzhak Mandelbaum // If exactly one is true, then they're different, no reason to check whether 994d34fbf2dSYitzhak Mandelbaum // they're definitely empty. 99525956d55SAMS21 if (MustNonEmpty1 || MustNonEmpty2) 99625956d55SAMS21 return ComparisonResult::Different; 997d34fbf2dSYitzhak Mandelbaum // Check if they're both definitely empty. 998d34fbf2dSYitzhak Mandelbaum return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)) 999c0725865SYitzhak Mandelbaum ? ComparisonResult::Same 1000c0725865SYitzhak Mandelbaum : ComparisonResult::Different; 10018fcdd625SStanislav Gatev } 10028fcdd625SStanislav Gatev 10038fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1, 10048fcdd625SStanislav Gatev const Environment &Env1, 10058fcdd625SStanislav Gatev const Value &Val2, 10068fcdd625SStanislav Gatev const Environment &Env2, 10078fcdd625SStanislav Gatev Value &MergedVal, 10088fcdd625SStanislav Gatev Environment &MergedEnv) { 1009c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 10108fcdd625SStanislav Gatev return true; 1011d34fbf2dSYitzhak Mandelbaum // FIXME: uses same approach as join for `BoolValues`. Requires non-const 1012d34fbf2dSYitzhak Mandelbaum // values, though, so will require updating the interface. 10138fcdd625SStanislav Gatev auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); 1014d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 1015d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 1016d34fbf2dSYitzhak Mandelbaum if (MustNonEmpty1 && MustNonEmpty2) 10178fcdd625SStanislav Gatev MergedEnv.addToFlowCondition(HasValueVal); 1018d34fbf2dSYitzhak Mandelbaum else if ( 1019d34fbf2dSYitzhak Mandelbaum // Only make the costly calls to `isEmptyOptional` if we got "unknown" 1020d34fbf2dSYitzhak Mandelbaum // (false) for both calls to `isNonEmptyOptional`. 1021d34fbf2dSYitzhak Mandelbaum !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) && 1022d34fbf2dSYitzhak Mandelbaum isEmptyOptional(Val2, Env2)) 10238fcdd625SStanislav Gatev MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal)); 10248fcdd625SStanislav Gatev setHasValue(MergedVal, HasValueVal); 10258fcdd625SStanislav Gatev return true; 10268fcdd625SStanislav Gatev } 10278fcdd625SStanislav Gatev 1028d34fbf2dSYitzhak Mandelbaum Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev, 1029d34fbf2dSYitzhak Mandelbaum const Environment &PrevEnv, 1030d34fbf2dSYitzhak Mandelbaum Value &Current, 1031d34fbf2dSYitzhak Mandelbaum Environment &CurrentEnv) { 1032d34fbf2dSYitzhak Mandelbaum switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) { 1033d34fbf2dSYitzhak Mandelbaum case ComparisonResult::Same: 1034d34fbf2dSYitzhak Mandelbaum return &Prev; 1035d34fbf2dSYitzhak Mandelbaum case ComparisonResult::Different: 1036d34fbf2dSYitzhak Mandelbaum if (auto *PrevHasVal = 1037d34fbf2dSYitzhak Mandelbaum cast_or_null<BoolValue>(Prev.getProperty("has_value"))) { 1038d34fbf2dSYitzhak Mandelbaum if (isa<TopBoolValue>(PrevHasVal)) 1039d34fbf2dSYitzhak Mandelbaum return &Prev; 1040d34fbf2dSYitzhak Mandelbaum } 1041d34fbf2dSYitzhak Mandelbaum if (auto *CurrentHasVal = 1042d34fbf2dSYitzhak Mandelbaum cast_or_null<BoolValue>(Current.getProperty("has_value"))) { 1043d34fbf2dSYitzhak Mandelbaum if (isa<TopBoolValue>(CurrentHasVal)) 1044d34fbf2dSYitzhak Mandelbaum return &Current; 1045d34fbf2dSYitzhak Mandelbaum } 1046d34fbf2dSYitzhak Mandelbaum return &createOptionalValue(CurrentEnv, CurrentEnv.makeTopBoolValue()); 1047d34fbf2dSYitzhak Mandelbaum case ComparisonResult::Unknown: 1048d34fbf2dSYitzhak Mandelbaum return nullptr; 1049d34fbf2dSYitzhak Mandelbaum } 1050d34fbf2dSYitzhak Mandelbaum llvm_unreachable("all cases covered in switch"); 1051d34fbf2dSYitzhak Mandelbaum } 1052d34fbf2dSYitzhak Mandelbaum 105358fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 105458fe7f96SSam Estep UncheckedOptionalAccessModelOptions Options) 105558fe7f96SSam Estep : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 105658fe7f96SSam Estep 105758fe7f96SSam Estep std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose( 10587538b360SWei Yi Tee ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) { 10597538b360SWei Yi Tee return DiagnoseMatchSwitch(*Elt, Ctx, Env); 106058fe7f96SSam Estep } 106158fe7f96SSam Estep 1062af98b0afSStanislav Gatev } // namespace dataflow 1063af98b0afSStanislav Gatev } // namespace clang 1064