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" 256272226bSSam McCall #include "clang/Analysis/FlowSensitive/Formula.h" 26cf1f978dSSam Estep #include "clang/Analysis/FlowSensitive/NoopLattice.h" 270086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h" 28af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h" 2958fe7f96SSam Estep #include "clang/Basic/SourceLocation.h" 30af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h" 31af98b0afSStanislav Gatev #include "llvm/Support/Casting.h" 32d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h" 33af98b0afSStanislav Gatev #include <cassert> 349e0fc676SStanislav Gatev #include <memory> 35a1580d7bSKazu Hirata #include <optional> 369e0fc676SStanislav Gatev #include <utility> 3758fe7f96SSam Estep #include <vector> 38af98b0afSStanislav Gatev 39af98b0afSStanislav Gatev namespace clang { 40af98b0afSStanislav Gatev namespace dataflow { 4109b462efSYitzhak Mandelbaum 4209b462efSYitzhak Mandelbaum static bool isTopLevelNamespaceWithName(const NamespaceDecl &NS, 4309b462efSYitzhak Mandelbaum llvm::StringRef Name) { 4409b462efSYitzhak Mandelbaum return NS.getDeclName().isIdentifier() && NS.getName() == Name && 4509b462efSYitzhak Mandelbaum NS.getParent() != nullptr && NS.getParent()->isTranslationUnit(); 4609b462efSYitzhak Mandelbaum } 4709b462efSYitzhak Mandelbaum 4809b462efSYitzhak Mandelbaum static bool hasOptionalClassName(const CXXRecordDecl &RD) { 4909b462efSYitzhak Mandelbaum if (!RD.getDeclName().isIdentifier()) 5009b462efSYitzhak Mandelbaum return false; 5109b462efSYitzhak Mandelbaum 5209b462efSYitzhak Mandelbaum if (RD.getName() == "optional") { 5309b462efSYitzhak Mandelbaum if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext())) 5409b462efSYitzhak Mandelbaum return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl"); 5509b462efSYitzhak Mandelbaum return false; 5609b462efSYitzhak Mandelbaum } 5709b462efSYitzhak Mandelbaum 5809b462efSYitzhak Mandelbaum if (RD.getName() == "Optional") { 592f0630f8SAnton Dukeman // Check whether namespace is "::base" or "::folly". 6009b462efSYitzhak Mandelbaum const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); 612f0630f8SAnton Dukeman return N != nullptr && (isTopLevelNamespaceWithName(*N, "base") || 622f0630f8SAnton Dukeman isTopLevelNamespaceWithName(*N, "folly")); 6309b462efSYitzhak Mandelbaum } 6409b462efSYitzhak Mandelbaum 6509b462efSYitzhak Mandelbaum return false; 6609b462efSYitzhak Mandelbaum } 6709b462efSYitzhak Mandelbaum 68af98b0afSStanislav Gatev namespace { 69af98b0afSStanislav Gatev 70af98b0afSStanislav Gatev using namespace ::clang::ast_matchers; 71cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>; 72af98b0afSStanislav Gatev 7309b462efSYitzhak Mandelbaum AST_MATCHER(CXXRecordDecl, hasOptionalClassNameMatcher) { 7409b462efSYitzhak Mandelbaum return hasOptionalClassName(Node); 7509b462efSYitzhak Mandelbaum } 7609b462efSYitzhak Mandelbaum 777e63a0d4SYitzhak Mandelbaum DeclarationMatcher optionalClass() { 78af98b0afSStanislav Gatev return classTemplateSpecializationDecl( 7909b462efSYitzhak Mandelbaum hasOptionalClassNameMatcher(), 80af98b0afSStanislav Gatev hasTemplateArgument(0, refersToType(type().bind("T")))); 81af98b0afSStanislav Gatev } 82af98b0afSStanislav Gatev 836adfc64eSYitzhak Mandelbaum auto optionalOrAliasType() { 8465e710c3SStanislav Gatev return hasUnqualifiedDesugaredType( 8565e710c3SStanislav Gatev recordType(hasDeclaration(optionalClass()))); 8665e710c3SStanislav Gatev } 8765e710c3SStanislav Gatev 886adfc64eSYitzhak Mandelbaum /// Matches any of the spellings of the optional types and sugar, aliases, etc. 896adfc64eSYitzhak Mandelbaum auto hasOptionalType() { return hasType(optionalOrAliasType()); } 906adfc64eSYitzhak Mandelbaum 912f0630f8SAnton Dukeman auto isOptionalMemberCallWithNameMatcher( 922f0630f8SAnton Dukeman ast_matchers::internal::Matcher<NamedDecl> matcher, 936ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 94a184a0d8SYitzhak Mandelbaum auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr())) 95a184a0d8SYitzhak Mandelbaum : cxxThisExpr()); 96af98b0afSStanislav Gatev return cxxMemberCallExpr( 9709b462efSYitzhak Mandelbaum on(expr(Exception, 9809b462efSYitzhak Mandelbaum anyOf(hasOptionalType(), 9909b462efSYitzhak Mandelbaum hasType(pointerType(pointee(optionalOrAliasType())))))), 1002f0630f8SAnton Dukeman callee(cxxMethodDecl(matcher))); 101af98b0afSStanislav Gatev } 102af98b0afSStanislav Gatev 103a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName( 104a184a0d8SYitzhak Mandelbaum llvm::StringRef operator_name, 1056ad0788cSKazu Hirata const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 106a184a0d8SYitzhak Mandelbaum return cxxOperatorCallExpr( 107a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName(operator_name), 108a184a0d8SYitzhak Mandelbaum callee(cxxMethodDecl(ofClass(optionalClass()))), 109a184a0d8SYitzhak Mandelbaum Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 110af98b0afSStanislav Gatev } 111af98b0afSStanislav Gatev 112092a530cSStanislav Gatev auto isMakeOptionalCall() { 1132f0630f8SAnton Dukeman return callExpr(callee(functionDecl(hasAnyName( 1142f0630f8SAnton Dukeman "std::make_optional", "base::make_optional", 1152f0630f8SAnton Dukeman "absl::make_optional", "folly::make_optional"))), 1169e0fc676SStanislav Gatev hasOptionalType()); 1179e0fc676SStanislav Gatev } 1189e0fc676SStanislav Gatev 119390029beSYitzhak Mandelbaum auto nulloptTypeDecl() { 1202f0630f8SAnton Dukeman return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t", 1212f0630f8SAnton Dukeman "base::nullopt_t", "folly::None")); 122092a530cSStanislav Gatev } 123092a530cSStanislav Gatev 124390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); } 125390029beSYitzhak Mandelbaum 126390029beSYitzhak Mandelbaum // `optional` or `nullopt_t` 127390029beSYitzhak Mandelbaum auto hasAnyOptionalType() { 128390029beSYitzhak Mandelbaum return hasType(hasUnqualifiedDesugaredType( 129390029beSYitzhak Mandelbaum recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass()))))); 130390029beSYitzhak Mandelbaum } 131390029beSYitzhak Mandelbaum 132092a530cSStanislav Gatev auto inPlaceClass() { 1332f0630f8SAnton Dukeman return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t", 1342f0630f8SAnton Dukeman "base::in_place_t", "folly::in_place_t")); 135092a530cSStanislav Gatev } 136092a530cSStanislav Gatev 137092a530cSStanislav Gatev auto isOptionalNulloptConstructor() { 1380086a355SYitzhak Mandelbaum return cxxConstructExpr( 1390086a355SYitzhak Mandelbaum hasOptionalType(), 1400086a355SYitzhak Mandelbaum hasDeclaration(cxxConstructorDecl(parameterCountIs(1), 1410086a355SYitzhak Mandelbaum hasParameter(0, hasNulloptType())))); 142092a530cSStanislav Gatev } 143092a530cSStanislav Gatev 144092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() { 145092a530cSStanislav Gatev return cxxConstructExpr(hasOptionalType(), 146092a530cSStanislav Gatev hasArgument(0, hasType(inPlaceClass()))); 147092a530cSStanislav Gatev } 148092a530cSStanislav Gatev 149092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() { 150092a530cSStanislav Gatev return cxxConstructExpr( 151092a530cSStanislav Gatev hasOptionalType(), 152092a530cSStanislav Gatev unless(hasDeclaration( 153092a530cSStanislav Gatev cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 154092a530cSStanislav Gatev argumentCountIs(1), hasArgument(0, unless(hasNulloptType()))); 155092a530cSStanislav Gatev } 156092a530cSStanislav Gatev 157b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() { 158b000b770SStanislav Gatev return cxxOperatorCallExpr( 159b000b770SStanislav Gatev hasOverloadedOperatorName("="), 160b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 161b000b770SStanislav Gatev unless(hasDeclaration(cxxMethodDecl( 162b000b770SStanislav Gatev anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 163b000b770SStanislav Gatev argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 164b000b770SStanislav Gatev } 165b000b770SStanislav Gatev 166390029beSYitzhak Mandelbaum auto isNulloptConstructor() { 167390029beSYitzhak Mandelbaum return cxxConstructExpr(hasNulloptType(), argumentCountIs(1), 168390029beSYitzhak Mandelbaum hasArgument(0, hasNulloptType())); 169390029beSYitzhak Mandelbaum } 170390029beSYitzhak Mandelbaum 171b000b770SStanislav Gatev auto isOptionalNulloptAssignment() { 172b000b770SStanislav Gatev return cxxOperatorCallExpr(hasOverloadedOperatorName("="), 173b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))), 174b000b770SStanislav Gatev argumentCountIs(2), 175b000b770SStanislav Gatev hasArgument(1, hasNulloptType())); 176b000b770SStanislav Gatev } 177b000b770SStanislav Gatev 1782ddd57aeSStanislav Gatev auto isStdSwapCall() { 1792ddd57aeSStanislav Gatev return callExpr(callee(functionDecl(hasName("std::swap"))), 1802ddd57aeSStanislav Gatev argumentCountIs(2), hasArgument(0, hasOptionalType()), 1812ddd57aeSStanislav Gatev hasArgument(1, hasOptionalType())); 1822ddd57aeSStanislav Gatev } 1832ddd57aeSStanislav Gatev 18425956d55SAMS21 auto isStdForwardCall() { 18525956d55SAMS21 return callExpr(callee(functionDecl(hasName("std::forward"))), 18625956d55SAMS21 argumentCountIs(1), hasArgument(0, hasOptionalType())); 18725956d55SAMS21 } 18825956d55SAMS21 1897f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 1907f076004SYitzhak Mandelbaum 1917f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() { 1927f076004SYitzhak Mandelbaum // `opt.value_or("").empty()` 1937f076004SYitzhak Mandelbaum return cxxMemberCallExpr( 1947f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("empty"))), 1957f076004SYitzhak Mandelbaum onImplicitObjectArgument(ignoringImplicit( 1967f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 1977f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 1987f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 1997f076004SYitzhak Mandelbaum hasArgument(0, stringLiteral(hasSize(0)))) 2007f076004SYitzhak Mandelbaum .bind(ValueOrCallID)))); 2017f076004SYitzhak Mandelbaum } 2027f076004SYitzhak Mandelbaum 2037f076004SYitzhak Mandelbaum auto isValueOrNotEqX() { 2047f076004SYitzhak Mandelbaum auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 2057f076004SYitzhak Mandelbaum return hasOperands( 2067f076004SYitzhak Mandelbaum ignoringImplicit( 2077f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 2087f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"), 2097f076004SYitzhak Mandelbaum ofClass(optionalClass()))), 2107f076004SYitzhak Mandelbaum hasArgument(0, Arg)) 2117f076004SYitzhak Mandelbaum .bind(ValueOrCallID)), 2127f076004SYitzhak Mandelbaum ignoringImplicit(Arg)); 2137f076004SYitzhak Mandelbaum }; 2147f076004SYitzhak Mandelbaum 2157f076004SYitzhak Mandelbaum // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 2167f076004SYitzhak Mandelbaum // support this pattern for any expression, but the AST does not have a 2177f076004SYitzhak Mandelbaum // generic expression comparison facility, so we specialize to common cases 2187f076004SYitzhak Mandelbaum // seen in practice. FIXME: define a matcher that compares values across 2197f076004SYitzhak Mandelbaum // nodes, which would let us generalize this to any `X`. 2207f076004SYitzhak Mandelbaum return binaryOperation(hasOperatorName("!="), 2217f076004SYitzhak Mandelbaum anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 2227f076004SYitzhak Mandelbaum ComparesToSame(stringLiteral(hasSize(0))), 2237f076004SYitzhak Mandelbaum ComparesToSame(integerLiteral(equals(0))))); 2247f076004SYitzhak Mandelbaum } 2257f076004SYitzhak Mandelbaum 22665e710c3SStanislav Gatev auto isCallReturningOptional() { 227cd0d5261SSam Estep return callExpr(hasType(qualType(anyOf( 228cd0d5261SSam Estep optionalOrAliasType(), referenceType(pointee(optionalOrAliasType())))))); 22965e710c3SStanislav Gatev } 23065e710c3SStanislav Gatev 231390029beSYitzhak Mandelbaum template <typename L, typename R> 232390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 233390029beSYitzhak Mandelbaum return cxxOperatorCallExpr( 234390029beSYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 235390029beSYitzhak Mandelbaum argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 236390029beSYitzhak Mandelbaum hasArgument(1, rhs_arg_matcher)); 237390029beSYitzhak Mandelbaum } 238390029beSYitzhak Mandelbaum 2396272226bSSam McCall /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula. 2406272226bSSam McCall const Formula &forceBoolValue(Environment &Env, const Expr &Expr) { 241e95134b9SMartin Braenne auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr)); 242390029beSYitzhak Mandelbaum if (Value != nullptr) 2436272226bSSam McCall return Value->formula(); 244390029beSYitzhak Mandelbaum 245390029beSYitzhak Mandelbaum Value = &Env.makeAtomicBoolValue(); 246*f76f6674SMartin Braenne Env.setValueStrict(Expr, *Value); 2476272226bSSam McCall return Value->formula(); 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 256f653d140SMartin Braenne /// Creates a symbolic value for an `optional` value at an existing storage 257f653d140SMartin Braenne /// location. Uses `HasValueVal` as the symbolic value of the "has_value" 258f653d140SMartin Braenne /// property. 259f653d140SMartin Braenne StructValue &createOptionalValue(AggregateStorageLocation &Loc, 260f653d140SMartin Braenne BoolValue &HasValueVal, Environment &Env) { 26144f98d01SMartin Braenne auto &OptionalVal = Env.create<StructValue>(Loc); 262f653d140SMartin Braenne Env.setValue(Loc, OptionalVal); 26344f98d01SMartin Braenne setHasValue(OptionalVal, HasValueVal); 264f653d140SMartin Braenne return OptionalVal; 265f653d140SMartin Braenne } 266f653d140SMartin Braenne 267af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the 26849ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null. 269dd38caf3SYitzhak Mandelbaum BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { 270dd38caf3SYitzhak Mandelbaum if (OptionalVal != nullptr) { 271dd38caf3SYitzhak Mandelbaum auto *HasValueVal = 272dd38caf3SYitzhak Mandelbaum cast_or_null<BoolValue>(OptionalVal->getProperty("has_value")); 273dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) { 274dd38caf3SYitzhak Mandelbaum HasValueVal = &Env.makeAtomicBoolValue(); 275dd38caf3SYitzhak Mandelbaum OptionalVal->setProperty("has_value", *HasValueVal); 276dd38caf3SYitzhak Mandelbaum } 277dd38caf3SYitzhak Mandelbaum return HasValueVal; 278af98b0afSStanislav Gatev } 279af98b0afSStanislav Gatev return nullptr; 280af98b0afSStanislav Gatev } 281af98b0afSStanislav Gatev 282092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type. 283c0725865SYitzhak Mandelbaum bool isOptionalType(QualType Type) { 284092a530cSStanislav Gatev if (!Type->isRecordType()) 285092a530cSStanislav Gatev return false; 286cd22e0dcSYitzhak Mandelbaum const CXXRecordDecl *D = Type->getAsCXXRecordDecl(); 28709b462efSYitzhak Mandelbaum return D != nullptr && hasOptionalClassName(*D); 288092a530cSStanislav Gatev } 289092a530cSStanislav Gatev 290092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`. 291092a530cSStanislav Gatev /// 292092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this 293092a530cSStanislav Gatev /// function will be 2. 294092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 295c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 296092a530cSStanislav Gatev return 0; 297092a530cSStanislav Gatev return 1 + countOptionalWrappers( 298092a530cSStanislav Gatev ASTCtx, 299092a530cSStanislav Gatev cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) 300092a530cSStanislav Gatev ->getTemplateArgs() 301092a530cSStanislav Gatev .get(0) 302092a530cSStanislav Gatev .getAsType() 303092a530cSStanislav Gatev .getDesugaredType(ASTCtx)); 304092a530cSStanislav Gatev } 305092a530cSStanislav Gatev 306dd38caf3SYitzhak Mandelbaum /// Tries to initialize the `optional`'s value (that is, contents), and return 307dd38caf3SYitzhak Mandelbaum /// its location. Returns nullptr if the value can't be represented. 308dd38caf3SYitzhak Mandelbaum StorageLocation *maybeInitializeOptionalValueMember(QualType Q, 309dd38caf3SYitzhak Mandelbaum Value &OptionalVal, 310dd38caf3SYitzhak Mandelbaum Environment &Env) { 311dd38caf3SYitzhak Mandelbaum // The "value" property represents a synthetic field. As such, it needs 312dd38caf3SYitzhak Mandelbaum // `StorageLocation`, like normal fields (and other variables). So, we model 313af22be39SMartin Braenne // it with a `PointerValue`, since that includes a storage location. Once 314dd38caf3SYitzhak Mandelbaum // the property is set, it will be shared by all environments that access the 315dd38caf3SYitzhak Mandelbaum // `Value` representing the optional (here, `OptionalVal`). 316dd38caf3SYitzhak Mandelbaum if (auto *ValueProp = OptionalVal.getProperty("value")) { 317af22be39SMartin Braenne auto *ValuePtr = clang::cast<PointerValue>(ValueProp); 318af22be39SMartin Braenne auto &ValueLoc = ValuePtr->getPointeeLoc(); 31944f98d01SMartin Braenne if (Env.getValue(ValueLoc) != nullptr) 32044f98d01SMartin Braenne return &ValueLoc; 32144f98d01SMartin Braenne 322dd38caf3SYitzhak Mandelbaum // The property was previously set, but the value has been lost. This can 323bbeda830SMartin Braenne // happen in various situations, for example: 32444f98d01SMartin Braenne // - Because of an environment merge (where the two environments mapped the 32544f98d01SMartin Braenne // property to different values, which resulted in them both being 326bbeda830SMartin Braenne // discarded). 327bbeda830SMartin Braenne // - When two blocks in the CFG, with neither a dominator of the other, 328bbeda830SMartin Braenne // visit the same optional value. (FIXME: This is something we can and 329bbeda830SMartin Braenne // should fix -- see also the lengthy FIXME below.) 330bbeda830SMartin Braenne // - Or even when a block is revisited during testing to collect 331bbeda830SMartin Braenne // per-statement state. 332dd38caf3SYitzhak Mandelbaum // FIXME: This situation means that the optional contents are not shared 333dd38caf3SYitzhak Mandelbaum // between branches and the like. Practically, this lack of sharing 334dd38caf3SYitzhak Mandelbaum // reduces the precision of the model when the contents are relevant to 335dd38caf3SYitzhak Mandelbaum // the check, like another optional or a boolean that influences control 336dd38caf3SYitzhak Mandelbaum // flow. 33744f98d01SMartin Braenne if (ValueLoc.getType()->isRecordType()) { 33844f98d01SMartin Braenne refreshStructValue(cast<AggregateStorageLocation>(ValueLoc), Env); 33944f98d01SMartin Braenne return &ValueLoc; 34044f98d01SMartin Braenne } else { 341dd38caf3SYitzhak Mandelbaum auto *ValueVal = Env.createValue(ValueLoc.getType()); 342dd38caf3SYitzhak Mandelbaum if (ValueVal == nullptr) 343dd38caf3SYitzhak Mandelbaum return nullptr; 344dd38caf3SYitzhak Mandelbaum Env.setValue(ValueLoc, *ValueVal); 345dd38caf3SYitzhak Mandelbaum return &ValueLoc; 346dd38caf3SYitzhak Mandelbaum } 34744f98d01SMartin Braenne } 348dd38caf3SYitzhak Mandelbaum 349c849843cSMartin Braenne auto Ty = Q.getNonReferenceType(); 35044f98d01SMartin Braenne auto &ValueLoc = Env.createObject(Ty); 351af22be39SMartin Braenne auto &ValuePtr = Env.create<PointerValue>(ValueLoc); 352bbeda830SMartin Braenne // FIXME: 353bbeda830SMartin Braenne // The change we make to the `value` property below may become visible to 354bbeda830SMartin Braenne // other blocks that aren't successors of the current block and therefore 355bbeda830SMartin Braenne // don't see the change we made above mapping `ValueLoc` to `ValueVal`. For 356bbeda830SMartin Braenne // example: 357bbeda830SMartin Braenne // 358bbeda830SMartin Braenne // void target(optional<int> oo, bool b) { 359bbeda830SMartin Braenne // // `oo` is associated with a `StructValue` here, which we will call 360bbeda830SMartin Braenne // // `OptionalVal`. 361bbeda830SMartin Braenne // 362bbeda830SMartin Braenne // // The `has_value` property is set on `OptionalVal` (but not the 363bbeda830SMartin Braenne // // `value` property yet). 364bbeda830SMartin Braenne // if (!oo.has_value()) return; 365bbeda830SMartin Braenne // 366bbeda830SMartin Braenne // if (b) { 367bbeda830SMartin Braenne // // Let's assume we transfer the `if` branch first. 368bbeda830SMartin Braenne // // 369bbeda830SMartin Braenne // // This causes us to call `maybeInitializeOptionalValueMember()`, 370bbeda830SMartin Braenne // // which causes us to set the `value` property on `OptionalVal` 371bbeda830SMartin Braenne // // (which had not been set until this point). This `value` property 372bbeda830SMartin Braenne // // refers to a `PointerValue`, which in turn refers to a 373bbeda830SMartin Braenne // // StorageLocation` that is associated to an `IntegerValue`. 374bbeda830SMartin Braenne // oo.value(); 375bbeda830SMartin Braenne // } else { 376bbeda830SMartin Braenne // // Let's assume we transfer the `else` branch after the `if` branch. 377bbeda830SMartin Braenne // // 378bbeda830SMartin Braenne // // We see the `value` property that the `if` branch set on 379bbeda830SMartin Braenne // // `OptionalVal`, but in the environment for this block, the 380bbeda830SMartin Braenne // // `StorageLocation` in the `PointerValue` is not associated with any 381bbeda830SMartin Braenne // // `Value`. 382bbeda830SMartin Braenne // oo.value(); 383bbeda830SMartin Braenne // } 384bbeda830SMartin Braenne // } 385bbeda830SMartin Braenne // 386bbeda830SMartin Braenne // This situation is currently "saved" by the code above that checks whether 387bbeda830SMartin Braenne // the `value` property is already set, and if, the `ValueLoc` is not 388bbeda830SMartin Braenne // associated with a `ValueVal`, creates a new `ValueVal`. 389bbeda830SMartin Braenne // 390bbeda830SMartin Braenne // However, what we should really do is to make sure that the change to the 391bbeda830SMartin Braenne // `value` property does not "leak" to other blocks that are not successors 392bbeda830SMartin Braenne // of this block. To do this, instead of simply setting the `value` property 393bbeda830SMartin Braenne // on the existing `OptionalVal`, we should create a new `Value` for the 394bbeda830SMartin Braenne // optional, set the property on that, and associate the storage location that 395bbeda830SMartin Braenne // is currently associated with the existing `OptionalVal` with the newly 396bbeda830SMartin Braenne // created `Value` instead. 397af22be39SMartin Braenne OptionalVal.setProperty("value", ValuePtr); 398dd38caf3SYitzhak Mandelbaum return &ValueLoc; 399dd38caf3SYitzhak Mandelbaum } 400dd38caf3SYitzhak Mandelbaum 401092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr, 402092a530cSStanislav Gatev const MatchFinder::MatchResult &, 403af98b0afSStanislav Gatev LatticeTransferState &State) { 404e95134b9SMartin Braenne if (auto *OptionalVal = State.Env.getValue(*OptionalExpr)) { 405af98b0afSStanislav Gatev if (OptionalVal->getProperty("has_value") == nullptr) { 4068fcdd625SStanislav Gatev setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); 407af98b0afSStanislav Gatev } 408af98b0afSStanislav Gatev } 409af98b0afSStanislav Gatev } 410af98b0afSStanislav Gatev 4118fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be 412ffb4f4dbSDmitri Gribenko /// empty in `Env`. 4138fcdd625SStanislav Gatev bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { 4148fcdd625SStanislav Gatev auto *HasValueVal = 4158fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 4168fcdd625SStanislav Gatev return HasValueVal != nullptr && 4176272226bSSam McCall Env.flowConditionImplies(Env.arena().makeNot(HasValueVal->formula())); 4188fcdd625SStanislav Gatev } 4198fcdd625SStanislav Gatev 4208fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be 421ffb4f4dbSDmitri Gribenko /// non-empty in `Env`. 4228fcdd625SStanislav Gatev bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { 4238fcdd625SStanislav Gatev auto *HasValueVal = 4248fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 4256272226bSSam McCall return HasValueVal != nullptr && 4266272226bSSam McCall Env.flowConditionImplies(HasValueVal->formula()); 4278fcdd625SStanislav Gatev } 4288fcdd625SStanislav Gatev 42948bc7150SMartin Braenne Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) { 430e95134b9SMartin Braenne Value *Val = Env.getValue(E); 43148bc7150SMartin Braenne if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Val)) 43248bc7150SMartin Braenne return Env.getValue(PointerVal->getPointeeLoc()); 43348bc7150SMartin Braenne return Val; 43448bc7150SMartin Braenne } 43548bc7150SMartin Braenne 436092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 437af98b0afSStanislav Gatev LatticeTransferState &State) { 43849ed5bf5SWei Yi Tee if (auto *OptionalVal = 43948bc7150SMartin Braenne getValueBehindPossiblePointer(*ObjectExpr, State.Env)) { 440*f76f6674SMartin Braenne if (State.Env.getStorageLocationStrict(*UnwrapExpr) == nullptr) 441dd38caf3SYitzhak Mandelbaum if (auto *Loc = maybeInitializeOptionalValueMember( 442dd38caf3SYitzhak Mandelbaum UnwrapExpr->getType(), *OptionalVal, State.Env)) 443*f76f6674SMartin Braenne State.Env.setStorageLocationStrict(*UnwrapExpr, *Loc); 444af98b0afSStanislav Gatev } 445dd38caf3SYitzhak Mandelbaum } 446af98b0afSStanislav Gatev 4473bc1ea5bSMartin Braenne void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 4483bc1ea5bSMartin Braenne LatticeTransferState &State) { 4493bc1ea5bSMartin Braenne if (auto *OptionalVal = 4503bc1ea5bSMartin Braenne getValueBehindPossiblePointer(*ObjectExpr, State.Env)) { 4513bc1ea5bSMartin Braenne if (auto *Loc = maybeInitializeOptionalValueMember( 4523bc1ea5bSMartin Braenne UnwrapExpr->getType()->getPointeeType(), *OptionalVal, State.Env)) { 4533bc1ea5bSMartin Braenne State.Env.setValueStrict(*UnwrapExpr, 4543bc1ea5bSMartin Braenne State.Env.create<PointerValue>(*Loc)); 4553bc1ea5bSMartin Braenne } 4563bc1ea5bSMartin Braenne } 4573bc1ea5bSMartin Braenne } 4583bc1ea5bSMartin Braenne 459092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E, 460092a530cSStanislav Gatev const MatchFinder::MatchResult &, 461092a530cSStanislav Gatev LatticeTransferState &State) { 46244f98d01SMartin Braenne createOptionalValue(State.Env.getResultObjectLocation(*E), 46344f98d01SMartin Braenne State.Env.getBoolLiteralValue(true), State.Env); 4649e0fc676SStanislav Gatev } 4659e0fc676SStanislav Gatev 466092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 467092a530cSStanislav Gatev const MatchFinder::MatchResult &, 468af98b0afSStanislav Gatev LatticeTransferState &State) { 469dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = getHasValue( 47048bc7150SMartin Braenne State.Env, getValueBehindPossiblePointer( 47148bc7150SMartin Braenne *CallExpr->getImplicitObjectArgument(), State.Env))) { 472*f76f6674SMartin Braenne State.Env.setValueStrict(*CallExpr, *HasValueVal); 473af98b0afSStanislav Gatev } 474af98b0afSStanislav Gatev } 475af98b0afSStanislav Gatev 4767f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in 4777f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property. 4786272226bSSam McCall void transferValueOrImpl( 4796272226bSSam McCall const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result, 4807f076004SYitzhak Mandelbaum LatticeTransferState &State, 4816272226bSSam McCall const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal, 4826272226bSSam McCall const Formula &HasValueVal)) { 4837f076004SYitzhak Mandelbaum auto &Env = State.Env; 4847f076004SYitzhak Mandelbaum 4857f076004SYitzhak Mandelbaum const auto *ObjectArgumentExpr = 4867f076004SYitzhak Mandelbaum Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) 4877f076004SYitzhak Mandelbaum ->getImplicitObjectArgument(); 4887f076004SYitzhak Mandelbaum 489dd38caf3SYitzhak Mandelbaum auto *HasValueVal = getHasValue( 49048bc7150SMartin Braenne State.Env, getValueBehindPossiblePointer(*ObjectArgumentExpr, State.Env)); 491dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) 4927f076004SYitzhak Mandelbaum return; 4937f076004SYitzhak Mandelbaum 4946272226bSSam McCall Env.addToFlowCondition(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), 4956272226bSSam McCall HasValueVal->formula())); 4967f076004SYitzhak Mandelbaum } 4977f076004SYitzhak Mandelbaum 4987f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 4997f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 5007f076004SYitzhak Mandelbaum LatticeTransferState &State) { 5017f076004SYitzhak Mandelbaum return transferValueOrImpl(ComparisonExpr, Result, State, 5026272226bSSam McCall [](Environment &Env, const Formula &ExprVal, 5036272226bSSam McCall const Formula &HasValueVal) -> const Formula & { 5046272226bSSam McCall auto &A = Env.arena(); 5057f076004SYitzhak Mandelbaum // If the result is *not* empty, then we know the 5067f076004SYitzhak Mandelbaum // optional must have been holding a value. If 5077f076004SYitzhak Mandelbaum // `ExprVal` is true, though, we don't learn 5087f076004SYitzhak Mandelbaum // anything definite about `has_value`, so we 5097f076004SYitzhak Mandelbaum // don't add any corresponding implications to 5107f076004SYitzhak Mandelbaum // the flow condition. 5116272226bSSam McCall return A.makeImplies(A.makeNot(ExprVal), 5127f076004SYitzhak Mandelbaum HasValueVal); 5137f076004SYitzhak Mandelbaum }); 5147f076004SYitzhak Mandelbaum } 5157f076004SYitzhak Mandelbaum 5167f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr, 5177f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result, 5187f076004SYitzhak Mandelbaum LatticeTransferState &State) { 5197f076004SYitzhak Mandelbaum transferValueOrImpl(ComparisonExpr, Result, State, 5206272226bSSam McCall [](Environment &Env, const Formula &ExprVal, 5216272226bSSam McCall const Formula &HasValueVal) -> const Formula & { 5226272226bSSam McCall auto &A = Env.arena(); 5237f076004SYitzhak Mandelbaum // We know that if `(opt.value_or(X) != X)` then 5247f076004SYitzhak Mandelbaum // `opt.hasValue()`, even without knowing further 5257f076004SYitzhak Mandelbaum // details about the contents of `opt`. 5266272226bSSam McCall return A.makeImplies(ExprVal, HasValueVal); 5277f076004SYitzhak Mandelbaum }); 5287f076004SYitzhak Mandelbaum } 5297f076004SYitzhak Mandelbaum 53065e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E, 53165e710c3SStanislav Gatev const MatchFinder::MatchResult &Result, 53265e710c3SStanislav Gatev LatticeTransferState &State) { 533*f76f6674SMartin Braenne if (State.Env.getValue(*E) != nullptr) 53465e710c3SStanislav Gatev return; 53565e710c3SStanislav Gatev 53644f98d01SMartin Braenne AggregateStorageLocation *Loc = nullptr; 53744f98d01SMartin Braenne if (E->isPRValue()) { 53844f98d01SMartin Braenne Loc = &State.Env.getResultObjectLocation(*E); 53944f98d01SMartin Braenne } else { 540*f76f6674SMartin Braenne Loc = cast_or_null<AggregateStorageLocation>( 541*f76f6674SMartin Braenne State.Env.getStorageLocationStrict(*E)); 542*f76f6674SMartin Braenne if (Loc == nullptr) { 543*f76f6674SMartin Braenne Loc = 544*f76f6674SMartin Braenne &cast<AggregateStorageLocation>(State.Env.createStorageLocation(*E)); 54544f98d01SMartin Braenne State.Env.setStorageLocationStrict(*E, *Loc); 54644f98d01SMartin Braenne } 547*f76f6674SMartin Braenne } 54844f98d01SMartin Braenne 54944f98d01SMartin Braenne createOptionalValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env); 55065e710c3SStanislav Gatev } 55165e710c3SStanislav Gatev 552f653d140SMartin Braenne void constructOptionalValue(const Expr &E, Environment &Env, 553092a530cSStanislav Gatev BoolValue &HasValueVal) { 55444f98d01SMartin Braenne AggregateStorageLocation &Loc = Env.getResultObjectLocation(E); 55544f98d01SMartin Braenne Env.setValueStrict(E, createOptionalValue(Loc, HasValueVal, Env)); 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`. 582e95134b9SMartin Braenne if (auto *HasValueVal = getHasValue(State.Env, State.Env.getValue(E))) 583dd38caf3SYitzhak Mandelbaum return *HasValueVal; 584b000b770SStanislav Gatev return State.Env.makeAtomicBoolValue(); 585b000b770SStanislav Gatev } 586b000b770SStanislav Gatev 587092a530cSStanislav Gatev void transferValueOrConversionConstructor( 588092a530cSStanislav Gatev const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 5899e0fc676SStanislav Gatev LatticeTransferState &State) { 590092a530cSStanislav Gatev assert(E->getNumArgs() > 0); 591092a530cSStanislav Gatev 592f653d140SMartin Braenne constructOptionalValue(*E, State.Env, 593390029beSYitzhak Mandelbaum valueOrConversionHasValue(*E->getConstructor(), 594b000b770SStanislav Gatev *E->getArg(0), MatchRes, 595b000b770SStanislav Gatev State)); 596b000b770SStanislav Gatev } 597092a530cSStanislav Gatev 598b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 599b000b770SStanislav Gatev LatticeTransferState &State) { 600b000b770SStanislav Gatev assert(E->getNumArgs() > 0); 601b000b770SStanislav Gatev 602f653d140SMartin Braenne if (auto *Loc = cast<AggregateStorageLocation>( 603f653d140SMartin Braenne State.Env.getStorageLocationStrict(*E->getArg(0)))) { 604f653d140SMartin Braenne createOptionalValue(*Loc, HasValueVal, State.Env); 605b000b770SStanislav Gatev 606b000b770SStanislav Gatev // Assign a storage location for the whole expression. 607f653d140SMartin Braenne State.Env.setStorageLocationStrict(*E, *Loc); 608f653d140SMartin Braenne } 609b000b770SStanislav Gatev } 610b000b770SStanislav Gatev 611b000b770SStanislav Gatev void transferValueOrConversionAssignment( 612b000b770SStanislav Gatev const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 613b000b770SStanislav Gatev LatticeTransferState &State) { 614b000b770SStanislav Gatev assert(E->getNumArgs() > 1); 615b000b770SStanislav Gatev transferAssignment(E, 616390029beSYitzhak Mandelbaum valueOrConversionHasValue(*E->getDirectCallee(), 61706decd0bSKazu Hirata *E->getArg(1), MatchRes, State), 618b000b770SStanislav Gatev State); 619b000b770SStanislav Gatev } 620b000b770SStanislav Gatev 621b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E, 622b000b770SStanislav Gatev const MatchFinder::MatchResult &, 623b000b770SStanislav Gatev LatticeTransferState &State) { 624b000b770SStanislav Gatev transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 6259e0fc676SStanislav Gatev } 6269e0fc676SStanislav Gatev 627f653d140SMartin Braenne void transferSwap(AggregateStorageLocation *Loc1, 628f653d140SMartin Braenne AggregateStorageLocation *Loc2, Environment &Env) { 629d4fb829bSYitzhak Mandelbaum // We account for cases where one or both of the optionals are not modeled, 630d4fb829bSYitzhak Mandelbaum // either lacking associated storage locations, or lacking values associated 631d4fb829bSYitzhak Mandelbaum // to such storage locations. 6322ddd57aeSStanislav Gatev 633d4fb829bSYitzhak Mandelbaum if (Loc1 == nullptr) { 634d4fb829bSYitzhak Mandelbaum if (Loc2 != nullptr) 635f653d140SMartin Braenne createOptionalValue(*Loc2, Env.makeAtomicBoolValue(), Env); 636d4fb829bSYitzhak Mandelbaum return; 637d4fb829bSYitzhak Mandelbaum } 638d4fb829bSYitzhak Mandelbaum if (Loc2 == nullptr) { 639f653d140SMartin Braenne createOptionalValue(*Loc1, Env.makeAtomicBoolValue(), Env); 640d4fb829bSYitzhak Mandelbaum return; 641d4fb829bSYitzhak Mandelbaum } 6422ddd57aeSStanislav Gatev 643d4fb829bSYitzhak Mandelbaum // Both expressions have locations, though they may not have corresponding 644d4fb829bSYitzhak Mandelbaum // values. In that case, we create a fresh value at this point. Note that if 645d4fb829bSYitzhak Mandelbaum // two branches both do this, they will not share the value, but it at least 646d4fb829bSYitzhak Mandelbaum // allows for local reasoning about the value. To avoid the above, we would 647d4fb829bSYitzhak Mandelbaum // need *lazy* value allocation. 648d4fb829bSYitzhak Mandelbaum // FIXME: allocate values lazily, instead of just creating a fresh value. 649f653d140SMartin Braenne BoolValue *BoolVal1 = getHasValue(Env, Env.getValue(*Loc1)); 650f653d140SMartin Braenne if (BoolVal1 == nullptr) 651f653d140SMartin Braenne BoolVal1 = &Env.makeAtomicBoolValue(); 652d4fb829bSYitzhak Mandelbaum 653f653d140SMartin Braenne BoolValue *BoolVal2 = getHasValue(Env, Env.getValue(*Loc2)); 654f653d140SMartin Braenne if (BoolVal2 == nullptr) 655f653d140SMartin Braenne BoolVal2 = &Env.makeAtomicBoolValue(); 656d4fb829bSYitzhak Mandelbaum 657f653d140SMartin Braenne createOptionalValue(*Loc1, *BoolVal2, Env); 658f653d140SMartin Braenne createOptionalValue(*Loc2, *BoolVal1, Env); 6592ddd57aeSStanislav Gatev } 6602ddd57aeSStanislav Gatev 6612ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E, 6622ddd57aeSStanislav Gatev const MatchFinder::MatchResult &, 6632ddd57aeSStanislav Gatev LatticeTransferState &State) { 6642ddd57aeSStanislav Gatev assert(E->getNumArgs() == 1); 665f653d140SMartin Braenne auto *OtherLoc = cast_or_null<AggregateStorageLocation>( 666f653d140SMartin Braenne State.Env.getStorageLocationStrict(*E->getArg(0))); 667f653d140SMartin Braenne transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env); 6682ddd57aeSStanislav Gatev } 6692ddd57aeSStanislav Gatev 6702ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 6712ddd57aeSStanislav Gatev LatticeTransferState &State) { 6722ddd57aeSStanislav Gatev assert(E->getNumArgs() == 2); 673f653d140SMartin Braenne auto *Arg0Loc = cast_or_null<AggregateStorageLocation>( 674f653d140SMartin Braenne State.Env.getStorageLocationStrict(*E->getArg(0))); 675f653d140SMartin Braenne auto *Arg1Loc = cast_or_null<AggregateStorageLocation>( 676f653d140SMartin Braenne State.Env.getStorageLocationStrict(*E->getArg(1))); 677f653d140SMartin Braenne transferSwap(Arg0Loc, Arg1Loc, State.Env); 6782ddd57aeSStanislav Gatev } 6792ddd57aeSStanislav Gatev 68025956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &, 68125956d55SAMS21 LatticeTransferState &State) { 68225956d55SAMS21 assert(E->getNumArgs() == 1); 68325956d55SAMS21 684243a79caSMartin Braenne if (auto *Loc = State.Env.getStorageLocationStrict(*E->getArg(0))) 685243a79caSMartin Braenne State.Env.setStorageLocationStrict(*E, *Loc); 68625956d55SAMS21 } 68725956d55SAMS21 6886272226bSSam McCall const Formula &evaluateEquality(Arena &A, const Formula &EqVal, 6896272226bSSam McCall const Formula &LHS, const Formula &RHS) { 690390029beSYitzhak Mandelbaum // Logically, an optional<T> object is composed of two values - a `has_value` 691390029beSYitzhak Mandelbaum // bit and a value of type T. Equality of optional objects compares both 692390029beSYitzhak Mandelbaum // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 693390029beSYitzhak Mandelbaum // when two optional objects are engaged, the equality of their respective 694390029beSYitzhak Mandelbaum // values of type T matters. Since we only track the `has_value` bits, we 695390029beSYitzhak Mandelbaum // can't make any conclusions about equality when we know that two optional 696390029beSYitzhak Mandelbaum // objects are engaged. 697390029beSYitzhak Mandelbaum // 698390029beSYitzhak Mandelbaum // We express this as two facts about the equality: 699390029beSYitzhak Mandelbaum // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 700390029beSYitzhak Mandelbaum // If they are equal, then either both are set or both are unset. 701390029beSYitzhak Mandelbaum // b) (!LHS & !RHS) => EqVal 702390029beSYitzhak Mandelbaum // If neither is set, then they are equal. 703390029beSYitzhak Mandelbaum // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 7046272226bSSam McCall return A.makeAnd( 7056272226bSSam McCall A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS), 7066272226bSSam McCall A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))), 7076272226bSSam McCall A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS))); 708390029beSYitzhak Mandelbaum } 709390029beSYitzhak Mandelbaum 710390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 711390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, 712390029beSYitzhak Mandelbaum LatticeTransferState &State) { 713390029beSYitzhak Mandelbaum Environment &Env = State.Env; 7146272226bSSam McCall auto &A = Env.arena(); 715390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 716e95134b9SMartin Braenne if (auto *LHasVal = getHasValue(Env, Env.getValue(*CmpExpr->getArg(0)))) 717e95134b9SMartin Braenne if (auto *RHasVal = getHasValue(Env, Env.getValue(*CmpExpr->getArg(1)))) { 718390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 7196272226bSSam McCall CmpValue = &A.makeNot(*CmpValue); 7206272226bSSam McCall Env.addToFlowCondition(evaluateEquality(A, *CmpValue, LHasVal->formula(), 7216272226bSSam McCall RHasVal->formula())); 722390029beSYitzhak Mandelbaum } 723390029beSYitzhak Mandelbaum } 724390029beSYitzhak Mandelbaum 725390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 726390029beSYitzhak Mandelbaum const clang::Expr *E, Environment &Env) { 7276272226bSSam McCall auto &A = Env.arena(); 728390029beSYitzhak Mandelbaum auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 729e95134b9SMartin Braenne if (auto *HasVal = getHasValue(Env, Env.getValue(*E))) { 730390029beSYitzhak Mandelbaum if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 7316272226bSSam McCall CmpValue = &A.makeNot(*CmpValue); 7326272226bSSam McCall Env.addToFlowCondition( 7336272226bSSam McCall evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true))); 734390029beSYitzhak Mandelbaum } 735390029beSYitzhak Mandelbaum } 736390029beSYitzhak Mandelbaum 7376ad0788cSKazu Hirata std::optional<StatementMatcher> 738a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 7395d22d1f5SYitzhak Mandelbaum if (Options.IgnoreSmartPointerDereference) { 7405d22d1f5SYitzhak Mandelbaum auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 7415d22d1f5SYitzhak Mandelbaum anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 7425d22d1f5SYitzhak Mandelbaum unless(hasArgument(0, expr(hasOptionalType())))))); 7435d22d1f5SYitzhak Mandelbaum return expr( 7445d22d1f5SYitzhak Mandelbaum anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 7455d22d1f5SYitzhak Mandelbaum } 74634e0d057SKazu Hirata return std::nullopt; 747a184a0d8SYitzhak Mandelbaum } 748a184a0d8SYitzhak Mandelbaum 74958fe7f96SSam Estep StatementMatcher 7506ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 7512f0630f8SAnton Dukeman return isOptionalMemberCallWithNameMatcher(hasName("value"), 7522f0630f8SAnton Dukeman IgnorableOptional); 75358fe7f96SSam Estep } 75458fe7f96SSam Estep 75558fe7f96SSam Estep StatementMatcher 7566ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 75758fe7f96SSam Estep return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 75858fe7f96SSam Estep isOptionalOperatorCallWithName("->", IgnorableOptional))); 75958fe7f96SSam Estep } 76058fe7f96SSam Estep 7615d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() { 762b000b770SStanislav Gatev // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 763b000b770SStanislav Gatev // lot of duplicated work (e.g. string comparisons), consider providing APIs 764b000b770SStanislav Gatev // that avoid it through memoization. 7657538b360SWei Yi Tee return CFGMatchSwitchBuilder<LatticeTransferState>() 766af98b0afSStanislav Gatev // Attach a symbolic "has_value" state to optional values that we see for 767af98b0afSStanislav Gatev // the first time. 7687538b360SWei Yi Tee .CaseOfCFGStmt<Expr>( 7696adfc64eSYitzhak Mandelbaum expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), 770af98b0afSStanislav Gatev initializeOptionalReference) 771af98b0afSStanislav Gatev 7729e0fc676SStanislav Gatev // make_optional 7737538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 774092a530cSStanislav Gatev 7750e8d4a6dSYitzhak Mandelbaum // optional::optional (in place) 7767538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 777092a530cSStanislav Gatev isOptionalInPlaceConstructor(), 778092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 779092a530cSStanislav Gatev LatticeTransferState &State) { 780f653d140SMartin Braenne constructOptionalValue(*E, State.Env, 7810e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(true)); 782092a530cSStanislav Gatev }) 7830e8d4a6dSYitzhak Mandelbaum // nullopt_t::nullopt_t 7847538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>( 785390029beSYitzhak Mandelbaum isNulloptConstructor(), 786092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 787092a530cSStanislav Gatev LatticeTransferState &State) { 788f653d140SMartin Braenne constructOptionalValue(*E, State.Env, 789092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false)); 790092a530cSStanislav Gatev }) 7910e8d4a6dSYitzhak Mandelbaum // optional::optional(nullopt_t) 792390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXConstructExpr>( 793390029beSYitzhak Mandelbaum isOptionalNulloptConstructor(), 794390029beSYitzhak Mandelbaum [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 795390029beSYitzhak Mandelbaum LatticeTransferState &State) { 796f653d140SMartin Braenne constructOptionalValue(*E, State.Env, 7970e8d4a6dSYitzhak Mandelbaum State.Env.getBoolLiteralValue(false)); 798390029beSYitzhak Mandelbaum }) 7990e8d4a6dSYitzhak Mandelbaum // optional::optional (value/conversion) 8007538b360SWei Yi Tee .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 801092a530cSStanislav Gatev transferValueOrConversionConstructor) 8029e0fc676SStanislav Gatev 803b000b770SStanislav Gatev // optional::operator= 8047538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>( 8057538b360SWei Yi Tee isOptionalValueOrConversionAssignment(), 806b000b770SStanislav Gatev transferValueOrConversionAssignment) 8077538b360SWei Yi Tee .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 808b000b770SStanislav Gatev transferNulloptAssignment) 809b000b770SStanislav Gatev 810af98b0afSStanislav Gatev // optional::value 8117538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8125d22d1f5SYitzhak Mandelbaum valueCall(std::nullopt), 813092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 814092a530cSStanislav Gatev LatticeTransferState &State) { 815af98b0afSStanislav Gatev transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 816af98b0afSStanislav Gatev }) 817af98b0afSStanislav Gatev 8183bc1ea5bSMartin Braenne // optional::operator* 8193bc1ea5bSMartin Braenne .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"), 8207538b360SWei Yi Tee [](const CallExpr *E, 8217538b360SWei Yi Tee const MatchFinder::MatchResult &, 822092a530cSStanislav Gatev LatticeTransferState &State) { 823af98b0afSStanislav Gatev transferUnwrapCall(E, E->getArg(0), State); 824af98b0afSStanislav Gatev }) 825af98b0afSStanislav Gatev 8263bc1ea5bSMartin Braenne // optional::operator-> 8273bc1ea5bSMartin Braenne .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"), 8283bc1ea5bSMartin Braenne [](const CallExpr *E, 8293bc1ea5bSMartin Braenne const MatchFinder::MatchResult &, 8303bc1ea5bSMartin Braenne LatticeTransferState &State) { 8313bc1ea5bSMartin Braenne transferArrowOpCall(E, E->getArg(0), State); 8323bc1ea5bSMartin Braenne }) 8333bc1ea5bSMartin Braenne 8342f0630f8SAnton Dukeman // optional::has_value, optional::hasValue 8352f0630f8SAnton Dukeman // Of the supported optionals only folly::Optional uses hasValue, but this 8362f0630f8SAnton Dukeman // will also pass for other types 8377538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8382f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher( 8392f0630f8SAnton Dukeman hasAnyName("has_value", "hasValue")), 840af98b0afSStanislav Gatev transferOptionalHasValueCall) 841af98b0afSStanislav Gatev 8429e0fc676SStanislav Gatev // optional::operator bool 8437538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8442f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("operator bool")), 8459e0fc676SStanislav Gatev transferOptionalHasValueCall) 8469e0fc676SStanislav Gatev 8479e0fc676SStanislav Gatev // optional::emplace 8487538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8492f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("emplace")), 850092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 851092a530cSStanislav Gatev LatticeTransferState &State) { 852f653d140SMartin Braenne if (AggregateStorageLocation *Loc = 853f653d140SMartin Braenne getImplicitObjectLocation(*E, State.Env)) { 854f653d140SMartin Braenne createOptionalValue(*Loc, State.Env.getBoolLiteralValue(true), 855f653d140SMartin Braenne State.Env); 856f653d140SMartin Braenne } 857092a530cSStanislav Gatev }) 8589e0fc676SStanislav Gatev 8599e0fc676SStanislav Gatev // optional::reset 8607538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 8612f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("reset")), 862092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 863092a530cSStanislav Gatev LatticeTransferState &State) { 864f653d140SMartin Braenne if (AggregateStorageLocation *Loc = 865f653d140SMartin Braenne getImplicitObjectLocation(*E, State.Env)) { 866f653d140SMartin Braenne createOptionalValue(*Loc, State.Env.getBoolLiteralValue(false), 867f653d140SMartin Braenne State.Env); 868f653d140SMartin Braenne } 869092a530cSStanislav Gatev }) 8709e0fc676SStanislav Gatev 8712ddd57aeSStanislav Gatev // optional::swap 8722f0630f8SAnton Dukeman .CaseOfCFGStmt<CXXMemberCallExpr>( 8732f0630f8SAnton Dukeman isOptionalMemberCallWithNameMatcher(hasName("swap")), 8742ddd57aeSStanislav Gatev transferSwapCall) 8752ddd57aeSStanislav Gatev 8762ddd57aeSStanislav Gatev // std::swap 8777538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 8782ddd57aeSStanislav Gatev 87925956d55SAMS21 // std::forward 88025956d55SAMS21 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 88125956d55SAMS21 8827f076004SYitzhak Mandelbaum // opt.value_or("").empty() 8837538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 8847538b360SWei Yi Tee transferValueOrStringEmptyCall) 8857f076004SYitzhak Mandelbaum 8867f076004SYitzhak Mandelbaum // opt.value_or(X) != X 8877538b360SWei Yi Tee .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 8887f076004SYitzhak Mandelbaum 889390029beSYitzhak Mandelbaum // Comparisons (==, !=): 890390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 891390029beSYitzhak Mandelbaum isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()), 892390029beSYitzhak Mandelbaum transferOptionalAndOptionalCmp) 893390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 894390029beSYitzhak Mandelbaum isComparisonOperatorCall(hasOptionalType(), 895390029beSYitzhak Mandelbaum unless(hasAnyOptionalType())), 896390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 897390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 898390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 899390029beSYitzhak Mandelbaum }) 900390029beSYitzhak Mandelbaum .CaseOfCFGStmt<CXXOperatorCallExpr>( 901390029beSYitzhak Mandelbaum isComparisonOperatorCall(unless(hasAnyOptionalType()), 902390029beSYitzhak Mandelbaum hasOptionalType()), 903390029beSYitzhak Mandelbaum [](const clang::CXXOperatorCallExpr *Cmp, 904390029beSYitzhak Mandelbaum const MatchFinder::MatchResult &, LatticeTransferState &State) { 905390029beSYitzhak Mandelbaum transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 906390029beSYitzhak Mandelbaum }) 907390029beSYitzhak Mandelbaum 90865e710c3SStanislav Gatev // returns optional 9097538b360SWei Yi Tee .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 91065e710c3SStanislav Gatev transferCallReturningOptional) 91165e710c3SStanislav Gatev 912af98b0afSStanislav Gatev .Build(); 913af98b0afSStanislav Gatev } 914af98b0afSStanislav Gatev 9156a81e694SMartin Braenne std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr, 91658fe7f96SSam Estep const Environment &Env) { 91748bc7150SMartin Braenne if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) { 91858fe7f96SSam Estep auto *Prop = OptionalVal->getProperty("has_value"); 91958fe7f96SSam Estep if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 9206272226bSSam McCall if (Env.flowConditionImplies(HasValueVal->formula())) 92158fe7f96SSam Estep return {}; 92258fe7f96SSam Estep } 92358fe7f96SSam Estep } 92458fe7f96SSam Estep 92558fe7f96SSam Estep // Record that this unwrap is *not* provably safe. 92658fe7f96SSam Estep // FIXME: include either the name of the optional (if applicable) or a source 92758fe7f96SSam Estep // range of the access for easier interpretation of the result. 92858fe7f96SSam Estep return {ObjectExpr->getBeginLoc()}; 92958fe7f96SSam Estep } 93058fe7f96SSam Estep 93158fe7f96SSam Estep auto buildDiagnoseMatchSwitch( 93258fe7f96SSam Estep const UncheckedOptionalAccessModelOptions &Options) { 93358fe7f96SSam Estep // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 93458fe7f96SSam Estep // lot of duplicated work (e.g. string comparisons), consider providing APIs 93558fe7f96SSam Estep // that avoid it through memoization. 93658fe7f96SSam Estep auto IgnorableOptional = ignorableOptional(Options); 9377538b360SWei Yi Tee return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>() 93858fe7f96SSam Estep // optional::value 9397538b360SWei Yi Tee .CaseOfCFGStmt<CXXMemberCallExpr>( 94058fe7f96SSam Estep valueCall(IgnorableOptional), 94158fe7f96SSam Estep [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 94258fe7f96SSam Estep const Environment &Env) { 9436a81e694SMartin Braenne return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env); 94458fe7f96SSam Estep }) 94558fe7f96SSam Estep 94658fe7f96SSam Estep // optional::operator*, optional::operator-> 9476a81e694SMartin Braenne .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 9486a81e694SMartin Braenne [](const CallExpr *E, 9496a81e694SMartin Braenne const MatchFinder::MatchResult &, 95058fe7f96SSam Estep const Environment &Env) { 9516a81e694SMartin Braenne return diagnoseUnwrapCall(E->getArg(0), Env); 95258fe7f96SSam Estep }) 95358fe7f96SSam Estep .Build(); 95458fe7f96SSam Estep } 95558fe7f96SSam Estep 956af98b0afSStanislav Gatev } // namespace 957af98b0afSStanislav Gatev 9587e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher 9597e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() { 9607e63a0d4SYitzhak Mandelbaum return optionalClass(); 9617e63a0d4SYitzhak Mandelbaum } 9627e63a0d4SYitzhak Mandelbaum 9635d22d1f5SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx) 964cf1f978dSSam Estep : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), 9655d22d1f5SYitzhak Mandelbaum TransferMatchSwitch(buildTransferMatchSwitch()) {} 966af98b0afSStanislav Gatev 9676b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 9687538b360SWei Yi Tee NoopLattice &L, Environment &Env) { 969af98b0afSStanislav Gatev LatticeTransferState State(L, Env); 9706b991ba4SYitzhak Mandelbaum TransferMatchSwitch(Elt, getASTContext(), State); 971af98b0afSStanislav Gatev } 972af98b0afSStanislav Gatev 973c0725865SYitzhak Mandelbaum ComparisonResult UncheckedOptionalAccessModel::compare( 974c0725865SYitzhak Mandelbaum QualType Type, const Value &Val1, const Environment &Env1, 975c0725865SYitzhak Mandelbaum const Value &Val2, const Environment &Env2) { 976c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 977c0725865SYitzhak Mandelbaum return ComparisonResult::Unknown; 978d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 979d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 98025956d55SAMS21 if (MustNonEmpty1 && MustNonEmpty2) 98125956d55SAMS21 return ComparisonResult::Same; 982d34fbf2dSYitzhak Mandelbaum // If exactly one is true, then they're different, no reason to check whether 983d34fbf2dSYitzhak Mandelbaum // they're definitely empty. 98425956d55SAMS21 if (MustNonEmpty1 || MustNonEmpty2) 98525956d55SAMS21 return ComparisonResult::Different; 986d34fbf2dSYitzhak Mandelbaum // Check if they're both definitely empty. 987d34fbf2dSYitzhak Mandelbaum return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)) 988c0725865SYitzhak Mandelbaum ? ComparisonResult::Same 989c0725865SYitzhak Mandelbaum : ComparisonResult::Different; 9908fcdd625SStanislav Gatev } 9918fcdd625SStanislav Gatev 9928fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1, 9938fcdd625SStanislav Gatev const Environment &Env1, 9948fcdd625SStanislav Gatev const Value &Val2, 9958fcdd625SStanislav Gatev const Environment &Env2, 9968fcdd625SStanislav Gatev Value &MergedVal, 9978fcdd625SStanislav Gatev Environment &MergedEnv) { 998c0725865SYitzhak Mandelbaum if (!isOptionalType(Type)) 9998fcdd625SStanislav Gatev return true; 1000d34fbf2dSYitzhak Mandelbaum // FIXME: uses same approach as join for `BoolValues`. Requires non-const 1001d34fbf2dSYitzhak Mandelbaum // values, though, so will require updating the interface. 10028fcdd625SStanislav Gatev auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); 1003d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); 1004d34fbf2dSYitzhak Mandelbaum bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); 1005d34fbf2dSYitzhak Mandelbaum if (MustNonEmpty1 && MustNonEmpty2) 10066272226bSSam McCall MergedEnv.addToFlowCondition(HasValueVal.formula()); 1007d34fbf2dSYitzhak Mandelbaum else if ( 1008d34fbf2dSYitzhak Mandelbaum // Only make the costly calls to `isEmptyOptional` if we got "unknown" 1009d34fbf2dSYitzhak Mandelbaum // (false) for both calls to `isNonEmptyOptional`. 1010d34fbf2dSYitzhak Mandelbaum !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) && 1011d34fbf2dSYitzhak Mandelbaum isEmptyOptional(Val2, Env2)) 10126272226bSSam McCall MergedEnv.addToFlowCondition( 10136272226bSSam McCall MergedEnv.arena().makeNot(HasValueVal.formula())); 10148fcdd625SStanislav Gatev setHasValue(MergedVal, HasValueVal); 10158fcdd625SStanislav Gatev return true; 10168fcdd625SStanislav Gatev } 10178fcdd625SStanislav Gatev 1018d34fbf2dSYitzhak Mandelbaum Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev, 1019d34fbf2dSYitzhak Mandelbaum const Environment &PrevEnv, 1020d34fbf2dSYitzhak Mandelbaum Value &Current, 1021d34fbf2dSYitzhak Mandelbaum Environment &CurrentEnv) { 1022d34fbf2dSYitzhak Mandelbaum switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) { 1023d34fbf2dSYitzhak Mandelbaum case ComparisonResult::Same: 1024d34fbf2dSYitzhak Mandelbaum return &Prev; 1025d34fbf2dSYitzhak Mandelbaum case ComparisonResult::Different: 1026d34fbf2dSYitzhak Mandelbaum if (auto *PrevHasVal = 1027d34fbf2dSYitzhak Mandelbaum cast_or_null<BoolValue>(Prev.getProperty("has_value"))) { 1028d34fbf2dSYitzhak Mandelbaum if (isa<TopBoolValue>(PrevHasVal)) 1029d34fbf2dSYitzhak Mandelbaum return &Prev; 1030d34fbf2dSYitzhak Mandelbaum } 1031d34fbf2dSYitzhak Mandelbaum if (auto *CurrentHasVal = 1032d34fbf2dSYitzhak Mandelbaum cast_or_null<BoolValue>(Current.getProperty("has_value"))) { 1033d34fbf2dSYitzhak Mandelbaum if (isa<TopBoolValue>(CurrentHasVal)) 1034d34fbf2dSYitzhak Mandelbaum return &Current; 1035d34fbf2dSYitzhak Mandelbaum } 103644f98d01SMartin Braenne return &createOptionalValue(cast<StructValue>(Current).getAggregateLoc(), 103744f98d01SMartin Braenne CurrentEnv.makeTopBoolValue(), CurrentEnv); 1038d34fbf2dSYitzhak Mandelbaum case ComparisonResult::Unknown: 1039d34fbf2dSYitzhak Mandelbaum return nullptr; 1040d34fbf2dSYitzhak Mandelbaum } 1041d34fbf2dSYitzhak Mandelbaum llvm_unreachable("all cases covered in switch"); 1042d34fbf2dSYitzhak Mandelbaum } 1043d34fbf2dSYitzhak Mandelbaum 104458fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 104558fe7f96SSam Estep UncheckedOptionalAccessModelOptions Options) 104658fe7f96SSam Estep : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 104758fe7f96SSam Estep 1048af98b0afSStanislav Gatev } // namespace dataflow 1049af98b0afSStanislav Gatev } // namespace clang 1050