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