xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision 2ddd57ae1ec42c4aad8e70645cff82c877a94e3f)
1b000b770SStanislav Gatev //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//
2b000b770SStanislav Gatev //
3b000b770SStanislav Gatev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b000b770SStanislav Gatev // See https://llvm.org/LICENSE.txt for license information.
5b000b770SStanislav Gatev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b000b770SStanislav Gatev //
7b000b770SStanislav Gatev //===----------------------------------------------------------------------===//
8b000b770SStanislav Gatev //
9b000b770SStanislav Gatev //  This file defines a dataflow analysis that detects unsafe uses of optional
10b000b770SStanislav Gatev //  values.
11b000b770SStanislav Gatev //
12b000b770SStanislav Gatev //===----------------------------------------------------------------------===//
13b000b770SStanislav Gatev 
14af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
15af98b0afSStanislav Gatev #include "clang/AST/ASTContext.h"
16af98b0afSStanislav Gatev #include "clang/AST/Expr.h"
17af98b0afSStanislav Gatev #include "clang/AST/ExprCXX.h"
18af98b0afSStanislav Gatev #include "clang/AST/Stmt.h"
19af98b0afSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h"
20af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
21af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
22af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h"
23af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h"
24af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h"
25af98b0afSStanislav Gatev #include "llvm/Support/Casting.h"
26af98b0afSStanislav Gatev #include <cassert>
279e0fc676SStanislav Gatev #include <memory>
289e0fc676SStanislav Gatev #include <utility>
29af98b0afSStanislav Gatev 
30af98b0afSStanislav Gatev namespace clang {
31af98b0afSStanislav Gatev namespace dataflow {
32af98b0afSStanislav Gatev namespace {
33af98b0afSStanislav Gatev 
34af98b0afSStanislav Gatev using namespace ::clang::ast_matchers;
35af98b0afSStanislav Gatev 
36af98b0afSStanislav Gatev using LatticeTransferState = TransferState<SourceLocationsLattice>;
37af98b0afSStanislav Gatev 
38092a530cSStanislav Gatev auto optionalClass() {
39af98b0afSStanislav Gatev   return classTemplateSpecializationDecl(
40af98b0afSStanislav Gatev       anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
41af98b0afSStanislav Gatev             hasName("__optional_destruct_base"), hasName("absl::optional"),
42af98b0afSStanislav Gatev             hasName("base::Optional")),
43af98b0afSStanislav Gatev       hasTemplateArgument(0, refersToType(type().bind("T"))));
44af98b0afSStanislav Gatev }
45af98b0afSStanislav Gatev 
46092a530cSStanislav Gatev auto hasOptionalType() { return hasType(optionalClass()); }
47af98b0afSStanislav Gatev 
48092a530cSStanislav Gatev auto isOptionalMemberCallWithName(llvm::StringRef MemberName) {
49af98b0afSStanislav Gatev   return cxxMemberCallExpr(
50af98b0afSStanislav Gatev       on(expr(unless(cxxThisExpr()))),
51af98b0afSStanislav Gatev       callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
52af98b0afSStanislav Gatev }
53af98b0afSStanislav Gatev 
54092a530cSStanislav Gatev auto isOptionalOperatorCallWithName(llvm::StringRef OperatorName) {
55af98b0afSStanislav Gatev   return cxxOperatorCallExpr(hasOverloadedOperatorName(OperatorName),
56af98b0afSStanislav Gatev                              callee(cxxMethodDecl(ofClass(optionalClass()))));
57af98b0afSStanislav Gatev }
58af98b0afSStanislav Gatev 
59092a530cSStanislav Gatev auto isMakeOptionalCall() {
609e0fc676SStanislav Gatev   return callExpr(
619e0fc676SStanislav Gatev       callee(functionDecl(hasAnyName(
629e0fc676SStanislav Gatev           "std::make_optional", "base::make_optional", "absl::make_optional"))),
639e0fc676SStanislav Gatev       hasOptionalType());
649e0fc676SStanislav Gatev }
659e0fc676SStanislav Gatev 
66092a530cSStanislav Gatev auto hasNulloptType() {
67092a530cSStanislav Gatev   return hasType(namedDecl(
68092a530cSStanislav Gatev       hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")));
69092a530cSStanislav Gatev }
70092a530cSStanislav Gatev 
71092a530cSStanislav Gatev auto inPlaceClass() {
72092a530cSStanislav Gatev   return recordDecl(
73092a530cSStanislav Gatev       hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
74092a530cSStanislav Gatev }
75092a530cSStanislav Gatev 
76092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
77092a530cSStanislav Gatev   return cxxConstructExpr(hasOptionalType(), argumentCountIs(1),
78092a530cSStanislav Gatev                           hasArgument(0, hasNulloptType()));
79092a530cSStanislav Gatev }
80092a530cSStanislav Gatev 
81092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
82092a530cSStanislav Gatev   return cxxConstructExpr(hasOptionalType(),
83092a530cSStanislav Gatev                           hasArgument(0, hasType(inPlaceClass())));
84092a530cSStanislav Gatev }
85092a530cSStanislav Gatev 
86092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
87092a530cSStanislav Gatev   return cxxConstructExpr(
88092a530cSStanislav Gatev       hasOptionalType(),
89092a530cSStanislav Gatev       unless(hasDeclaration(
90092a530cSStanislav Gatev           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
91092a530cSStanislav Gatev       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
92092a530cSStanislav Gatev }
93092a530cSStanislav Gatev 
94b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
95b000b770SStanislav Gatev   return cxxOperatorCallExpr(
96b000b770SStanislav Gatev       hasOverloadedOperatorName("="),
97b000b770SStanislav Gatev       callee(cxxMethodDecl(ofClass(optionalClass()))),
98b000b770SStanislav Gatev       unless(hasDeclaration(cxxMethodDecl(
99b000b770SStanislav Gatev           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
100b000b770SStanislav Gatev       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
101b000b770SStanislav Gatev }
102b000b770SStanislav Gatev 
103b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
104b000b770SStanislav Gatev   return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
105b000b770SStanislav Gatev                              callee(cxxMethodDecl(ofClass(optionalClass()))),
106b000b770SStanislav Gatev                              argumentCountIs(2),
107b000b770SStanislav Gatev                              hasArgument(1, hasNulloptType()));
108b000b770SStanislav Gatev }
109b000b770SStanislav Gatev 
110*2ddd57aeSStanislav Gatev auto isStdSwapCall() {
111*2ddd57aeSStanislav Gatev   return callExpr(callee(functionDecl(hasName("std::swap"))),
112*2ddd57aeSStanislav Gatev                   argumentCountIs(2), hasArgument(0, hasOptionalType()),
113*2ddd57aeSStanislav Gatev                   hasArgument(1, hasOptionalType()));
114*2ddd57aeSStanislav Gatev }
115*2ddd57aeSStanislav Gatev 
1169e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the
1179e0fc676SStanislav Gatev /// symbolic value of its "has_value" property.
1189e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
1199e0fc676SStanislav Gatev   auto OptionalVal = std::make_unique<StructValue>();
1209e0fc676SStanislav Gatev   OptionalVal->setProperty("has_value", HasValueVal);
1219e0fc676SStanislav Gatev   return Env.takeOwnership(std::move(OptionalVal));
1229e0fc676SStanislav Gatev }
1239e0fc676SStanislav Gatev 
124af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
125af98b0afSStanislav Gatev /// optional value `Val`. Returns null if `Val` is null.
126092a530cSStanislav Gatev BoolValue *getHasValue(Value *Val) {
127af98b0afSStanislav Gatev   if (auto *OptionalVal = cast_or_null<StructValue>(Val)) {
128af98b0afSStanislav Gatev     return cast<BoolValue>(OptionalVal->getProperty("has_value"));
129af98b0afSStanislav Gatev   }
130af98b0afSStanislav Gatev   return nullptr;
131af98b0afSStanislav Gatev }
132af98b0afSStanislav Gatev 
133092a530cSStanislav Gatev /// If `Type` is a reference type, returns the type of its pointee. Otherwise,
134092a530cSStanislav Gatev /// returns `Type` itself.
135092a530cSStanislav Gatev QualType stripReference(QualType Type) {
136092a530cSStanislav Gatev   return Type->isReferenceType() ? Type->getPointeeType() : Type;
137092a530cSStanislav Gatev }
138092a530cSStanislav Gatev 
139092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type.
140092a530cSStanislav Gatev bool IsOptionalType(QualType Type) {
141092a530cSStanislav Gatev   if (!Type->isRecordType())
142092a530cSStanislav Gatev     return false;
143092a530cSStanislav Gatev   // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call.
144092a530cSStanislav Gatev   auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString();
145092a530cSStanislav Gatev   return TypeName == "std::optional" || TypeName == "absl::optional" ||
146092a530cSStanislav Gatev          TypeName == "base::Optional";
147092a530cSStanislav Gatev }
148092a530cSStanislav Gatev 
149092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
150092a530cSStanislav Gatev ///
151092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
152092a530cSStanislav Gatev /// function will be 2.
153092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
154092a530cSStanislav Gatev   if (!IsOptionalType(Type))
155092a530cSStanislav Gatev     return 0;
156092a530cSStanislav Gatev   return 1 + countOptionalWrappers(
157092a530cSStanislav Gatev                  ASTCtx,
158092a530cSStanislav Gatev                  cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
159092a530cSStanislav Gatev                      ->getTemplateArgs()
160092a530cSStanislav Gatev                      .get(0)
161092a530cSStanislav Gatev                      .getAsType()
162092a530cSStanislav Gatev                      .getDesugaredType(ASTCtx));
163092a530cSStanislav Gatev }
164092a530cSStanislav Gatev 
165092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr,
166092a530cSStanislav Gatev                                  const MatchFinder::MatchResult &,
167af98b0afSStanislav Gatev                                  LatticeTransferState &State) {
168af98b0afSStanislav Gatev   if (auto *OptionalVal = cast_or_null<StructValue>(
169af98b0afSStanislav Gatev           State.Env.getValue(*OptionalExpr, SkipPast::Reference))) {
170af98b0afSStanislav Gatev     if (OptionalVal->getProperty("has_value") == nullptr) {
171af98b0afSStanislav Gatev       OptionalVal->setProperty("has_value", State.Env.makeAtomicBoolValue());
172af98b0afSStanislav Gatev     }
173af98b0afSStanislav Gatev   }
174af98b0afSStanislav Gatev }
175af98b0afSStanislav Gatev 
176092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
177af98b0afSStanislav Gatev                         LatticeTransferState &State) {
178af98b0afSStanislav Gatev   if (auto *OptionalVal = cast_or_null<StructValue>(
179af98b0afSStanislav Gatev           State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer))) {
180af98b0afSStanislav Gatev     auto *HasValueVal = getHasValue(OptionalVal);
181af98b0afSStanislav Gatev     assert(HasValueVal != nullptr);
182af98b0afSStanislav Gatev 
183af98b0afSStanislav Gatev     if (State.Env.flowConditionImplies(*HasValueVal))
184af98b0afSStanislav Gatev       return;
185af98b0afSStanislav Gatev   }
186af98b0afSStanislav Gatev 
187af98b0afSStanislav Gatev   // Record that this unwrap is *not* provably safe.
188af98b0afSStanislav Gatev   State.Lattice.getSourceLocations().insert(ObjectExpr->getBeginLoc());
189af98b0afSStanislav Gatev }
190af98b0afSStanislav Gatev 
191092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
192092a530cSStanislav Gatev                               const MatchFinder::MatchResult &,
193092a530cSStanislav Gatev                               LatticeTransferState &State) {
1949e0fc676SStanislav Gatev   auto &Loc = State.Env.createStorageLocation(*E);
1959e0fc676SStanislav Gatev   State.Env.setStorageLocation(*E, Loc);
1969e0fc676SStanislav Gatev   State.Env.setValue(
1979e0fc676SStanislav Gatev       Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
1989e0fc676SStanislav Gatev }
1999e0fc676SStanislav Gatev 
200092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
201092a530cSStanislav Gatev                                   const MatchFinder::MatchResult &,
202af98b0afSStanislav Gatev                                   LatticeTransferState &State) {
203af98b0afSStanislav Gatev   if (auto *OptionalVal = cast_or_null<StructValue>(
204af98b0afSStanislav Gatev           State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
205af98b0afSStanislav Gatev                              SkipPast::ReferenceThenPointer))) {
206af98b0afSStanislav Gatev     auto *HasValueVal = getHasValue(OptionalVal);
207af98b0afSStanislav Gatev     assert(HasValueVal != nullptr);
208af98b0afSStanislav Gatev 
209af98b0afSStanislav Gatev     auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
210af98b0afSStanislav Gatev     State.Env.setValue(CallExprLoc, *HasValueVal);
211af98b0afSStanislav Gatev     State.Env.setStorageLocation(*CallExpr, CallExprLoc);
212af98b0afSStanislav Gatev   }
213af98b0afSStanislav Gatev }
214af98b0afSStanislav Gatev 
215092a530cSStanislav Gatev void assignOptionalValue(const Expr &E, LatticeTransferState &State,
216092a530cSStanislav Gatev                          BoolValue &HasValueVal) {
217092a530cSStanislav Gatev   if (auto *OptionalLoc =
218092a530cSStanislav Gatev           State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
219092a530cSStanislav Gatev     State.Env.setValue(*OptionalLoc,
220092a530cSStanislav Gatev                        createOptionalValue(State.Env, HasValueVal));
2219e0fc676SStanislav Gatev   }
2229e0fc676SStanislav Gatev }
2239e0fc676SStanislav Gatev 
224b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
225b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
226b000b770SStanislav Gatev /// where `T` is constructible from `U`.
227b000b770SStanislav Gatev BoolValue &
228b000b770SStanislav Gatev getValueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
229b000b770SStanislav Gatev                              const MatchFinder::MatchResult &MatchRes,
230b000b770SStanislav Gatev                              LatticeTransferState &State) {
231b000b770SStanislav Gatev   assert(F.getTemplateSpecializationArgs()->size() > 0);
232b000b770SStanislav Gatev 
233b000b770SStanislav Gatev   const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
234b000b770SStanislav Gatev       *MatchRes.Context,
235b000b770SStanislav Gatev       stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType()));
236b000b770SStanislav Gatev   const int ArgTypeOptionalWrappersCount =
237b000b770SStanislav Gatev       countOptionalWrappers(*MatchRes.Context, stripReference(E.getType()));
238b000b770SStanislav Gatev 
239b000b770SStanislav Gatev   // Check if this is a constructor/assignment call for `optional<T>` with
240b000b770SStanislav Gatev   // argument of type `U` such that `T` is constructible from `U`.
241b000b770SStanislav Gatev   if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
242b000b770SStanislav Gatev     return State.Env.getBoolLiteralValue(true);
243b000b770SStanislav Gatev 
244b000b770SStanislav Gatev   // This is a constructor/assignment call for `optional<T>` with argument of
245b000b770SStanislav Gatev   // type `optional<U>` such that `T` is constructible from `U`.
246b000b770SStanislav Gatev   if (BoolValue *Val = getHasValue(State.Env.getValue(E, SkipPast::Reference)))
247b000b770SStanislav Gatev     return *Val;
248b000b770SStanislav Gatev   return State.Env.makeAtomicBoolValue();
249b000b770SStanislav Gatev }
250b000b770SStanislav Gatev 
251092a530cSStanislav Gatev void transferValueOrConversionConstructor(
252092a530cSStanislav Gatev     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
2539e0fc676SStanislav Gatev     LatticeTransferState &State) {
254092a530cSStanislav Gatev   assert(E->getNumArgs() > 0);
255092a530cSStanislav Gatev 
256b000b770SStanislav Gatev   assignOptionalValue(*E, State,
257b000b770SStanislav Gatev                       getValueOrConversionHasValue(*E->getConstructor(),
258b000b770SStanislav Gatev                                                    *E->getArg(0), MatchRes,
259b000b770SStanislav Gatev                                                    State));
260b000b770SStanislav Gatev }
261092a530cSStanislav Gatev 
262b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
263b000b770SStanislav Gatev                         LatticeTransferState &State) {
264b000b770SStanislav Gatev   assert(E->getNumArgs() > 0);
265b000b770SStanislav Gatev 
266b000b770SStanislav Gatev   auto *OptionalLoc =
267b000b770SStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
268b000b770SStanislav Gatev   assert(OptionalLoc != nullptr);
269b000b770SStanislav Gatev 
270b000b770SStanislav Gatev   State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
271b000b770SStanislav Gatev 
272b000b770SStanislav Gatev   // Assign a storage location for the whole expression.
273b000b770SStanislav Gatev   State.Env.setStorageLocation(*E, *OptionalLoc);
274b000b770SStanislav Gatev }
275b000b770SStanislav Gatev 
276b000b770SStanislav Gatev void transferValueOrConversionAssignment(
277b000b770SStanislav Gatev     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
278b000b770SStanislav Gatev     LatticeTransferState &State) {
279b000b770SStanislav Gatev   assert(E->getNumArgs() > 1);
280b000b770SStanislav Gatev   transferAssignment(E,
281b000b770SStanislav Gatev                      getValueOrConversionHasValue(
282b000b770SStanislav Gatev                          *E->getDirectCallee(), *E->getArg(1), MatchRes, State),
283b000b770SStanislav Gatev                      State);
284b000b770SStanislav Gatev }
285b000b770SStanislav Gatev 
286b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
287b000b770SStanislav Gatev                                const MatchFinder::MatchResult &,
288b000b770SStanislav Gatev                                LatticeTransferState &State) {
289b000b770SStanislav Gatev   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
2909e0fc676SStanislav Gatev }
2919e0fc676SStanislav Gatev 
292*2ddd57aeSStanislav Gatev void transferSwap(const StorageLocation &OptionalLoc1,
293*2ddd57aeSStanislav Gatev                   const StorageLocation &OptionalLoc2,
294*2ddd57aeSStanislav Gatev                   LatticeTransferState &State) {
295*2ddd57aeSStanislav Gatev   auto *OptionalVal1 = State.Env.getValue(OptionalLoc1);
296*2ddd57aeSStanislav Gatev   assert(OptionalVal1 != nullptr);
297*2ddd57aeSStanislav Gatev 
298*2ddd57aeSStanislav Gatev   auto *OptionalVal2 = State.Env.getValue(OptionalLoc2);
299*2ddd57aeSStanislav Gatev   assert(OptionalVal2 != nullptr);
300*2ddd57aeSStanislav Gatev 
301*2ddd57aeSStanislav Gatev   State.Env.setValue(OptionalLoc1, *OptionalVal2);
302*2ddd57aeSStanislav Gatev   State.Env.setValue(OptionalLoc2, *OptionalVal1);
303*2ddd57aeSStanislav Gatev }
304*2ddd57aeSStanislav Gatev 
305*2ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
306*2ddd57aeSStanislav Gatev                       const MatchFinder::MatchResult &,
307*2ddd57aeSStanislav Gatev                       LatticeTransferState &State) {
308*2ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 1);
309*2ddd57aeSStanislav Gatev 
310*2ddd57aeSStanislav Gatev   auto *OptionalLoc1 = State.Env.getStorageLocation(
311*2ddd57aeSStanislav Gatev       *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer);
312*2ddd57aeSStanislav Gatev   assert(OptionalLoc1 != nullptr);
313*2ddd57aeSStanislav Gatev 
314*2ddd57aeSStanislav Gatev   auto *OptionalLoc2 =
315*2ddd57aeSStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
316*2ddd57aeSStanislav Gatev   assert(OptionalLoc2 != nullptr);
317*2ddd57aeSStanislav Gatev 
318*2ddd57aeSStanislav Gatev   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
319*2ddd57aeSStanislav Gatev }
320*2ddd57aeSStanislav Gatev 
321*2ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
322*2ddd57aeSStanislav Gatev                          LatticeTransferState &State) {
323*2ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 2);
324*2ddd57aeSStanislav Gatev 
325*2ddd57aeSStanislav Gatev   auto *OptionalLoc1 =
326*2ddd57aeSStanislav Gatev       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
327*2ddd57aeSStanislav Gatev   assert(OptionalLoc1 != nullptr);
328*2ddd57aeSStanislav Gatev 
329*2ddd57aeSStanislav Gatev   auto *OptionalLoc2 =
330*2ddd57aeSStanislav Gatev       State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference);
331*2ddd57aeSStanislav Gatev   assert(OptionalLoc2 != nullptr);
332*2ddd57aeSStanislav Gatev 
333*2ddd57aeSStanislav Gatev   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
334*2ddd57aeSStanislav Gatev }
335*2ddd57aeSStanislav Gatev 
336092a530cSStanislav Gatev auto buildTransferMatchSwitch() {
337b000b770SStanislav Gatev   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
338b000b770SStanislav Gatev   // lot of duplicated work (e.g. string comparisons), consider providing APIs
339b000b770SStanislav Gatev   // that avoid it through memoization.
340af98b0afSStanislav Gatev   return MatchSwitchBuilder<LatticeTransferState>()
341af98b0afSStanislav Gatev       // Attach a symbolic "has_value" state to optional values that we see for
342af98b0afSStanislav Gatev       // the first time.
343092a530cSStanislav Gatev       .CaseOf<Expr>(expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
344af98b0afSStanislav Gatev                     initializeOptionalReference)
345af98b0afSStanislav Gatev 
3469e0fc676SStanislav Gatev       // make_optional
347092a530cSStanislav Gatev       .CaseOf<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
348092a530cSStanislav Gatev 
349b000b770SStanislav Gatev       // optional::optional
350092a530cSStanislav Gatev       .CaseOf<CXXConstructExpr>(
351092a530cSStanislav Gatev           isOptionalInPlaceConstructor(),
352092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
353092a530cSStanislav Gatev              LatticeTransferState &State) {
354092a530cSStanislav Gatev             assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true));
355092a530cSStanislav Gatev           })
356092a530cSStanislav Gatev       .CaseOf<CXXConstructExpr>(
357092a530cSStanislav Gatev           isOptionalNulloptConstructor(),
358092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
359092a530cSStanislav Gatev              LatticeTransferState &State) {
360092a530cSStanislav Gatev             assignOptionalValue(*E, State,
361092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
362092a530cSStanislav Gatev           })
363092a530cSStanislav Gatev       .CaseOf<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
364092a530cSStanislav Gatev                                 transferValueOrConversionConstructor)
3659e0fc676SStanislav Gatev 
366b000b770SStanislav Gatev       // optional::operator=
367b000b770SStanislav Gatev       .CaseOf<CXXOperatorCallExpr>(isOptionalValueOrConversionAssignment(),
368b000b770SStanislav Gatev                                    transferValueOrConversionAssignment)
369b000b770SStanislav Gatev       .CaseOf<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
370b000b770SStanislav Gatev                                    transferNulloptAssignment)
371b000b770SStanislav Gatev 
372af98b0afSStanislav Gatev       // optional::value
373092a530cSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(
374af98b0afSStanislav Gatev           isOptionalMemberCallWithName("value"),
375092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
376092a530cSStanislav Gatev              LatticeTransferState &State) {
377af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
378af98b0afSStanislav Gatev           })
379af98b0afSStanislav Gatev 
380af98b0afSStanislav Gatev       // optional::operator*, optional::operator->
381092a530cSStanislav Gatev       .CaseOf<CallExpr>(expr(anyOf(isOptionalOperatorCallWithName("*"),
382af98b0afSStanislav Gatev                                    isOptionalOperatorCallWithName("->"))),
383092a530cSStanislav Gatev                         [](const CallExpr *E, const MatchFinder::MatchResult &,
384092a530cSStanislav Gatev                            LatticeTransferState &State) {
385af98b0afSStanislav Gatev                           transferUnwrapCall(E, E->getArg(0), State);
386af98b0afSStanislav Gatev                         })
387af98b0afSStanislav Gatev 
388af98b0afSStanislav Gatev       // optional::has_value
389092a530cSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("has_value"),
390af98b0afSStanislav Gatev                                  transferOptionalHasValueCall)
391af98b0afSStanislav Gatev 
3929e0fc676SStanislav Gatev       // optional::operator bool
393092a530cSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("operator bool"),
3949e0fc676SStanislav Gatev                                  transferOptionalHasValueCall)
3959e0fc676SStanislav Gatev 
3969e0fc676SStanislav Gatev       // optional::emplace
397092a530cSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(
398092a530cSStanislav Gatev           isOptionalMemberCallWithName("emplace"),
399092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
400092a530cSStanislav Gatev              LatticeTransferState &State) {
401092a530cSStanislav Gatev             assignOptionalValue(*E->getImplicitObjectArgument(), State,
402092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(true));
403092a530cSStanislav Gatev           })
4049e0fc676SStanislav Gatev 
4059e0fc676SStanislav Gatev       // optional::reset
406092a530cSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(
407092a530cSStanislav Gatev           isOptionalMemberCallWithName("reset"),
408092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
409092a530cSStanislav Gatev              LatticeTransferState &State) {
410092a530cSStanislav Gatev             assignOptionalValue(*E->getImplicitObjectArgument(), State,
411092a530cSStanislav Gatev                                 State.Env.getBoolLiteralValue(false));
412092a530cSStanislav Gatev           })
4139e0fc676SStanislav Gatev 
414*2ddd57aeSStanislav Gatev       // optional::swap
415*2ddd57aeSStanislav Gatev       .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
416*2ddd57aeSStanislav Gatev                                  transferSwapCall)
417*2ddd57aeSStanislav Gatev 
418*2ddd57aeSStanislav Gatev       // std::swap
419*2ddd57aeSStanislav Gatev       .CaseOf<CallExpr>(isStdSwapCall(), transferStdSwapCall)
420*2ddd57aeSStanislav Gatev 
421af98b0afSStanislav Gatev       .Build();
422af98b0afSStanislav Gatev }
423af98b0afSStanislav Gatev 
424af98b0afSStanislav Gatev } // namespace
425af98b0afSStanislav Gatev 
426af98b0afSStanislav Gatev UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
427af98b0afSStanislav Gatev     : DataflowAnalysis<UncheckedOptionalAccessModel, SourceLocationsLattice>(
428af98b0afSStanislav Gatev           Ctx),
429af98b0afSStanislav Gatev       TransferMatchSwitch(buildTransferMatchSwitch()) {}
430af98b0afSStanislav Gatev 
431af98b0afSStanislav Gatev void UncheckedOptionalAccessModel::transfer(const Stmt *S,
432af98b0afSStanislav Gatev                                             SourceLocationsLattice &L,
433af98b0afSStanislav Gatev                                             Environment &Env) {
434af98b0afSStanislav Gatev   LatticeTransferState State(L, Env);
435af98b0afSStanislav Gatev   TransferMatchSwitch(*S, getASTContext(), State);
436af98b0afSStanislav Gatev }
437af98b0afSStanislav Gatev 
438af98b0afSStanislav Gatev } // namespace dataflow
439af98b0afSStanislav Gatev } // namespace clang
440