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