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