1 //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines a dataflow analysis that detects unsafe uses of optional 10 // values. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h" 15 #include "clang/AST/ASTContext.h" 16 #include "clang/AST/DeclCXX.h" 17 #include "clang/AST/Expr.h" 18 #include "clang/AST/ExprCXX.h" 19 #include "clang/AST/Stmt.h" 20 #include "clang/AST/Type.h" 21 #include "clang/ASTMatchers/ASTMatchers.h" 22 #include "clang/ASTMatchers/ASTMatchersMacros.h" 23 #include "clang/Analysis/CFG.h" 24 #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h" 25 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 26 #include "clang/Analysis/FlowSensitive/Formula.h" 27 #include "clang/Analysis/FlowSensitive/RecordOps.h" 28 #include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h" 29 #include "clang/Analysis/FlowSensitive/StorageLocation.h" 30 #include "clang/Analysis/FlowSensitive/Value.h" 31 #include "clang/Basic/OperatorKinds.h" 32 #include "clang/Basic/SourceLocation.h" 33 #include "llvm/ADT/StringRef.h" 34 #include "llvm/Support/Casting.h" 35 #include "llvm/Support/ErrorHandling.h" 36 #include <cassert> 37 #include <memory> 38 #include <optional> 39 #include <utility> 40 41 namespace clang { 42 namespace dataflow { 43 44 // Note: the Names appear in reverse order. E.g., to check 45 // if NS is foo::bar::, call isFullyQualifiedNamespaceEqualTo(NS, "bar", "foo") 46 template <class... NameTypes> 47 static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS, 48 llvm::StringRef Name, 49 NameTypes... Names) { 50 if (!(NS.getDeclName().isIdentifier() && NS.getName() == Name && 51 NS.getParent() != nullptr)) 52 return false; 53 54 if constexpr (sizeof...(NameTypes) > 0) { 55 if (NS.getParent()->isTranslationUnit()) 56 return false; 57 if (const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.getParent())) 58 return isFullyQualifiedNamespaceEqualTo(*NextNS, Names...); 59 return false; 60 } else { 61 return NS.getParent()->isTranslationUnit(); 62 } 63 } 64 65 static bool hasOptionalClassName(const CXXRecordDecl &RD) { 66 if (!RD.getDeclName().isIdentifier()) 67 return false; 68 69 if (RD.getName() == "optional") { 70 if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext())) 71 return N->isStdNamespace() || 72 isFullyQualifiedNamespaceEqualTo(*N, "absl") || 73 isFullyQualifiedNamespaceEqualTo(*N, "bsl"); 74 return false; 75 } 76 77 if (RD.getName() == "Optional") { 78 // Check whether namespace is "::base" or "::folly". 79 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); 80 return N != nullptr && (isFullyQualifiedNamespaceEqualTo(*N, "base") || 81 isFullyQualifiedNamespaceEqualTo(*N, "folly")); 82 } 83 84 if (RD.getName() == "NullableValue") { 85 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()); 86 return N != nullptr && 87 isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP"); 88 } 89 90 return false; 91 } 92 93 static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) { 94 if (RD == nullptr) 95 return nullptr; 96 if (hasOptionalClassName(*RD)) 97 return RD; 98 99 if (!RD->hasDefinition()) 100 return nullptr; 101 102 for (const CXXBaseSpecifier &Base : RD->bases()) 103 if (const CXXRecordDecl *BaseClass = 104 getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl())) 105 return BaseClass; 106 107 return nullptr; 108 } 109 110 static bool isSupportedOptionalType(QualType Ty) { 111 const CXXRecordDecl *Optional = 112 getOptionalBaseClass(Ty->getAsCXXRecordDecl()); 113 return Optional != nullptr; 114 } 115 116 namespace { 117 118 using namespace ::clang::ast_matchers; 119 120 using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>; 121 122 AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); } 123 124 AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) { 125 return getOptionalBaseClass(&Node) != nullptr; 126 } 127 128 auto desugarsToOptionalType() { 129 return hasUnqualifiedDesugaredType( 130 recordType(hasDeclaration(cxxRecordDecl(optionalClass())))); 131 } 132 133 auto desugarsToOptionalOrDerivedType() { 134 return hasUnqualifiedDesugaredType( 135 recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass())))); 136 } 137 138 auto hasOptionalType() { return hasType(desugarsToOptionalType()); } 139 140 /// Matches any of the spellings of the optional types and sugar, aliases, 141 /// derived classes, etc. 142 auto hasOptionalOrDerivedType() { 143 return hasType(desugarsToOptionalOrDerivedType()); 144 } 145 146 QualType getPublicType(const Expr *E) { 147 auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens()); 148 if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) { 149 QualType Ty = E->getType(); 150 if (Ty->isPointerType()) 151 return Ty->getPointeeType(); 152 return Ty; 153 } 154 155 // Is the derived type that we're casting from the type of `*this`? In this 156 // special case, we can upcast to the base class even if the base is 157 // non-public. 158 bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr()); 159 160 // Find the least-derived type in the path (i.e. the last entry in the list) 161 // that we can access. 162 const CXXBaseSpecifier *PublicBase = nullptr; 163 for (const CXXBaseSpecifier *Base : Cast->path()) { 164 if (Base->getAccessSpecifier() != AS_public && !CastingFromThis) 165 break; 166 PublicBase = Base; 167 CastingFromThis = false; 168 } 169 170 if (PublicBase != nullptr) 171 return PublicBase->getType(); 172 173 // We didn't find any public type that we could cast to. There may be more 174 // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this 175 // will return the type of `getSubExpr()`.) 176 return getPublicType(Cast->getSubExpr()); 177 } 178 179 // Returns the least-derived type for the receiver of `MCE` that 180 // `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to. 181 // Effectively, we upcast until we reach a non-public base class, unless that 182 // base is a base of `*this`. 183 // 184 // This is needed to correctly match methods called on types derived from 185 // `std::optional`. 186 // 187 // Say we have a `struct Derived : public std::optional<int> {} d;` For a call 188 // `d.has_value()`, the `getImplicitObjectArgument()` looks like this: 189 // 190 // ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue 191 // | <UncheckedDerivedToBase (optional -> __optional_storage_base)> 192 // `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived' 193 // 194 // The type of the implicit object argument is `__optional_storage_base` 195 // (since this is the internal type that `has_value()` is declared on). If we 196 // call `IgnoreParenImpCasts()` on the implicit object argument, we get the 197 // `DeclRefExpr`, which has type `Derived`. Neither of these types is 198 // `optional`, and hence neither is sufficient for querying whether we are 199 // calling a method on `optional`. 200 // 201 // Instead, starting with the most derived type, we need to follow the chain of 202 // casts 203 QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) { 204 return getPublicType(MCE.getImplicitObjectArgument()); 205 } 206 207 AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType, 208 ast_matchers::internal::Matcher<QualType>, InnerMatcher) { 209 return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder); 210 } 211 212 auto isOptionalMemberCallWithNameMatcher( 213 ast_matchers::internal::Matcher<NamedDecl> matcher, 214 const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 215 return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable))) 216 : anything(), 217 publicReceiverType(desugarsToOptionalType()), 218 callee(cxxMethodDecl(matcher))); 219 } 220 221 auto isOptionalOperatorCallWithName( 222 llvm::StringRef operator_name, 223 const std::optional<StatementMatcher> &Ignorable = std::nullopt) { 224 return cxxOperatorCallExpr( 225 hasOverloadedOperatorName(operator_name), 226 callee(cxxMethodDecl(ofClass(optionalClass()))), 227 Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 228 } 229 230 auto isMakeOptionalCall() { 231 return callExpr( 232 callee(functionDecl(hasAnyName( 233 "std::make_optional", "base::make_optional", "absl::make_optional", 234 "folly::make_optional", "bsl::make_optional"))), 235 hasOptionalType()); 236 } 237 238 auto nulloptTypeDecl() { 239 return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t", 240 "base::nullopt_t", "folly::None", 241 "bsl::nullopt_t")); 242 } 243 244 auto hasNulloptType() { return hasType(nulloptTypeDecl()); } 245 246 auto inPlaceClass() { 247 return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t", 248 "base::in_place_t", "folly::in_place_t", 249 "bsl::in_place_t")); 250 } 251 252 auto isOptionalNulloptConstructor() { 253 return cxxConstructExpr( 254 hasDeclaration(cxxConstructorDecl(parameterCountIs(1), 255 hasParameter(0, hasNulloptType()))), 256 hasOptionalOrDerivedType()); 257 } 258 259 auto isOptionalInPlaceConstructor() { 260 return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())), 261 hasOptionalOrDerivedType()); 262 } 263 264 auto isOptionalValueOrConversionConstructor() { 265 return cxxConstructExpr( 266 unless(hasDeclaration( 267 cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 268 argumentCountIs(1), hasArgument(0, unless(hasNulloptType())), 269 hasOptionalOrDerivedType()); 270 } 271 272 auto isOptionalValueOrConversionAssignment() { 273 return cxxOperatorCallExpr( 274 hasOverloadedOperatorName("="), 275 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))), 276 unless(hasDeclaration(cxxMethodDecl( 277 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 278 argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 279 } 280 281 auto isOptionalNulloptAssignment() { 282 return cxxOperatorCallExpr( 283 hasOverloadedOperatorName("="), 284 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))), 285 argumentCountIs(2), hasArgument(1, hasNulloptType())); 286 } 287 288 auto isStdSwapCall() { 289 return callExpr(callee(functionDecl(hasName("std::swap"))), 290 argumentCountIs(2), 291 hasArgument(0, hasOptionalOrDerivedType()), 292 hasArgument(1, hasOptionalOrDerivedType())); 293 } 294 295 auto isStdForwardCall() { 296 return callExpr(callee(functionDecl(hasName("std::forward"))), 297 argumentCountIs(1), 298 hasArgument(0, hasOptionalOrDerivedType())); 299 } 300 301 constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 302 303 auto isValueOrStringEmptyCall() { 304 // `opt.value_or("").empty()` 305 return cxxMemberCallExpr( 306 callee(cxxMethodDecl(hasName("empty"))), 307 onImplicitObjectArgument(ignoringImplicit( 308 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 309 callee(cxxMethodDecl(hasName("value_or"), 310 ofClass(optionalClass()))), 311 hasArgument(0, stringLiteral(hasSize(0)))) 312 .bind(ValueOrCallID)))); 313 } 314 315 auto isValueOrNotEqX() { 316 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 317 return hasOperands( 318 ignoringImplicit( 319 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 320 callee(cxxMethodDecl(hasName("value_or"), 321 ofClass(optionalClass()))), 322 hasArgument(0, Arg)) 323 .bind(ValueOrCallID)), 324 ignoringImplicit(Arg)); 325 }; 326 327 // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 328 // support this pattern for any expression, but the AST does not have a 329 // generic expression comparison facility, so we specialize to common cases 330 // seen in practice. FIXME: define a matcher that compares values across 331 // nodes, which would let us generalize this to any `X`. 332 return binaryOperation(hasOperatorName("!="), 333 anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 334 ComparesToSame(stringLiteral(hasSize(0))), 335 ComparesToSame(integerLiteral(equals(0))))); 336 } 337 338 auto isZeroParamConstMemberCall() { 339 return cxxMemberCallExpr( 340 callee(cxxMethodDecl(parameterCountIs(0), isConst()))); 341 } 342 343 auto isZeroParamConstMemberOperatorCall() { 344 return cxxOperatorCallExpr( 345 callee(cxxMethodDecl(parameterCountIs(0), isConst()))); 346 } 347 348 auto isNonConstMemberCall() { 349 return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst())))); 350 } 351 352 auto isNonConstMemberOperatorCall() { 353 return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst())))); 354 } 355 356 auto isCallReturningOptional() { 357 return callExpr(hasType(qualType( 358 anyOf(desugarsToOptionalOrDerivedType(), 359 referenceType(pointee(desugarsToOptionalOrDerivedType())))))); 360 } 361 362 template <typename L, typename R> 363 auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 364 return cxxOperatorCallExpr( 365 anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 366 argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 367 hasArgument(1, rhs_arg_matcher)); 368 } 369 370 /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula. 371 const Formula &forceBoolValue(Environment &Env, const Expr &Expr) { 372 auto *Value = Env.get<BoolValue>(Expr); 373 if (Value != nullptr) 374 return Value->formula(); 375 376 Value = &Env.makeAtomicBoolValue(); 377 Env.setValue(Expr, *Value); 378 return Value->formula(); 379 } 380 381 StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) { 382 return OptionalLoc.getSyntheticField("has_value"); 383 } 384 385 StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) { 386 return OptionalLoc.getSyntheticField("value"); 387 } 388 389 /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 390 /// property of the optional at `OptionalLoc`. 391 void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal, 392 Environment &Env) { 393 Env.setValue(locForHasValue(OptionalLoc), HasValueVal); 394 } 395 396 /// Returns the symbolic value that represents the "has_value" property of the 397 /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null. 398 BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) { 399 if (OptionalLoc == nullptr) 400 return nullptr; 401 StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc); 402 auto *HasValueVal = Env.get<BoolValue>(HasValueLoc); 403 if (HasValueVal == nullptr) { 404 HasValueVal = &Env.makeAtomicBoolValue(); 405 Env.setValue(HasValueLoc, *HasValueVal); 406 } 407 return HasValueVal; 408 } 409 410 QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) { 411 auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD); 412 return CTSD.getTemplateArgs()[0].getAsType(); 413 } 414 415 /// Returns the number of optional wrappers in `Type`. 416 /// 417 /// For example, if `Type` is `optional<optional<int>>`, the result of this 418 /// function will be 2. 419 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 420 const CXXRecordDecl *Optional = 421 getOptionalBaseClass(Type->getAsCXXRecordDecl()); 422 if (Optional == nullptr) 423 return 0; 424 return 1 + countOptionalWrappers( 425 ASTCtx, 426 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx)); 427 } 428 429 StorageLocation *getLocBehindPossiblePointer(const Expr &E, 430 const Environment &Env) { 431 if (E.isPRValue()) { 432 if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E))) 433 return &PointerVal->getPointeeLoc(); 434 return nullptr; 435 } 436 return Env.getStorageLocation(E); 437 } 438 439 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 440 LatticeTransferState &State) { 441 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 442 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) { 443 if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr) 444 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc)); 445 } 446 } 447 448 void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 449 LatticeTransferState &State) { 450 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 451 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) 452 State.Env.setValue( 453 *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc))); 454 } 455 456 void transferMakeOptionalCall(const CallExpr *E, 457 const MatchFinder::MatchResult &, 458 LatticeTransferState &State) { 459 setHasValue(State.Env.getResultObjectLocation(*E), 460 State.Env.getBoolLiteralValue(true), State.Env); 461 } 462 463 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 464 const MatchFinder::MatchResult &, 465 LatticeTransferState &State) { 466 if (auto *HasValueVal = getHasValue( 467 State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 468 State.Env.setValue(*CallExpr, *HasValueVal); 469 } 470 } 471 472 void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr, 473 const MatchFinder::MatchResult &, 474 LatticeTransferState &State) { 475 if (auto *HasValueVal = getHasValue( 476 State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 477 State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal)); 478 } 479 } 480 481 /// `ModelPred` builds a logical formula relating the predicate in 482 /// `ValueOrPredExpr` to the optional's `has_value` property. 483 void transferValueOrImpl( 484 const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result, 485 LatticeTransferState &State, 486 const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal, 487 const Formula &HasValueVal)) { 488 auto &Env = State.Env; 489 490 const auto *MCE = 491 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID); 492 493 auto *HasValueVal = 494 getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env)); 495 if (HasValueVal == nullptr) 496 return; 497 498 Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), 499 HasValueVal->formula())); 500 } 501 502 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 503 const MatchFinder::MatchResult &Result, 504 LatticeTransferState &State) { 505 return transferValueOrImpl(ComparisonExpr, Result, State, 506 [](Environment &Env, const Formula &ExprVal, 507 const Formula &HasValueVal) -> const Formula & { 508 auto &A = Env.arena(); 509 // If the result is *not* empty, then we know the 510 // optional must have been holding a value. If 511 // `ExprVal` is true, though, we don't learn 512 // anything definite about `has_value`, so we 513 // don't add any corresponding implications to 514 // the flow condition. 515 return A.makeImplies(A.makeNot(ExprVal), 516 HasValueVal); 517 }); 518 } 519 520 void transferValueOrNotEqX(const Expr *ComparisonExpr, 521 const MatchFinder::MatchResult &Result, 522 LatticeTransferState &State) { 523 transferValueOrImpl(ComparisonExpr, Result, State, 524 [](Environment &Env, const Formula &ExprVal, 525 const Formula &HasValueVal) -> const Formula & { 526 auto &A = Env.arena(); 527 // We know that if `(opt.value_or(X) != X)` then 528 // `opt.hasValue()`, even without knowing further 529 // details about the contents of `opt`. 530 return A.makeImplies(ExprVal, HasValueVal); 531 }); 532 } 533 534 void transferCallReturningOptional(const CallExpr *E, 535 const MatchFinder::MatchResult &Result, 536 LatticeTransferState &State) { 537 RecordStorageLocation *Loc = nullptr; 538 if (E->isPRValue()) { 539 Loc = &State.Env.getResultObjectLocation(*E); 540 } else { 541 Loc = State.Env.get<RecordStorageLocation>(*E); 542 if (Loc == nullptr) { 543 Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E)); 544 State.Env.setStorageLocation(*E, *Loc); 545 } 546 } 547 548 if (State.Env.getValue(locForHasValue(*Loc)) != nullptr) 549 return; 550 551 setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env); 552 } 553 554 void handleConstMemberCall(const CallExpr *CE, 555 dataflow::RecordStorageLocation *RecordLoc, 556 const MatchFinder::MatchResult &Result, 557 LatticeTransferState &State) { 558 // If the const method returns an optional or reference to an optional. 559 if (RecordLoc != nullptr && isSupportedOptionalType(CE->getType())) { 560 const FunctionDecl *DirectCallee = CE->getDirectCallee(); 561 if (DirectCallee == nullptr) 562 return; 563 StorageLocation &Loc = 564 State.Lattice.getOrCreateConstMethodReturnStorageLocation( 565 *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) { 566 setHasValue(cast<RecordStorageLocation>(Loc), 567 State.Env.makeAtomicBoolValue(), State.Env); 568 }); 569 if (CE->isGLValue()) { 570 // If the call to the const method returns a reference to an optional, 571 // link the call expression to the cached StorageLocation. 572 State.Env.setStorageLocation(*CE, Loc); 573 } else { 574 // If the call to the const method returns an optional by value, we 575 // need to use CopyRecord to link the optional to the result object 576 // of the call expression. 577 auto &ResultLoc = State.Env.getResultObjectLocation(*CE); 578 copyRecord(cast<RecordStorageLocation>(Loc), ResultLoc, State.Env); 579 } 580 return; 581 } 582 583 // Cache if the const method returns a boolean or pointer type. 584 // We may decide to cache other return types in the future. 585 if (RecordLoc != nullptr && 586 (CE->getType()->isBooleanType() || CE->getType()->isPointerType())) { 587 Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE, 588 State.Env); 589 if (Val == nullptr) 590 return; 591 State.Env.setValue(*CE, *Val); 592 return; 593 } 594 595 // Perform default handling if the call returns an optional 596 // but wasn't handled above (if RecordLoc is nullptr). 597 if (isSupportedOptionalType(CE->getType())) { 598 transferCallReturningOptional(CE, Result, State); 599 } 600 } 601 602 void transferValue_ConstMemberCall(const CXXMemberCallExpr *MCE, 603 const MatchFinder::MatchResult &Result, 604 LatticeTransferState &State) { 605 handleConstMemberCall( 606 MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State); 607 } 608 609 void transferValue_ConstMemberOperatorCall( 610 const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result, 611 LatticeTransferState &State) { 612 auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>( 613 State.Env.getStorageLocation(*OCE->getArg(0))); 614 handleConstMemberCall(OCE, RecordLoc, Result, State); 615 } 616 617 void handleNonConstMemberCall(const CallExpr *CE, 618 dataflow::RecordStorageLocation *RecordLoc, 619 const MatchFinder::MatchResult &Result, 620 LatticeTransferState &State) { 621 if (RecordLoc != nullptr) { 622 // When a non-const member function is called, clear all (non-const) 623 // optional fields of the receiver. Const-qualified fields can't be 624 // changed (at least, not without UB). 625 for (const auto &[Field, FieldLoc] : RecordLoc->children()) { 626 QualType FieldType = Field->getType(); 627 if (!FieldType.isConstQualified() && 628 isSupportedOptionalType(Field->getType())) { 629 auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc); 630 if (FieldRecordLoc) { 631 setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(), 632 State.Env); 633 } 634 } 635 } 636 State.Lattice.clearConstMethodReturnValues(*RecordLoc); 637 State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc); 638 } 639 640 // Perform default handling if the call returns an optional. 641 if (isSupportedOptionalType(CE->getType())) { 642 transferCallReturningOptional(CE, Result, State); 643 } 644 } 645 646 void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE, 647 const MatchFinder::MatchResult &Result, 648 LatticeTransferState &State) { 649 handleNonConstMemberCall( 650 MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State); 651 } 652 653 void transferValue_NonConstMemberOperatorCall( 654 const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result, 655 LatticeTransferState &State) { 656 auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>( 657 State.Env.getStorageLocation(*OCE->getArg(0))); 658 handleNonConstMemberCall(OCE, RecordLoc, Result, State); 659 } 660 661 void constructOptionalValue(const Expr &E, Environment &Env, 662 BoolValue &HasValueVal) { 663 RecordStorageLocation &Loc = Env.getResultObjectLocation(E); 664 setHasValue(Loc, HasValueVal, Env); 665 } 666 667 /// Returns a symbolic value for the "has_value" property of an `optional<T>` 668 /// value that is constructed/assigned from a value of type `U` or `optional<U>` 669 /// where `T` is constructible from `U`. 670 BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E, 671 const MatchFinder::MatchResult &MatchRes, 672 LatticeTransferState &State) { 673 const int DestTypeOptionalWrappersCount = 674 countOptionalWrappers(*MatchRes.Context, DestType); 675 const int ArgTypeOptionalWrappersCount = countOptionalWrappers( 676 *MatchRes.Context, E.getType().getNonReferenceType()); 677 678 // Is this an constructor of the form `template<class U> optional(U &&)` / 679 // assignment of the form `template<class U> optional& operator=(U &&)` 680 // (where `T` is assignable / constructible from `U`)? 681 // We recognize this because the number of optionals in the optional being 682 // assigned to is different from the function argument type. 683 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount) 684 return State.Env.getBoolLiteralValue(true); 685 686 // Otherwise, this must be a constructor of the form 687 // `template <class U> optional<optional<U> &&)` / assignment of the form 688 // `template <class U> optional& operator=(optional<U> &&) 689 // (where, again, `T` is assignable / constructible from `U`). 690 auto *Loc = State.Env.get<RecordStorageLocation>(E); 691 if (auto *HasValueVal = getHasValue(State.Env, Loc)) 692 return *HasValueVal; 693 return State.Env.makeAtomicBoolValue(); 694 } 695 696 void transferValueOrConversionConstructor( 697 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 698 LatticeTransferState &State) { 699 assert(E->getNumArgs() > 0); 700 701 constructOptionalValue( 702 *E, State.Env, 703 valueOrConversionHasValue( 704 E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0), 705 MatchRes, State)); 706 } 707 708 void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 709 LatticeTransferState &State) { 710 assert(E->getNumArgs() > 0); 711 712 if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) { 713 setHasValue(*Loc, HasValueVal, State.Env); 714 715 // Assign a storage location for the whole expression. 716 State.Env.setStorageLocation(*E, *Loc); 717 } 718 } 719 720 void transferValueOrConversionAssignment( 721 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 722 LatticeTransferState &State) { 723 assert(E->getNumArgs() > 1); 724 transferAssignment( 725 E, 726 valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(), 727 *E->getArg(1), MatchRes, State), 728 State); 729 } 730 731 void transferNulloptAssignment(const CXXOperatorCallExpr *E, 732 const MatchFinder::MatchResult &, 733 LatticeTransferState &State) { 734 transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 735 } 736 737 void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2, 738 Environment &Env) { 739 // We account for cases where one or both of the optionals are not modeled, 740 // either lacking associated storage locations, or lacking values associated 741 // to such storage locations. 742 743 if (Loc1 == nullptr) { 744 if (Loc2 != nullptr) 745 setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env); 746 return; 747 } 748 if (Loc2 == nullptr) { 749 setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env); 750 return; 751 } 752 753 // Both expressions have locations, though they may not have corresponding 754 // values. In that case, we create a fresh value at this point. Note that if 755 // two branches both do this, they will not share the value, but it at least 756 // allows for local reasoning about the value. To avoid the above, we would 757 // need *lazy* value allocation. 758 // FIXME: allocate values lazily, instead of just creating a fresh value. 759 BoolValue *BoolVal1 = getHasValue(Env, Loc1); 760 if (BoolVal1 == nullptr) 761 BoolVal1 = &Env.makeAtomicBoolValue(); 762 763 BoolValue *BoolVal2 = getHasValue(Env, Loc2); 764 if (BoolVal2 == nullptr) 765 BoolVal2 = &Env.makeAtomicBoolValue(); 766 767 setHasValue(*Loc1, *BoolVal2, Env); 768 setHasValue(*Loc2, *BoolVal1, Env); 769 } 770 771 void transferSwapCall(const CXXMemberCallExpr *E, 772 const MatchFinder::MatchResult &, 773 LatticeTransferState &State) { 774 assert(E->getNumArgs() == 1); 775 auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 776 transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env); 777 } 778 779 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 780 LatticeTransferState &State) { 781 assert(E->getNumArgs() == 2); 782 auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 783 auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1)); 784 transferSwap(Arg0Loc, Arg1Loc, State.Env); 785 } 786 787 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &, 788 LatticeTransferState &State) { 789 assert(E->getNumArgs() == 1); 790 791 if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0))) 792 State.Env.setStorageLocation(*E, *Loc); 793 } 794 795 const Formula &evaluateEquality(Arena &A, const Formula &EqVal, 796 const Formula &LHS, const Formula &RHS) { 797 // Logically, an optional<T> object is composed of two values - a `has_value` 798 // bit and a value of type T. Equality of optional objects compares both 799 // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 800 // when two optional objects are engaged, the equality of their respective 801 // values of type T matters. Since we only track the `has_value` bits, we 802 // can't make any conclusions about equality when we know that two optional 803 // objects are engaged. 804 // 805 // We express this as two facts about the equality: 806 // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 807 // If they are equal, then either both are set or both are unset. 808 // b) (!LHS & !RHS) => EqVal 809 // If neither is set, then they are equal. 810 // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 811 return A.makeAnd( 812 A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS), 813 A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))), 814 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS))); 815 } 816 817 void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 818 const MatchFinder::MatchResult &, 819 LatticeTransferState &State) { 820 Environment &Env = State.Env; 821 auto &A = Env.arena(); 822 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 823 auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0)); 824 if (auto *LHasVal = getHasValue(Env, Arg0Loc)) { 825 auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1)); 826 if (auto *RHasVal = getHasValue(Env, Arg1Loc)) { 827 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 828 CmpValue = &A.makeNot(*CmpValue); 829 Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(), 830 RHasVal->formula())); 831 } 832 } 833 } 834 835 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 836 const clang::Expr *E, Environment &Env) { 837 auto &A = Env.arena(); 838 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 839 auto *Loc = Env.get<RecordStorageLocation>(*E); 840 if (auto *HasVal = getHasValue(Env, Loc)) { 841 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 842 CmpValue = &A.makeNot(*CmpValue); 843 Env.assume( 844 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true))); 845 } 846 } 847 848 void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr, 849 const clang::Expr *E, Environment &Env) { 850 auto &A = Env.arena(); 851 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 852 auto *Loc = Env.get<RecordStorageLocation>(*E); 853 if (auto *HasVal = getHasValue(Env, Loc)) { 854 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 855 CmpValue = &A.makeNot(*CmpValue); 856 Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(), 857 A.makeLiteral(false))); 858 } 859 } 860 861 std::optional<StatementMatcher> 862 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 863 if (Options.IgnoreSmartPointerDereference) { 864 auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 865 anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 866 unless(hasArgument(0, expr(hasOptionalType())))))); 867 return expr( 868 anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 869 } 870 return std::nullopt; 871 } 872 873 StatementMatcher 874 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 875 return isOptionalMemberCallWithNameMatcher(hasName("value"), 876 IgnorableOptional); 877 } 878 879 StatementMatcher 880 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 881 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 882 isOptionalOperatorCallWithName("->", IgnorableOptional))); 883 } 884 885 auto buildTransferMatchSwitch() { 886 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 887 // lot of duplicated work (e.g. string comparisons), consider providing APIs 888 // that avoid it through memoization. 889 return CFGMatchSwitchBuilder<LatticeTransferState>() 890 // make_optional 891 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 892 893 // optional::optional (in place) 894 .CaseOfCFGStmt<CXXConstructExpr>( 895 isOptionalInPlaceConstructor(), 896 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 897 LatticeTransferState &State) { 898 constructOptionalValue(*E, State.Env, 899 State.Env.getBoolLiteralValue(true)); 900 }) 901 // optional::optional(nullopt_t) 902 .CaseOfCFGStmt<CXXConstructExpr>( 903 isOptionalNulloptConstructor(), 904 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 905 LatticeTransferState &State) { 906 constructOptionalValue(*E, State.Env, 907 State.Env.getBoolLiteralValue(false)); 908 }) 909 // optional::optional (value/conversion) 910 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 911 transferValueOrConversionConstructor) 912 913 // optional::operator= 914 .CaseOfCFGStmt<CXXOperatorCallExpr>( 915 isOptionalValueOrConversionAssignment(), 916 transferValueOrConversionAssignment) 917 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 918 transferNulloptAssignment) 919 920 // optional::value 921 .CaseOfCFGStmt<CXXMemberCallExpr>( 922 valueCall(std::nullopt), 923 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 924 LatticeTransferState &State) { 925 transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 926 }) 927 928 // optional::operator* 929 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"), 930 [](const CallExpr *E, 931 const MatchFinder::MatchResult &, 932 LatticeTransferState &State) { 933 transferUnwrapCall(E, E->getArg(0), State); 934 }) 935 936 // optional::operator-> 937 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"), 938 [](const CallExpr *E, 939 const MatchFinder::MatchResult &, 940 LatticeTransferState &State) { 941 transferArrowOpCall(E, E->getArg(0), State); 942 }) 943 944 // optional::has_value, optional::hasValue 945 // Of the supported optionals only folly::Optional uses hasValue, but this 946 // will also pass for other types 947 .CaseOfCFGStmt<CXXMemberCallExpr>( 948 isOptionalMemberCallWithNameMatcher( 949 hasAnyName("has_value", "hasValue")), 950 transferOptionalHasValueCall) 951 952 // optional::operator bool 953 .CaseOfCFGStmt<CXXMemberCallExpr>( 954 isOptionalMemberCallWithNameMatcher(hasName("operator bool")), 955 transferOptionalHasValueCall) 956 957 // NullableValue::isNull 958 // Only NullableValue has isNull 959 .CaseOfCFGStmt<CXXMemberCallExpr>( 960 isOptionalMemberCallWithNameMatcher(hasName("isNull")), 961 transferOptionalIsNullCall) 962 963 // optional::emplace 964 .CaseOfCFGStmt<CXXMemberCallExpr>( 965 isOptionalMemberCallWithNameMatcher(hasName("emplace")), 966 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 967 LatticeTransferState &State) { 968 if (RecordStorageLocation *Loc = 969 getImplicitObjectLocation(*E, State.Env)) { 970 setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env); 971 } 972 }) 973 974 // optional::reset 975 .CaseOfCFGStmt<CXXMemberCallExpr>( 976 isOptionalMemberCallWithNameMatcher(hasName("reset")), 977 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 978 LatticeTransferState &State) { 979 if (RecordStorageLocation *Loc = 980 getImplicitObjectLocation(*E, State.Env)) { 981 setHasValue(*Loc, State.Env.getBoolLiteralValue(false), 982 State.Env); 983 } 984 }) 985 986 // optional::swap 987 .CaseOfCFGStmt<CXXMemberCallExpr>( 988 isOptionalMemberCallWithNameMatcher(hasName("swap")), 989 transferSwapCall) 990 991 // std::swap 992 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 993 994 // std::forward 995 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 996 997 // opt.value_or("").empty() 998 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 999 transferValueOrStringEmptyCall) 1000 1001 // opt.value_or(X) != X 1002 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 1003 1004 // Comparisons (==, !=): 1005 .CaseOfCFGStmt<CXXOperatorCallExpr>( 1006 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()), 1007 transferOptionalAndOptionalCmp) 1008 .CaseOfCFGStmt<CXXOperatorCallExpr>( 1009 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()), 1010 [](const clang::CXXOperatorCallExpr *Cmp, 1011 const MatchFinder::MatchResult &, LatticeTransferState &State) { 1012 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env); 1013 }) 1014 .CaseOfCFGStmt<CXXOperatorCallExpr>( 1015 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()), 1016 [](const clang::CXXOperatorCallExpr *Cmp, 1017 const MatchFinder::MatchResult &, LatticeTransferState &State) { 1018 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env); 1019 }) 1020 .CaseOfCFGStmt<CXXOperatorCallExpr>( 1021 isComparisonOperatorCall( 1022 hasOptionalType(), 1023 unless(anyOf(hasOptionalType(), hasNulloptType()))), 1024 [](const clang::CXXOperatorCallExpr *Cmp, 1025 const MatchFinder::MatchResult &, LatticeTransferState &State) { 1026 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 1027 }) 1028 .CaseOfCFGStmt<CXXOperatorCallExpr>( 1029 isComparisonOperatorCall( 1030 unless(anyOf(hasOptionalType(), hasNulloptType())), 1031 hasOptionalType()), 1032 [](const clang::CXXOperatorCallExpr *Cmp, 1033 const MatchFinder::MatchResult &, LatticeTransferState &State) { 1034 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 1035 }) 1036 1037 // Smart-pointer-like operator* and operator-> calls that may look like 1038 // const accessors (below) but need special handling to allow mixing 1039 // the accessor calls. 1040 .CaseOfCFGStmt<CXXOperatorCallExpr>( 1041 isSmartPointerLikeOperatorStar(), 1042 [](const CXXOperatorCallExpr *E, 1043 const MatchFinder::MatchResult &Result, 1044 LatticeTransferState &State) { 1045 transferSmartPointerLikeCachedDeref( 1046 E, 1047 dyn_cast_or_null<RecordStorageLocation>( 1048 getLocBehindPossiblePointer(*E->getArg(0), State.Env)), 1049 State, [](StorageLocation &Loc) {}); 1050 }) 1051 .CaseOfCFGStmt<CXXOperatorCallExpr>( 1052 isSmartPointerLikeOperatorArrow(), 1053 [](const CXXOperatorCallExpr *E, 1054 const MatchFinder::MatchResult &Result, 1055 LatticeTransferState &State) { 1056 transferSmartPointerLikeCachedGet( 1057 E, 1058 dyn_cast_or_null<RecordStorageLocation>( 1059 getLocBehindPossiblePointer(*E->getArg(0), State.Env)), 1060 State, [](StorageLocation &Loc) {}); 1061 }) 1062 .CaseOfCFGStmt<CXXMemberCallExpr>( 1063 isSmartPointerLikeValueMethodCall(), 1064 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result, 1065 LatticeTransferState &State) { 1066 transferSmartPointerLikeCachedDeref( 1067 E, getImplicitObjectLocation(*E, State.Env), State, 1068 [](StorageLocation &Loc) {}); 1069 }) 1070 .CaseOfCFGStmt<CXXMemberCallExpr>( 1071 isSmartPointerLikeGetMethodCall(), 1072 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result, 1073 LatticeTransferState &State) { 1074 transferSmartPointerLikeCachedGet( 1075 E, getImplicitObjectLocation(*E, State.Env), State, 1076 [](StorageLocation &Loc) {}); 1077 }) 1078 1079 // const accessor calls 1080 .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(), 1081 transferValue_ConstMemberCall) 1082 .CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(), 1083 transferValue_ConstMemberOperatorCall) 1084 // non-const member calls that may modify the state of an object. 1085 .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(), 1086 transferValue_NonConstMemberCall) 1087 .CaseOfCFGStmt<CXXOperatorCallExpr>( 1088 isNonConstMemberOperatorCall(), 1089 transferValue_NonConstMemberOperatorCall) 1090 1091 // other cases of returning optional 1092 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 1093 transferCallReturningOptional) 1094 1095 .Build(); 1096 } 1097 1098 llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr, 1099 const Environment &Env) { 1100 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 1101 getLocBehindPossiblePointer(*ObjectExpr, Env))) { 1102 auto *Prop = Env.getValue(locForHasValue(*OptionalLoc)); 1103 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 1104 if (Env.proves(HasValueVal->formula())) 1105 return {}; 1106 } 1107 } 1108 1109 // Record that this unwrap is *not* provably safe. 1110 // FIXME: include either the name of the optional (if applicable) or a source 1111 // range of the access for easier interpretation of the result. 1112 return {ObjectExpr->getBeginLoc()}; 1113 } 1114 1115 auto buildDiagnoseMatchSwitch( 1116 const UncheckedOptionalAccessModelOptions &Options) { 1117 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 1118 // lot of duplicated work (e.g. string comparisons), consider providing APIs 1119 // that avoid it through memoization. 1120 auto IgnorableOptional = ignorableOptional(Options); 1121 return CFGMatchSwitchBuilder<const Environment, 1122 llvm::SmallVector<SourceLocation>>() 1123 // optional::value 1124 .CaseOfCFGStmt<CXXMemberCallExpr>( 1125 valueCall(IgnorableOptional), 1126 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 1127 const Environment &Env) { 1128 return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env); 1129 }) 1130 1131 // optional::operator*, optional::operator-> 1132 .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 1133 [](const CallExpr *E, 1134 const MatchFinder::MatchResult &, 1135 const Environment &Env) { 1136 return diagnoseUnwrapCall(E->getArg(0), Env); 1137 }) 1138 .Build(); 1139 } 1140 1141 } // namespace 1142 1143 ast_matchers::DeclarationMatcher 1144 UncheckedOptionalAccessModel::optionalClassDecl() { 1145 return cxxRecordDecl(optionalClass()); 1146 } 1147 1148 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, 1149 Environment &Env) 1150 : DataflowAnalysis<UncheckedOptionalAccessModel, 1151 UncheckedOptionalAccessLattice>(Ctx), 1152 TransferMatchSwitch(buildTransferMatchSwitch()) { 1153 Env.getDataflowAnalysisContext().setSyntheticFieldCallback( 1154 [&Ctx](QualType Ty) -> llvm::StringMap<QualType> { 1155 const CXXRecordDecl *Optional = 1156 getOptionalBaseClass(Ty->getAsCXXRecordDecl()); 1157 if (Optional == nullptr) 1158 return {}; 1159 return {{"value", valueTypeFromOptionalDecl(*Optional)}, 1160 {"has_value", Ctx.BoolTy}}; 1161 }); 1162 } 1163 1164 void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 1165 UncheckedOptionalAccessLattice &L, 1166 Environment &Env) { 1167 LatticeTransferState State(L, Env); 1168 TransferMatchSwitch(Elt, getASTContext(), State); 1169 } 1170 1171 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 1172 UncheckedOptionalAccessModelOptions Options) 1173 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 1174 1175 } // namespace dataflow 1176 } // namespace clang 1177