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 optionalOrAliasType() { 47 return hasUnqualifiedDesugaredType( 48 recordType(hasDeclaration(optionalClass()))); 49 } 50 51 /// Matches any of the spellings of the optional types and sugar, aliases, etc. 52 auto hasOptionalType() { return hasType(optionalOrAliasType()); } 53 54 auto isOptionalMemberCallWithName( 55 llvm::StringRef MemberName, 56 llvm::Optional<StatementMatcher> Ignorable = llvm::None) { 57 auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr())) 58 : cxxThisExpr()); 59 return cxxMemberCallExpr( 60 on(expr(Exception)), 61 callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass())))); 62 } 63 64 auto isOptionalOperatorCallWithName( 65 llvm::StringRef operator_name, 66 llvm::Optional<StatementMatcher> Ignorable = llvm::None) { 67 return cxxOperatorCallExpr( 68 hasOverloadedOperatorName(operator_name), 69 callee(cxxMethodDecl(ofClass(optionalClass()))), 70 Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()); 71 } 72 73 auto isMakeOptionalCall() { 74 return callExpr( 75 callee(functionDecl(hasAnyName( 76 "std::make_optional", "base::make_optional", "absl::make_optional"))), 77 hasOptionalType()); 78 } 79 80 auto hasNulloptType() { 81 return hasType(namedDecl( 82 hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t"))); 83 } 84 85 auto inPlaceClass() { 86 return recordDecl( 87 hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t")); 88 } 89 90 auto isOptionalNulloptConstructor() { 91 return cxxConstructExpr(hasOptionalType(), argumentCountIs(1), 92 hasArgument(0, hasNulloptType())); 93 } 94 95 auto isOptionalInPlaceConstructor() { 96 return cxxConstructExpr(hasOptionalType(), 97 hasArgument(0, hasType(inPlaceClass()))); 98 } 99 100 auto isOptionalValueOrConversionConstructor() { 101 return cxxConstructExpr( 102 hasOptionalType(), 103 unless(hasDeclaration( 104 cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), 105 argumentCountIs(1), hasArgument(0, unless(hasNulloptType()))); 106 } 107 108 auto isOptionalValueOrConversionAssignment() { 109 return cxxOperatorCallExpr( 110 hasOverloadedOperatorName("="), 111 callee(cxxMethodDecl(ofClass(optionalClass()))), 112 unless(hasDeclaration(cxxMethodDecl( 113 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), 114 argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); 115 } 116 117 auto isOptionalNulloptAssignment() { 118 return cxxOperatorCallExpr(hasOverloadedOperatorName("="), 119 callee(cxxMethodDecl(ofClass(optionalClass()))), 120 argumentCountIs(2), 121 hasArgument(1, hasNulloptType())); 122 } 123 124 auto isStdSwapCall() { 125 return callExpr(callee(functionDecl(hasName("std::swap"))), 126 argumentCountIs(2), hasArgument(0, hasOptionalType()), 127 hasArgument(1, hasOptionalType())); 128 } 129 130 constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; 131 132 auto isValueOrStringEmptyCall() { 133 // `opt.value_or("").empty()` 134 return cxxMemberCallExpr( 135 callee(cxxMethodDecl(hasName("empty"))), 136 onImplicitObjectArgument(ignoringImplicit( 137 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 138 callee(cxxMethodDecl(hasName("value_or"), 139 ofClass(optionalClass()))), 140 hasArgument(0, stringLiteral(hasSize(0)))) 141 .bind(ValueOrCallID)))); 142 } 143 144 auto isValueOrNotEqX() { 145 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { 146 return hasOperands( 147 ignoringImplicit( 148 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), 149 callee(cxxMethodDecl(hasName("value_or"), 150 ofClass(optionalClass()))), 151 hasArgument(0, Arg)) 152 .bind(ValueOrCallID)), 153 ignoringImplicit(Arg)); 154 }; 155 156 // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd 157 // support this pattern for any expression, but the AST does not have a 158 // generic expression comparison facility, so we specialize to common cases 159 // seen in practice. FIXME: define a matcher that compares values across 160 // nodes, which would let us generalize this to any `X`. 161 return binaryOperation(hasOperatorName("!="), 162 anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), 163 ComparesToSame(stringLiteral(hasSize(0))), 164 ComparesToSame(integerLiteral(equals(0))))); 165 } 166 167 auto isCallReturningOptional() { 168 return callExpr(hasType(qualType(anyOf( 169 optionalOrAliasType(), referenceType(pointee(optionalOrAliasType())))))); 170 } 171 172 /// Sets `HasValueVal` as the symbolic value that represents the "has_value" 173 /// property of the optional value `OptionalVal`. 174 void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) { 175 OptionalVal.setProperty("has_value", HasValueVal); 176 } 177 178 /// Creates a symbolic value for an `optional` value using `HasValueVal` as the 179 /// symbolic value of its "has_value" property. 180 StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) { 181 auto OptionalVal = std::make_unique<StructValue>(); 182 setHasValue(*OptionalVal, HasValueVal); 183 return Env.takeOwnership(std::move(OptionalVal)); 184 } 185 186 /// Returns the symbolic value that represents the "has_value" property of the 187 /// optional value `OptionalVal`. Returns null if `OptionalVal` is null. 188 BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { 189 if (OptionalVal != nullptr) { 190 auto *HasValueVal = 191 cast_or_null<BoolValue>(OptionalVal->getProperty("has_value")); 192 if (HasValueVal == nullptr) { 193 HasValueVal = &Env.makeAtomicBoolValue(); 194 OptionalVal->setProperty("has_value", *HasValueVal); 195 } 196 return HasValueVal; 197 } 198 return nullptr; 199 } 200 201 /// If `Type` is a reference type, returns the type of its pointee. Otherwise, 202 /// returns `Type` itself. 203 QualType stripReference(QualType Type) { 204 return Type->isReferenceType() ? Type->getPointeeType() : Type; 205 } 206 207 /// Returns true if and only if `Type` is an optional type. 208 bool IsOptionalType(QualType Type) { 209 if (!Type->isRecordType()) 210 return false; 211 // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call. 212 auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString(); 213 return TypeName == "std::optional" || TypeName == "absl::optional" || 214 TypeName == "base::Optional"; 215 } 216 217 /// Returns the number of optional wrappers in `Type`. 218 /// 219 /// For example, if `Type` is `optional<optional<int>>`, the result of this 220 /// function will be 2. 221 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { 222 if (!IsOptionalType(Type)) 223 return 0; 224 return 1 + countOptionalWrappers( 225 ASTCtx, 226 cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) 227 ->getTemplateArgs() 228 .get(0) 229 .getAsType() 230 .getDesugaredType(ASTCtx)); 231 } 232 233 /// Tries to initialize the `optional`'s value (that is, contents), and return 234 /// its location. Returns nullptr if the value can't be represented. 235 StorageLocation *maybeInitializeOptionalValueMember(QualType Q, 236 Value &OptionalVal, 237 Environment &Env) { 238 // The "value" property represents a synthetic field. As such, it needs 239 // `StorageLocation`, like normal fields (and other variables). So, we model 240 // it with a `ReferenceValue`, since that includes a storage location. Once 241 // the property is set, it will be shared by all environments that access the 242 // `Value` representing the optional (here, `OptionalVal`). 243 if (auto *ValueProp = OptionalVal.getProperty("value")) { 244 auto *ValueRef = clang::cast<ReferenceValue>(ValueProp); 245 auto &ValueLoc = ValueRef->getReferentLoc(); 246 if (Env.getValue(ValueLoc) == nullptr) { 247 // The property was previously set, but the value has been lost. This can 248 // happen, for example, because of an environment merge (where the two 249 // environments mapped the property to different values, which resulted in 250 // them both being discarded), or when two blocks in the CFG, with neither 251 // a dominator of the other, visit the same optional value, or even when a 252 // block is revisited during testing to collect per-statement state. 253 // FIXME: This situation means that the optional contents are not shared 254 // between branches and the like. Practically, this lack of sharing 255 // reduces the precision of the model when the contents are relevant to 256 // the check, like another optional or a boolean that influences control 257 // flow. 258 auto *ValueVal = Env.createValue(ValueLoc.getType()); 259 if (ValueVal == nullptr) 260 return nullptr; 261 Env.setValue(ValueLoc, *ValueVal); 262 } 263 return &ValueLoc; 264 } 265 266 auto Ty = stripReference(Q); 267 auto *ValueVal = Env.createValue(Ty); 268 if (ValueVal == nullptr) 269 return nullptr; 270 auto &ValueLoc = Env.createStorageLocation(Ty); 271 Env.setValue(ValueLoc, *ValueVal); 272 auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc); 273 OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef))); 274 return &ValueLoc; 275 } 276 277 void initializeOptionalReference(const Expr *OptionalExpr, 278 const MatchFinder::MatchResult &, 279 LatticeTransferState &State) { 280 if (auto *OptionalVal = 281 State.Env.getValue(*OptionalExpr, SkipPast::Reference)) { 282 if (OptionalVal->getProperty("has_value") == nullptr) { 283 setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); 284 } 285 } 286 } 287 288 /// Returns true if and only if `OptionalVal` is initialized and known to be 289 /// empty in `Env. 290 bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { 291 auto *HasValueVal = 292 cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 293 return HasValueVal != nullptr && 294 Env.flowConditionImplies(Env.makeNot(*HasValueVal)); 295 } 296 297 /// Returns true if and only if `OptionalVal` is initialized and known to be 298 /// non-empty in `Env. 299 bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { 300 auto *HasValueVal = 301 cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); 302 return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal); 303 } 304 305 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, 306 LatticeTransferState &State) { 307 if (auto *OptionalVal = 308 State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { 309 if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr) 310 if (auto *Loc = maybeInitializeOptionalValueMember( 311 UnwrapExpr->getType(), *OptionalVal, State.Env)) 312 State.Env.setStorageLocation(*UnwrapExpr, *Loc); 313 314 auto *Prop = OptionalVal->getProperty("has_value"); 315 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { 316 if (State.Env.flowConditionImplies(*HasValueVal)) 317 return; 318 } 319 } 320 321 // Record that this unwrap is *not* provably safe. 322 // FIXME: include either the name of the optional (if applicable) or a source 323 // range of the access for easier interpretation of the result. 324 State.Lattice.getSourceLocations().insert(ObjectExpr->getBeginLoc()); 325 } 326 327 void transferMakeOptionalCall(const CallExpr *E, 328 const MatchFinder::MatchResult &, 329 LatticeTransferState &State) { 330 auto &Loc = State.Env.createStorageLocation(*E); 331 State.Env.setStorageLocation(*E, Loc); 332 State.Env.setValue( 333 Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true))); 334 } 335 336 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, 337 const MatchFinder::MatchResult &, 338 LatticeTransferState &State) { 339 if (auto *HasValueVal = getHasValue( 340 State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(), 341 SkipPast::ReferenceThenPointer))) { 342 auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr); 343 State.Env.setValue(CallExprLoc, *HasValueVal); 344 State.Env.setStorageLocation(*CallExpr, CallExprLoc); 345 } 346 } 347 348 /// `ModelPred` builds a logical formula relating the predicate in 349 /// `ValueOrPredExpr` to the optional's `has_value` property. 350 void transferValueOrImpl(const clang::Expr *ValueOrPredExpr, 351 const MatchFinder::MatchResult &Result, 352 LatticeTransferState &State, 353 BoolValue &(*ModelPred)(Environment &Env, 354 BoolValue &ExprVal, 355 BoolValue &HasValueVal)) { 356 auto &Env = State.Env; 357 358 const auto *ObjectArgumentExpr = 359 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) 360 ->getImplicitObjectArgument(); 361 362 auto *HasValueVal = getHasValue( 363 State.Env, 364 State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer)); 365 if (HasValueVal == nullptr) 366 return; 367 368 auto *ExprValue = cast_or_null<BoolValue>( 369 State.Env.getValue(*ValueOrPredExpr, SkipPast::None)); 370 if (ExprValue == nullptr) { 371 auto &ExprLoc = State.Env.createStorageLocation(*ValueOrPredExpr); 372 ExprValue = &State.Env.makeAtomicBoolValue(); 373 State.Env.setValue(ExprLoc, *ExprValue); 374 State.Env.setStorageLocation(*ValueOrPredExpr, ExprLoc); 375 } 376 377 Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal)); 378 } 379 380 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, 381 const MatchFinder::MatchResult &Result, 382 LatticeTransferState &State) { 383 return transferValueOrImpl(ComparisonExpr, Result, State, 384 [](Environment &Env, BoolValue &ExprVal, 385 BoolValue &HasValueVal) -> BoolValue & { 386 // If the result is *not* empty, then we know the 387 // optional must have been holding a value. If 388 // `ExprVal` is true, though, we don't learn 389 // anything definite about `has_value`, so we 390 // don't add any corresponding implications to 391 // the flow condition. 392 return Env.makeImplication(Env.makeNot(ExprVal), 393 HasValueVal); 394 }); 395 } 396 397 void transferValueOrNotEqX(const Expr *ComparisonExpr, 398 const MatchFinder::MatchResult &Result, 399 LatticeTransferState &State) { 400 transferValueOrImpl(ComparisonExpr, Result, State, 401 [](Environment &Env, BoolValue &ExprVal, 402 BoolValue &HasValueVal) -> BoolValue & { 403 // We know that if `(opt.value_or(X) != X)` then 404 // `opt.hasValue()`, even without knowing further 405 // details about the contents of `opt`. 406 return Env.makeImplication(ExprVal, HasValueVal); 407 }); 408 } 409 410 void transferCallReturningOptional(const CallExpr *E, 411 const MatchFinder::MatchResult &Result, 412 LatticeTransferState &State) { 413 if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) 414 return; 415 416 auto &Loc = State.Env.createStorageLocation(*E); 417 State.Env.setStorageLocation(*E, Loc); 418 State.Env.setValue( 419 Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue())); 420 } 421 422 void assignOptionalValue(const Expr &E, LatticeTransferState &State, 423 BoolValue &HasValueVal) { 424 if (auto *OptionalLoc = 425 State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) { 426 State.Env.setValue(*OptionalLoc, 427 createOptionalValue(State.Env, HasValueVal)); 428 } 429 } 430 431 /// Returns a symbolic value for the "has_value" property of an `optional<T>` 432 /// value that is constructed/assigned from a value of type `U` or `optional<U>` 433 /// where `T` is constructible from `U`. 434 BoolValue & 435 getValueOrConversionHasValue(const FunctionDecl &F, const Expr &E, 436 const MatchFinder::MatchResult &MatchRes, 437 LatticeTransferState &State) { 438 assert(F.getTemplateSpecializationArgs()->size() > 0); 439 440 const int TemplateParamOptionalWrappersCount = countOptionalWrappers( 441 *MatchRes.Context, 442 stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType())); 443 const int ArgTypeOptionalWrappersCount = 444 countOptionalWrappers(*MatchRes.Context, stripReference(E.getType())); 445 446 // Check if this is a constructor/assignment call for `optional<T>` with 447 // argument of type `U` such that `T` is constructible from `U`. 448 if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) 449 return State.Env.getBoolLiteralValue(true); 450 451 // This is a constructor/assignment call for `optional<T>` with argument of 452 // type `optional<U>` such that `T` is constructible from `U`. 453 if (auto *HasValueVal = 454 getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference))) 455 return *HasValueVal; 456 return State.Env.makeAtomicBoolValue(); 457 } 458 459 void transferValueOrConversionConstructor( 460 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, 461 LatticeTransferState &State) { 462 assert(E->getNumArgs() > 0); 463 464 assignOptionalValue(*E, State, 465 getValueOrConversionHasValue(*E->getConstructor(), 466 *E->getArg(0), MatchRes, 467 State)); 468 } 469 470 void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, 471 LatticeTransferState &State) { 472 assert(E->getNumArgs() > 0); 473 474 auto *OptionalLoc = 475 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 476 if (OptionalLoc == nullptr) 477 return; 478 479 State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal)); 480 481 // Assign a storage location for the whole expression. 482 State.Env.setStorageLocation(*E, *OptionalLoc); 483 } 484 485 void transferValueOrConversionAssignment( 486 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, 487 LatticeTransferState &State) { 488 assert(E->getNumArgs() > 1); 489 transferAssignment(E, 490 getValueOrConversionHasValue( 491 *E->getDirectCallee(), *E->getArg(1), MatchRes, State), 492 State); 493 } 494 495 void transferNulloptAssignment(const CXXOperatorCallExpr *E, 496 const MatchFinder::MatchResult &, 497 LatticeTransferState &State) { 498 transferAssignment(E, State.Env.getBoolLiteralValue(false), State); 499 } 500 501 void transferSwap(const StorageLocation &OptionalLoc1, 502 const StorageLocation &OptionalLoc2, 503 LatticeTransferState &State) { 504 auto *OptionalVal1 = State.Env.getValue(OptionalLoc1); 505 assert(OptionalVal1 != nullptr); 506 507 auto *OptionalVal2 = State.Env.getValue(OptionalLoc2); 508 assert(OptionalVal2 != nullptr); 509 510 State.Env.setValue(OptionalLoc1, *OptionalVal2); 511 State.Env.setValue(OptionalLoc2, *OptionalVal1); 512 } 513 514 void transferSwapCall(const CXXMemberCallExpr *E, 515 const MatchFinder::MatchResult &, 516 LatticeTransferState &State) { 517 assert(E->getNumArgs() == 1); 518 519 auto *OptionalLoc1 = State.Env.getStorageLocation( 520 *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer); 521 assert(OptionalLoc1 != nullptr); 522 523 auto *OptionalLoc2 = 524 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 525 assert(OptionalLoc2 != nullptr); 526 527 transferSwap(*OptionalLoc1, *OptionalLoc2, State); 528 } 529 530 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, 531 LatticeTransferState &State) { 532 assert(E->getNumArgs() == 2); 533 534 auto *OptionalLoc1 = 535 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); 536 assert(OptionalLoc1 != nullptr); 537 538 auto *OptionalLoc2 = 539 State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference); 540 assert(OptionalLoc2 != nullptr); 541 542 transferSwap(*OptionalLoc1, *OptionalLoc2, State); 543 } 544 545 llvm::Optional<StatementMatcher> 546 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { 547 if (Options.IgnoreSmartPointerDereference) 548 return memberExpr(hasObjectExpression(ignoringParenImpCasts( 549 cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("->"), 550 hasOverloadedOperatorName("*")), 551 unless(hasArgument(0, expr(hasOptionalType()))))))); 552 return llvm::None; 553 } 554 555 auto buildTransferMatchSwitch( 556 const UncheckedOptionalAccessModelOptions &Options) { 557 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a 558 // lot of duplicated work (e.g. string comparisons), consider providing APIs 559 // that avoid it through memoization. 560 auto IgnorableOptional = ignorableOptional(Options); 561 return MatchSwitchBuilder<LatticeTransferState>() 562 // Attach a symbolic "has_value" state to optional values that we see for 563 // the first time. 564 .CaseOf<Expr>( 565 expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), 566 initializeOptionalReference) 567 568 // make_optional 569 .CaseOf<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) 570 571 // optional::optional 572 .CaseOf<CXXConstructExpr>( 573 isOptionalInPlaceConstructor(), 574 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 575 LatticeTransferState &State) { 576 assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true)); 577 }) 578 .CaseOf<CXXConstructExpr>( 579 isOptionalNulloptConstructor(), 580 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, 581 LatticeTransferState &State) { 582 assignOptionalValue(*E, State, 583 State.Env.getBoolLiteralValue(false)); 584 }) 585 .CaseOf<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), 586 transferValueOrConversionConstructor) 587 588 // optional::operator= 589 .CaseOf<CXXOperatorCallExpr>(isOptionalValueOrConversionAssignment(), 590 transferValueOrConversionAssignment) 591 .CaseOf<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), 592 transferNulloptAssignment) 593 594 // optional::value 595 .CaseOf<CXXMemberCallExpr>( 596 isOptionalMemberCallWithName("value", IgnorableOptional), 597 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 598 LatticeTransferState &State) { 599 transferUnwrapCall(E, E->getImplicitObjectArgument(), State); 600 }) 601 602 // optional::operator*, optional::operator-> 603 .CaseOf<CallExpr>( 604 expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), 605 isOptionalOperatorCallWithName("->", IgnorableOptional))), 606 [](const CallExpr *E, const MatchFinder::MatchResult &, 607 LatticeTransferState &State) { 608 transferUnwrapCall(E, E->getArg(0), State); 609 }) 610 611 // optional::has_value 612 .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("has_value"), 613 transferOptionalHasValueCall) 614 615 // optional::operator bool 616 .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("operator bool"), 617 transferOptionalHasValueCall) 618 619 // optional::emplace 620 .CaseOf<CXXMemberCallExpr>( 621 isOptionalMemberCallWithName("emplace"), 622 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 623 LatticeTransferState &State) { 624 assignOptionalValue(*E->getImplicitObjectArgument(), State, 625 State.Env.getBoolLiteralValue(true)); 626 }) 627 628 // optional::reset 629 .CaseOf<CXXMemberCallExpr>( 630 isOptionalMemberCallWithName("reset"), 631 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, 632 LatticeTransferState &State) { 633 assignOptionalValue(*E->getImplicitObjectArgument(), State, 634 State.Env.getBoolLiteralValue(false)); 635 }) 636 637 // optional::swap 638 .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"), 639 transferSwapCall) 640 641 // std::swap 642 .CaseOf<CallExpr>(isStdSwapCall(), transferStdSwapCall) 643 644 // opt.value_or("").empty() 645 .CaseOf<Expr>(isValueOrStringEmptyCall(), transferValueOrStringEmptyCall) 646 647 // opt.value_or(X) != X 648 .CaseOf<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) 649 650 // returns optional 651 .CaseOf<CallExpr>(isCallReturningOptional(), 652 transferCallReturningOptional) 653 654 .Build(); 655 } 656 657 } // namespace 658 659 ast_matchers::DeclarationMatcher 660 UncheckedOptionalAccessModel::optionalClassDecl() { 661 return optionalClass(); 662 } 663 664 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel( 665 ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options) 666 : DataflowAnalysis<UncheckedOptionalAccessModel, SourceLocationsLattice>( 667 Ctx), 668 TransferMatchSwitch(buildTransferMatchSwitch(Options)) {} 669 670 void UncheckedOptionalAccessModel::transfer(const Stmt *S, 671 SourceLocationsLattice &L, 672 Environment &Env) { 673 LatticeTransferState State(L, Env); 674 TransferMatchSwitch(*S, getASTContext(), State); 675 } 676 677 bool UncheckedOptionalAccessModel::compareEquivalent(QualType Type, 678 const Value &Val1, 679 const Environment &Env1, 680 const Value &Val2, 681 const Environment &Env2) { 682 return isNonEmptyOptional(Val1, Env1) == isNonEmptyOptional(Val2, Env2); 683 } 684 685 bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1, 686 const Environment &Env1, 687 const Value &Val2, 688 const Environment &Env2, 689 Value &MergedVal, 690 Environment &MergedEnv) { 691 if (!IsOptionalType(Type)) 692 return true; 693 694 auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); 695 if (isNonEmptyOptional(Val1, Env1) && isNonEmptyOptional(Val2, Env2)) 696 MergedEnv.addToFlowCondition(HasValueVal); 697 else if (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)) 698 MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal)); 699 setHasValue(MergedVal, HasValueVal); 700 return true; 701 } 702 703 } // namespace dataflow 704 } // namespace clang 705