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/ASTMatchers/ASTMatchers.h" 21 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 22 #include "clang/Analysis/FlowSensitive/MatchSwitch.h" 23 #include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h" 24 #include "clang/Analysis/FlowSensitive/Value.h" 25 #include "llvm/ADT/StringRef.h" 26 #include "llvm/Support/Casting.h" 27 #include <cassert> 28 #include <memory> 29 #include <utility> 30 31 namespace clang { 32 namespace dataflow { 33 namespace { 34 35 using namespace ::clang::ast_matchers; 36 using LatticeTransferState = TransferState<SourceLocationsLattice>; 37 38 DeclarationMatcher optionalClass() { 39 return classTemplateSpecializationDecl( 40 anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"), 41 hasName("__optional_destruct_base"), hasName("absl::optional"), 42 hasName("base::Optional")), 43 hasTemplateArgument(0, refersToType(type().bind("T")))); 44 } 45 46 auto hasOptionalType() { return hasType(optionalClass()); } 47 48 auto hasOptionalOrAliasType() { 49 return hasUnqualifiedDesugaredType( 50 recordType(hasDeclaration(optionalClass()))); 51 } 52 53 auto isOptionalMemberCallWithName( 54 llvm::StringRef MemberName, 55 llvm::Optional<StatementMatcher> Ignorable = llvm::None) { 56 auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr())) 57 : cxxThisExpr()); 58 return cxxMemberCallExpr( 59 on(expr(Exception)), 60 callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass())))); 61 } 62 63 auto isOptionalOperatorCallWithName( 64 llvm::StringRef operator_name, 65 llvm::Optional<StatementMatcher> Ignorable = llvm::None) { 66 return cxxOperatorCallExpr( 67 hasOverloadedOperatorName(operator_name), 68 callee(cxxMethodDecl(ofClass(optionalClass()))), 69 Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 70 } 71 72 auto isMakeOptionalCall() { 73 return callExpr( 74 callee(functionDecl(hasAnyName( 75 "std::make_optional", "base::make_optional", "absl::make_optional"))), 76 hasOptionalType()); 77 } 78 79 auto hasNulloptType() { 80 return hasType(namedDecl( 81 hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t"))); 82 } 83 84 auto inPlaceClass() { 85 return recordDecl( 86 hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t")); 87 } 88 89 auto isOptionalNulloptConstructor() { 90 return cxxConstructExpr(hasOptionalType(), argumentCountIs(1), 91 hasArgument(0, hasNulloptType())); 92 } 93 94 auto isOptionalInPlaceConstructor() { 95 return cxxConstructExpr(hasOptionalType(), 96 hasArgument(0, hasType(inPlaceClass()))); 97 } 98 99 auto isOptionalValueOrConversionConstructor() { 100 return cxxConstructExpr( 101 hasOptionalType(), 102 unless(hasDeclaration( 103 cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 104 argumentCountIs(1), hasArgument(0, unless(hasNulloptType()))); 105 } 106 107 auto isOptionalValueOrConversionAssignment() { 108 return cxxOperatorCallExpr( 109 hasOverloadedOperatorName("="), 110 callee(cxxMethodDecl(ofClass(optionalClass()))), 111 unless(hasDeclaration(cxxMethodDecl( 112 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 113 argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 114 } 115 116 auto isOptionalNulloptAssignment() { 117 return cxxOperatorCallExpr(hasOverloadedOperatorName("="), 118 callee(cxxMethodDecl(ofClass(optionalClass()))), 119 argumentCountIs(2), 120 hasArgument(1, hasNulloptType())); 121 } 122 123 auto isStdSwapCall() { 124 return callExpr(callee(functionDecl(hasName("std::swap"))), 125 argumentCountIs(2), hasArgument(0, hasOptionalType()), 126 hasArgument(1, hasOptionalType())); 127 } 128 129 constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 130 131 auto isValueOrStringEmptyCall() { 132 // `opt.value_or("").empty()` 133 return cxxMemberCallExpr( 134 callee(cxxMethodDecl(hasName("empty"))), 135 onImplicitObjectArgument(ignoringImplicit( 136 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 137 callee(cxxMethodDecl(hasName("value_or"), 138 ofClass(optionalClass()))), 139 hasArgument(0, stringLiteral(hasSize(0)))) 140 .bind(ValueOrCallID)))); 141 } 142 143 auto isValueOrNotEqX() { 144 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 145 return hasOperands( 146 ignoringImplicit( 147 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 148 callee(cxxMethodDecl(hasName("value_or"), 149 ofClass(optionalClass()))), 150 hasArgument(0, Arg)) 151 .bind(ValueOrCallID)), 152 ignoringImplicit(Arg)); 153 }; 154 155 // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 156 // support this pattern for any expression, but the AST does not have a 157 // generic expression comparison facility, so we specialize to common cases 158 // seen in practice. FIXME: define a matcher that compares values across 159 // nodes, which would let us generalize this to any `X`. 160 return binaryOperation(hasOperatorName("!="), 161 anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 162 ComparesToSame(stringLiteral(hasSize(0))), 163 ComparesToSame(integerLiteral(equals(0))))); 164 } 165 166 auto isCallReturningOptional() { 167 return callExpr(callee(functionDecl( 168 returns(anyOf(hasOptionalOrAliasType(), 169 referenceType(pointee(hasOptionalOrAliasType()))))))); 170 } 171 172 /// Creates a symbolic value for an `optional` value using `HasValueVal` as the 173 /// symbolic value of its "has_value" property. 174 StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) { 175 auto OptionalVal = std::make_unique<StructValue>(); 176 OptionalVal->setProperty("has_value", HasValueVal); 177 return Env.takeOwnership(std::move(OptionalVal)); 178 } 179 180 /// Returns the symbolic value that represents the "has_value" property of the 181 /// optional value `Val`. Returns null if `Val` is null. 182 BoolValue *getHasValue(Value *Val) { 183 if (auto *OptionalVal = cast_or_null<StructValue>(Val)) { 184 return cast<BoolValue>(OptionalVal->getProperty("has_value")); 185 } 186 return nullptr; 187 } 188 189 /// If `Type` is a reference type, returns the type of its pointee. Otherwise, 190 /// returns `Type` itself. 191 QualType stripReference(QualType Type) { 192 return Type->isReferenceType() ? Type->getPointeeType() : Type; 193 } 194 195 /// Returns true if and only if `Type` is an optional type. 196 bool IsOptionalType(QualType Type) { 197 if (!Type->isRecordType()) 198 return false; 199 // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call. 200 auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString(); 201 return TypeName == "std::optional" || TypeName == "absl::optional" || 202 TypeName == "base::Optional"; 203 } 204 205 /// Returns the number of optional wrappers in `Type`. 206 /// 207 /// For example, if `Type` is `optional<optional<int>>`, the result of this 208 /// function will be 2. 209 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 210 if (!IsOptionalType(Type)) 211 return 0; 212 return 1 + countOptionalWrappers( 213 ASTCtx, 214 cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) 215 ->getTemplateArgs() 216 .get(0) 217 .getAsType() 218 .getDesugaredType(ASTCtx)); 219 } 220 221 void initializeOptionalReference(const Expr *OptionalExpr, 222 const MatchFinder::MatchResult &, 223 LatticeTransferState &State) { 224 if (auto *OptionalVal = cast_or_null<StructValue>( 225 State.Env.getValue(*OptionalExpr, SkipPast::Reference))) { 226 if (OptionalVal->getProperty("has_value") == nullptr) { 227 OptionalVal->setProperty("has_value", State.Env.makeAtomicBoolValue()); 228 } 229 } 230 } 231 232 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 233 LatticeTransferState &State) { 234 if (auto *OptionalVal = cast_or_null<StructValue>( 235 State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer))) { 236 auto *HasValueVal = getHasValue(OptionalVal); 237 assert(HasValueVal != nullptr); 238 239 if (State.Env.flowConditionImplies(*HasValueVal)) 240 return; 241 } 242 243 // Record that this unwrap is *not* provably safe. 244 // FIXME: include either the name of the optional (if applicable) or a source 245 // range of the access for easier interpretation of the result. 246 State.Lattice.getSourceLocations().insert(ObjectExpr->getBeginLoc()); 247 } 248 249 void transferMakeOptionalCall(const CallExpr *E, 250 const MatchFinder::MatchResult &, 251 LatticeTransferState &State) { 252 auto &Loc = State.Env.createStorageLocation(*E); 253 State.Env.setStorageLocation(*E, Loc); 254 State.Env.setValue( 255 Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true))); 256 } 257 258 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 259 const MatchFinder::MatchResult &, 260 LatticeTransferState &State) { 261 if (auto *OptionalVal = cast_or_null<StructValue>( 262 State.Env.getValue(*CallExpr->getImplicitObjectArgument(), 263 SkipPast::ReferenceThenPointer))) { 264 auto *HasValueVal = getHasValue(OptionalVal); 265 assert(HasValueVal != nullptr); 266 267 auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr); 268 State.Env.setValue(CallExprLoc, *HasValueVal); 269 State.Env.setStorageLocation(*CallExpr, CallExprLoc); 270 } 271 } 272 273 /// `ModelPred` builds a logical formula relating the predicate in 274 /// `ValueOrPredExpr` to the optional's `has_value` property. 275 void transferValueOrImpl(const clang::Expr *ValueOrPredExpr, 276 const MatchFinder::MatchResult &Result, 277 LatticeTransferState &State, 278 BoolValue &(*ModelPred)(Environment &Env, 279 BoolValue &ExprVal, 280 BoolValue &HasValueVal)) { 281 auto &Env = State.Env; 282 283 const auto *ObjectArgumentExpr = 284 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) 285 ->getImplicitObjectArgument(); 286 287 auto *OptionalVal = cast_or_null<StructValue>( 288 Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer)); 289 if (OptionalVal == nullptr) 290 return; 291 auto *HasValueVal = getHasValue(OptionalVal); 292 assert(HasValueVal != nullptr); 293 294 auto *ExprValue = cast_or_null<BoolValue>( 295 State.Env.getValue(*ValueOrPredExpr, SkipPast::None)); 296 if (ExprValue == nullptr) { 297 auto &ExprLoc = State.Env.createStorageLocation(*ValueOrPredExpr); 298 ExprValue = &State.Env.makeAtomicBoolValue(); 299 State.Env.setValue(ExprLoc, *ExprValue); 300 State.Env.setStorageLocation(*ValueOrPredExpr, ExprLoc); 301 } 302 303 Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal)); 304 } 305 306 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 307 const MatchFinder::MatchResult &Result, 308 LatticeTransferState &State) { 309 return transferValueOrImpl(ComparisonExpr, Result, State, 310 [](Environment &Env, BoolValue &ExprVal, 311 BoolValue &HasValueVal) -> BoolValue & { 312 // If the result is *not* empty, then we know the 313 // optional must have been holding a value. If 314 // `ExprVal` is true, though, we don't learn 315 // anything definite about `has_value`, so we 316 // don't add any corresponding implications to 317 // the flow condition. 318 return Env.makeImplication(Env.makeNot(ExprVal), 319 HasValueVal); 320 }); 321 } 322 323 void transferValueOrNotEqX(const Expr *ComparisonExpr, 324 const MatchFinder::MatchResult &Result, 325 LatticeTransferState &State) { 326 transferValueOrImpl(ComparisonExpr, Result, State, 327 [](Environment &Env, BoolValue &ExprVal, 328 BoolValue &HasValueVal) -> BoolValue & { 329 // We know that if `(opt.value_or(X) != X)` then 330 // `opt.hasValue()`, even without knowing further 331 // details about the contents of `opt`. 332 return Env.makeImplication(ExprVal, HasValueVal); 333 }); 334 } 335 336 void transferCallReturningOptional(const CallExpr *E, 337 const MatchFinder::MatchResult &Result, 338 LatticeTransferState &State) { 339 if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) 340 return; 341 342 auto &Loc = State.Env.createStorageLocation(*E); 343 State.Env.setStorageLocation(*E, Loc); 344 State.Env.setValue( 345 Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue())); 346 } 347 348 void assignOptionalValue(const Expr &E, LatticeTransferState &State, 349 BoolValue &HasValueVal) { 350 if (auto *OptionalLoc = 351 State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) { 352 State.Env.setValue(*OptionalLoc, 353 createOptionalValue(State.Env, HasValueVal)); 354 } 355 } 356 357 /// Returns a symbolic value for the "has_value" property of an `optional<T>` 358 /// value that is constructed/assigned from a value of type `U` or `optional<U>` 359 /// where `T` is constructible from `U`. 360 BoolValue & 361 getValueOrConversionHasValue(const FunctionDecl &F, const Expr &E, 362 const MatchFinder::MatchResult &MatchRes, 363 LatticeTransferState &State) { 364 assert(F.getTemplateSpecializationArgs()->size() > 0); 365 366 const int TemplateParamOptionalWrappersCount = countOptionalWrappers( 367 *MatchRes.Context, 368 stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType())); 369 const int ArgTypeOptionalWrappersCount = 370 countOptionalWrappers(*MatchRes.Context, stripReference(E.getType())); 371 372 // Check if this is a constructor/assignment call for `optional<T>` with 373 // argument of type `U` such that `T` is constructible from `U`. 374 if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) 375 return State.Env.getBoolLiteralValue(true); 376 377 // This is a constructor/assignment call for `optional<T>` with argument of 378 // type `optional<U>` such that `T` is constructible from `U`. 379 if (BoolValue *Val = getHasValue(State.Env.getValue(E, SkipPast::Reference))) 380 return *Val; 381 return State.Env.makeAtomicBoolValue(); 382 } 383 384 void transferValueOrConversionConstructor( 385 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 386 LatticeTransferState &State) { 387 assert(E->getNumArgs() > 0); 388 389 assignOptionalValue(*E, State, 390 getValueOrConversionHasValue(*E->getConstructor(), 391 *E->getArg(0), MatchRes, 392 State)); 393 } 394 395 void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 396 LatticeTransferState &State) { 397 assert(E->getNumArgs() > 0); 398 399 auto *OptionalLoc = 400 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 401 assert(OptionalLoc != nullptr); 402 403 State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal)); 404 405 // Assign a storage location for the whole expression. 406 State.Env.setStorageLocation(*E, *OptionalLoc); 407 } 408 409 void transferValueOrConversionAssignment( 410 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 411 LatticeTransferState &State) { 412 assert(E->getNumArgs() > 1); 413 transferAssignment(E, 414 getValueOrConversionHasValue( 415 *E->getDirectCallee(), *E->getArg(1), MatchRes, State), 416 State); 417 } 418 419 void transferNulloptAssignment(const CXXOperatorCallExpr *E, 420 const MatchFinder::MatchResult &, 421 LatticeTransferState &State) { 422 transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 423 } 424 425 void transferSwap(const StorageLocation &OptionalLoc1, 426 const StorageLocation &OptionalLoc2, 427 LatticeTransferState &State) { 428 auto *OptionalVal1 = State.Env.getValue(OptionalLoc1); 429 assert(OptionalVal1 != nullptr); 430 431 auto *OptionalVal2 = State.Env.getValue(OptionalLoc2); 432 assert(OptionalVal2 != nullptr); 433 434 State.Env.setValue(OptionalLoc1, *OptionalVal2); 435 State.Env.setValue(OptionalLoc2, *OptionalVal1); 436 } 437 438 void transferSwapCall(const CXXMemberCallExpr *E, 439 const MatchFinder::MatchResult &, 440 LatticeTransferState &State) { 441 assert(E->getNumArgs() == 1); 442 443 auto *OptionalLoc1 = State.Env.getStorageLocation( 444 *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer); 445 assert(OptionalLoc1 != nullptr); 446 447 auto *OptionalLoc2 = 448 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 449 assert(OptionalLoc2 != nullptr); 450 451 transferSwap(*OptionalLoc1, *OptionalLoc2, State); 452 } 453 454 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 455 LatticeTransferState &State) { 456 assert(E->getNumArgs() == 2); 457 458 auto *OptionalLoc1 = 459 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 460 assert(OptionalLoc1 != nullptr); 461 462 auto *OptionalLoc2 = 463 State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference); 464 assert(OptionalLoc2 != nullptr); 465 466 transferSwap(*OptionalLoc1, *OptionalLoc2, State); 467 } 468 469 llvm::Optional<StatementMatcher> 470 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 471 if (Options.IgnoreSmartPointerDereference) 472 return memberExpr(hasObjectExpression(ignoringParenImpCasts( 473 cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("->"), 474 hasOverloadedOperatorName("*")), 475 unless(hasArgument(0, expr(hasOptionalType()))))))); 476 return llvm::None; 477 } 478 479 auto buildTransferMatchSwitch( 480 const UncheckedOptionalAccessModelOptions &Options) { 481 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 482 // lot of duplicated work (e.g. string comparisons), consider providing APIs 483 // that avoid it through memoization. 484 auto IgnorableOptional = ignorableOptional(Options); 485 return MatchSwitchBuilder<LatticeTransferState>() 486 // Attach a symbolic "has_value" state to optional values that we see for 487 // the first time. 488 .CaseOf<Expr>(expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), 489 initializeOptionalReference) 490 491 // make_optional 492 .CaseOf<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 493 494 // optional::optional 495 .CaseOf<CXXConstructExpr>( 496 isOptionalInPlaceConstructor(), 497 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 498 LatticeTransferState &State) { 499 assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true)); 500 }) 501 .CaseOf<CXXConstructExpr>( 502 isOptionalNulloptConstructor(), 503 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 504 LatticeTransferState &State) { 505 assignOptionalValue(*E, State, 506 State.Env.getBoolLiteralValue(false)); 507 }) 508 .CaseOf<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 509 transferValueOrConversionConstructor) 510 511 // optional::operator= 512 .CaseOf<CXXOperatorCallExpr>(isOptionalValueOrConversionAssignment(), 513 transferValueOrConversionAssignment) 514 .CaseOf<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 515 transferNulloptAssignment) 516 517 // optional::value 518 .CaseOf<CXXMemberCallExpr>( 519 isOptionalMemberCallWithName("value", IgnorableOptional), 520 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 521 LatticeTransferState &State) { 522 transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 523 }) 524 525 // optional::operator*, optional::operator-> 526 .CaseOf<CallExpr>( 527 expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 528 isOptionalOperatorCallWithName("->", IgnorableOptional))), 529 [](const CallExpr *E, const MatchFinder::MatchResult &, 530 LatticeTransferState &State) { 531 transferUnwrapCall(E, E->getArg(0), State); 532 }) 533 534 // optional::has_value 535 .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("has_value"), 536 transferOptionalHasValueCall) 537 538 // optional::operator bool 539 .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("operator bool"), 540 transferOptionalHasValueCall) 541 542 // optional::emplace 543 .CaseOf<CXXMemberCallExpr>( 544 isOptionalMemberCallWithName("emplace"), 545 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 546 LatticeTransferState &State) { 547 assignOptionalValue(*E->getImplicitObjectArgument(), State, 548 State.Env.getBoolLiteralValue(true)); 549 }) 550 551 // optional::reset 552 .CaseOf<CXXMemberCallExpr>( 553 isOptionalMemberCallWithName("reset"), 554 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 555 LatticeTransferState &State) { 556 assignOptionalValue(*E->getImplicitObjectArgument(), State, 557 State.Env.getBoolLiteralValue(false)); 558 }) 559 560 // optional::swap 561 .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"), 562 transferSwapCall) 563 564 // std::swap 565 .CaseOf<CallExpr>(isStdSwapCall(), transferStdSwapCall) 566 567 // opt.value_or("").empty() 568 .CaseOf<Expr>(isValueOrStringEmptyCall(), transferValueOrStringEmptyCall) 569 570 // opt.value_or(X) != X 571 .CaseOf<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 572 573 // returns optional 574 .CaseOf<CallExpr>(isCallReturningOptional(), 575 transferCallReturningOptional) 576 577 .Build(); 578 } 579 580 } // namespace 581 582 ast_matchers::DeclarationMatcher 583 UncheckedOptionalAccessModel::optionalClassDecl() { 584 return optionalClass(); 585 } 586 587 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel( 588 ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options) 589 : DataflowAnalysis<UncheckedOptionalAccessModel, SourceLocationsLattice>( 590 Ctx), 591 TransferMatchSwitch(buildTransferMatchSwitch(Options)) {} 592 593 void UncheckedOptionalAccessModel::transfer(const Stmt *S, 594 SourceLocationsLattice &L, 595 Environment &Env) { 596 LatticeTransferState State(L, Env); 597 TransferMatchSwitch(*S, getASTContext(), State); 598 } 599 600 } // namespace dataflow 601 } // namespace clang 602