xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision 11c423f9bebc3be27722225ca8120e8775be836c)
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"
167e63a0d4SYitzhak Mandelbaum #include "clang/AST/DeclCXX.h"
17af98b0afSStanislav Gatev #include "clang/AST/Expr.h"
18af98b0afSStanislav Gatev #include "clang/AST/ExprCXX.h"
19af98b0afSStanislav Gatev #include "clang/AST/Stmt.h"
20af98b0afSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h"
2109b462efSYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchersMacros.h"
227538b360SWei Yi Tee #include "clang/Analysis/CFG.h"
237538b360SWei Yi Tee #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
24af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
256272226bSSam McCall #include "clang/Analysis/FlowSensitive/Formula.h"
26cf1f978dSSam Estep #include "clang/Analysis/FlowSensitive/NoopLattice.h"
270086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h"
28af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h"
2958fe7f96SSam Estep #include "clang/Basic/SourceLocation.h"
30af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h"
31af98b0afSStanislav Gatev #include "llvm/Support/Casting.h"
32d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h"
33af98b0afSStanislav Gatev #include <cassert>
349e0fc676SStanislav Gatev #include <memory>
35a1580d7bSKazu Hirata #include <optional>
369e0fc676SStanislav Gatev #include <utility>
37af98b0afSStanislav Gatev 
38af98b0afSStanislav Gatev namespace clang {
39af98b0afSStanislav Gatev namespace dataflow {
4009b462efSYitzhak Mandelbaum 
41*11c423f9SChris Cotter // Note: the Names appear in reverse order. E.g., to check
42*11c423f9SChris Cotter // if NS is foo::bar::, call isFullyQualifiedNamespaceEqualTo(NS, "bar", "foo")
43*11c423f9SChris Cotter template <class... NameTypes>
44*11c423f9SChris Cotter static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS,
45*11c423f9SChris Cotter                                              llvm::StringRef Name,
46*11c423f9SChris Cotter                                              NameTypes... Names) {
47*11c423f9SChris Cotter   if (!(NS.getDeclName().isIdentifier() && NS.getName() == Name &&
48*11c423f9SChris Cotter         NS.getParent() != nullptr))
49*11c423f9SChris Cotter     return false;
50*11c423f9SChris Cotter 
51*11c423f9SChris Cotter   if constexpr (sizeof...(NameTypes) > 0) {
52*11c423f9SChris Cotter     if (NS.getParent()->isTranslationUnit())
53*11c423f9SChris Cotter       return false;
54*11c423f9SChris Cotter     if (const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.getParent()))
55*11c423f9SChris Cotter       return isFullyQualifiedNamespaceEqualTo(*NextNS, Names...);
56*11c423f9SChris Cotter     return false;
57*11c423f9SChris Cotter   } else {
58*11c423f9SChris Cotter     return NS.getParent()->isTranslationUnit();
59*11c423f9SChris Cotter   }
6009b462efSYitzhak Mandelbaum }
6109b462efSYitzhak Mandelbaum 
6209b462efSYitzhak Mandelbaum static bool hasOptionalClassName(const CXXRecordDecl &RD) {
6309b462efSYitzhak Mandelbaum   if (!RD.getDeclName().isIdentifier())
6409b462efSYitzhak Mandelbaum     return false;
6509b462efSYitzhak Mandelbaum 
6609b462efSYitzhak Mandelbaum   if (RD.getName() == "optional") {
6709b462efSYitzhak Mandelbaum     if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
68*11c423f9SChris Cotter       return N->isStdNamespace() ||
69*11c423f9SChris Cotter              isFullyQualifiedNamespaceEqualTo(*N, "absl") ||
70*11c423f9SChris Cotter              isFullyQualifiedNamespaceEqualTo(*N, "bsl");
7109b462efSYitzhak Mandelbaum     return false;
7209b462efSYitzhak Mandelbaum   }
7309b462efSYitzhak Mandelbaum 
7409b462efSYitzhak Mandelbaum   if (RD.getName() == "Optional") {
752f0630f8SAnton Dukeman     // Check whether namespace is "::base" or "::folly".
7609b462efSYitzhak Mandelbaum     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
77*11c423f9SChris Cotter     return N != nullptr && (isFullyQualifiedNamespaceEqualTo(*N, "base") ||
78*11c423f9SChris Cotter                             isFullyQualifiedNamespaceEqualTo(*N, "folly"));
79*11c423f9SChris Cotter   }
80*11c423f9SChris Cotter 
81*11c423f9SChris Cotter   if (RD.getName() == "NullableValue") {
82*11c423f9SChris Cotter     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
83*11c423f9SChris Cotter     return N != nullptr &&
84*11c423f9SChris Cotter            isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP");
8509b462efSYitzhak Mandelbaum   }
8609b462efSYitzhak Mandelbaum 
8709b462efSYitzhak Mandelbaum   return false;
8809b462efSYitzhak Mandelbaum }
8909b462efSYitzhak Mandelbaum 
90d712c5edSmartinboehme static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) {
91d712c5edSmartinboehme   if (RD == nullptr)
92d712c5edSmartinboehme     return nullptr;
93d712c5edSmartinboehme   if (hasOptionalClassName(*RD))
94d712c5edSmartinboehme     return RD;
95d712c5edSmartinboehme 
96d712c5edSmartinboehme   if (!RD->hasDefinition())
97d712c5edSmartinboehme     return nullptr;
98d712c5edSmartinboehme 
99d712c5edSmartinboehme   for (const CXXBaseSpecifier &Base : RD->bases())
100d712c5edSmartinboehme     if (const CXXRecordDecl *BaseClass =
101d712c5edSmartinboehme             getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl()))
102d712c5edSmartinboehme       return BaseClass;
103d712c5edSmartinboehme 
104d712c5edSmartinboehme   return nullptr;
105d712c5edSmartinboehme }
106d712c5edSmartinboehme 
107af98b0afSStanislav Gatev namespace {
108af98b0afSStanislav Gatev 
109af98b0afSStanislav Gatev using namespace ::clang::ast_matchers;
110cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>;
111af98b0afSStanislav Gatev 
112d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); }
113d712c5edSmartinboehme 
114d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
115d712c5edSmartinboehme   return getOptionalBaseClass(&Node) != nullptr;
11609b462efSYitzhak Mandelbaum }
11709b462efSYitzhak Mandelbaum 
118d712c5edSmartinboehme auto desugarsToOptionalType() {
11965e710c3SStanislav Gatev   return hasUnqualifiedDesugaredType(
120d712c5edSmartinboehme       recordType(hasDeclaration(cxxRecordDecl(optionalClass()))));
12165e710c3SStanislav Gatev }
12265e710c3SStanislav Gatev 
123d712c5edSmartinboehme auto desugarsToOptionalOrDerivedType() {
124d712c5edSmartinboehme   return hasUnqualifiedDesugaredType(
125d712c5edSmartinboehme       recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass()))));
126d712c5edSmartinboehme }
127d712c5edSmartinboehme 
128d712c5edSmartinboehme auto hasOptionalType() { return hasType(desugarsToOptionalType()); }
129d712c5edSmartinboehme 
130d712c5edSmartinboehme /// Matches any of the spellings of the optional types and sugar, aliases,
131d712c5edSmartinboehme /// derived classes, etc.
132d712c5edSmartinboehme auto hasOptionalOrDerivedType() {
133d712c5edSmartinboehme   return hasType(desugarsToOptionalOrDerivedType());
134d712c5edSmartinboehme }
135d712c5edSmartinboehme 
136d712c5edSmartinboehme QualType getPublicType(const Expr *E) {
137d712c5edSmartinboehme   auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());
138d712c5edSmartinboehme   if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) {
139d712c5edSmartinboehme     QualType Ty = E->getType();
140d712c5edSmartinboehme     if (Ty->isPointerType())
141d712c5edSmartinboehme       return Ty->getPointeeType();
142d712c5edSmartinboehme     return Ty;
143d712c5edSmartinboehme   }
144d712c5edSmartinboehme 
145d712c5edSmartinboehme   // Is the derived type that we're casting from the type of `*this`? In this
146d712c5edSmartinboehme   // special case, we can upcast to the base class even if the base is
147d712c5edSmartinboehme   // non-public.
148d712c5edSmartinboehme   bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr());
149d712c5edSmartinboehme 
150d712c5edSmartinboehme   // Find the least-derived type in the path (i.e. the last entry in the list)
151d712c5edSmartinboehme   // that we can access.
152d712c5edSmartinboehme   const CXXBaseSpecifier *PublicBase = nullptr;
153d712c5edSmartinboehme   for (const CXXBaseSpecifier *Base : Cast->path()) {
154d712c5edSmartinboehme     if (Base->getAccessSpecifier() != AS_public && !CastingFromThis)
155d712c5edSmartinboehme       break;
156d712c5edSmartinboehme     PublicBase = Base;
157d712c5edSmartinboehme     CastingFromThis = false;
158d712c5edSmartinboehme   }
159d712c5edSmartinboehme 
160d712c5edSmartinboehme   if (PublicBase != nullptr)
161d712c5edSmartinboehme     return PublicBase->getType();
162d712c5edSmartinboehme 
163d712c5edSmartinboehme   // We didn't find any public type that we could cast to. There may be more
164d712c5edSmartinboehme   // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this
165d712c5edSmartinboehme   // will return the type of `getSubExpr()`.)
166d712c5edSmartinboehme   return getPublicType(Cast->getSubExpr());
167d712c5edSmartinboehme }
168d712c5edSmartinboehme 
169d712c5edSmartinboehme // Returns the least-derived type for the receiver of `MCE` that
170d712c5edSmartinboehme // `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to.
171d712c5edSmartinboehme // Effectively, we upcast until we reach a non-public base class, unless that
172d712c5edSmartinboehme // base is a base of `*this`.
173d712c5edSmartinboehme //
174d712c5edSmartinboehme // This is needed to correctly match methods called on types derived from
175d712c5edSmartinboehme // `std::optional`.
176d712c5edSmartinboehme //
177d712c5edSmartinboehme // Say we have a `struct Derived : public std::optional<int> {} d;` For a call
178d712c5edSmartinboehme // `d.has_value()`, the `getImplicitObjectArgument()` looks like this:
179d712c5edSmartinboehme //
180d712c5edSmartinboehme //   ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue
181d712c5edSmartinboehme //   |            <UncheckedDerivedToBase (optional -> __optional_storage_base)>
182d712c5edSmartinboehme //   `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived'
183d712c5edSmartinboehme //
184d712c5edSmartinboehme // The type of the implicit object argument is `__optional_storage_base`
185d712c5edSmartinboehme // (since this is the internal type that `has_value()` is declared on). If we
186d712c5edSmartinboehme // call `IgnoreParenImpCasts()` on the implicit object argument, we get the
187d712c5edSmartinboehme // `DeclRefExpr`, which has type `Derived`. Neither of these types is
188d712c5edSmartinboehme // `optional`, and hence neither is sufficient for querying whether we are
189d712c5edSmartinboehme // calling a method on `optional`.
190d712c5edSmartinboehme //
191d712c5edSmartinboehme // Instead, starting with the most derived type, we need to follow the chain of
192d712c5edSmartinboehme // casts
193d712c5edSmartinboehme QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) {
194d712c5edSmartinboehme   return getPublicType(MCE.getImplicitObjectArgument());
195d712c5edSmartinboehme }
196d712c5edSmartinboehme 
197d712c5edSmartinboehme AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType,
198d712c5edSmartinboehme               ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
199d712c5edSmartinboehme   return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder);
200d712c5edSmartinboehme }
2016adfc64eSYitzhak Mandelbaum 
2022f0630f8SAnton Dukeman auto isOptionalMemberCallWithNameMatcher(
2032f0630f8SAnton Dukeman     ast_matchers::internal::Matcher<NamedDecl> matcher,
2046ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
205d712c5edSmartinboehme   return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable)))
206d712c5edSmartinboehme                                      : anything(),
207d712c5edSmartinboehme                            publicReceiverType(desugarsToOptionalType()),
2082f0630f8SAnton Dukeman                            callee(cxxMethodDecl(matcher)));
209af98b0afSStanislav Gatev }
210af98b0afSStanislav Gatev 
211a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName(
212a184a0d8SYitzhak Mandelbaum     llvm::StringRef operator_name,
2136ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
214a184a0d8SYitzhak Mandelbaum   return cxxOperatorCallExpr(
215a184a0d8SYitzhak Mandelbaum       hasOverloadedOperatorName(operator_name),
216a184a0d8SYitzhak Mandelbaum       callee(cxxMethodDecl(ofClass(optionalClass()))),
217a184a0d8SYitzhak Mandelbaum       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
218af98b0afSStanislav Gatev }
219af98b0afSStanislav Gatev 
220092a530cSStanislav Gatev auto isMakeOptionalCall() {
221*11c423f9SChris Cotter   return callExpr(
222*11c423f9SChris Cotter       callee(functionDecl(hasAnyName(
223*11c423f9SChris Cotter           "std::make_optional", "base::make_optional", "absl::make_optional",
224*11c423f9SChris Cotter           "folly::make_optional", "bsl::make_optional"))),
2259e0fc676SStanislav Gatev       hasOptionalType());
2269e0fc676SStanislav Gatev }
2279e0fc676SStanislav Gatev 
228390029beSYitzhak Mandelbaum auto nulloptTypeDecl() {
2292f0630f8SAnton Dukeman   return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",
230*11c423f9SChris Cotter                               "base::nullopt_t", "folly::None",
231*11c423f9SChris Cotter                               "bsl::nullopt_t"));
232092a530cSStanislav Gatev }
233092a530cSStanislav Gatev 
234390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
235390029beSYitzhak Mandelbaum 
236092a530cSStanislav Gatev auto inPlaceClass() {
2372f0630f8SAnton Dukeman   return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
238*11c423f9SChris Cotter                                "base::in_place_t", "folly::in_place_t",
239*11c423f9SChris Cotter                                "bsl::in_place_t"));
240092a530cSStanislav Gatev }
241092a530cSStanislav Gatev 
242092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
2430086a355SYitzhak Mandelbaum   return cxxConstructExpr(
2440086a355SYitzhak Mandelbaum       hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
245d712c5edSmartinboehme                                         hasParameter(0, hasNulloptType()))),
246d712c5edSmartinboehme       hasOptionalOrDerivedType());
247092a530cSStanislav Gatev }
248092a530cSStanislav Gatev 
249092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
250d712c5edSmartinboehme   return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())),
251d712c5edSmartinboehme                           hasOptionalOrDerivedType());
252092a530cSStanislav Gatev }
253092a530cSStanislav Gatev 
254092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
255092a530cSStanislav Gatev   return cxxConstructExpr(
256092a530cSStanislav Gatev       unless(hasDeclaration(
257092a530cSStanislav Gatev           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
258d712c5edSmartinboehme       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())),
259d712c5edSmartinboehme       hasOptionalOrDerivedType());
260092a530cSStanislav Gatev }
261092a530cSStanislav Gatev 
262b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
263b000b770SStanislav Gatev   return cxxOperatorCallExpr(
264b000b770SStanislav Gatev       hasOverloadedOperatorName("="),
265d712c5edSmartinboehme       callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
266b000b770SStanislav Gatev       unless(hasDeclaration(cxxMethodDecl(
267b000b770SStanislav Gatev           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
268b000b770SStanislav Gatev       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
269b000b770SStanislav Gatev }
270b000b770SStanislav Gatev 
271b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
272d712c5edSmartinboehme   return cxxOperatorCallExpr(
273d712c5edSmartinboehme       hasOverloadedOperatorName("="),
274d712c5edSmartinboehme       callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
275d712c5edSmartinboehme       argumentCountIs(2), hasArgument(1, hasNulloptType()));
276b000b770SStanislav Gatev }
277b000b770SStanislav Gatev 
2782ddd57aeSStanislav Gatev auto isStdSwapCall() {
2792ddd57aeSStanislav Gatev   return callExpr(callee(functionDecl(hasName("std::swap"))),
280d712c5edSmartinboehme                   argumentCountIs(2),
281d712c5edSmartinboehme                   hasArgument(0, hasOptionalOrDerivedType()),
282d712c5edSmartinboehme                   hasArgument(1, hasOptionalOrDerivedType()));
2832ddd57aeSStanislav Gatev }
2842ddd57aeSStanislav Gatev 
28525956d55SAMS21 auto isStdForwardCall() {
28625956d55SAMS21   return callExpr(callee(functionDecl(hasName("std::forward"))),
287d712c5edSmartinboehme                   argumentCountIs(1),
288d712c5edSmartinboehme                   hasArgument(0, hasOptionalOrDerivedType()));
28925956d55SAMS21 }
29025956d55SAMS21 
2917f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
2927f076004SYitzhak Mandelbaum 
2937f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() {
2947f076004SYitzhak Mandelbaum   // `opt.value_or("").empty()`
2957f076004SYitzhak Mandelbaum   return cxxMemberCallExpr(
2967f076004SYitzhak Mandelbaum       callee(cxxMethodDecl(hasName("empty"))),
2977f076004SYitzhak Mandelbaum       onImplicitObjectArgument(ignoringImplicit(
2987f076004SYitzhak Mandelbaum           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
2997f076004SYitzhak Mandelbaum                             callee(cxxMethodDecl(hasName("value_or"),
3007f076004SYitzhak Mandelbaum                                                  ofClass(optionalClass()))),
3017f076004SYitzhak Mandelbaum                             hasArgument(0, stringLiteral(hasSize(0))))
3027f076004SYitzhak Mandelbaum               .bind(ValueOrCallID))));
3037f076004SYitzhak Mandelbaum }
3047f076004SYitzhak Mandelbaum 
3057f076004SYitzhak Mandelbaum auto isValueOrNotEqX() {
3067f076004SYitzhak Mandelbaum   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
3077f076004SYitzhak Mandelbaum     return hasOperands(
3087f076004SYitzhak Mandelbaum         ignoringImplicit(
3097f076004SYitzhak Mandelbaum             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
3107f076004SYitzhak Mandelbaum                               callee(cxxMethodDecl(hasName("value_or"),
3117f076004SYitzhak Mandelbaum                                                    ofClass(optionalClass()))),
3127f076004SYitzhak Mandelbaum                               hasArgument(0, Arg))
3137f076004SYitzhak Mandelbaum                 .bind(ValueOrCallID)),
3147f076004SYitzhak Mandelbaum         ignoringImplicit(Arg));
3157f076004SYitzhak Mandelbaum   };
3167f076004SYitzhak Mandelbaum 
3177f076004SYitzhak Mandelbaum   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
3187f076004SYitzhak Mandelbaum   // support this pattern for any expression, but the AST does not have a
3197f076004SYitzhak Mandelbaum   // generic expression comparison facility, so we specialize to common cases
3207f076004SYitzhak Mandelbaum   // seen in practice.  FIXME: define a matcher that compares values across
3217f076004SYitzhak Mandelbaum   // nodes, which would let us generalize this to any `X`.
3227f076004SYitzhak Mandelbaum   return binaryOperation(hasOperatorName("!="),
3237f076004SYitzhak Mandelbaum                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
3247f076004SYitzhak Mandelbaum                                ComparesToSame(stringLiteral(hasSize(0))),
3257f076004SYitzhak Mandelbaum                                ComparesToSame(integerLiteral(equals(0)))));
3267f076004SYitzhak Mandelbaum }
3277f076004SYitzhak Mandelbaum 
32865e710c3SStanislav Gatev auto isCallReturningOptional() {
329d712c5edSmartinboehme   return callExpr(hasType(qualType(
330d712c5edSmartinboehme       anyOf(desugarsToOptionalOrDerivedType(),
331d712c5edSmartinboehme             referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
33265e710c3SStanislav Gatev }
33365e710c3SStanislav Gatev 
334390029beSYitzhak Mandelbaum template <typename L, typename R>
335390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
336390029beSYitzhak Mandelbaum   return cxxOperatorCallExpr(
337390029beSYitzhak Mandelbaum       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
338390029beSYitzhak Mandelbaum       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
339390029beSYitzhak Mandelbaum       hasArgument(1, rhs_arg_matcher));
340390029beSYitzhak Mandelbaum }
341390029beSYitzhak Mandelbaum 
3426272226bSSam McCall /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
3436272226bSSam McCall const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
3442ee396b0Smartinboehme   auto *Value = Env.get<BoolValue>(Expr);
345390029beSYitzhak Mandelbaum   if (Value != nullptr)
3466272226bSSam McCall     return Value->formula();
347390029beSYitzhak Mandelbaum 
348390029beSYitzhak Mandelbaum   Value = &Env.makeAtomicBoolValue();
349b244b6aeSMartin Braenne   Env.setValue(Expr, *Value);
3506272226bSSam McCall   return Value->formula();
351390029beSYitzhak Mandelbaum }
352390029beSYitzhak Mandelbaum 
35371f2ec2dSmartinboehme StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) {
35471f2ec2dSmartinboehme   return OptionalLoc.getSyntheticField("has_value");
35571f2ec2dSmartinboehme }
35671f2ec2dSmartinboehme 
35771f2ec2dSmartinboehme StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) {
35871f2ec2dSmartinboehme   return OptionalLoc.getSyntheticField("value");
35971f2ec2dSmartinboehme }
36071f2ec2dSmartinboehme 
3618fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
36271f2ec2dSmartinboehme /// property of the optional at `OptionalLoc`.
36371f2ec2dSmartinboehme void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,
36471f2ec2dSmartinboehme                  Environment &Env) {
36571f2ec2dSmartinboehme   Env.setValue(locForHasValue(OptionalLoc), HasValueVal);
3668fcdd625SStanislav Gatev }
3678fcdd625SStanislav Gatev 
368af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
36971f2ec2dSmartinboehme /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null.
37071f2ec2dSmartinboehme BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {
37171f2ec2dSmartinboehme   if (OptionalLoc == nullptr)
37271f2ec2dSmartinboehme     return nullptr;
37371f2ec2dSmartinboehme   StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
3742ee396b0Smartinboehme   auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);
375dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr) {
376dd38caf3SYitzhak Mandelbaum     HasValueVal = &Env.makeAtomicBoolValue();
37771f2ec2dSmartinboehme     Env.setValue(HasValueLoc, *HasValueVal);
378dd38caf3SYitzhak Mandelbaum   }
379dd38caf3SYitzhak Mandelbaum   return HasValueVal;
380af98b0afSStanislav Gatev }
381af98b0afSStanislav Gatev 
382d712c5edSmartinboehme QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {
383d712c5edSmartinboehme   auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);
384d712c5edSmartinboehme   return CTSD.getTemplateArgs()[0].getAsType();
385092a530cSStanislav Gatev }
386092a530cSStanislav Gatev 
387092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
388092a530cSStanislav Gatev ///
389092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
390092a530cSStanislav Gatev /// function will be 2.
391092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
392d712c5edSmartinboehme   const CXXRecordDecl *Optional =
393d712c5edSmartinboehme       getOptionalBaseClass(Type->getAsCXXRecordDecl());
394d712c5edSmartinboehme   if (Optional == nullptr)
395092a530cSStanislav Gatev     return 0;
396092a530cSStanislav Gatev   return 1 + countOptionalWrappers(
397092a530cSStanislav Gatev                  ASTCtx,
398d712c5edSmartinboehme                  valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
399092a530cSStanislav Gatev }
400092a530cSStanislav Gatev 
40171f2ec2dSmartinboehme StorageLocation *getLocBehindPossiblePointer(const Expr &E,
40271f2ec2dSmartinboehme                                              const Environment &Env) {
40371f2ec2dSmartinboehme   if (E.isPRValue()) {
40471f2ec2dSmartinboehme     if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
40571f2ec2dSmartinboehme       return &PointerVal->getPointeeLoc();
406dd38caf3SYitzhak Mandelbaum     return nullptr;
407dd38caf3SYitzhak Mandelbaum   }
40871f2ec2dSmartinboehme   return Env.getStorageLocation(E);
40948bc7150SMartin Braenne }
41048bc7150SMartin Braenne 
411092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
412af98b0afSStanislav Gatev                         LatticeTransferState &State) {
41371f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
41471f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
415b244b6aeSMartin Braenne     if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)
41671f2ec2dSmartinboehme       State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
417af98b0afSStanislav Gatev   }
418dd38caf3SYitzhak Mandelbaum }
419af98b0afSStanislav Gatev 
4203bc1ea5bSMartin Braenne void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
4213bc1ea5bSMartin Braenne                          LatticeTransferState &State) {
42271f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
42371f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
42471f2ec2dSmartinboehme     State.Env.setValue(
42571f2ec2dSmartinboehme         *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));
4263bc1ea5bSMartin Braenne }
4273bc1ea5bSMartin Braenne 
428092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
429092a530cSStanislav Gatev                               const MatchFinder::MatchResult &,
430092a530cSStanislav Gatev                               LatticeTransferState &State) {
431e8fce958Smartinboehme   setHasValue(State.Env.getResultObjectLocation(*E),
432e8fce958Smartinboehme               State.Env.getBoolLiteralValue(true), State.Env);
4339e0fc676SStanislav Gatev }
4349e0fc676SStanislav Gatev 
435092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
436092a530cSStanislav Gatev                                   const MatchFinder::MatchResult &,
437af98b0afSStanislav Gatev                                   LatticeTransferState &State) {
438dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal = getHasValue(
43971f2ec2dSmartinboehme           State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
440b244b6aeSMartin Braenne     State.Env.setValue(*CallExpr, *HasValueVal);
441af98b0afSStanislav Gatev   }
442af98b0afSStanislav Gatev }
443af98b0afSStanislav Gatev 
444*11c423f9SChris Cotter void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr,
445*11c423f9SChris Cotter                                 const MatchFinder::MatchResult &,
446*11c423f9SChris Cotter                                 LatticeTransferState &State) {
447*11c423f9SChris Cotter   if (auto *HasValueVal = getHasValue(
448*11c423f9SChris Cotter           State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
449*11c423f9SChris Cotter     State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal));
450*11c423f9SChris Cotter   }
451*11c423f9SChris Cotter }
452*11c423f9SChris Cotter 
4537f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in
4547f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property.
4556272226bSSam McCall void transferValueOrImpl(
4566272226bSSam McCall     const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
4577f076004SYitzhak Mandelbaum     LatticeTransferState &State,
4586272226bSSam McCall     const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
4596272226bSSam McCall                                 const Formula &HasValueVal)) {
4607f076004SYitzhak Mandelbaum   auto &Env = State.Env;
4617f076004SYitzhak Mandelbaum 
46271f2ec2dSmartinboehme   const auto *MCE =
46371f2ec2dSmartinboehme       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);
4647f076004SYitzhak Mandelbaum 
46571f2ec2dSmartinboehme   auto *HasValueVal =
46671f2ec2dSmartinboehme       getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env));
467dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr)
4687f076004SYitzhak Mandelbaum     return;
4697f076004SYitzhak Mandelbaum 
470526c9b7eSmartinboehme   Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
4716272226bSSam McCall                        HasValueVal->formula()));
4727f076004SYitzhak Mandelbaum }
4737f076004SYitzhak Mandelbaum 
4747f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
4757f076004SYitzhak Mandelbaum                                     const MatchFinder::MatchResult &Result,
4767f076004SYitzhak Mandelbaum                                     LatticeTransferState &State) {
4777f076004SYitzhak Mandelbaum   return transferValueOrImpl(ComparisonExpr, Result, State,
4786272226bSSam McCall                              [](Environment &Env, const Formula &ExprVal,
4796272226bSSam McCall                                 const Formula &HasValueVal) -> const Formula & {
4806272226bSSam McCall                                auto &A = Env.arena();
4817f076004SYitzhak Mandelbaum                                // If the result is *not* empty, then we know the
4827f076004SYitzhak Mandelbaum                                // optional must have been holding a value. If
4837f076004SYitzhak Mandelbaum                                // `ExprVal` is true, though, we don't learn
4847f076004SYitzhak Mandelbaum                                // anything definite about `has_value`, so we
4857f076004SYitzhak Mandelbaum                                // don't add any corresponding implications to
4867f076004SYitzhak Mandelbaum                                // the flow condition.
4876272226bSSam McCall                                return A.makeImplies(A.makeNot(ExprVal),
4887f076004SYitzhak Mandelbaum                                                     HasValueVal);
4897f076004SYitzhak Mandelbaum                              });
4907f076004SYitzhak Mandelbaum }
4917f076004SYitzhak Mandelbaum 
4927f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr,
4937f076004SYitzhak Mandelbaum                            const MatchFinder::MatchResult &Result,
4947f076004SYitzhak Mandelbaum                            LatticeTransferState &State) {
4957f076004SYitzhak Mandelbaum   transferValueOrImpl(ComparisonExpr, Result, State,
4966272226bSSam McCall                       [](Environment &Env, const Formula &ExprVal,
4976272226bSSam McCall                          const Formula &HasValueVal) -> const Formula & {
4986272226bSSam McCall                         auto &A = Env.arena();
4997f076004SYitzhak Mandelbaum                         // We know that if `(opt.value_or(X) != X)` then
5007f076004SYitzhak Mandelbaum                         // `opt.hasValue()`, even without knowing further
5017f076004SYitzhak Mandelbaum                         // details about the contents of `opt`.
5026272226bSSam McCall                         return A.makeImplies(ExprVal, HasValueVal);
5037f076004SYitzhak Mandelbaum                       });
5047f076004SYitzhak Mandelbaum }
5057f076004SYitzhak Mandelbaum 
50665e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E,
50765e710c3SStanislav Gatev                                    const MatchFinder::MatchResult &Result,
50865e710c3SStanislav Gatev                                    LatticeTransferState &State) {
5099ecdbe38SMartin Braenne   RecordStorageLocation *Loc = nullptr;
51044f98d01SMartin Braenne   if (E->isPRValue()) {
51144f98d01SMartin Braenne     Loc = &State.Env.getResultObjectLocation(*E);
51244f98d01SMartin Braenne   } else {
5132ee396b0Smartinboehme     Loc = State.Env.get<RecordStorageLocation>(*E);
514f76f6674SMartin Braenne     if (Loc == nullptr) {
5159ecdbe38SMartin Braenne       Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));
516b244b6aeSMartin Braenne       State.Env.setStorageLocation(*E, *Loc);
51744f98d01SMartin Braenne     }
518f76f6674SMartin Braenne   }
51944f98d01SMartin Braenne 
520e8fce958Smartinboehme   if (State.Env.getValue(locForHasValue(*Loc)) != nullptr)
521e8fce958Smartinboehme     return;
522e8fce958Smartinboehme 
523e8fce958Smartinboehme   setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
52465e710c3SStanislav Gatev }
52565e710c3SStanislav Gatev 
526f653d140SMartin Braenne void constructOptionalValue(const Expr &E, Environment &Env,
527092a530cSStanislav Gatev                             BoolValue &HasValueVal) {
5289ecdbe38SMartin Braenne   RecordStorageLocation &Loc = Env.getResultObjectLocation(E);
529e8fce958Smartinboehme   setHasValue(Loc, HasValueVal, Env);
5309e0fc676SStanislav Gatev }
5319e0fc676SStanislav Gatev 
532b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
533b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
534b000b770SStanislav Gatev /// where `T` is constructible from `U`.
535ae280281Smartinboehme BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E,
536b000b770SStanislav Gatev                                      const MatchFinder::MatchResult &MatchRes,
537b000b770SStanislav Gatev                                      LatticeTransferState &State) {
538ae280281Smartinboehme   const int DestTypeOptionalWrappersCount =
539ae280281Smartinboehme       countOptionalWrappers(*MatchRes.Context, DestType);
540c849843cSMartin Braenne   const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
541c849843cSMartin Braenne       *MatchRes.Context, E.getType().getNonReferenceType());
542b000b770SStanislav Gatev 
543ae280281Smartinboehme   // Is this an constructor of the form `template<class U> optional(U &&)` /
544ae280281Smartinboehme   // assignment of the form `template<class U> optional& operator=(U &&)`
545ae280281Smartinboehme   // (where `T` is assignable / constructible from `U`)?
546ae280281Smartinboehme   // We recognize this because the number of optionals in the optional being
547ae280281Smartinboehme   // assigned to is different from the function argument type.
548ae280281Smartinboehme   if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)
549b000b770SStanislav Gatev     return State.Env.getBoolLiteralValue(true);
550b000b770SStanislav Gatev 
551ae280281Smartinboehme   // Otherwise, this must be a constructor of the form
552ae280281Smartinboehme   // `template <class U> optional<optional<U> &&)` / assignment of the form
553ae280281Smartinboehme   // `template <class U> optional& operator=(optional<U> &&)
554ae280281Smartinboehme   // (where, again, `T` is assignable / constructible from `U`).
5552ee396b0Smartinboehme   auto *Loc = State.Env.get<RecordStorageLocation>(E);
55671f2ec2dSmartinboehme   if (auto *HasValueVal = getHasValue(State.Env, Loc))
557dd38caf3SYitzhak Mandelbaum     return *HasValueVal;
558b000b770SStanislav Gatev   return State.Env.makeAtomicBoolValue();
559b000b770SStanislav Gatev }
560b000b770SStanislav Gatev 
561092a530cSStanislav Gatev void transferValueOrConversionConstructor(
562092a530cSStanislav Gatev     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
5639e0fc676SStanislav Gatev     LatticeTransferState &State) {
564092a530cSStanislav Gatev   assert(E->getNumArgs() > 0);
565092a530cSStanislav Gatev 
566ae280281Smartinboehme   constructOptionalValue(
567ae280281Smartinboehme       *E, State.Env,
568ae280281Smartinboehme       valueOrConversionHasValue(
569ae280281Smartinboehme           E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0),
570ae280281Smartinboehme           MatchRes, State));
571b000b770SStanislav Gatev }
572092a530cSStanislav Gatev 
573b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
574b000b770SStanislav Gatev                         LatticeTransferState &State) {
575b000b770SStanislav Gatev   assert(E->getNumArgs() > 0);
576b000b770SStanislav Gatev 
5772ee396b0Smartinboehme   if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) {
578e8fce958Smartinboehme     setHasValue(*Loc, HasValueVal, State.Env);
579b000b770SStanislav Gatev 
580b000b770SStanislav Gatev     // Assign a storage location for the whole expression.
581b244b6aeSMartin Braenne     State.Env.setStorageLocation(*E, *Loc);
582f653d140SMartin Braenne   }
583b000b770SStanislav Gatev }
584b000b770SStanislav Gatev 
585b000b770SStanislav Gatev void transferValueOrConversionAssignment(
586b000b770SStanislav Gatev     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
587b000b770SStanislav Gatev     LatticeTransferState &State) {
588b000b770SStanislav Gatev   assert(E->getNumArgs() > 1);
589ae280281Smartinboehme   transferAssignment(
590ae280281Smartinboehme       E,
591ae280281Smartinboehme       valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(),
59206decd0bSKazu Hirata                                 *E->getArg(1), MatchRes, State),
593b000b770SStanislav Gatev       State);
594b000b770SStanislav Gatev }
595b000b770SStanislav Gatev 
596b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
597b000b770SStanislav Gatev                                const MatchFinder::MatchResult &,
598b000b770SStanislav Gatev                                LatticeTransferState &State) {
599b000b770SStanislav Gatev   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
6009e0fc676SStanislav Gatev }
6019e0fc676SStanislav Gatev 
6029ecdbe38SMartin Braenne void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
6039ecdbe38SMartin Braenne                   Environment &Env) {
604d4fb829bSYitzhak Mandelbaum   // We account for cases where one or both of the optionals are not modeled,
605d4fb829bSYitzhak Mandelbaum   // either lacking associated storage locations, or lacking values associated
606d4fb829bSYitzhak Mandelbaum   // to such storage locations.
6072ddd57aeSStanislav Gatev 
608d4fb829bSYitzhak Mandelbaum   if (Loc1 == nullptr) {
609d4fb829bSYitzhak Mandelbaum     if (Loc2 != nullptr)
610e8fce958Smartinboehme       setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env);
611d4fb829bSYitzhak Mandelbaum     return;
612d4fb829bSYitzhak Mandelbaum   }
613d4fb829bSYitzhak Mandelbaum   if (Loc2 == nullptr) {
614e8fce958Smartinboehme     setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env);
615d4fb829bSYitzhak Mandelbaum     return;
616d4fb829bSYitzhak Mandelbaum   }
6172ddd57aeSStanislav Gatev 
618d4fb829bSYitzhak Mandelbaum   // Both expressions have locations, though they may not have corresponding
619d4fb829bSYitzhak Mandelbaum   // values. In that case, we create a fresh value at this point. Note that if
620d4fb829bSYitzhak Mandelbaum   // two branches both do this, they will not share the value, but it at least
621d4fb829bSYitzhak Mandelbaum   // allows for local reasoning about the value. To avoid the above, we would
622d4fb829bSYitzhak Mandelbaum   // need *lazy* value allocation.
623d4fb829bSYitzhak Mandelbaum   // FIXME: allocate values lazily, instead of just creating a fresh value.
62471f2ec2dSmartinboehme   BoolValue *BoolVal1 = getHasValue(Env, Loc1);
625f653d140SMartin Braenne   if (BoolVal1 == nullptr)
626f653d140SMartin Braenne     BoolVal1 = &Env.makeAtomicBoolValue();
627d4fb829bSYitzhak Mandelbaum 
62871f2ec2dSmartinboehme   BoolValue *BoolVal2 = getHasValue(Env, Loc2);
629f653d140SMartin Braenne   if (BoolVal2 == nullptr)
630f653d140SMartin Braenne     BoolVal2 = &Env.makeAtomicBoolValue();
631d4fb829bSYitzhak Mandelbaum 
632e8fce958Smartinboehme   setHasValue(*Loc1, *BoolVal2, Env);
633e8fce958Smartinboehme   setHasValue(*Loc2, *BoolVal1, Env);
6342ddd57aeSStanislav Gatev }
6352ddd57aeSStanislav Gatev 
6362ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
6372ddd57aeSStanislav Gatev                       const MatchFinder::MatchResult &,
6382ddd57aeSStanislav Gatev                       LatticeTransferState &State) {
6392ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 1);
6402ee396b0Smartinboehme   auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
641f653d140SMartin Braenne   transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
6422ddd57aeSStanislav Gatev }
6432ddd57aeSStanislav Gatev 
6442ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
6452ddd57aeSStanislav Gatev                          LatticeTransferState &State) {
6462ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 2);
6472ee396b0Smartinboehme   auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
6482ee396b0Smartinboehme   auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1));
649f653d140SMartin Braenne   transferSwap(Arg0Loc, Arg1Loc, State.Env);
6502ddd57aeSStanislav Gatev }
6512ddd57aeSStanislav Gatev 
65225956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
65325956d55SAMS21                             LatticeTransferState &State) {
65425956d55SAMS21   assert(E->getNumArgs() == 1);
65525956d55SAMS21 
656b244b6aeSMartin Braenne   if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
657b244b6aeSMartin Braenne     State.Env.setStorageLocation(*E, *Loc);
65825956d55SAMS21 }
65925956d55SAMS21 
6606272226bSSam McCall const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
6616272226bSSam McCall                                 const Formula &LHS, const Formula &RHS) {
662390029beSYitzhak Mandelbaum   // Logically, an optional<T> object is composed of two values - a `has_value`
663390029beSYitzhak Mandelbaum   // bit and a value of type T. Equality of optional objects compares both
664390029beSYitzhak Mandelbaum   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
665390029beSYitzhak Mandelbaum   // when two optional objects are engaged, the equality of their respective
666390029beSYitzhak Mandelbaum   // values of type T matters. Since we only track the `has_value` bits, we
667390029beSYitzhak Mandelbaum   // can't make any conclusions about equality when we know that two optional
668390029beSYitzhak Mandelbaum   // objects are engaged.
669390029beSYitzhak Mandelbaum   //
670390029beSYitzhak Mandelbaum   // We express this as two facts about the equality:
671390029beSYitzhak Mandelbaum   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
672390029beSYitzhak Mandelbaum   //    If they are equal, then either both are set or both are unset.
673390029beSYitzhak Mandelbaum   // b) (!LHS & !RHS) => EqVal
674390029beSYitzhak Mandelbaum   //    If neither is set, then they are equal.
675390029beSYitzhak Mandelbaum   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
6766272226bSSam McCall   return A.makeAnd(
6776272226bSSam McCall       A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
6786272226bSSam McCall                                     A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
6796272226bSSam McCall       A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
680390029beSYitzhak Mandelbaum }
681390029beSYitzhak Mandelbaum 
682390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
683390029beSYitzhak Mandelbaum                                     const MatchFinder::MatchResult &,
684390029beSYitzhak Mandelbaum                                     LatticeTransferState &State) {
685390029beSYitzhak Mandelbaum   Environment &Env = State.Env;
6866272226bSSam McCall   auto &A = Env.arena();
687390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
6882ee396b0Smartinboehme   auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0));
68971f2ec2dSmartinboehme   if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {
6902ee396b0Smartinboehme     auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1));
69171f2ec2dSmartinboehme     if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {
692390029beSYitzhak Mandelbaum       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
6936272226bSSam McCall         CmpValue = &A.makeNot(*CmpValue);
694526c9b7eSmartinboehme       Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
6956272226bSSam McCall                                   RHasVal->formula()));
696390029beSYitzhak Mandelbaum     }
697390029beSYitzhak Mandelbaum   }
69871f2ec2dSmartinboehme }
699390029beSYitzhak Mandelbaum 
700390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
701390029beSYitzhak Mandelbaum                                  const clang::Expr *E, Environment &Env) {
7026272226bSSam McCall   auto &A = Env.arena();
703390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
7042ee396b0Smartinboehme   auto *Loc = Env.get<RecordStorageLocation>(*E);
70571f2ec2dSmartinboehme   if (auto *HasVal = getHasValue(Env, Loc)) {
706390029beSYitzhak Mandelbaum     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
7076272226bSSam McCall       CmpValue = &A.makeNot(*CmpValue);
708526c9b7eSmartinboehme     Env.assume(
7096272226bSSam McCall         evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
710390029beSYitzhak Mandelbaum   }
711390029beSYitzhak Mandelbaum }
712390029beSYitzhak Mandelbaum 
71371f2ec2dSmartinboehme void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,
71471f2ec2dSmartinboehme                                    const clang::Expr *E, Environment &Env) {
71571f2ec2dSmartinboehme   auto &A = Env.arena();
71671f2ec2dSmartinboehme   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
7172ee396b0Smartinboehme   auto *Loc = Env.get<RecordStorageLocation>(*E);
71871f2ec2dSmartinboehme   if (auto *HasVal = getHasValue(Env, Loc)) {
71971f2ec2dSmartinboehme     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
72071f2ec2dSmartinboehme       CmpValue = &A.makeNot(*CmpValue);
72171f2ec2dSmartinboehme     Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
72271f2ec2dSmartinboehme                                 A.makeLiteral(false)));
72371f2ec2dSmartinboehme   }
72471f2ec2dSmartinboehme }
72571f2ec2dSmartinboehme 
7266ad0788cSKazu Hirata std::optional<StatementMatcher>
727a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
7285d22d1f5SYitzhak Mandelbaum   if (Options.IgnoreSmartPointerDereference) {
7295d22d1f5SYitzhak Mandelbaum     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
7305d22d1f5SYitzhak Mandelbaum         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
7315d22d1f5SYitzhak Mandelbaum         unless(hasArgument(0, expr(hasOptionalType()))))));
7325d22d1f5SYitzhak Mandelbaum     return expr(
7335d22d1f5SYitzhak Mandelbaum         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
7345d22d1f5SYitzhak Mandelbaum   }
73534e0d057SKazu Hirata   return std::nullopt;
736a184a0d8SYitzhak Mandelbaum }
737a184a0d8SYitzhak Mandelbaum 
73858fe7f96SSam Estep StatementMatcher
7396ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
7402f0630f8SAnton Dukeman   return isOptionalMemberCallWithNameMatcher(hasName("value"),
7412f0630f8SAnton Dukeman                                              IgnorableOptional);
74258fe7f96SSam Estep }
74358fe7f96SSam Estep 
74458fe7f96SSam Estep StatementMatcher
7456ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
74658fe7f96SSam Estep   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
74758fe7f96SSam Estep                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
74858fe7f96SSam Estep }
74958fe7f96SSam Estep 
7505d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() {
751b000b770SStanislav Gatev   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
752b000b770SStanislav Gatev   // lot of duplicated work (e.g. string comparisons), consider providing APIs
753b000b770SStanislav Gatev   // that avoid it through memoization.
7547538b360SWei Yi Tee   return CFGMatchSwitchBuilder<LatticeTransferState>()
7559e0fc676SStanislav Gatev       // make_optional
7567538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
757092a530cSStanislav Gatev 
7580e8d4a6dSYitzhak Mandelbaum       // optional::optional (in place)
7597538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
760092a530cSStanislav Gatev           isOptionalInPlaceConstructor(),
761092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
762092a530cSStanislav Gatev              LatticeTransferState &State) {
763f653d140SMartin Braenne             constructOptionalValue(*E, State.Env,
7640e8d4a6dSYitzhak Mandelbaum                                    State.Env.getBoolLiteralValue(true));
765092a530cSStanislav Gatev           })
7660e8d4a6dSYitzhak Mandelbaum       // optional::optional(nullopt_t)
767390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXConstructExpr>(
768390029beSYitzhak Mandelbaum           isOptionalNulloptConstructor(),
769390029beSYitzhak Mandelbaum           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
770390029beSYitzhak Mandelbaum              LatticeTransferState &State) {
771f653d140SMartin Braenne             constructOptionalValue(*E, State.Env,
7720e8d4a6dSYitzhak Mandelbaum                                    State.Env.getBoolLiteralValue(false));
773390029beSYitzhak Mandelbaum           })
7740e8d4a6dSYitzhak Mandelbaum       // optional::optional (value/conversion)
7757538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
776092a530cSStanislav Gatev                                        transferValueOrConversionConstructor)
7779e0fc676SStanislav Gatev 
778b000b770SStanislav Gatev       // optional::operator=
7797538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(
7807538b360SWei Yi Tee           isOptionalValueOrConversionAssignment(),
781b000b770SStanislav Gatev           transferValueOrConversionAssignment)
7827538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
783b000b770SStanislav Gatev                                           transferNulloptAssignment)
784b000b770SStanislav Gatev 
785af98b0afSStanislav Gatev       // optional::value
7867538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
7875d22d1f5SYitzhak Mandelbaum           valueCall(std::nullopt),
788092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
789092a530cSStanislav Gatev              LatticeTransferState &State) {
790af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
791af98b0afSStanislav Gatev           })
792af98b0afSStanislav Gatev 
7933bc1ea5bSMartin Braenne       // optional::operator*
7943bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
7957538b360SWei Yi Tee                                [](const CallExpr *E,
7967538b360SWei Yi Tee                                   const MatchFinder::MatchResult &,
797092a530cSStanislav Gatev                                   LatticeTransferState &State) {
798af98b0afSStanislav Gatev                                  transferUnwrapCall(E, E->getArg(0), State);
799af98b0afSStanislav Gatev                                })
800af98b0afSStanislav Gatev 
8013bc1ea5bSMartin Braenne       // optional::operator->
8023bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
8033bc1ea5bSMartin Braenne                                [](const CallExpr *E,
8043bc1ea5bSMartin Braenne                                   const MatchFinder::MatchResult &,
8053bc1ea5bSMartin Braenne                                   LatticeTransferState &State) {
8063bc1ea5bSMartin Braenne                                  transferArrowOpCall(E, E->getArg(0), State);
8073bc1ea5bSMartin Braenne                                })
8083bc1ea5bSMartin Braenne 
8092f0630f8SAnton Dukeman       // optional::has_value, optional::hasValue
8102f0630f8SAnton Dukeman       // Of the supported optionals only folly::Optional uses hasValue, but this
8112f0630f8SAnton Dukeman       // will also pass for other types
8127538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8132f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(
8142f0630f8SAnton Dukeman               hasAnyName("has_value", "hasValue")),
815af98b0afSStanislav Gatev           transferOptionalHasValueCall)
816af98b0afSStanislav Gatev 
8179e0fc676SStanislav Gatev       // optional::operator bool
8187538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8192f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
8209e0fc676SStanislav Gatev           transferOptionalHasValueCall)
8219e0fc676SStanislav Gatev 
822*11c423f9SChris Cotter       // NullableValue::isNull
823*11c423f9SChris Cotter       // Only NullableValue has isNull
824*11c423f9SChris Cotter       .CaseOfCFGStmt<CXXMemberCallExpr>(
825*11c423f9SChris Cotter           isOptionalMemberCallWithNameMatcher(hasName("isNull")),
826*11c423f9SChris Cotter           transferOptionalIsNullCall)
827*11c423f9SChris Cotter 
8289e0fc676SStanislav Gatev       // optional::emplace
8297538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8302f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("emplace")),
831092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
832092a530cSStanislav Gatev              LatticeTransferState &State) {
8339ecdbe38SMartin Braenne             if (RecordStorageLocation *Loc =
834f653d140SMartin Braenne                     getImplicitObjectLocation(*E, State.Env)) {
835e8fce958Smartinboehme               setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);
836f653d140SMartin Braenne             }
837092a530cSStanislav Gatev           })
8389e0fc676SStanislav Gatev 
8399e0fc676SStanislav Gatev       // optional::reset
8407538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
8412f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("reset")),
842092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
843092a530cSStanislav Gatev              LatticeTransferState &State) {
8449ecdbe38SMartin Braenne             if (RecordStorageLocation *Loc =
845f653d140SMartin Braenne                     getImplicitObjectLocation(*E, State.Env)) {
846e8fce958Smartinboehme               setHasValue(*Loc, State.Env.getBoolLiteralValue(false),
847f653d140SMartin Braenne                           State.Env);
848f653d140SMartin Braenne             }
849092a530cSStanislav Gatev           })
8509e0fc676SStanislav Gatev 
8512ddd57aeSStanislav Gatev       // optional::swap
8522f0630f8SAnton Dukeman       .CaseOfCFGStmt<CXXMemberCallExpr>(
8532f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("swap")),
8542ddd57aeSStanislav Gatev           transferSwapCall)
8552ddd57aeSStanislav Gatev 
8562ddd57aeSStanislav Gatev       // std::swap
8577538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
8582ddd57aeSStanislav Gatev 
85925956d55SAMS21       // std::forward
86025956d55SAMS21       .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
86125956d55SAMS21 
8627f076004SYitzhak Mandelbaum       // opt.value_or("").empty()
8637538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
8647538b360SWei Yi Tee                            transferValueOrStringEmptyCall)
8657f076004SYitzhak Mandelbaum 
8667f076004SYitzhak Mandelbaum       // opt.value_or(X) != X
8677538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
8687f076004SYitzhak Mandelbaum 
869390029beSYitzhak Mandelbaum       // Comparisons (==, !=):
870390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
87171f2ec2dSmartinboehme           isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
872390029beSYitzhak Mandelbaum           transferOptionalAndOptionalCmp)
873390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
87471f2ec2dSmartinboehme           isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
87571f2ec2dSmartinboehme           [](const clang::CXXOperatorCallExpr *Cmp,
87671f2ec2dSmartinboehme              const MatchFinder::MatchResult &, LatticeTransferState &State) {
87771f2ec2dSmartinboehme             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);
87871f2ec2dSmartinboehme           })
87971f2ec2dSmartinboehme       .CaseOfCFGStmt<CXXOperatorCallExpr>(
88071f2ec2dSmartinboehme           isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
88171f2ec2dSmartinboehme           [](const clang::CXXOperatorCallExpr *Cmp,
88271f2ec2dSmartinboehme              const MatchFinder::MatchResult &, LatticeTransferState &State) {
88371f2ec2dSmartinboehme             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);
88471f2ec2dSmartinboehme           })
88571f2ec2dSmartinboehme       .CaseOfCFGStmt<CXXOperatorCallExpr>(
88671f2ec2dSmartinboehme           isComparisonOperatorCall(
88771f2ec2dSmartinboehme               hasOptionalType(),
88871f2ec2dSmartinboehme               unless(anyOf(hasOptionalType(), hasNulloptType()))),
889390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
890390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
891390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
892390029beSYitzhak Mandelbaum           })
893390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
89471f2ec2dSmartinboehme           isComparisonOperatorCall(
89571f2ec2dSmartinboehme               unless(anyOf(hasOptionalType(), hasNulloptType())),
896390029beSYitzhak Mandelbaum               hasOptionalType()),
897390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
898390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
899390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
900390029beSYitzhak Mandelbaum           })
901390029beSYitzhak Mandelbaum 
90265e710c3SStanislav Gatev       // returns optional
9037538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
90465e710c3SStanislav Gatev                                transferCallReturningOptional)
90565e710c3SStanislav Gatev 
906af98b0afSStanislav Gatev       .Build();
907af98b0afSStanislav Gatev }
908af98b0afSStanislav Gatev 
909004a7ceaSYitzhak Mandelbaum llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
91058fe7f96SSam Estep                                                      const Environment &Env) {
91171f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
91271f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, Env))) {
91371f2ec2dSmartinboehme     auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
91458fe7f96SSam Estep     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
915526c9b7eSmartinboehme       if (Env.proves(HasValueVal->formula()))
91658fe7f96SSam Estep         return {};
91758fe7f96SSam Estep     }
91858fe7f96SSam Estep   }
91958fe7f96SSam Estep 
92058fe7f96SSam Estep   // Record that this unwrap is *not* provably safe.
92158fe7f96SSam Estep   // FIXME: include either the name of the optional (if applicable) or a source
92258fe7f96SSam Estep   // range of the access for easier interpretation of the result.
92358fe7f96SSam Estep   return {ObjectExpr->getBeginLoc()};
92458fe7f96SSam Estep }
92558fe7f96SSam Estep 
92658fe7f96SSam Estep auto buildDiagnoseMatchSwitch(
92758fe7f96SSam Estep     const UncheckedOptionalAccessModelOptions &Options) {
92858fe7f96SSam Estep   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
92958fe7f96SSam Estep   // lot of duplicated work (e.g. string comparisons), consider providing APIs
93058fe7f96SSam Estep   // that avoid it through memoization.
93158fe7f96SSam Estep   auto IgnorableOptional = ignorableOptional(Options);
932004a7ceaSYitzhak Mandelbaum   return CFGMatchSwitchBuilder<const Environment,
933004a7ceaSYitzhak Mandelbaum                                llvm::SmallVector<SourceLocation>>()
93458fe7f96SSam Estep       // optional::value
9357538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
93658fe7f96SSam Estep           valueCall(IgnorableOptional),
93758fe7f96SSam Estep           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
93858fe7f96SSam Estep              const Environment &Env) {
9396a81e694SMartin Braenne             return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
94058fe7f96SSam Estep           })
94158fe7f96SSam Estep 
94258fe7f96SSam Estep       // optional::operator*, optional::operator->
9436a81e694SMartin Braenne       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
9446a81e694SMartin Braenne                                [](const CallExpr *E,
9456a81e694SMartin Braenne                                   const MatchFinder::MatchResult &,
94658fe7f96SSam Estep                                   const Environment &Env) {
9476a81e694SMartin Braenne                                  return diagnoseUnwrapCall(E->getArg(0), Env);
94858fe7f96SSam Estep                                })
94958fe7f96SSam Estep       .Build();
95058fe7f96SSam Estep }
95158fe7f96SSam Estep 
952af98b0afSStanislav Gatev } // namespace
953af98b0afSStanislav Gatev 
9547e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher
9557e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() {
956d712c5edSmartinboehme   return cxxRecordDecl(optionalClass());
95771f2ec2dSmartinboehme }
95871f2ec2dSmartinboehme 
95971f2ec2dSmartinboehme UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
96071f2ec2dSmartinboehme                                                            Environment &Env)
961cf1f978dSSam Estep     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
96271f2ec2dSmartinboehme       TransferMatchSwitch(buildTransferMatchSwitch()) {
96371f2ec2dSmartinboehme   Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
96471f2ec2dSmartinboehme       [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
965d712c5edSmartinboehme         const CXXRecordDecl *Optional =
966d712c5edSmartinboehme             getOptionalBaseClass(Ty->getAsCXXRecordDecl());
967d712c5edSmartinboehme         if (Optional == nullptr)
96871f2ec2dSmartinboehme           return {};
969d712c5edSmartinboehme         return {{"value", valueTypeFromOptionalDecl(*Optional)},
97071f2ec2dSmartinboehme                 {"has_value", Ctx.BoolTy}};
97171f2ec2dSmartinboehme       });
97271f2ec2dSmartinboehme }
973af98b0afSStanislav Gatev 
9746b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
9757538b360SWei Yi Tee                                             NoopLattice &L, Environment &Env) {
976af98b0afSStanislav Gatev   LatticeTransferState State(L, Env);
9776b991ba4SYitzhak Mandelbaum   TransferMatchSwitch(Elt, getASTContext(), State);
978af98b0afSStanislav Gatev }
979af98b0afSStanislav Gatev 
98058fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
98158fe7f96SSam Estep     UncheckedOptionalAccessModelOptions Options)
98258fe7f96SSam Estep     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
98358fe7f96SSam Estep 
984af98b0afSStanislav Gatev } // namespace dataflow
985af98b0afSStanislav Gatev } // namespace clang
986