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 isNonConstMemberCall() { 342 return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst())))); 343 } 344 345 auto isNonConstMemberOperatorCall() { 346 return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst())))); 347 } 348 349 auto isCallReturningOptional() { 350 return callExpr(hasType(qualType( 351 anyOf(desugarsToOptionalOrDerivedType(), 352 referenceType(pointee(desugarsToOptionalOrDerivedType())))))); 353 } 354 355 template <typename L, typename R> 356 auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) { 357 return cxxOperatorCallExpr( 358 anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")), 359 argumentCountIs(2), hasArgument(0, lhs_arg_matcher), 360 hasArgument(1, rhs_arg_matcher)); 361 } 362 363 /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula. 364 const Formula &forceBoolValue(Environment &Env, const Expr &Expr) { 365 auto *Value = Env.get<BoolValue>(Expr); 366 if (Value != nullptr) 367 return Value->formula(); 368 369 Value = &Env.makeAtomicBoolValue(); 370 Env.setValue(Expr, *Value); 371 return Value->formula(); 372 } 373 374 StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) { 375 return OptionalLoc.getSyntheticField("has_value"); 376 } 377 378 StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) { 379 return OptionalLoc.getSyntheticField("value"); 380 } 381 382 /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 383 /// property of the optional at `OptionalLoc`. 384 void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal, 385 Environment &Env) { 386 Env.setValue(locForHasValue(OptionalLoc), HasValueVal); 387 } 388 389 /// Returns the symbolic value that represents the "has_value" property of the 390 /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null. 391 BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) { 392 if (OptionalLoc == nullptr) 393 return nullptr; 394 StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc); 395 auto *HasValueVal = Env.get<BoolValue>(HasValueLoc); 396 if (HasValueVal == nullptr) { 397 HasValueVal = &Env.makeAtomicBoolValue(); 398 Env.setValue(HasValueLoc, *HasValueVal); 399 } 400 return HasValueVal; 401 } 402 403 QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) { 404 auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD); 405 return CTSD.getTemplateArgs()[0].getAsType(); 406 } 407 408 /// Returns the number of optional wrappers in `Type`. 409 /// 410 /// For example, if `Type` is `optional<optional<int>>`, the result of this 411 /// function will be 2. 412 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 413 const CXXRecordDecl *Optional = 414 getOptionalBaseClass(Type->getAsCXXRecordDecl()); 415 if (Optional == nullptr) 416 return 0; 417 return 1 + countOptionalWrappers( 418 ASTCtx, 419 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx)); 420 } 421 422 StorageLocation *getLocBehindPossiblePointer(const Expr &E, 423 const Environment &Env) { 424 if (E.isPRValue()) { 425 if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E))) 426 return &PointerVal->getPointeeLoc(); 427 return nullptr; 428 } 429 return Env.getStorageLocation(E); 430 } 431 432 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 433 LatticeTransferState &State) { 434 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 435 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) { 436 if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr) 437 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc)); 438 } 439 } 440 441 void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 442 LatticeTransferState &State) { 443 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 444 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) 445 State.Env.setValue( 446 *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc))); 447 } 448 449 void transferMakeOptionalCall(const CallExpr *E, 450 const MatchFinder::MatchResult &, 451 LatticeTransferState &State) { 452 setHasValue(State.Env.getResultObjectLocation(*E), 453 State.Env.getBoolLiteralValue(true), State.Env); 454 } 455 456 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 457 const MatchFinder::MatchResult &, 458 LatticeTransferState &State) { 459 if (auto *HasValueVal = getHasValue( 460 State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 461 State.Env.setValue(*CallExpr, *HasValueVal); 462 } 463 } 464 465 void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr, 466 const MatchFinder::MatchResult &, 467 LatticeTransferState &State) { 468 if (auto *HasValueVal = getHasValue( 469 State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { 470 State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal)); 471 } 472 } 473 474 /// `ModelPred` builds a logical formula relating the predicate in 475 /// `ValueOrPredExpr` to the optional's `has_value` property. 476 void transferValueOrImpl( 477 const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result, 478 LatticeTransferState &State, 479 const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal, 480 const Formula &HasValueVal)) { 481 auto &Env = State.Env; 482 483 const auto *MCE = 484 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID); 485 486 auto *HasValueVal = 487 getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env)); 488 if (HasValueVal == nullptr) 489 return; 490 491 Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), 492 HasValueVal->formula())); 493 } 494 495 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 496 const MatchFinder::MatchResult &Result, 497 LatticeTransferState &State) { 498 return transferValueOrImpl(ComparisonExpr, Result, State, 499 [](Environment &Env, const Formula &ExprVal, 500 const Formula &HasValueVal) -> const Formula & { 501 auto &A = Env.arena(); 502 // If the result is *not* empty, then we know the 503 // optional must have been holding a value. If 504 // `ExprVal` is true, though, we don't learn 505 // anything definite about `has_value`, so we 506 // don't add any corresponding implications to 507 // the flow condition. 508 return A.makeImplies(A.makeNot(ExprVal), 509 HasValueVal); 510 }); 511 } 512 513 void transferValueOrNotEqX(const Expr *ComparisonExpr, 514 const MatchFinder::MatchResult &Result, 515 LatticeTransferState &State) { 516 transferValueOrImpl(ComparisonExpr, Result, State, 517 [](Environment &Env, const Formula &ExprVal, 518 const Formula &HasValueVal) -> const Formula & { 519 auto &A = Env.arena(); 520 // We know that if `(opt.value_or(X) != X)` then 521 // `opt.hasValue()`, even without knowing further 522 // details about the contents of `opt`. 523 return A.makeImplies(ExprVal, HasValueVal); 524 }); 525 } 526 527 void transferCallReturningOptional(const CallExpr *E, 528 const MatchFinder::MatchResult &Result, 529 LatticeTransferState &State) { 530 RecordStorageLocation *Loc = nullptr; 531 if (E->isPRValue()) { 532 Loc = &State.Env.getResultObjectLocation(*E); 533 } else { 534 Loc = State.Env.get<RecordStorageLocation>(*E); 535 if (Loc == nullptr) { 536 Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E)); 537 State.Env.setStorageLocation(*E, *Loc); 538 } 539 } 540 541 if (State.Env.getValue(locForHasValue(*Loc)) != nullptr) 542 return; 543 544 setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env); 545 } 546 547 void handleConstMemberCall(const CallExpr *CE, 548 dataflow::RecordStorageLocation *RecordLoc, 549 const MatchFinder::MatchResult &Result, 550 LatticeTransferState &State) { 551 // If the const method returns an optional or reference to an optional. 552 if (RecordLoc != nullptr && isSupportedOptionalType(CE->getType())) { 553 StorageLocation *Loc = 554 State.Lattice.getOrCreateConstMethodReturnStorageLocation( 555 *RecordLoc, CE, State.Env, [&](StorageLocation &Loc) { 556 setHasValue(cast<RecordStorageLocation>(Loc), 557 State.Env.makeAtomicBoolValue(), State.Env); 558 }); 559 if (Loc == nullptr) 560 return; 561 if (CE->isGLValue()) { 562 // If the call to the const method returns a reference to an optional, 563 // link the call expression to the cached StorageLocation. 564 State.Env.setStorageLocation(*CE, *Loc); 565 } else { 566 // If the call to the const method returns an optional by value, we 567 // need to use CopyRecord to link the optional to the result object 568 // of the call expression. 569 auto &ResultLoc = State.Env.getResultObjectLocation(*CE); 570 copyRecord(*cast<RecordStorageLocation>(Loc), ResultLoc, State.Env); 571 } 572 return; 573 } 574 575 // Cache if the const method returns a boolean type. 576 // We may decide to cache other return types in the future. 577 if (RecordLoc != nullptr && CE->getType()->isBooleanType()) { 578 Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE, 579 State.Env); 580 if (Val == nullptr) 581 return; 582 State.Env.setValue(*CE, *Val); 583 return; 584 } 585 586 // Perform default handling if the call returns an optional 587 // but wasn't handled above (if RecordLoc is nullptr). 588 if (isSupportedOptionalType(CE->getType())) { 589 transferCallReturningOptional(CE, Result, State); 590 } 591 } 592 593 void transferValue_ConstMemberCall(const CXXMemberCallExpr *MCE, 594 const MatchFinder::MatchResult &Result, 595 LatticeTransferState &State) { 596 handleConstMemberCall( 597 MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State); 598 } 599 600 void handleNonConstMemberCall(const CallExpr *CE, 601 dataflow::RecordStorageLocation *RecordLoc, 602 const MatchFinder::MatchResult &Result, 603 LatticeTransferState &State) { 604 if (RecordLoc != nullptr) { 605 // When a non-const member function is called, clear all (non-const) 606 // optional fields of the receiver. Const-qualified fields can't be 607 // changed (at least, not without UB). 608 for (const auto &[Field, FieldLoc] : RecordLoc->children()) { 609 QualType FieldType = Field->getType(); 610 if (!FieldType.isConstQualified() && 611 isSupportedOptionalType(Field->getType())) { 612 auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc); 613 if (FieldRecordLoc) { 614 setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(), 615 State.Env); 616 } 617 } 618 } 619 State.Lattice.clearConstMethodReturnValues(*RecordLoc); 620 State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc); 621 } 622 623 // Perform default handling if the call returns an optional. 624 if (isSupportedOptionalType(CE->getType())) { 625 transferCallReturningOptional(CE, Result, State); 626 } 627 } 628 629 void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE, 630 const MatchFinder::MatchResult &Result, 631 LatticeTransferState &State) { 632 handleNonConstMemberCall( 633 MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State); 634 } 635 636 void transferValue_NonConstMemberOperatorCall( 637 const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result, 638 LatticeTransferState &State) { 639 auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>( 640 State.Env.getStorageLocation(*OCE->getArg(0))); 641 handleNonConstMemberCall(OCE, RecordLoc, Result, State); 642 } 643 644 void constructOptionalValue(const Expr &E, Environment &Env, 645 BoolValue &HasValueVal) { 646 RecordStorageLocation &Loc = Env.getResultObjectLocation(E); 647 setHasValue(Loc, HasValueVal, Env); 648 } 649 650 /// Returns a symbolic value for the "has_value" property of an `optional<T>` 651 /// value that is constructed/assigned from a value of type `U` or `optional<U>` 652 /// where `T` is constructible from `U`. 653 BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E, 654 const MatchFinder::MatchResult &MatchRes, 655 LatticeTransferState &State) { 656 const int DestTypeOptionalWrappersCount = 657 countOptionalWrappers(*MatchRes.Context, DestType); 658 const int ArgTypeOptionalWrappersCount = countOptionalWrappers( 659 *MatchRes.Context, E.getType().getNonReferenceType()); 660 661 // Is this an constructor of the form `template<class U> optional(U &&)` / 662 // assignment of the form `template<class U> optional& operator=(U &&)` 663 // (where `T` is assignable / constructible from `U`)? 664 // We recognize this because the number of optionals in the optional being 665 // assigned to is different from the function argument type. 666 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount) 667 return State.Env.getBoolLiteralValue(true); 668 669 // Otherwise, this must be a constructor of the form 670 // `template <class U> optional<optional<U> &&)` / assignment of the form 671 // `template <class U> optional& operator=(optional<U> &&) 672 // (where, again, `T` is assignable / constructible from `U`). 673 auto *Loc = State.Env.get<RecordStorageLocation>(E); 674 if (auto *HasValueVal = getHasValue(State.Env, Loc)) 675 return *HasValueVal; 676 return State.Env.makeAtomicBoolValue(); 677 } 678 679 void transferValueOrConversionConstructor( 680 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 681 LatticeTransferState &State) { 682 assert(E->getNumArgs() > 0); 683 684 constructOptionalValue( 685 *E, State.Env, 686 valueOrConversionHasValue( 687 E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0), 688 MatchRes, State)); 689 } 690 691 void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 692 LatticeTransferState &State) { 693 assert(E->getNumArgs() > 0); 694 695 if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) { 696 setHasValue(*Loc, HasValueVal, State.Env); 697 698 // Assign a storage location for the whole expression. 699 State.Env.setStorageLocation(*E, *Loc); 700 } 701 } 702 703 void transferValueOrConversionAssignment( 704 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 705 LatticeTransferState &State) { 706 assert(E->getNumArgs() > 1); 707 transferAssignment( 708 E, 709 valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(), 710 *E->getArg(1), MatchRes, State), 711 State); 712 } 713 714 void transferNulloptAssignment(const CXXOperatorCallExpr *E, 715 const MatchFinder::MatchResult &, 716 LatticeTransferState &State) { 717 transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 718 } 719 720 void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2, 721 Environment &Env) { 722 // We account for cases where one or both of the optionals are not modeled, 723 // either lacking associated storage locations, or lacking values associated 724 // to such storage locations. 725 726 if (Loc1 == nullptr) { 727 if (Loc2 != nullptr) 728 setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env); 729 return; 730 } 731 if (Loc2 == nullptr) { 732 setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env); 733 return; 734 } 735 736 // Both expressions have locations, though they may not have corresponding 737 // values. In that case, we create a fresh value at this point. Note that if 738 // two branches both do this, they will not share the value, but it at least 739 // allows for local reasoning about the value. To avoid the above, we would 740 // need *lazy* value allocation. 741 // FIXME: allocate values lazily, instead of just creating a fresh value. 742 BoolValue *BoolVal1 = getHasValue(Env, Loc1); 743 if (BoolVal1 == nullptr) 744 BoolVal1 = &Env.makeAtomicBoolValue(); 745 746 BoolValue *BoolVal2 = getHasValue(Env, Loc2); 747 if (BoolVal2 == nullptr) 748 BoolVal2 = &Env.makeAtomicBoolValue(); 749 750 setHasValue(*Loc1, *BoolVal2, Env); 751 setHasValue(*Loc2, *BoolVal1, Env); 752 } 753 754 void transferSwapCall(const CXXMemberCallExpr *E, 755 const MatchFinder::MatchResult &, 756 LatticeTransferState &State) { 757 assert(E->getNumArgs() == 1); 758 auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 759 transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env); 760 } 761 762 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 763 LatticeTransferState &State) { 764 assert(E->getNumArgs() == 2); 765 auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0)); 766 auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1)); 767 transferSwap(Arg0Loc, Arg1Loc, State.Env); 768 } 769 770 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &, 771 LatticeTransferState &State) { 772 assert(E->getNumArgs() == 1); 773 774 if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0))) 775 State.Env.setStorageLocation(*E, *Loc); 776 } 777 778 const Formula &evaluateEquality(Arena &A, const Formula &EqVal, 779 const Formula &LHS, const Formula &RHS) { 780 // Logically, an optional<T> object is composed of two values - a `has_value` 781 // bit and a value of type T. Equality of optional objects compares both 782 // values. Therefore, merely comparing the `has_value` bits isn't sufficient: 783 // when two optional objects are engaged, the equality of their respective 784 // values of type T matters. Since we only track the `has_value` bits, we 785 // can't make any conclusions about equality when we know that two optional 786 // objects are engaged. 787 // 788 // We express this as two facts about the equality: 789 // a) EqVal => (LHS & RHS) v (!RHS & !LHS) 790 // If they are equal, then either both are set or both are unset. 791 // b) (!LHS & !RHS) => EqVal 792 // If neither is set, then they are equal. 793 // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula. 794 return A.makeAnd( 795 A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS), 796 A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))), 797 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS))); 798 } 799 800 void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, 801 const MatchFinder::MatchResult &, 802 LatticeTransferState &State) { 803 Environment &Env = State.Env; 804 auto &A = Env.arena(); 805 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 806 auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0)); 807 if (auto *LHasVal = getHasValue(Env, Arg0Loc)) { 808 auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1)); 809 if (auto *RHasVal = getHasValue(Env, Arg1Loc)) { 810 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 811 CmpValue = &A.makeNot(*CmpValue); 812 Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(), 813 RHasVal->formula())); 814 } 815 } 816 } 817 818 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, 819 const clang::Expr *E, Environment &Env) { 820 auto &A = Env.arena(); 821 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 822 auto *Loc = Env.get<RecordStorageLocation>(*E); 823 if (auto *HasVal = getHasValue(Env, Loc)) { 824 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 825 CmpValue = &A.makeNot(*CmpValue); 826 Env.assume( 827 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true))); 828 } 829 } 830 831 void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr, 832 const clang::Expr *E, Environment &Env) { 833 auto &A = Env.arena(); 834 auto *CmpValue = &forceBoolValue(Env, *CmpExpr); 835 auto *Loc = Env.get<RecordStorageLocation>(*E); 836 if (auto *HasVal = getHasValue(Env, Loc)) { 837 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) 838 CmpValue = &A.makeNot(*CmpValue); 839 Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(), 840 A.makeLiteral(false))); 841 } 842 } 843 844 std::optional<StatementMatcher> 845 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 846 if (Options.IgnoreSmartPointerDereference) { 847 auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr( 848 anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")), 849 unless(hasArgument(0, expr(hasOptionalType())))))); 850 return expr( 851 anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse)))); 852 } 853 return std::nullopt; 854 } 855 856 StatementMatcher 857 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) { 858 return isOptionalMemberCallWithNameMatcher(hasName("value"), 859 IgnorableOptional); 860 } 861 862 StatementMatcher 863 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) { 864 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 865 isOptionalOperatorCallWithName("->", IgnorableOptional))); 866 } 867 868 auto buildTransferMatchSwitch() { 869 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 870 // lot of duplicated work (e.g. string comparisons), consider providing APIs 871 // that avoid it through memoization. 872 return CFGMatchSwitchBuilder<LatticeTransferState>() 873 // make_optional 874 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 875 876 // optional::optional (in place) 877 .CaseOfCFGStmt<CXXConstructExpr>( 878 isOptionalInPlaceConstructor(), 879 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 880 LatticeTransferState &State) { 881 constructOptionalValue(*E, State.Env, 882 State.Env.getBoolLiteralValue(true)); 883 }) 884 // optional::optional(nullopt_t) 885 .CaseOfCFGStmt<CXXConstructExpr>( 886 isOptionalNulloptConstructor(), 887 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 888 LatticeTransferState &State) { 889 constructOptionalValue(*E, State.Env, 890 State.Env.getBoolLiteralValue(false)); 891 }) 892 // optional::optional (value/conversion) 893 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 894 transferValueOrConversionConstructor) 895 896 // optional::operator= 897 .CaseOfCFGStmt<CXXOperatorCallExpr>( 898 isOptionalValueOrConversionAssignment(), 899 transferValueOrConversionAssignment) 900 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 901 transferNulloptAssignment) 902 903 // optional::value 904 .CaseOfCFGStmt<CXXMemberCallExpr>( 905 valueCall(std::nullopt), 906 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 907 LatticeTransferState &State) { 908 transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 909 }) 910 911 // optional::operator* 912 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"), 913 [](const CallExpr *E, 914 const MatchFinder::MatchResult &, 915 LatticeTransferState &State) { 916 transferUnwrapCall(E, E->getArg(0), State); 917 }) 918 919 // optional::operator-> 920 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"), 921 [](const CallExpr *E, 922 const MatchFinder::MatchResult &, 923 LatticeTransferState &State) { 924 transferArrowOpCall(E, E->getArg(0), State); 925 }) 926 927 // optional::has_value, optional::hasValue 928 // Of the supported optionals only folly::Optional uses hasValue, but this 929 // will also pass for other types 930 .CaseOfCFGStmt<CXXMemberCallExpr>( 931 isOptionalMemberCallWithNameMatcher( 932 hasAnyName("has_value", "hasValue")), 933 transferOptionalHasValueCall) 934 935 // optional::operator bool 936 .CaseOfCFGStmt<CXXMemberCallExpr>( 937 isOptionalMemberCallWithNameMatcher(hasName("operator bool")), 938 transferOptionalHasValueCall) 939 940 // NullableValue::isNull 941 // Only NullableValue has isNull 942 .CaseOfCFGStmt<CXXMemberCallExpr>( 943 isOptionalMemberCallWithNameMatcher(hasName("isNull")), 944 transferOptionalIsNullCall) 945 946 // optional::emplace 947 .CaseOfCFGStmt<CXXMemberCallExpr>( 948 isOptionalMemberCallWithNameMatcher(hasName("emplace")), 949 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 950 LatticeTransferState &State) { 951 if (RecordStorageLocation *Loc = 952 getImplicitObjectLocation(*E, State.Env)) { 953 setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env); 954 } 955 }) 956 957 // optional::reset 958 .CaseOfCFGStmt<CXXMemberCallExpr>( 959 isOptionalMemberCallWithNameMatcher(hasName("reset")), 960 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 961 LatticeTransferState &State) { 962 if (RecordStorageLocation *Loc = 963 getImplicitObjectLocation(*E, State.Env)) { 964 setHasValue(*Loc, State.Env.getBoolLiteralValue(false), 965 State.Env); 966 } 967 }) 968 969 // optional::swap 970 .CaseOfCFGStmt<CXXMemberCallExpr>( 971 isOptionalMemberCallWithNameMatcher(hasName("swap")), 972 transferSwapCall) 973 974 // std::swap 975 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall) 976 977 // std::forward 978 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall) 979 980 // opt.value_or("").empty() 981 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(), 982 transferValueOrStringEmptyCall) 983 984 // opt.value_or(X) != X 985 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 986 987 // Comparisons (==, !=): 988 .CaseOfCFGStmt<CXXOperatorCallExpr>( 989 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()), 990 transferOptionalAndOptionalCmp) 991 .CaseOfCFGStmt<CXXOperatorCallExpr>( 992 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()), 993 [](const clang::CXXOperatorCallExpr *Cmp, 994 const MatchFinder::MatchResult &, LatticeTransferState &State) { 995 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env); 996 }) 997 .CaseOfCFGStmt<CXXOperatorCallExpr>( 998 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()), 999 [](const clang::CXXOperatorCallExpr *Cmp, 1000 const MatchFinder::MatchResult &, LatticeTransferState &State) { 1001 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env); 1002 }) 1003 .CaseOfCFGStmt<CXXOperatorCallExpr>( 1004 isComparisonOperatorCall( 1005 hasOptionalType(), 1006 unless(anyOf(hasOptionalType(), hasNulloptType()))), 1007 [](const clang::CXXOperatorCallExpr *Cmp, 1008 const MatchFinder::MatchResult &, LatticeTransferState &State) { 1009 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); 1010 }) 1011 .CaseOfCFGStmt<CXXOperatorCallExpr>( 1012 isComparisonOperatorCall( 1013 unless(anyOf(hasOptionalType(), hasNulloptType())), 1014 hasOptionalType()), 1015 [](const clang::CXXOperatorCallExpr *Cmp, 1016 const MatchFinder::MatchResult &, LatticeTransferState &State) { 1017 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); 1018 }) 1019 1020 // const accessor calls 1021 .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(), 1022 transferValue_ConstMemberCall) 1023 // non-const member calls that may modify the state of an object. 1024 .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(), 1025 transferValue_NonConstMemberCall) 1026 .CaseOfCFGStmt<CXXOperatorCallExpr>( 1027 isNonConstMemberOperatorCall(), 1028 transferValue_NonConstMemberOperatorCall) 1029 1030 // other cases of returning optional 1031 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(), 1032 transferCallReturningOptional) 1033 1034 .Build(); 1035 } 1036 1037 llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr, 1038 const Environment &Env) { 1039 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>( 1040 getLocBehindPossiblePointer(*ObjectExpr, Env))) { 1041 auto *Prop = Env.getValue(locForHasValue(*OptionalLoc)); 1042 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 1043 if (Env.proves(HasValueVal->formula())) 1044 return {}; 1045 } 1046 } 1047 1048 // Record that this unwrap is *not* provably safe. 1049 // FIXME: include either the name of the optional (if applicable) or a source 1050 // range of the access for easier interpretation of the result. 1051 return {ObjectExpr->getBeginLoc()}; 1052 } 1053 1054 auto buildDiagnoseMatchSwitch( 1055 const UncheckedOptionalAccessModelOptions &Options) { 1056 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 1057 // lot of duplicated work (e.g. string comparisons), consider providing APIs 1058 // that avoid it through memoization. 1059 auto IgnorableOptional = ignorableOptional(Options); 1060 return CFGMatchSwitchBuilder<const Environment, 1061 llvm::SmallVector<SourceLocation>>() 1062 // optional::value 1063 .CaseOfCFGStmt<CXXMemberCallExpr>( 1064 valueCall(IgnorableOptional), 1065 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 1066 const Environment &Env) { 1067 return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env); 1068 }) 1069 1070 // optional::operator*, optional::operator-> 1071 .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional), 1072 [](const CallExpr *E, 1073 const MatchFinder::MatchResult &, 1074 const Environment &Env) { 1075 return diagnoseUnwrapCall(E->getArg(0), Env); 1076 }) 1077 .Build(); 1078 } 1079 1080 } // namespace 1081 1082 ast_matchers::DeclarationMatcher 1083 UncheckedOptionalAccessModel::optionalClassDecl() { 1084 return cxxRecordDecl(optionalClass()); 1085 } 1086 1087 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, 1088 Environment &Env) 1089 : DataflowAnalysis<UncheckedOptionalAccessModel, 1090 UncheckedOptionalAccessLattice>(Ctx), 1091 TransferMatchSwitch(buildTransferMatchSwitch()) { 1092 Env.getDataflowAnalysisContext().setSyntheticFieldCallback( 1093 [&Ctx](QualType Ty) -> llvm::StringMap<QualType> { 1094 const CXXRecordDecl *Optional = 1095 getOptionalBaseClass(Ty->getAsCXXRecordDecl()); 1096 if (Optional == nullptr) 1097 return {}; 1098 return {{"value", valueTypeFromOptionalDecl(*Optional)}, 1099 {"has_value", Ctx.BoolTy}}; 1100 }); 1101 } 1102 1103 void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, 1104 UncheckedOptionalAccessLattice &L, 1105 Environment &Env) { 1106 LatticeTransferState State(L, Env); 1107 TransferMatchSwitch(Elt, getASTContext(), State); 1108 } 1109 1110 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( 1111 UncheckedOptionalAccessModelOptions Options) 1112 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} 1113 1114 } // namespace dataflow 1115 } // namespace clang 1116