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