1b000b770SStanislav Gatev //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===// 2b000b770SStanislav Gatev // 3b000b770SStanislav Gatev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4b000b770SStanislav Gatev // See https://llvm.org/LICENSE.txt for license information. 5b000b770SStanislav Gatev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6b000b770SStanislav Gatev // 7b000b770SStanislav Gatev //===----------------------------------------------------------------------===// 8b000b770SStanislav Gatev // 9b000b770SStanislav Gatev // This file defines a dataflow analysis that detects unsafe uses of optional 10b000b770SStanislav Gatev // values. 11b000b770SStanislav Gatev // 12b000b770SStanislav Gatev //===----------------------------------------------------------------------===// 13b000b770SStanislav Gatev 14af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h" 15af98b0afSStanislav Gatev #include "clang/AST/ASTContext.h" 167e63a0d4SYitzhak Mandelbaum #include "clang/AST/DeclCXX.h" 17af98b0afSStanislav Gatev #include "clang/AST/Expr.h" 18af98b0afSStanislav Gatev #include "clang/AST/ExprCXX.h" 19af98b0afSStanislav Gatev #include "clang/AST/Stmt.h" 20af98b0afSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h" 217538b360SWei Yi Tee #include "clang/Analysis/CFG.h" 227538b360SWei Yi Tee #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h" 23af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 24cf1f978dSSam Estep #include "clang/Analysis/FlowSensitive/NoopLattice.h" 250086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h" 26af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h" 2758fe7f96SSam Estep #include "clang/Basic/SourceLocation.h" 28af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h" 29af98b0afSStanislav Gatev #include "llvm/Support/Casting.h" 30d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h" 31af98b0afSStanislav Gatev #include <cassert> 329e0fc676SStanislav Gatev #include <memory> 33a1580d7bSKazu Hirata #include <optional> 349e0fc676SStanislav Gatev #include <utility> 3558fe7f96SSam Estep #include <vector> 36af98b0afSStanislav Gatev 37af98b0afSStanislav Gatev namespace clang { 38af98b0afSStanislav Gatev namespace dataflow { 39af98b0afSStanislav Gatev namespace { 40af98b0afSStanislav Gatev 41af98b0afSStanislav Gatev using namespace ::clang::ast_matchers; 42cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>; 43af98b0afSStanislav Gatev 447e63a0d4SYitzhak Mandelbaum DeclarationMatcher optionalClass() { 45af98b0afSStanislav Gatev return classTemplateSpecializationDecl( 46*cd22e0dcSYitzhak Mandelbaum hasAnyName("::std::optional", "::std::__optional_storage_base", 47*cd22e0dcSYitzhak Mandelbaum "::std::__optional_destruct_base", "::absl::optional", 48*cd22e0dcSYitzhak Mandelbaum "::base::Optional"), 49af98b0afSStanislav Gatev hasTemplateArgument(0, refersToType(type().bind("T")))); 50af98b0afSStanislav Gatev } 51af98b0afSStanislav Gatev 526adfc64eSYitzhak Mandelbaum auto optionalOrAliasType() { 5365e710c3SStanislav Gatev return hasUnqualifiedDesugaredType( 5465e710c3SStanislav Gatev recordType(hasDeclaration(optionalClass()))); 5565e710c3SStanislav Gatev } 5665e710c3SStanislav Gatev 576adfc64eSYitzhak Mandelbaum /// Matches any of the spellings of the optional types and sugar, aliases, etc. 586adfc64eSYitzhak Mandelbaum auto hasOptionalType() { return hasType(optionalOrAliasType()); } 596adfc64eSYitzhak Mandelbaum 60a184a0d8SYitzhak Mandelbaum auto isOptionalMemberCallWithName( 61a184a0d8SYitzhak Mandelbaum llvm::StringRef MemberName, 626ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 63a184a0d8SYitzhak Mandelbaum auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr())) 64a184a0d8SYitzhak Mandelbaum : cxxThisExpr()); 65af98b0afSStanislav Gatev return cxxMemberCallExpr( 66a184a0d8SYitzhak Mandelbaum on(expr(Exception)), 67af98b0afSStanislav Gatev callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass())))); 68af98b0afSStanislav Gatev } 69af98b0afSStanislav Gatev 70a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName( 71a184a0d8SYitzhak Mandelbaum llvm::StringRef operator_name, 726ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 73a184a0d8SYitzhak Mandelbaum return cxxOperatorCallExpr( 74a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName(operator_name), 75a184a0d8SYitzhak Mandelbaum callee(cxxMethodDecl(ofClass(optionalClass()))), 76a184a0d8SYitzhak Mandelbaum Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 77af98b0afSStanislav Gatev } 78af98b0afSStanislav Gatev 79092a530cSStanislav Gatev auto isMakeOptionalCall() { 809e0fc676SStanislav Gatev return callExpr( 819e0fc676SStanislav Gatev callee(functionDecl(hasAnyName( 829e0fc676SStanislav Gatev "std::make_optional", "base::make_optional", "absl::make_optional"))), 839e0fc676SStanislav Gatev hasOptionalType()); 849e0fc676SStanislav Gatev } 859e0fc676SStanislav Gatev 86390029beSYitzhak Mandelbaum auto nulloptTypeDecl() { 87390029beSYitzhak Mandelbaum return namedDecl( 88390029beSYitzhak Mandelbaum hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")); 89092a530cSStanislav Gatev } 90092a530cSStanislav Gatev 91390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); } 92390029beSYitzhak Mandelbaum 93390029beSYitzhak Mandelbaum // `optional` or `nullopt_t` 94390029beSYitzhak Mandelbaum auto hasAnyOptionalType() { 95390029beSYitzhak Mandelbaum return hasType(hasUnqualifiedDesugaredType( 96390029beSYitzhak Mandelbaum recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass()))))); 97390029beSYitzhak Mandelbaum } 98390029beSYitzhak Mandelbaum 99092a530cSStanislav Gatev auto inPlaceClass() { 100092a530cSStanislav Gatev return recordDecl( 101092a530cSStanislav Gatev hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t")); 102092a530cSStanislav Gatev } 103092a530cSStanislav Gatev 104092a530cSStanislav Gatev auto isOptionalNulloptConstructor() { 1050086a355SYitzhak Mandelbaum return cxxConstructExpr( 1060086a355SYitzhak Mandelbaum hasOptionalType(), 1070086a355SYitzhak Mandelbaum hasDeclaration(cxxConstructorDecl(parameterCountIs(1), 1080086a355SYitzhak Mandelbaum hasParameter(0, hasNulloptType())))); 109092a530cSStanislav Gatev } 110092a530cSStanislav Gatev 111092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() { 112092a530cSStanislav Gatev return cxxConstructExpr(hasOptionalType(), 113092a530cSStanislav Gatev hasArgument(0, hasType(inPlaceClass()))); 114092a530cSStanislav Gatev } 115092a530cSStanislav Gatev 116092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() { 117092a530cSStanislav Gatev return cxxConstructExpr( 118092a530cSStanislav Gatev hasOptionalType(), 119092a530cSStanislav Gatev unless(hasDeclaration( 120092a530cSStanislav Gatev cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 121092a530cSStanislav Gatev argumentCountIs(1), hasArgument(0, unless(hasNulloptType()))); 122092a530cSStanislav Gatev } 123092a530cSStanislav Gatev 124b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() { 125b000b770SStanislav Gatev return cxxOperatorCallExpr( 126b000b770SStanislav Gatev hasOverloadedOperatorName("="), 127b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 128b000b770SStanislav Gatev unless(hasDeclaration(cxxMethodDecl( 129b000b770SStanislav Gatev anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 130b000b770SStanislav Gatev argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 131b000b770SStanislav Gatev } 132b000b770SStanislav Gatev 133390029beSYitzhak Mandelbaum auto isNulloptConstructor() { 134390029beSYitzhak Mandelbaum return cxxConstructExpr(hasNulloptType(), argumentCountIs(1), 135390029beSYitzhak Mandelbaum hasArgument(0, hasNulloptType())); 136390029beSYitzhak Mandelbaum } 137390029beSYitzhak Mandelbaum 138b000b770SStanislav Gatev auto isOptionalNulloptAssignment() { 139b000b770SStanislav Gatev return cxxOperatorCallExpr(hasOverloadedOperatorName("="), 140b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 141b000b770SStanislav Gatev argumentCountIs(2), 142b000b770SStanislav Gatev hasArgument(1, hasNulloptType())); 143b000b770SStanislav Gatev } 144b000b770SStanislav Gatev 1452ddd57aeSStanislav Gatev auto isStdSwapCall() { 1462ddd57aeSStanislav Gatev return callExpr(callee(functionDecl(hasName("std::swap"))), 1472ddd57aeSStanislav Gatev argumentCountIs(2), hasArgument(0, hasOptionalType()), 1482ddd57aeSStanislav Gatev hasArgument(1, hasOptionalType())); 1492ddd57aeSStanislav Gatev } 1502ddd57aeSStanislav Gatev 15125956d55SAMS21 auto isStdForwardCall() { 15225956d55SAMS21 return callExpr(callee(functionDecl(hasName("std::forward"))), 15325956d55SAMS21 argumentCountIs(1), hasArgument(0, hasOptionalType())); 15425956d55SAMS21 } 15525956d55SAMS21 1567f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 1577f076004SYitzhak Mandelbaum 1587f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() { 1597f076004SYitzhak Mandelbaum // `opt.value_or("").empty()` 1607f076004SYitzhak Mandelbaum return cxxMemberCallExpr( 1617f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("empty"))), 1627f076004SYitzhak Mandelbaum onImplicitObjectArgument(ignoringImplicit( 1637f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 1647f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 1657f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 1667f076004SYitzhak Mandelbaum hasArgument(0, stringLiteral(hasSize(0)))) 1677f076004SYitzhak Mandelbaum .bind(ValueOrCallID)))); 1687f076004SYitzhak Mandelbaum } 1697f076004SYitzhak Mandelbaum 1707f076004SYitzhak Mandelbaum auto isValueOrNotEqX() { 1717f076004SYitzhak Mandelbaum auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 1727f076004SYitzhak Mandelbaum return hasOperands( 1737f076004SYitzhak Mandelbaum ignoringImplicit( 1747f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 1757f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 1767f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 1777f076004SYitzhak Mandelbaum hasArgument(0, Arg)) 1787f076004SYitzhak Mandelbaum .bind(ValueOrCallID)), 1797f076004SYitzhak Mandelbaum ignoringImplicit(Arg)); 1807f076004SYitzhak Mandelbaum }; 1817f076004SYitzhak Mandelbaum 1827f076004SYitzhak Mandelbaum // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 1837f076004SYitzhak Mandelbaum // support this pattern for any expression, but the AST does not have a 1847f076004SYitzhak Mandelbaum // generic expression comparison facility, so we specialize to common cases 1857f076004SYitzhak Mandelbaum // seen in practice. FIXME: define a matcher that compares values across 1867f076004SYitzhak Mandelbaum // nodes, which would let us generalize this to any `X`. 1877f076004SYitzhak Mandelbaum return binaryOperation(hasOperatorName("!="), 1887f076004SYitzhak Mandelbaum anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 1897f076004SYitzhak Mandelbaum ComparesToSame(stringLiteral(hasSize(0))), 1907f076004SYitzhak Mandelbaum ComparesToSame(integerLiteral(equals(0))))); 1917f076004SYitzhak Mandelbaum } 1927f076004SYitzhak Mandelbaum 19365e710c3SStanislav Gatev auto isCallReturningOptional() { 194cd0d5261SSam Estep return callExpr(hasType(qualType(anyOf( 195cd0d5261SSam Estep optionalOrAliasType(), referenceType(pointee(optionalOrAliasType())))))); 19665e710c3SStanislav Gatev } 19765e710c3SStanislav Gatev 198390029beSYitzhak Mandelbaum template <typename L, typename R> 199390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 200390029beSYitzhak Mandelbaum return cxxOperatorCallExpr( 201390029beSYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 202390029beSYitzhak Mandelbaum argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 203390029beSYitzhak Mandelbaum hasArgument(1, rhs_arg_matcher)); 204390029beSYitzhak Mandelbaum } 205390029beSYitzhak Mandelbaum 206390029beSYitzhak Mandelbaum // Ensures that `Expr` is mapped to a `BoolValue` and returns it. 207390029beSYitzhak Mandelbaum BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) { 208390029beSYitzhak Mandelbaum auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None)); 209390029beSYitzhak Mandelbaum if (Value != nullptr) 210390029beSYitzhak Mandelbaum return *Value; 211390029beSYitzhak Mandelbaum 212390029beSYitzhak Mandelbaum auto &Loc = Env.createStorageLocation(Expr); 213390029beSYitzhak Mandelbaum Value = &Env.makeAtomicBoolValue(); 214390029beSYitzhak Mandelbaum Env.setValue(Loc, *Value); 215390029beSYitzhak Mandelbaum Env.setStorageLocation(Expr, Loc); 216390029beSYitzhak Mandelbaum return *Value; 217390029beSYitzhak Mandelbaum } 218390029beSYitzhak Mandelbaum 2198fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 2208fcdd625SStanislav Gatev /// property of the optional value `OptionalVal`. 2218fcdd625SStanislav Gatev void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) { 2228fcdd625SStanislav Gatev OptionalVal.setProperty("has_value", HasValueVal); 2238fcdd625SStanislav Gatev } 2248fcdd625SStanislav Gatev 2259e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the 2269e0fc676SStanislav Gatev /// symbolic value of its "has_value" property. 2279e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) { 228745a957fSMartin Braenne auto &OptionalVal = Env.create<StructValue>(); 229745a957fSMartin Braenne setHasValue(OptionalVal, HasValueVal); 230745a957fSMartin Braenne return OptionalVal; 2319e0fc676SStanislav Gatev } 2329e0fc676SStanislav Gatev 233af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the 23449ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null. 235dd38caf3SYitzhak Mandelbaum BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { 236dd38caf3SYitzhak Mandelbaum if (OptionalVal != nullptr) { 237dd38caf3SYitzhak Mandelbaum auto *HasValueVal = 238dd38caf3SYitzhak Mandelbaum cast_or_null<BoolValue>(OptionalVal->getProperty("has_value")); 239dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) { 240dd38caf3SYitzhak Mandelbaum HasValueVal = &Env.makeAtomicBoolValue(); 241dd38caf3SYitzhak Mandelbaum OptionalVal->setProperty("has_value", *HasValueVal); 242dd38caf3SYitzhak Mandelbaum } 243dd38caf3SYitzhak Mandelbaum return HasValueVal; 244af98b0afSStanislav Gatev } 245af98b0afSStanislav Gatev return nullptr; 246af98b0afSStanislav Gatev } 247af98b0afSStanislav Gatev 248092a530cSStanislav Gatev /// If `Type` is a reference type, returns the type of its pointee. Otherwise, 249092a530cSStanislav Gatev /// returns `Type` itself. 250092a530cSStanislav Gatev QualType stripReference(QualType Type) { 251092a530cSStanislav Gatev return Type->isReferenceType() ? Type->getPointeeType() : Type; 252092a530cSStanislav Gatev } 253092a530cSStanislav Gatev 254*cd22e0dcSYitzhak Mandelbaum bool isTopLevelNamespaceWithName(const NamespaceDecl &NS, 255*cd22e0dcSYitzhak Mandelbaum llvm::StringRef Name) { 256*cd22e0dcSYitzhak Mandelbaum return NS.getDeclName().isIdentifier() && NS.getName() == Name && 257*cd22e0dcSYitzhak Mandelbaum NS.getParent() != nullptr && NS.getParent()->isTranslationUnit(); 258*cd22e0dcSYitzhak Mandelbaum } 259*cd22e0dcSYitzhak Mandelbaum 260092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type. 261c0725865SYitzhak Mandelbaum bool isOptionalType(QualType Type) { 262092a530cSStanislav Gatev if (!Type->isRecordType()) 263092a530cSStanislav Gatev return false; 264*cd22e0dcSYitzhak Mandelbaum const CXXRecordDecl *D = Type->getAsCXXRecordDecl(); 265*cd22e0dcSYitzhak Mandelbaum if (D == nullptr || !D->getDeclName().isIdentifier()) 266*cd22e0dcSYitzhak Mandelbaum return false; 267*cd22e0dcSYitzhak Mandelbaum if (D->getName() == "optional") { 268*cd22e0dcSYitzhak Mandelbaum if (const auto *N = 269*cd22e0dcSYitzhak Mandelbaum dyn_cast_or_null<NamespaceDecl>(D->getDeclContext())) 270*cd22e0dcSYitzhak Mandelbaum return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl"); 271*cd22e0dcSYitzhak Mandelbaum return false; 272*cd22e0dcSYitzhak Mandelbaum } 273*cd22e0dcSYitzhak Mandelbaum 274*cd22e0dcSYitzhak Mandelbaum if (D->getName() == "Optional") { 275*cd22e0dcSYitzhak Mandelbaum // Check whether namespace is "::base". 276*cd22e0dcSYitzhak Mandelbaum const auto *N = 277*cd22e0dcSYitzhak Mandelbaum dyn_cast_or_null<NamespaceDecl>(D->getDeclContext()); 278*cd22e0dcSYitzhak Mandelbaum return N != nullptr && isTopLevelNamespaceWithName(*N, "base"); 279*cd22e0dcSYitzhak Mandelbaum } 280*cd22e0dcSYitzhak Mandelbaum 281*cd22e0dcSYitzhak Mandelbaum return false; 282092a530cSStanislav Gatev } 283092a530cSStanislav Gatev 284092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`. 285092a530cSStanislav Gatev /// 286092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this 287092a530cSStanislav Gatev /// function will be 2. 288092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 289c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 290092a530cSStanislav Gatev return 0; 291092a530cSStanislav Gatev return 1 + countOptionalWrappers( 292092a530cSStanislav Gatev ASTCtx, 293092a530cSStanislav Gatev cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) 294092a530cSStanislav Gatev ->getTemplateArgs() 295092a530cSStanislav Gatev .get(0) 296092a530cSStanislav Gatev .getAsType() 297092a530cSStanislav Gatev .getDesugaredType(ASTCtx)); 298092a530cSStanislav Gatev } 299092a530cSStanislav Gatev 300dd38caf3SYitzhak Mandelbaum /// Tries to initialize the `optional`'s value (that is, contents), and return 301dd38caf3SYitzhak Mandelbaum /// its location. Returns nullptr if the value can't be represented. 302dd38caf3SYitzhak Mandelbaum StorageLocation *maybeInitializeOptionalValueMember(QualType Q, 303dd38caf3SYitzhak Mandelbaum Value &OptionalVal, 304dd38caf3SYitzhak Mandelbaum Environment &Env) { 305dd38caf3SYitzhak Mandelbaum // The "value" property represents a synthetic field. As such, it needs 306dd38caf3SYitzhak Mandelbaum // `StorageLocation`, like normal fields (and other variables). So, we model 307dd38caf3SYitzhak Mandelbaum // it with a `ReferenceValue`, since that includes a storage location. Once 308dd38caf3SYitzhak Mandelbaum // the property is set, it will be shared by all environments that access the 309dd38caf3SYitzhak Mandelbaum // `Value` representing the optional (here, `OptionalVal`). 310dd38caf3SYitzhak Mandelbaum if (auto *ValueProp = OptionalVal.getProperty("value")) { 311dd38caf3SYitzhak Mandelbaum auto *ValueRef = clang::cast<ReferenceValue>(ValueProp); 31297d69cdaSWei Yi Tee auto &ValueLoc = ValueRef->getReferentLoc(); 313dd38caf3SYitzhak Mandelbaum if (Env.getValue(ValueLoc) == nullptr) { 314dd38caf3SYitzhak Mandelbaum // The property was previously set, but the value has been lost. This can 315dd38caf3SYitzhak Mandelbaum // happen, for example, because of an environment merge (where the two 316dd38caf3SYitzhak Mandelbaum // environments mapped the property to different values, which resulted in 317dd38caf3SYitzhak Mandelbaum // them both being discarded), or when two blocks in the CFG, with neither 318dd38caf3SYitzhak Mandelbaum // a dominator of the other, visit the same optional value, or even when a 319dd38caf3SYitzhak Mandelbaum // block is revisited during testing to collect per-statement state. 320dd38caf3SYitzhak Mandelbaum // FIXME: This situation means that the optional contents are not shared 321dd38caf3SYitzhak Mandelbaum // between branches and the like. Practically, this lack of sharing 322dd38caf3SYitzhak Mandelbaum // reduces the precision of the model when the contents are relevant to 323dd38caf3SYitzhak Mandelbaum // the check, like another optional or a boolean that influences control 324dd38caf3SYitzhak Mandelbaum // flow. 325dd38caf3SYitzhak Mandelbaum auto *ValueVal = Env.createValue(ValueLoc.getType()); 326dd38caf3SYitzhak Mandelbaum if (ValueVal == nullptr) 327dd38caf3SYitzhak Mandelbaum return nullptr; 328dd38caf3SYitzhak Mandelbaum Env.setValue(ValueLoc, *ValueVal); 329dd38caf3SYitzhak Mandelbaum } 330dd38caf3SYitzhak Mandelbaum return &ValueLoc; 331dd38caf3SYitzhak Mandelbaum } 332dd38caf3SYitzhak Mandelbaum 333dd38caf3SYitzhak Mandelbaum auto Ty = stripReference(Q); 334dd38caf3SYitzhak Mandelbaum auto *ValueVal = Env.createValue(Ty); 335dd38caf3SYitzhak Mandelbaum if (ValueVal == nullptr) 336dd38caf3SYitzhak Mandelbaum return nullptr; 337dd38caf3SYitzhak Mandelbaum auto &ValueLoc = Env.createStorageLocation(Ty); 338dd38caf3SYitzhak Mandelbaum Env.setValue(ValueLoc, *ValueVal); 339745a957fSMartin Braenne auto &ValueRef = Env.create<ReferenceValue>(ValueLoc); 340745a957fSMartin Braenne OptionalVal.setProperty("value", ValueRef); 341dd38caf3SYitzhak Mandelbaum return &ValueLoc; 342dd38caf3SYitzhak Mandelbaum } 343dd38caf3SYitzhak Mandelbaum 344092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr, 345092a530cSStanislav Gatev const MatchFinder::MatchResult &, 346af98b0afSStanislav Gatev LatticeTransferState &State) { 34749ed5bf5SWei Yi Tee if (auto *OptionalVal = 34849ed5bf5SWei Yi Tee State.Env.getValue(*OptionalExpr, SkipPast::Reference)) { 349af98b0afSStanislav Gatev if (OptionalVal->getProperty("has_value") == nullptr) { 3508fcdd625SStanislav Gatev setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); 351af98b0afSStanislav Gatev } 352af98b0afSStanislav Gatev } 353af98b0afSStanislav Gatev } 354af98b0afSStanislav Gatev 3558fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be 3568fcdd625SStanislav Gatev /// empty in `Env. 3578fcdd625SStanislav Gatev bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { 3588fcdd625SStanislav Gatev auto *HasValueVal = 3598fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 3608fcdd625SStanislav Gatev return HasValueVal != nullptr && 3618fcdd625SStanislav Gatev Env.flowConditionImplies(Env.makeNot(*HasValueVal)); 3628fcdd625SStanislav Gatev } 3638fcdd625SStanislav Gatev 3648fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be 3658fcdd625SStanislav Gatev /// non-empty in `Env. 3668fcdd625SStanislav Gatev bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { 3678fcdd625SStanislav Gatev auto *HasValueVal = 3688fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 3698fcdd625SStanislav Gatev return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal); 3708fcdd625SStanislav Gatev } 3718fcdd625SStanislav Gatev 372092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 373af98b0afSStanislav Gatev LatticeTransferState &State) { 37449ed5bf5SWei Yi Tee if (auto *OptionalVal = 37549ed5bf5SWei Yi Tee State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 376dd38caf3SYitzhak Mandelbaum if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr) 377dd38caf3SYitzhak Mandelbaum if (auto *Loc = maybeInitializeOptionalValueMember( 378dd38caf3SYitzhak Mandelbaum UnwrapExpr->getType(), *OptionalVal, State.Env)) 379dd38caf3SYitzhak Mandelbaum State.Env.setStorageLocation(*UnwrapExpr, *Loc); 380af98b0afSStanislav Gatev } 381dd38caf3SYitzhak Mandelbaum } 382af98b0afSStanislav Gatev 383092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E, 384092a530cSStanislav Gatev const MatchFinder::MatchResult &, 385092a530cSStanislav Gatev LatticeTransferState &State) { 3869e0fc676SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E); 3879e0fc676SStanislav Gatev State.Env.setStorageLocation(*E, Loc); 3889e0fc676SStanislav Gatev State.Env.setValue( 3899e0fc676SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true))); 3909e0fc676SStanislav Gatev } 3919e0fc676SStanislav Gatev 392092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 393092a530cSStanislav Gatev const MatchFinder::MatchResult &, 394af98b0afSStanislav Gatev LatticeTransferState &State) { 395dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = getHasValue( 396dd38caf3SYitzhak Mandelbaum State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(), 397af98b0afSStanislav Gatev SkipPast::ReferenceThenPointer))) { 398af98b0afSStanislav Gatev auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr); 399af98b0afSStanislav Gatev State.Env.setValue(CallExprLoc, *HasValueVal); 400af98b0afSStanislav Gatev State.Env.setStorageLocation(*CallExpr, CallExprLoc); 401af98b0afSStanislav Gatev } 402af98b0afSStanislav Gatev } 403af98b0afSStanislav Gatev 4047f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in 4057f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property. 4067f076004SYitzhak Mandelbaum void transferValueOrImpl(const clang::Expr *ValueOrPredExpr, 4077f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 4087f076004SYitzhak Mandelbaum LatticeTransferState &State, 4097f076004SYitzhak Mandelbaum BoolValue &(*ModelPred)(Environment &Env, 4107f076004SYitzhak Mandelbaum BoolValue &ExprVal, 4117f076004SYitzhak Mandelbaum BoolValue &HasValueVal)) { 4127f076004SYitzhak Mandelbaum auto &Env = State.Env; 4137f076004SYitzhak Mandelbaum 4147f076004SYitzhak Mandelbaum const auto *ObjectArgumentExpr = 4157f076004SYitzhak Mandelbaum Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) 4167f076004SYitzhak Mandelbaum ->getImplicitObjectArgument(); 4177f076004SYitzhak Mandelbaum 418dd38caf3SYitzhak Mandelbaum auto *HasValueVal = getHasValue( 419dd38caf3SYitzhak Mandelbaum State.Env, 420dd38caf3SYitzhak Mandelbaum State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer)); 421dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) 4227f076004SYitzhak Mandelbaum return; 4237f076004SYitzhak Mandelbaum 424390029beSYitzhak Mandelbaum Env.addToFlowCondition( 425390029beSYitzhak Mandelbaum ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal)); 4267f076004SYitzhak Mandelbaum } 4277f076004SYitzhak Mandelbaum 4287f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 4297f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 4307f076004SYitzhak Mandelbaum LatticeTransferState &State) { 4317f076004SYitzhak Mandelbaum return transferValueOrImpl(ComparisonExpr, Result, State, 4327f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal, 4337f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & { 4347f076004SYitzhak Mandelbaum // If the result is *not* empty, then we know the 4357f076004SYitzhak Mandelbaum // optional must have been holding a value. If 4367f076004SYitzhak Mandelbaum // `ExprVal` is true, though, we don't learn 4377f076004SYitzhak Mandelbaum // anything definite about `has_value`, so we 4387f076004SYitzhak Mandelbaum // don't add any corresponding implications to 4397f076004SYitzhak Mandelbaum // the flow condition. 4407f076004SYitzhak Mandelbaum return Env.makeImplication(Env.makeNot(ExprVal), 4417f076004SYitzhak Mandelbaum HasValueVal); 4427f076004SYitzhak Mandelbaum }); 4437f076004SYitzhak Mandelbaum } 4447f076004SYitzhak Mandelbaum 4457f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr, 4467f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 4477f076004SYitzhak Mandelbaum LatticeTransferState &State) { 4487f076004SYitzhak Mandelbaum transferValueOrImpl(ComparisonExpr, Result, State, 4497f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal, 4507f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & { 4517f076004SYitzhak Mandelbaum // We know that if `(opt.value_or(X) != X)` then 4527f076004SYitzhak Mandelbaum // `opt.hasValue()`, even without knowing further 4537f076004SYitzhak Mandelbaum // details about the contents of `opt`. 4547f076004SYitzhak Mandelbaum return Env.makeImplication(ExprVal, HasValueVal); 4557f076004SYitzhak Mandelbaum }); 4567f076004SYitzhak Mandelbaum } 4577f076004SYitzhak Mandelbaum 45865e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E, 45965e710c3SStanislav Gatev const MatchFinder::MatchResult &Result, 46065e710c3SStanislav Gatev LatticeTransferState &State) { 46165e710c3SStanislav Gatev if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) 46265e710c3SStanislav Gatev return; 46365e710c3SStanislav Gatev 46465e710c3SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E); 46565e710c3SStanislav Gatev State.Env.setStorageLocation(*E, Loc); 46665e710c3SStanislav Gatev State.Env.setValue( 46765e710c3SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue())); 46865e710c3SStanislav Gatev } 46965e710c3SStanislav Gatev 4700e8d4a6dSYitzhak Mandelbaum void assignOptionalValue(const Expr &E, Environment &Env, 471092a530cSStanislav Gatev BoolValue &HasValueVal) { 472092a530cSStanislav Gatev if (auto *OptionalLoc = 4730e8d4a6dSYitzhak Mandelbaum Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) { 4740e8d4a6dSYitzhak Mandelbaum Env.setValue(*OptionalLoc, createOptionalValue(Env, HasValueVal)); 4759e0fc676SStanislav Gatev } 4769e0fc676SStanislav Gatev } 4779e0fc676SStanislav Gatev 478b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>` 479b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>` 480b000b770SStanislav Gatev /// where `T` is constructible from `U`. 481390029beSYitzhak Mandelbaum BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E, 482b000b770SStanislav Gatev const MatchFinder::MatchResult &MatchRes, 483b000b770SStanislav Gatev LatticeTransferState &State) { 4840086a355SYitzhak Mandelbaum assert(F.getTemplateSpecializationArgs() != nullptr); 485b000b770SStanislav Gatev assert(F.getTemplateSpecializationArgs()->size() > 0); 486b000b770SStanislav Gatev 487b000b770SStanislav Gatev const int TemplateParamOptionalWrappersCount = countOptionalWrappers( 488b000b770SStanislav Gatev *MatchRes.Context, 489b000b770SStanislav Gatev stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType())); 490b000b770SStanislav Gatev const int ArgTypeOptionalWrappersCount = 491b000b770SStanislav Gatev countOptionalWrappers(*MatchRes.Context, stripReference(E.getType())); 492b000b770SStanislav Gatev 493b000b770SStanislav Gatev // Check if this is a constructor/assignment call for `optional<T>` with 494b000b770SStanislav Gatev // argument of type `U` such that `T` is constructible from `U`. 495b000b770SStanislav Gatev if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) 496b000b770SStanislav Gatev return State.Env.getBoolLiteralValue(true); 497b000b770SStanislav Gatev 498b000b770SStanislav Gatev // This is a constructor/assignment call for `optional<T>` with argument of 499b000b770SStanislav Gatev // type `optional<U>` such that `T` is constructible from `U`. 500dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = 501dd38caf3SYitzhak Mandelbaum getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference))) 502dd38caf3SYitzhak Mandelbaum return *HasValueVal; 503b000b770SStanislav Gatev return State.Env.makeAtomicBoolValue(); 504b000b770SStanislav Gatev } 505b000b770SStanislav Gatev 506092a530cSStanislav Gatev void transferValueOrConversionConstructor( 507092a530cSStanislav Gatev const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 5089e0fc676SStanislav Gatev LatticeTransferState &State) { 509092a530cSStanislav Gatev assert(E->getNumArgs() > 0); 510092a530cSStanislav Gatev 5110e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E, State.Env, 512390029beSYitzhak Mandelbaum valueOrConversionHasValue(*E->getConstructor(), 513b000b770SStanislav Gatev *E->getArg(0), MatchRes, 514b000b770SStanislav Gatev State)); 515b000b770SStanislav Gatev } 516092a530cSStanislav Gatev 517b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 518b000b770SStanislav Gatev LatticeTransferState &State) { 519b000b770SStanislav Gatev assert(E->getNumArgs() > 0); 520b000b770SStanislav Gatev 521b000b770SStanislav Gatev auto *OptionalLoc = 522b000b770SStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 523a9ad689eSSam Estep if (OptionalLoc == nullptr) 524a9ad689eSSam Estep return; 525b000b770SStanislav Gatev 526b000b770SStanislav Gatev State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal)); 527b000b770SStanislav Gatev 528b000b770SStanislav Gatev // Assign a storage location for the whole expression. 529b000b770SStanislav Gatev State.Env.setStorageLocation(*E, *OptionalLoc); 530b000b770SStanislav Gatev } 531b000b770SStanislav Gatev 532b000b770SStanislav Gatev void transferValueOrConversionAssignment( 533b000b770SStanislav Gatev const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 534b000b770SStanislav Gatev LatticeTransferState &State) { 535b000b770SStanislav Gatev assert(E->getNumArgs() > 1); 536b000b770SStanislav Gatev transferAssignment(E, 537390029beSYitzhak Mandelbaum valueOrConversionHasValue(*E->getDirectCallee(), 53806decd0bSKazu Hirata *E->getArg(1), MatchRes, State), 539b000b770SStanislav Gatev State); 540b000b770SStanislav Gatev } 541b000b770SStanislav Gatev 542b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E, 543b000b770SStanislav Gatev const MatchFinder::MatchResult &, 544b000b770SStanislav Gatev LatticeTransferState &State) { 545b000b770SStanislav Gatev transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 5469e0fc676SStanislav Gatev } 5479e0fc676SStanislav Gatev 548d4fb829bSYitzhak Mandelbaum void transferSwap(const Expr &E1, SkipPast E1Skip, const Expr &E2, 549d4fb829bSYitzhak Mandelbaum Environment &Env) { 550d4fb829bSYitzhak Mandelbaum // We account for cases where one or both of the optionals are not modeled, 551d4fb829bSYitzhak Mandelbaum // either lacking associated storage locations, or lacking values associated 552d4fb829bSYitzhak Mandelbaum // to such storage locations. 553d4fb829bSYitzhak Mandelbaum auto *Loc1 = Env.getStorageLocation(E1, E1Skip); 554d4fb829bSYitzhak Mandelbaum auto *Loc2 = Env.getStorageLocation(E2, SkipPast::Reference); 5552ddd57aeSStanislav Gatev 556d4fb829bSYitzhak Mandelbaum if (Loc1 == nullptr) { 557d4fb829bSYitzhak Mandelbaum if (Loc2 != nullptr) 558d4fb829bSYitzhak Mandelbaum Env.setValue(*Loc2, createOptionalValue(Env, Env.makeAtomicBoolValue())); 559d4fb829bSYitzhak Mandelbaum return; 560d4fb829bSYitzhak Mandelbaum } 561d4fb829bSYitzhak Mandelbaum if (Loc2 == nullptr) { 562d4fb829bSYitzhak Mandelbaum Env.setValue(*Loc1, createOptionalValue(Env, Env.makeAtomicBoolValue())); 563d4fb829bSYitzhak Mandelbaum return; 564d4fb829bSYitzhak Mandelbaum } 5652ddd57aeSStanislav Gatev 566d4fb829bSYitzhak Mandelbaum // Both expressions have locations, though they may not have corresponding 567d4fb829bSYitzhak Mandelbaum // values. In that case, we create a fresh value at this point. Note that if 568d4fb829bSYitzhak Mandelbaum // two branches both do this, they will not share the value, but it at least 569d4fb829bSYitzhak Mandelbaum // allows for local reasoning about the value. To avoid the above, we would 570d4fb829bSYitzhak Mandelbaum // need *lazy* value allocation. 571d4fb829bSYitzhak Mandelbaum // FIXME: allocate values lazily, instead of just creating a fresh value. 572d4fb829bSYitzhak Mandelbaum auto *Val1 = Env.getValue(*Loc1); 573d4fb829bSYitzhak Mandelbaum if (Val1 == nullptr) 574d4fb829bSYitzhak Mandelbaum Val1 = &createOptionalValue(Env, Env.makeAtomicBoolValue()); 575d4fb829bSYitzhak Mandelbaum 576d4fb829bSYitzhak Mandelbaum auto *Val2 = Env.getValue(*Loc2); 577d4fb829bSYitzhak Mandelbaum if (Val2 == nullptr) 578d4fb829bSYitzhak Mandelbaum Val2 = &createOptionalValue(Env, Env.makeAtomicBoolValue()); 579d4fb829bSYitzhak Mandelbaum 580d4fb829bSYitzhak Mandelbaum Env.setValue(*Loc1, *Val2); 581d4fb829bSYitzhak Mandelbaum Env.setValue(*Loc2, *Val1); 5822ddd57aeSStanislav Gatev } 5832ddd57aeSStanislav Gatev 5842ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E, 5852ddd57aeSStanislav Gatev const MatchFinder::MatchResult &, 5862ddd57aeSStanislav Gatev LatticeTransferState &State) { 5872ddd57aeSStanislav Gatev assert(E->getNumArgs() == 1); 588d4fb829bSYitzhak Mandelbaum transferSwap(*E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer, 589d4fb829bSYitzhak Mandelbaum *E->getArg(0), State.Env); 5902ddd57aeSStanislav Gatev } 5912ddd57aeSStanislav Gatev 5922ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 5932ddd57aeSStanislav Gatev LatticeTransferState &State) { 5942ddd57aeSStanislav Gatev assert(E->getNumArgs() == 2); 595d4fb829bSYitzhak Mandelbaum transferSwap(*E->getArg(0), SkipPast::Reference, *E->getArg(1), State.Env); 5962ddd57aeSStanislav Gatev } 5972ddd57aeSStanislav Gatev 59825956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &, 59925956d55SAMS21 LatticeTransferState &State) { 60025956d55SAMS21 assert(E->getNumArgs() == 1); 60125956d55SAMS21 60225956d55SAMS21 StorageLocation *LocRet = State.Env.getStorageLocation(*E, SkipPast::None); 60325956d55SAMS21 if (LocRet != nullptr) 60425956d55SAMS21 return; 60525956d55SAMS21 60625956d55SAMS21 StorageLocation *LocArg = 60725956d55SAMS21 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 60825956d55SAMS21 60925956d55SAMS21 if (LocArg == nullptr) 61025956d55SAMS21 return; 61125956d55SAMS21 61225956d55SAMS21 Value *ValArg = State.Env.getValue(*LocArg); 61325956d55SAMS21 if (ValArg == nullptr) 61425956d55SAMS21 ValArg = &createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()); 61525956d55SAMS21 61625956d55SAMS21 // Create a new storage location 61725956d55SAMS21 LocRet = &State.Env.createStorageLocation(*E); 61825956d55SAMS21 State.Env.setStorageLocation(*E, *LocRet); 61925956d55SAMS21 62025956d55SAMS21 State.Env.setValue(*LocRet, *ValArg); 62125956d55SAMS21 } 62225956d55SAMS21 623390029beSYitzhak Mandelbaum BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS, 624390029beSYitzhak Mandelbaum BoolValue &RHS) { 625390029beSYitzhak Mandelbaum // Logically, an optional<T> object is composed of two values - a `has_value` 626390029beSYitzhak Mandelbaum // bit and a value of type T. Equality of optional objects compares both 627390029beSYitzhak Mandelbaum // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 628390029beSYitzhak Mandelbaum // when two optional objects are engaged, the equality of their respective 629390029beSYitzhak Mandelbaum // values of type T matters. Since we only track the `has_value` bits, we 630390029beSYitzhak Mandelbaum // can't make any conclusions about equality when we know that two optional 631390029beSYitzhak Mandelbaum // objects are engaged. 632390029beSYitzhak Mandelbaum // 633390029beSYitzhak Mandelbaum // We express this as two facts about the equality: 634390029beSYitzhak Mandelbaum // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 635390029beSYitzhak Mandelbaum // If they are equal, then either both are set or both are unset. 636390029beSYitzhak Mandelbaum // b) (!LHS & !RHS) => EqVal 637390029beSYitzhak Mandelbaum // If neither is set, then they are equal. 638390029beSYitzhak Mandelbaum // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 639390029beSYitzhak Mandelbaum return Env.makeAnd( 640390029beSYitzhak Mandelbaum Env.makeImplication( 641390029beSYitzhak Mandelbaum EqVal, Env.makeOr(Env.makeAnd(LHS, RHS), 642390029beSYitzhak Mandelbaum Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))), 643390029beSYitzhak Mandelbaum Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS))); 644390029beSYitzhak Mandelbaum } 645390029beSYitzhak Mandelbaum 646390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 647390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, 648390029beSYitzhak Mandelbaum LatticeTransferState &State) { 649390029beSYitzhak Mandelbaum Environment &Env = State.Env; 650390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 651390029beSYitzhak Mandelbaum if (auto *LHasVal = getHasValue( 652390029beSYitzhak Mandelbaum Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference))) 653390029beSYitzhak Mandelbaum if (auto *RHasVal = getHasValue( 654390029beSYitzhak Mandelbaum Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) { 655390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 656390029beSYitzhak Mandelbaum CmpValue = &State.Env.makeNot(*CmpValue); 657390029beSYitzhak Mandelbaum Env.addToFlowCondition( 658390029beSYitzhak Mandelbaum evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal)); 659390029beSYitzhak Mandelbaum } 660390029beSYitzhak Mandelbaum } 661390029beSYitzhak Mandelbaum 662390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 663390029beSYitzhak Mandelbaum const clang::Expr *E, Environment &Env) { 664390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 665390029beSYitzhak Mandelbaum if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) { 666390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 667390029beSYitzhak Mandelbaum CmpValue = &Env.makeNot(*CmpValue); 668390029beSYitzhak Mandelbaum Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal, 669390029beSYitzhak Mandelbaum Env.getBoolLiteralValue(true))); 670390029beSYitzhak Mandelbaum } 671390029beSYitzhak Mandelbaum } 672390029beSYitzhak Mandelbaum 6736ad0788cSKazu Hirata std::optional<StatementMatcher> 674a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 6755d22d1f5SYitzhak Mandelbaum if (Options.IgnoreSmartPointerDereference) { 6765d22d1f5SYitzhak Mandelbaum auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 6775d22d1f5SYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 6785d22d1f5SYitzhak Mandelbaum unless(hasArgument(0, expr(hasOptionalType())))))); 6795d22d1f5SYitzhak Mandelbaum return expr( 6805d22d1f5SYitzhak Mandelbaum anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 6815d22d1f5SYitzhak Mandelbaum } 68234e0d057SKazu Hirata return std::nullopt; 683a184a0d8SYitzhak Mandelbaum } 684a184a0d8SYitzhak Mandelbaum 68558fe7f96SSam Estep StatementMatcher 6866ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 68758fe7f96SSam Estep return isOptionalMemberCallWithName("value", IgnorableOptional); 68858fe7f96SSam Estep } 68958fe7f96SSam Estep 69058fe7f96SSam Estep StatementMatcher 6916ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 69258fe7f96SSam Estep return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 69358fe7f96SSam Estep isOptionalOperatorCallWithName("->", IgnorableOptional))); 69458fe7f96SSam Estep } 69558fe7f96SSam Estep 6965d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() { 697b000b770SStanislav Gatev // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 698b000b770SStanislav Gatev // lot of duplicated work (e.g. string comparisons), consider providing APIs 699b000b770SStanislav Gatev // that avoid it through memoization. 7007538b360SWei Yi Tee return CFGMatchSwitchBuilder<LatticeTransferState>() 701af98b0afSStanislav Gatev // Attach a symbolic "has_value" state to optional values that we see for 702af98b0afSStanislav Gatev // the first time. 7037538b360SWei Yi Tee .CaseOfCFGStmt<Expr>( 7046adfc64eSYitzhak Mandelbaum expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), 705af98b0afSStanislav Gatev initializeOptionalReference) 706af98b0afSStanislav Gatev 7079e0fc676SStanislav Gatev // make_optional 7087538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 709092a530cSStanislav Gatev 7100e8d4a6dSYitzhak Mandelbaum // optional::optional (in place) 7117538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 712092a530cSStanislav Gatev isOptionalInPlaceConstructor(), 713092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 714092a530cSStanislav Gatev LatticeTransferState &State) { 7150e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E, State.Env, 7160e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(true)); 717092a530cSStanislav Gatev }) 7180e8d4a6dSYitzhak Mandelbaum // nullopt_t::nullopt_t 7197538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 720390029beSYitzhak Mandelbaum isNulloptConstructor(), 721092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 722092a530cSStanislav Gatev LatticeTransferState &State) { 7230e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E, State.Env, 724092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false)); 725092a530cSStanislav Gatev }) 7260e8d4a6dSYitzhak Mandelbaum // optional::optional(nullopt_t) 727390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXConstructExpr>( 728390029beSYitzhak Mandelbaum isOptionalNulloptConstructor(), 729390029beSYitzhak Mandelbaum [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 730390029beSYitzhak Mandelbaum LatticeTransferState &State) { 7310e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E, State.Env, 7320e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(false)); 733390029beSYitzhak Mandelbaum }) 7340e8d4a6dSYitzhak Mandelbaum // optional::optional (value/conversion) 7357538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 736092a530cSStanislav Gatev transferValueOrConversionConstructor) 7379e0fc676SStanislav Gatev 738b000b770SStanislav Gatev // optional::operator= 7397538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>( 7407538b360SWei Yi Tee isOptionalValueOrConversionAssignment(), 741b000b770SStanislav Gatev transferValueOrConversionAssignment) 7427538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 743b000b770SStanislav Gatev transferNulloptAssignment) 744b000b770SStanislav Gatev 745af98b0afSStanislav Gatev // optional::value 7467538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 7475d22d1f5SYitzhak Mandelbaum valueCall(std::nullopt), 748092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 749092a530cSStanislav Gatev LatticeTransferState &State) { 750af98b0afSStanislav Gatev transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 751af98b0afSStanislav Gatev }) 752af98b0afSStanislav Gatev 753af98b0afSStanislav Gatev // optional::operator*, optional::operator-> 7545d22d1f5SYitzhak Mandelbaum .CaseOfCFGStmt<CallExpr>(valueOperatorCall(std::nullopt), 7557538b360SWei Yi Tee [](const CallExpr *E, 7567538b360SWei Yi Tee const MatchFinder::MatchResult &, 757092a530cSStanislav Gatev LatticeTransferState &State) { 758af98b0afSStanislav Gatev transferUnwrapCall(E, E->getArg(0), State); 759af98b0afSStanislav Gatev }) 760af98b0afSStanislav Gatev 761af98b0afSStanislav Gatev // optional::has_value 7627538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 7637538b360SWei Yi Tee isOptionalMemberCallWithName("has_value"), 764af98b0afSStanislav Gatev transferOptionalHasValueCall) 765af98b0afSStanislav Gatev 7669e0fc676SStanislav Gatev // optional::operator bool 7677538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 7687538b360SWei Yi Tee isOptionalMemberCallWithName("operator bool"), 7699e0fc676SStanislav Gatev transferOptionalHasValueCall) 7709e0fc676SStanislav Gatev 7719e0fc676SStanislav Gatev // optional::emplace 7727538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 773092a530cSStanislav Gatev isOptionalMemberCallWithName("emplace"), 774092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 775092a530cSStanislav Gatev LatticeTransferState &State) { 7760e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E->getImplicitObjectArgument(), State.Env, 777092a530cSStanislav Gatev State.Env.getBoolLiteralValue(true)); 778092a530cSStanislav Gatev }) 7799e0fc676SStanislav Gatev 7809e0fc676SStanislav Gatev // optional::reset 7817538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 782092a530cSStanislav Gatev isOptionalMemberCallWithName("reset"), 783092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 784092a530cSStanislav Gatev LatticeTransferState &State) { 7850e8d4a6dSYitzhak Mandelbaum assignOptionalValue(*E->getImplicitObjectArgument(), State.Env, 786092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false)); 787092a530cSStanislav Gatev }) 7889e0fc676SStanislav Gatev 7892ddd57aeSStanislav Gatev // optional::swap 7907538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"), 7912ddd57aeSStanislav Gatev transferSwapCall) 7922ddd57aeSStanislav Gatev 7932ddd57aeSStanislav Gatev // std::swap 7947538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 7952ddd57aeSStanislav Gatev 79625956d55SAMS21 // std::forward 79725956d55SAMS21 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 79825956d55SAMS21 7997f076004SYitzhak Mandelbaum // opt.value_or("").empty() 8007538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 8017538b360SWei Yi Tee transferValueOrStringEmptyCall) 8027f076004SYitzhak Mandelbaum 8037f076004SYitzhak Mandelbaum // opt.value_or(X) != X 8047538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 8057f076004SYitzhak Mandelbaum 806390029beSYitzhak Mandelbaum // Comparisons (==, !=): 807390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 808390029beSYitzhak Mandelbaum isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()), 809390029beSYitzhak Mandelbaum transferOptionalAndOptionalCmp) 810390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 811390029beSYitzhak Mandelbaum isComparisonOperatorCall(hasOptionalType(), 812390029beSYitzhak Mandelbaum unless(hasAnyOptionalType())), 813390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 814390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 815390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 816390029beSYitzhak Mandelbaum }) 817390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 818390029beSYitzhak Mandelbaum isComparisonOperatorCall(unless(hasAnyOptionalType()), 819390029beSYitzhak Mandelbaum hasOptionalType()), 820390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 821390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 822390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 823390029beSYitzhak Mandelbaum }) 824390029beSYitzhak Mandelbaum 82565e710c3SStanislav Gatev // returns optional 8267538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 82765e710c3SStanislav Gatev transferCallReturningOptional) 82865e710c3SStanislav Gatev 829af98b0afSStanislav Gatev .Build(); 830af98b0afSStanislav Gatev } 831af98b0afSStanislav Gatev 83258fe7f96SSam Estep std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr, 83358fe7f96SSam Estep const Expr *ObjectExpr, 83458fe7f96SSam Estep const Environment &Env) { 83558fe7f96SSam Estep if (auto *OptionalVal = 83658fe7f96SSam Estep Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 83758fe7f96SSam Estep auto *Prop = OptionalVal->getProperty("has_value"); 83858fe7f96SSam Estep if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 83958fe7f96SSam Estep if (Env.flowConditionImplies(*HasValueVal)) 84058fe7f96SSam Estep return {}; 84158fe7f96SSam Estep } 84258fe7f96SSam Estep } 84358fe7f96SSam Estep 84458fe7f96SSam Estep // Record that this unwrap is *not* provably safe. 84558fe7f96SSam Estep // FIXME: include either the name of the optional (if applicable) or a source 84658fe7f96SSam Estep // range of the access for easier interpretation of the result. 84758fe7f96SSam Estep return {ObjectExpr->getBeginLoc()}; 84858fe7f96SSam Estep } 84958fe7f96SSam Estep 85058fe7f96SSam Estep auto buildDiagnoseMatchSwitch( 85158fe7f96SSam Estep const UncheckedOptionalAccessModelOptions &Options) { 85258fe7f96SSam Estep // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 85358fe7f96SSam Estep // lot of duplicated work (e.g. string comparisons), consider providing APIs 85458fe7f96SSam Estep // that avoid it through memoization. 85558fe7f96SSam Estep auto IgnorableOptional = ignorableOptional(Options); 8567538b360SWei Yi Tee return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>() 85758fe7f96SSam Estep // optional::value 8587538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 85958fe7f96SSam Estep valueCall(IgnorableOptional), 86058fe7f96SSam Estep [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 86158fe7f96SSam Estep const Environment &Env) { 86258fe7f96SSam Estep return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env); 86358fe7f96SSam Estep }) 86458fe7f96SSam Estep 86558fe7f96SSam Estep // optional::operator*, optional::operator-> 8667538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>( 86758fe7f96SSam Estep valueOperatorCall(IgnorableOptional), 86858fe7f96SSam Estep [](const CallExpr *E, const MatchFinder::MatchResult &, 86958fe7f96SSam Estep const Environment &Env) { 87058fe7f96SSam Estep return diagnoseUnwrapCall(E, E->getArg(0), Env); 87158fe7f96SSam Estep }) 87258fe7f96SSam Estep .Build(); 87358fe7f96SSam Estep } 87458fe7f96SSam Estep 875af98b0afSStanislav Gatev } // namespace 876af98b0afSStanislav Gatev 8777e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher 8787e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() { 8797e63a0d4SYitzhak Mandelbaum return optionalClass(); 8807e63a0d4SYitzhak Mandelbaum } 8817e63a0d4SYitzhak Mandelbaum 8825d22d1f5SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx) 883cf1f978dSSam Estep : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 8845d22d1f5SYitzhak Mandelbaum TransferMatchSwitch(buildTransferMatchSwitch()) {} 885af98b0afSStanislav Gatev 8866b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 8877538b360SWei Yi Tee NoopLattice &L, Environment &Env) { 888af98b0afSStanislav Gatev LatticeTransferState State(L, Env); 8896b991ba4SYitzhak Mandelbaum TransferMatchSwitch(Elt, getASTContext(), State); 890af98b0afSStanislav Gatev } 891af98b0afSStanislav Gatev 892c0725865SYitzhak Mandelbaum ComparisonResult UncheckedOptionalAccessModel::compare( 893c0725865SYitzhak Mandelbaum QualType Type, const Value &Val1, const Environment &Env1, 894c0725865SYitzhak Mandelbaum const Value &Val2, const Environment &Env2) { 895c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 896c0725865SYitzhak Mandelbaum return ComparisonResult::Unknown; 897d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 898d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 89925956d55SAMS21 if (MustNonEmpty1 && MustNonEmpty2) 90025956d55SAMS21 return ComparisonResult::Same; 901d34fbf2dSYitzhak Mandelbaum // If exactly one is true, then they're different, no reason to check whether 902d34fbf2dSYitzhak Mandelbaum // they're definitely empty. 90325956d55SAMS21 if (MustNonEmpty1 || MustNonEmpty2) 90425956d55SAMS21 return ComparisonResult::Different; 905d34fbf2dSYitzhak Mandelbaum // Check if they're both definitely empty. 906d34fbf2dSYitzhak Mandelbaum return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)) 907c0725865SYitzhak Mandelbaum ? ComparisonResult::Same 908c0725865SYitzhak Mandelbaum : ComparisonResult::Different; 9098fcdd625SStanislav Gatev } 9108fcdd625SStanislav Gatev 9118fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1, 9128fcdd625SStanislav Gatev const Environment &Env1, 9138fcdd625SStanislav Gatev const Value &Val2, 9148fcdd625SStanislav Gatev const Environment &Env2, 9158fcdd625SStanislav Gatev Value &MergedVal, 9168fcdd625SStanislav Gatev Environment &MergedEnv) { 917c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 9188fcdd625SStanislav Gatev return true; 919d34fbf2dSYitzhak Mandelbaum // FIXME: uses same approach as join for `BoolValues`. Requires non-const 920d34fbf2dSYitzhak Mandelbaum // values, though, so will require updating the interface. 9218fcdd625SStanislav Gatev auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); 922d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 923d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 924d34fbf2dSYitzhak Mandelbaum if (MustNonEmpty1 && MustNonEmpty2) 9258fcdd625SStanislav Gatev MergedEnv.addToFlowCondition(HasValueVal); 926d34fbf2dSYitzhak Mandelbaum else if ( 927d34fbf2dSYitzhak Mandelbaum // Only make the costly calls to `isEmptyOptional` if we got "unknown" 928d34fbf2dSYitzhak Mandelbaum // (false) for both calls to `isNonEmptyOptional`. 929d34fbf2dSYitzhak Mandelbaum !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) && 930d34fbf2dSYitzhak Mandelbaum isEmptyOptional(Val2, Env2)) 9318fcdd625SStanislav Gatev MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal)); 9328fcdd625SStanislav Gatev setHasValue(MergedVal, HasValueVal); 9338fcdd625SStanislav Gatev return true; 9348fcdd625SStanislav Gatev } 9358fcdd625SStanislav Gatev 936d34fbf2dSYitzhak Mandelbaum Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev, 937d34fbf2dSYitzhak Mandelbaum const Environment &PrevEnv, 938d34fbf2dSYitzhak Mandelbaum Value &Current, 939d34fbf2dSYitzhak Mandelbaum Environment &CurrentEnv) { 940d34fbf2dSYitzhak Mandelbaum switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) { 941d34fbf2dSYitzhak Mandelbaum case ComparisonResult::Same: 942d34fbf2dSYitzhak Mandelbaum return &Prev; 943d34fbf2dSYitzhak Mandelbaum case ComparisonResult::Different: 944d34fbf2dSYitzhak Mandelbaum if (auto *PrevHasVal = 945d34fbf2dSYitzhak Mandelbaum cast_or_null<BoolValue>(Prev.getProperty("has_value"))) { 946d34fbf2dSYitzhak Mandelbaum if (isa<TopBoolValue>(PrevHasVal)) 947d34fbf2dSYitzhak Mandelbaum return &Prev; 948d34fbf2dSYitzhak Mandelbaum } 949d34fbf2dSYitzhak Mandelbaum if (auto *CurrentHasVal = 950d34fbf2dSYitzhak Mandelbaum cast_or_null<BoolValue>(Current.getProperty("has_value"))) { 951d34fbf2dSYitzhak Mandelbaum if (isa<TopBoolValue>(CurrentHasVal)) 952d34fbf2dSYitzhak Mandelbaum return &Current; 953d34fbf2dSYitzhak Mandelbaum } 954d34fbf2dSYitzhak Mandelbaum return &createOptionalValue(CurrentEnv, CurrentEnv.makeTopBoolValue()); 955d34fbf2dSYitzhak Mandelbaum case ComparisonResult::Unknown: 956d34fbf2dSYitzhak Mandelbaum return nullptr; 957d34fbf2dSYitzhak Mandelbaum } 958d34fbf2dSYitzhak Mandelbaum llvm_unreachable("all cases covered in switch"); 959d34fbf2dSYitzhak Mandelbaum } 960d34fbf2dSYitzhak Mandelbaum 96158fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 96258fe7f96SSam Estep UncheckedOptionalAccessModelOptions Options) 96358fe7f96SSam Estep : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 96458fe7f96SSam Estep 96558fe7f96SSam Estep std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose( 9667538b360SWei Yi Tee ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) { 9677538b360SWei Yi Tee return DiagnoseMatchSwitch(*Elt, Ctx, Env); 96858fe7f96SSam Estep } 96958fe7f96SSam Estep 970af98b0afSStanislav Gatev } // namespace dataflow 971af98b0afSStanislav Gatev } // namespace clang 972