xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision 6761b24ae2f34b923df46412475a9ece50542b97)
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"
20*6761b24aSJan Voung #include "clang/AST/Type.h"
21af98b0afSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h"
2209b462efSYitzhak Mandelbaum #include "clang/ASTMatchers/ASTMatchersMacros.h"
237538b360SWei Yi Tee #include "clang/Analysis/CFG.h"
247538b360SWei Yi Tee #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
25af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
266272226bSSam McCall #include "clang/Analysis/FlowSensitive/Formula.h"
27*6761b24aSJan Voung #include "clang/Analysis/FlowSensitive/RecordOps.h"
280086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h"
29af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h"
3058fe7f96SSam Estep #include "clang/Basic/SourceLocation.h"
31af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h"
32af98b0afSStanislav Gatev #include "llvm/Support/Casting.h"
33d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h"
34af98b0afSStanislav Gatev #include <cassert>
359e0fc676SStanislav Gatev #include <memory>
36a1580d7bSKazu Hirata #include <optional>
379e0fc676SStanislav Gatev #include <utility>
38af98b0afSStanislav Gatev 
39af98b0afSStanislav Gatev namespace clang {
40af98b0afSStanislav Gatev namespace dataflow {
4109b462efSYitzhak Mandelbaum 
4211c423f9SChris Cotter // Note: the Names appear in reverse order. E.g., to check
4311c423f9SChris Cotter // if NS is foo::bar::, call isFullyQualifiedNamespaceEqualTo(NS, "bar", "foo")
4411c423f9SChris Cotter template <class... NameTypes>
4511c423f9SChris Cotter static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS,
4611c423f9SChris Cotter                                              llvm::StringRef Name,
4711c423f9SChris Cotter                                              NameTypes... Names) {
4811c423f9SChris Cotter   if (!(NS.getDeclName().isIdentifier() && NS.getName() == Name &&
4911c423f9SChris Cotter         NS.getParent() != nullptr))
5011c423f9SChris Cotter     return false;
5111c423f9SChris Cotter 
5211c423f9SChris Cotter   if constexpr (sizeof...(NameTypes) > 0) {
5311c423f9SChris Cotter     if (NS.getParent()->isTranslationUnit())
5411c423f9SChris Cotter       return false;
5511c423f9SChris Cotter     if (const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.getParent()))
5611c423f9SChris Cotter       return isFullyQualifiedNamespaceEqualTo(*NextNS, Names...);
5711c423f9SChris Cotter     return false;
5811c423f9SChris Cotter   } else {
5911c423f9SChris Cotter     return NS.getParent()->isTranslationUnit();
6011c423f9SChris Cotter   }
6109b462efSYitzhak Mandelbaum }
6209b462efSYitzhak Mandelbaum 
6309b462efSYitzhak Mandelbaum static bool hasOptionalClassName(const CXXRecordDecl &RD) {
6409b462efSYitzhak Mandelbaum   if (!RD.getDeclName().isIdentifier())
6509b462efSYitzhak Mandelbaum     return false;
6609b462efSYitzhak Mandelbaum 
6709b462efSYitzhak Mandelbaum   if (RD.getName() == "optional") {
6809b462efSYitzhak Mandelbaum     if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
6911c423f9SChris Cotter       return N->isStdNamespace() ||
7011c423f9SChris Cotter              isFullyQualifiedNamespaceEqualTo(*N, "absl") ||
7111c423f9SChris Cotter              isFullyQualifiedNamespaceEqualTo(*N, "bsl");
7209b462efSYitzhak Mandelbaum     return false;
7309b462efSYitzhak Mandelbaum   }
7409b462efSYitzhak Mandelbaum 
7509b462efSYitzhak Mandelbaum   if (RD.getName() == "Optional") {
762f0630f8SAnton Dukeman     // Check whether namespace is "::base" or "::folly".
7709b462efSYitzhak Mandelbaum     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
7811c423f9SChris Cotter     return N != nullptr && (isFullyQualifiedNamespaceEqualTo(*N, "base") ||
7911c423f9SChris Cotter                             isFullyQualifiedNamespaceEqualTo(*N, "folly"));
8011c423f9SChris Cotter   }
8111c423f9SChris Cotter 
8211c423f9SChris Cotter   if (RD.getName() == "NullableValue") {
8311c423f9SChris Cotter     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
8411c423f9SChris Cotter     return N != nullptr &&
8511c423f9SChris Cotter            isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP");
8609b462efSYitzhak Mandelbaum   }
8709b462efSYitzhak Mandelbaum 
8809b462efSYitzhak Mandelbaum   return false;
8909b462efSYitzhak Mandelbaum }
9009b462efSYitzhak Mandelbaum 
91d712c5edSmartinboehme static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) {
92d712c5edSmartinboehme   if (RD == nullptr)
93d712c5edSmartinboehme     return nullptr;
94d712c5edSmartinboehme   if (hasOptionalClassName(*RD))
95d712c5edSmartinboehme     return RD;
96d712c5edSmartinboehme 
97d712c5edSmartinboehme   if (!RD->hasDefinition())
98d712c5edSmartinboehme     return nullptr;
99d712c5edSmartinboehme 
100d712c5edSmartinboehme   for (const CXXBaseSpecifier &Base : RD->bases())
101d712c5edSmartinboehme     if (const CXXRecordDecl *BaseClass =
102d712c5edSmartinboehme             getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl()))
103d712c5edSmartinboehme       return BaseClass;
104d712c5edSmartinboehme 
105d712c5edSmartinboehme   return nullptr;
106d712c5edSmartinboehme }
107d712c5edSmartinboehme 
108*6761b24aSJan Voung static bool isSupportedOptionalType(QualType Ty) {
109*6761b24aSJan Voung   const CXXRecordDecl *Optional =
110*6761b24aSJan Voung       getOptionalBaseClass(Ty->getAsCXXRecordDecl());
111*6761b24aSJan Voung   return Optional != nullptr;
112*6761b24aSJan Voung }
113*6761b24aSJan Voung 
114af98b0afSStanislav Gatev namespace {
115af98b0afSStanislav Gatev 
116af98b0afSStanislav Gatev using namespace ::clang::ast_matchers;
117*6761b24aSJan Voung 
118*6761b24aSJan Voung using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>;
119af98b0afSStanislav Gatev 
120d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); }
121d712c5edSmartinboehme 
122d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
123d712c5edSmartinboehme   return getOptionalBaseClass(&Node) != nullptr;
12409b462efSYitzhak Mandelbaum }
12509b462efSYitzhak Mandelbaum 
126d712c5edSmartinboehme auto desugarsToOptionalType() {
12765e710c3SStanislav Gatev   return hasUnqualifiedDesugaredType(
128d712c5edSmartinboehme       recordType(hasDeclaration(cxxRecordDecl(optionalClass()))));
12965e710c3SStanislav Gatev }
13065e710c3SStanislav Gatev 
131d712c5edSmartinboehme auto desugarsToOptionalOrDerivedType() {
132d712c5edSmartinboehme   return hasUnqualifiedDesugaredType(
133d712c5edSmartinboehme       recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass()))));
134d712c5edSmartinboehme }
135d712c5edSmartinboehme 
136d712c5edSmartinboehme auto hasOptionalType() { return hasType(desugarsToOptionalType()); }
137d712c5edSmartinboehme 
138d712c5edSmartinboehme /// Matches any of the spellings of the optional types and sugar, aliases,
139d712c5edSmartinboehme /// derived classes, etc.
140d712c5edSmartinboehme auto hasOptionalOrDerivedType() {
141d712c5edSmartinboehme   return hasType(desugarsToOptionalOrDerivedType());
142d712c5edSmartinboehme }
143d712c5edSmartinboehme 
144d712c5edSmartinboehme QualType getPublicType(const Expr *E) {
145d712c5edSmartinboehme   auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());
146d712c5edSmartinboehme   if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) {
147d712c5edSmartinboehme     QualType Ty = E->getType();
148d712c5edSmartinboehme     if (Ty->isPointerType())
149d712c5edSmartinboehme       return Ty->getPointeeType();
150d712c5edSmartinboehme     return Ty;
151d712c5edSmartinboehme   }
152d712c5edSmartinboehme 
153d712c5edSmartinboehme   // Is the derived type that we're casting from the type of `*this`? In this
154d712c5edSmartinboehme   // special case, we can upcast to the base class even if the base is
155d712c5edSmartinboehme   // non-public.
156d712c5edSmartinboehme   bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr());
157d712c5edSmartinboehme 
158d712c5edSmartinboehme   // Find the least-derived type in the path (i.e. the last entry in the list)
159d712c5edSmartinboehme   // that we can access.
160d712c5edSmartinboehme   const CXXBaseSpecifier *PublicBase = nullptr;
161d712c5edSmartinboehme   for (const CXXBaseSpecifier *Base : Cast->path()) {
162d712c5edSmartinboehme     if (Base->getAccessSpecifier() != AS_public && !CastingFromThis)
163d712c5edSmartinboehme       break;
164d712c5edSmartinboehme     PublicBase = Base;
165d712c5edSmartinboehme     CastingFromThis = false;
166d712c5edSmartinboehme   }
167d712c5edSmartinboehme 
168d712c5edSmartinboehme   if (PublicBase != nullptr)
169d712c5edSmartinboehme     return PublicBase->getType();
170d712c5edSmartinboehme 
171d712c5edSmartinboehme   // We didn't find any public type that we could cast to. There may be more
172d712c5edSmartinboehme   // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this
173d712c5edSmartinboehme   // will return the type of `getSubExpr()`.)
174d712c5edSmartinboehme   return getPublicType(Cast->getSubExpr());
175d712c5edSmartinboehme }
176d712c5edSmartinboehme 
177d712c5edSmartinboehme // Returns the least-derived type for the receiver of `MCE` that
178d712c5edSmartinboehme // `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to.
179d712c5edSmartinboehme // Effectively, we upcast until we reach a non-public base class, unless that
180d712c5edSmartinboehme // base is a base of `*this`.
181d712c5edSmartinboehme //
182d712c5edSmartinboehme // This is needed to correctly match methods called on types derived from
183d712c5edSmartinboehme // `std::optional`.
184d712c5edSmartinboehme //
185d712c5edSmartinboehme // Say we have a `struct Derived : public std::optional<int> {} d;` For a call
186d712c5edSmartinboehme // `d.has_value()`, the `getImplicitObjectArgument()` looks like this:
187d712c5edSmartinboehme //
188d712c5edSmartinboehme //   ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue
189d712c5edSmartinboehme //   |            <UncheckedDerivedToBase (optional -> __optional_storage_base)>
190d712c5edSmartinboehme //   `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived'
191d712c5edSmartinboehme //
192d712c5edSmartinboehme // The type of the implicit object argument is `__optional_storage_base`
193d712c5edSmartinboehme // (since this is the internal type that `has_value()` is declared on). If we
194d712c5edSmartinboehme // call `IgnoreParenImpCasts()` on the implicit object argument, we get the
195d712c5edSmartinboehme // `DeclRefExpr`, which has type `Derived`. Neither of these types is
196d712c5edSmartinboehme // `optional`, and hence neither is sufficient for querying whether we are
197d712c5edSmartinboehme // calling a method on `optional`.
198d712c5edSmartinboehme //
199d712c5edSmartinboehme // Instead, starting with the most derived type, we need to follow the chain of
200d712c5edSmartinboehme // casts
201d712c5edSmartinboehme QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) {
202d712c5edSmartinboehme   return getPublicType(MCE.getImplicitObjectArgument());
203d712c5edSmartinboehme }
204d712c5edSmartinboehme 
205d712c5edSmartinboehme AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType,
206d712c5edSmartinboehme               ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
207d712c5edSmartinboehme   return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder);
208d712c5edSmartinboehme }
2096adfc64eSYitzhak Mandelbaum 
2102f0630f8SAnton Dukeman auto isOptionalMemberCallWithNameMatcher(
2112f0630f8SAnton Dukeman     ast_matchers::internal::Matcher<NamedDecl> matcher,
2126ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
213d712c5edSmartinboehme   return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable)))
214d712c5edSmartinboehme                                      : anything(),
215d712c5edSmartinboehme                            publicReceiverType(desugarsToOptionalType()),
2162f0630f8SAnton Dukeman                            callee(cxxMethodDecl(matcher)));
217af98b0afSStanislav Gatev }
218af98b0afSStanislav Gatev 
219a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName(
220a184a0d8SYitzhak Mandelbaum     llvm::StringRef operator_name,
2216ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
222a184a0d8SYitzhak Mandelbaum   return cxxOperatorCallExpr(
223a184a0d8SYitzhak Mandelbaum       hasOverloadedOperatorName(operator_name),
224a184a0d8SYitzhak Mandelbaum       callee(cxxMethodDecl(ofClass(optionalClass()))),
225a184a0d8SYitzhak Mandelbaum       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
226af98b0afSStanislav Gatev }
227af98b0afSStanislav Gatev 
228092a530cSStanislav Gatev auto isMakeOptionalCall() {
22911c423f9SChris Cotter   return callExpr(
23011c423f9SChris Cotter       callee(functionDecl(hasAnyName(
23111c423f9SChris Cotter           "std::make_optional", "base::make_optional", "absl::make_optional",
23211c423f9SChris Cotter           "folly::make_optional", "bsl::make_optional"))),
2339e0fc676SStanislav Gatev       hasOptionalType());
2349e0fc676SStanislav Gatev }
2359e0fc676SStanislav Gatev 
236390029beSYitzhak Mandelbaum auto nulloptTypeDecl() {
2372f0630f8SAnton Dukeman   return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",
23811c423f9SChris Cotter                               "base::nullopt_t", "folly::None",
23911c423f9SChris Cotter                               "bsl::nullopt_t"));
240092a530cSStanislav Gatev }
241092a530cSStanislav Gatev 
242390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
243390029beSYitzhak Mandelbaum 
244092a530cSStanislav Gatev auto inPlaceClass() {
2452f0630f8SAnton Dukeman   return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
24611c423f9SChris Cotter                                "base::in_place_t", "folly::in_place_t",
24711c423f9SChris Cotter                                "bsl::in_place_t"));
248092a530cSStanislav Gatev }
249092a530cSStanislav Gatev 
250092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
2510086a355SYitzhak Mandelbaum   return cxxConstructExpr(
2520086a355SYitzhak Mandelbaum       hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
253d712c5edSmartinboehme                                         hasParameter(0, hasNulloptType()))),
254d712c5edSmartinboehme       hasOptionalOrDerivedType());
255092a530cSStanislav Gatev }
256092a530cSStanislav Gatev 
257092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
258d712c5edSmartinboehme   return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())),
259d712c5edSmartinboehme                           hasOptionalOrDerivedType());
260092a530cSStanislav Gatev }
261092a530cSStanislav Gatev 
262092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
263092a530cSStanislav Gatev   return cxxConstructExpr(
264092a530cSStanislav Gatev       unless(hasDeclaration(
265092a530cSStanislav Gatev           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
266d712c5edSmartinboehme       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())),
267d712c5edSmartinboehme       hasOptionalOrDerivedType());
268092a530cSStanislav Gatev }
269092a530cSStanislav Gatev 
270b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
271b000b770SStanislav Gatev   return cxxOperatorCallExpr(
272b000b770SStanislav Gatev       hasOverloadedOperatorName("="),
273d712c5edSmartinboehme       callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
274b000b770SStanislav Gatev       unless(hasDeclaration(cxxMethodDecl(
275b000b770SStanislav Gatev           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
276b000b770SStanislav Gatev       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
277b000b770SStanislav Gatev }
278b000b770SStanislav Gatev 
279b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
280d712c5edSmartinboehme   return cxxOperatorCallExpr(
281d712c5edSmartinboehme       hasOverloadedOperatorName("="),
282d712c5edSmartinboehme       callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
283d712c5edSmartinboehme       argumentCountIs(2), hasArgument(1, hasNulloptType()));
284b000b770SStanislav Gatev }
285b000b770SStanislav Gatev 
2862ddd57aeSStanislav Gatev auto isStdSwapCall() {
2872ddd57aeSStanislav Gatev   return callExpr(callee(functionDecl(hasName("std::swap"))),
288d712c5edSmartinboehme                   argumentCountIs(2),
289d712c5edSmartinboehme                   hasArgument(0, hasOptionalOrDerivedType()),
290d712c5edSmartinboehme                   hasArgument(1, hasOptionalOrDerivedType()));
2912ddd57aeSStanislav Gatev }
2922ddd57aeSStanislav Gatev 
29325956d55SAMS21 auto isStdForwardCall() {
29425956d55SAMS21   return callExpr(callee(functionDecl(hasName("std::forward"))),
295d712c5edSmartinboehme                   argumentCountIs(1),
296d712c5edSmartinboehme                   hasArgument(0, hasOptionalOrDerivedType()));
29725956d55SAMS21 }
29825956d55SAMS21 
2997f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
3007f076004SYitzhak Mandelbaum 
3017f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() {
3027f076004SYitzhak Mandelbaum   // `opt.value_or("").empty()`
3037f076004SYitzhak Mandelbaum   return cxxMemberCallExpr(
3047f076004SYitzhak Mandelbaum       callee(cxxMethodDecl(hasName("empty"))),
3057f076004SYitzhak Mandelbaum       onImplicitObjectArgument(ignoringImplicit(
3067f076004SYitzhak Mandelbaum           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
3077f076004SYitzhak Mandelbaum                             callee(cxxMethodDecl(hasName("value_or"),
3087f076004SYitzhak Mandelbaum                                                  ofClass(optionalClass()))),
3097f076004SYitzhak Mandelbaum                             hasArgument(0, stringLiteral(hasSize(0))))
3107f076004SYitzhak Mandelbaum               .bind(ValueOrCallID))));
3117f076004SYitzhak Mandelbaum }
3127f076004SYitzhak Mandelbaum 
3137f076004SYitzhak Mandelbaum auto isValueOrNotEqX() {
3147f076004SYitzhak Mandelbaum   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
3157f076004SYitzhak Mandelbaum     return hasOperands(
3167f076004SYitzhak Mandelbaum         ignoringImplicit(
3177f076004SYitzhak Mandelbaum             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
3187f076004SYitzhak Mandelbaum                               callee(cxxMethodDecl(hasName("value_or"),
3197f076004SYitzhak Mandelbaum                                                    ofClass(optionalClass()))),
3207f076004SYitzhak Mandelbaum                               hasArgument(0, Arg))
3217f076004SYitzhak Mandelbaum                 .bind(ValueOrCallID)),
3227f076004SYitzhak Mandelbaum         ignoringImplicit(Arg));
3237f076004SYitzhak Mandelbaum   };
3247f076004SYitzhak Mandelbaum 
3257f076004SYitzhak Mandelbaum   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
3267f076004SYitzhak Mandelbaum   // support this pattern for any expression, but the AST does not have a
3277f076004SYitzhak Mandelbaum   // generic expression comparison facility, so we specialize to common cases
3287f076004SYitzhak Mandelbaum   // seen in practice.  FIXME: define a matcher that compares values across
3297f076004SYitzhak Mandelbaum   // nodes, which would let us generalize this to any `X`.
3307f076004SYitzhak Mandelbaum   return binaryOperation(hasOperatorName("!="),
3317f076004SYitzhak Mandelbaum                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
3327f076004SYitzhak Mandelbaum                                ComparesToSame(stringLiteral(hasSize(0))),
3337f076004SYitzhak Mandelbaum                                ComparesToSame(integerLiteral(equals(0)))));
3347f076004SYitzhak Mandelbaum }
3357f076004SYitzhak Mandelbaum 
336*6761b24aSJan Voung auto isZeroParamConstMemberCall() {
337*6761b24aSJan Voung   return cxxMemberCallExpr(
338*6761b24aSJan Voung       callee(cxxMethodDecl(parameterCountIs(0), isConst())));
339*6761b24aSJan Voung }
340*6761b24aSJan Voung 
341*6761b24aSJan Voung auto isNonConstMemberCall() {
342*6761b24aSJan Voung   return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst()))));
343*6761b24aSJan Voung }
344*6761b24aSJan Voung 
345*6761b24aSJan Voung auto isNonConstMemberOperatorCall() {
346*6761b24aSJan Voung   return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));
347*6761b24aSJan Voung }
348*6761b24aSJan Voung 
34965e710c3SStanislav Gatev auto isCallReturningOptional() {
350d712c5edSmartinboehme   return callExpr(hasType(qualType(
351d712c5edSmartinboehme       anyOf(desugarsToOptionalOrDerivedType(),
352d712c5edSmartinboehme             referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
35365e710c3SStanislav Gatev }
35465e710c3SStanislav Gatev 
355390029beSYitzhak Mandelbaum template <typename L, typename R>
356390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
357390029beSYitzhak Mandelbaum   return cxxOperatorCallExpr(
358390029beSYitzhak Mandelbaum       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
359390029beSYitzhak Mandelbaum       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
360390029beSYitzhak Mandelbaum       hasArgument(1, rhs_arg_matcher));
361390029beSYitzhak Mandelbaum }
362390029beSYitzhak Mandelbaum 
3636272226bSSam McCall /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
3646272226bSSam McCall const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
3652ee396b0Smartinboehme   auto *Value = Env.get<BoolValue>(Expr);
366390029beSYitzhak Mandelbaum   if (Value != nullptr)
3676272226bSSam McCall     return Value->formula();
368390029beSYitzhak Mandelbaum 
369390029beSYitzhak Mandelbaum   Value = &Env.makeAtomicBoolValue();
370b244b6aeSMartin Braenne   Env.setValue(Expr, *Value);
3716272226bSSam McCall   return Value->formula();
372390029beSYitzhak Mandelbaum }
373390029beSYitzhak Mandelbaum 
37471f2ec2dSmartinboehme StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) {
37571f2ec2dSmartinboehme   return OptionalLoc.getSyntheticField("has_value");
37671f2ec2dSmartinboehme }
37771f2ec2dSmartinboehme 
37871f2ec2dSmartinboehme StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) {
37971f2ec2dSmartinboehme   return OptionalLoc.getSyntheticField("value");
38071f2ec2dSmartinboehme }
38171f2ec2dSmartinboehme 
3828fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
38371f2ec2dSmartinboehme /// property of the optional at `OptionalLoc`.
38471f2ec2dSmartinboehme void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,
38571f2ec2dSmartinboehme                  Environment &Env) {
38671f2ec2dSmartinboehme   Env.setValue(locForHasValue(OptionalLoc), HasValueVal);
3878fcdd625SStanislav Gatev }
3888fcdd625SStanislav Gatev 
389af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
39071f2ec2dSmartinboehme /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null.
39171f2ec2dSmartinboehme BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {
39271f2ec2dSmartinboehme   if (OptionalLoc == nullptr)
39371f2ec2dSmartinboehme     return nullptr;
39471f2ec2dSmartinboehme   StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
3952ee396b0Smartinboehme   auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);
396dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr) {
397dd38caf3SYitzhak Mandelbaum     HasValueVal = &Env.makeAtomicBoolValue();
39871f2ec2dSmartinboehme     Env.setValue(HasValueLoc, *HasValueVal);
399dd38caf3SYitzhak Mandelbaum   }
400dd38caf3SYitzhak Mandelbaum   return HasValueVal;
401af98b0afSStanislav Gatev }
402af98b0afSStanislav Gatev 
403d712c5edSmartinboehme QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {
404d712c5edSmartinboehme   auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);
405d712c5edSmartinboehme   return CTSD.getTemplateArgs()[0].getAsType();
406092a530cSStanislav Gatev }
407092a530cSStanislav Gatev 
408092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
409092a530cSStanislav Gatev ///
410092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
411092a530cSStanislav Gatev /// function will be 2.
412092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
413d712c5edSmartinboehme   const CXXRecordDecl *Optional =
414d712c5edSmartinboehme       getOptionalBaseClass(Type->getAsCXXRecordDecl());
415d712c5edSmartinboehme   if (Optional == nullptr)
416092a530cSStanislav Gatev     return 0;
417092a530cSStanislav Gatev   return 1 + countOptionalWrappers(
418092a530cSStanislav Gatev                  ASTCtx,
419d712c5edSmartinboehme                  valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
420092a530cSStanislav Gatev }
421092a530cSStanislav Gatev 
42271f2ec2dSmartinboehme StorageLocation *getLocBehindPossiblePointer(const Expr &E,
42371f2ec2dSmartinboehme                                              const Environment &Env) {
42471f2ec2dSmartinboehme   if (E.isPRValue()) {
42571f2ec2dSmartinboehme     if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
42671f2ec2dSmartinboehme       return &PointerVal->getPointeeLoc();
427dd38caf3SYitzhak Mandelbaum     return nullptr;
428dd38caf3SYitzhak Mandelbaum   }
42971f2ec2dSmartinboehme   return Env.getStorageLocation(E);
43048bc7150SMartin Braenne }
43148bc7150SMartin Braenne 
432092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
433af98b0afSStanislav Gatev                         LatticeTransferState &State) {
43471f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
43571f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
436b244b6aeSMartin Braenne     if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)
43771f2ec2dSmartinboehme       State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
438af98b0afSStanislav Gatev   }
439dd38caf3SYitzhak Mandelbaum }
440af98b0afSStanislav Gatev 
4413bc1ea5bSMartin Braenne void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
4423bc1ea5bSMartin Braenne                          LatticeTransferState &State) {
44371f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
44471f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
44571f2ec2dSmartinboehme     State.Env.setValue(
44671f2ec2dSmartinboehme         *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));
4473bc1ea5bSMartin Braenne }
4483bc1ea5bSMartin Braenne 
449092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
450092a530cSStanislav Gatev                               const MatchFinder::MatchResult &,
451092a530cSStanislav Gatev                               LatticeTransferState &State) {
452e8fce958Smartinboehme   setHasValue(State.Env.getResultObjectLocation(*E),
453e8fce958Smartinboehme               State.Env.getBoolLiteralValue(true), State.Env);
4549e0fc676SStanislav Gatev }
4559e0fc676SStanislav Gatev 
456092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
457092a530cSStanislav Gatev                                   const MatchFinder::MatchResult &,
458af98b0afSStanislav Gatev                                   LatticeTransferState &State) {
459dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal = getHasValue(
46071f2ec2dSmartinboehme           State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
461b244b6aeSMartin Braenne     State.Env.setValue(*CallExpr, *HasValueVal);
462af98b0afSStanislav Gatev   }
463af98b0afSStanislav Gatev }
464af98b0afSStanislav Gatev 
46511c423f9SChris Cotter void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr,
46611c423f9SChris Cotter                                 const MatchFinder::MatchResult &,
46711c423f9SChris Cotter                                 LatticeTransferState &State) {
46811c423f9SChris Cotter   if (auto *HasValueVal = getHasValue(
46911c423f9SChris Cotter           State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
47011c423f9SChris Cotter     State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal));
47111c423f9SChris Cotter   }
47211c423f9SChris Cotter }
47311c423f9SChris Cotter 
4747f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in
4757f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property.
4766272226bSSam McCall void transferValueOrImpl(
4776272226bSSam McCall     const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
4787f076004SYitzhak Mandelbaum     LatticeTransferState &State,
4796272226bSSam McCall     const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
4806272226bSSam McCall                                 const Formula &HasValueVal)) {
4817f076004SYitzhak Mandelbaum   auto &Env = State.Env;
4827f076004SYitzhak Mandelbaum 
48371f2ec2dSmartinboehme   const auto *MCE =
48471f2ec2dSmartinboehme       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);
4857f076004SYitzhak Mandelbaum 
48671f2ec2dSmartinboehme   auto *HasValueVal =
48771f2ec2dSmartinboehme       getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env));
488dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr)
4897f076004SYitzhak Mandelbaum     return;
4907f076004SYitzhak Mandelbaum 
491526c9b7eSmartinboehme   Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
4926272226bSSam McCall                        HasValueVal->formula()));
4937f076004SYitzhak Mandelbaum }
4947f076004SYitzhak Mandelbaum 
4957f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
4967f076004SYitzhak Mandelbaum                                     const MatchFinder::MatchResult &Result,
4977f076004SYitzhak Mandelbaum                                     LatticeTransferState &State) {
4987f076004SYitzhak Mandelbaum   return transferValueOrImpl(ComparisonExpr, Result, State,
4996272226bSSam McCall                              [](Environment &Env, const Formula &ExprVal,
5006272226bSSam McCall                                 const Formula &HasValueVal) -> const Formula & {
5016272226bSSam McCall                                auto &A = Env.arena();
5027f076004SYitzhak Mandelbaum                                // If the result is *not* empty, then we know the
5037f076004SYitzhak Mandelbaum                                // optional must have been holding a value. If
5047f076004SYitzhak Mandelbaum                                // `ExprVal` is true, though, we don't learn
5057f076004SYitzhak Mandelbaum                                // anything definite about `has_value`, so we
5067f076004SYitzhak Mandelbaum                                // don't add any corresponding implications to
5077f076004SYitzhak Mandelbaum                                // the flow condition.
5086272226bSSam McCall                                return A.makeImplies(A.makeNot(ExprVal),
5097f076004SYitzhak Mandelbaum                                                     HasValueVal);
5107f076004SYitzhak Mandelbaum                              });
5117f076004SYitzhak Mandelbaum }
5127f076004SYitzhak Mandelbaum 
5137f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr,
5147f076004SYitzhak Mandelbaum                            const MatchFinder::MatchResult &Result,
5157f076004SYitzhak Mandelbaum                            LatticeTransferState &State) {
5167f076004SYitzhak Mandelbaum   transferValueOrImpl(ComparisonExpr, Result, State,
5176272226bSSam McCall                       [](Environment &Env, const Formula &ExprVal,
5186272226bSSam McCall                          const Formula &HasValueVal) -> const Formula & {
5196272226bSSam McCall                         auto &A = Env.arena();
5207f076004SYitzhak Mandelbaum                         // We know that if `(opt.value_or(X) != X)` then
5217f076004SYitzhak Mandelbaum                         // `opt.hasValue()`, even without knowing further
5227f076004SYitzhak Mandelbaum                         // details about the contents of `opt`.
5236272226bSSam McCall                         return A.makeImplies(ExprVal, HasValueVal);
5247f076004SYitzhak Mandelbaum                       });
5257f076004SYitzhak Mandelbaum }
5267f076004SYitzhak Mandelbaum 
52765e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E,
52865e710c3SStanislav Gatev                                    const MatchFinder::MatchResult &Result,
52965e710c3SStanislav Gatev                                    LatticeTransferState &State) {
5309ecdbe38SMartin Braenne   RecordStorageLocation *Loc = nullptr;
53144f98d01SMartin Braenne   if (E->isPRValue()) {
53244f98d01SMartin Braenne     Loc = &State.Env.getResultObjectLocation(*E);
53344f98d01SMartin Braenne   } else {
5342ee396b0Smartinboehme     Loc = State.Env.get<RecordStorageLocation>(*E);
535f76f6674SMartin Braenne     if (Loc == nullptr) {
5369ecdbe38SMartin Braenne       Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));
537b244b6aeSMartin Braenne       State.Env.setStorageLocation(*E, *Loc);
53844f98d01SMartin Braenne     }
539f76f6674SMartin Braenne   }
54044f98d01SMartin Braenne 
541e8fce958Smartinboehme   if (State.Env.getValue(locForHasValue(*Loc)) != nullptr)
542e8fce958Smartinboehme     return;
543e8fce958Smartinboehme 
544e8fce958Smartinboehme   setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
54565e710c3SStanislav Gatev }
54665e710c3SStanislav Gatev 
547*6761b24aSJan Voung void handleConstMemberCall(const CallExpr *CE,
548*6761b24aSJan Voung                            dataflow::RecordStorageLocation *RecordLoc,
549*6761b24aSJan Voung                            const MatchFinder::MatchResult &Result,
550*6761b24aSJan Voung                            LatticeTransferState &State) {
551*6761b24aSJan Voung   // If the const method returns an optional or reference to an optional.
552*6761b24aSJan Voung   if (RecordLoc != nullptr && isSupportedOptionalType(CE->getType())) {
553*6761b24aSJan Voung     StorageLocation *Loc =
554*6761b24aSJan Voung         State.Lattice.getOrCreateConstMethodReturnStorageLocation(
555*6761b24aSJan Voung             *RecordLoc, CE, State.Env, [&](StorageLocation &Loc) {
556*6761b24aSJan Voung               setHasValue(cast<RecordStorageLocation>(Loc),
557*6761b24aSJan Voung                           State.Env.makeAtomicBoolValue(), State.Env);
558*6761b24aSJan Voung             });
559*6761b24aSJan Voung     if (Loc == nullptr)
560*6761b24aSJan Voung       return;
561*6761b24aSJan Voung     if (CE->isGLValue()) {
562*6761b24aSJan Voung       // If the call to the const method returns a reference to an optional,
563*6761b24aSJan Voung       // link the call expression to the cached StorageLocation.
564*6761b24aSJan Voung       State.Env.setStorageLocation(*CE, *Loc);
565*6761b24aSJan Voung     } else {
566*6761b24aSJan Voung       // If the call to the const method returns an optional by value, we
567*6761b24aSJan Voung       // need to use CopyRecord to link the optional to the result object
568*6761b24aSJan Voung       // of the call expression.
569*6761b24aSJan Voung       auto &ResultLoc = State.Env.getResultObjectLocation(*CE);
570*6761b24aSJan Voung       copyRecord(*cast<RecordStorageLocation>(Loc), ResultLoc, State.Env);
571*6761b24aSJan Voung     }
572*6761b24aSJan Voung     return;
573*6761b24aSJan Voung   }
574*6761b24aSJan Voung 
575*6761b24aSJan Voung   // Cache if the const method returns a boolean type.
576*6761b24aSJan Voung   // We may decide to cache other return types in the future.
577*6761b24aSJan Voung   if (RecordLoc != nullptr && CE->getType()->isBooleanType()) {
578*6761b24aSJan Voung     Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE,
579*6761b24aSJan Voung                                                                  State.Env);
580*6761b24aSJan Voung     if (Val == nullptr)
581*6761b24aSJan Voung       return;
582*6761b24aSJan Voung     State.Env.setValue(*CE, *Val);
583*6761b24aSJan Voung     return;
584*6761b24aSJan Voung   }
585*6761b24aSJan Voung 
586*6761b24aSJan Voung   // Perform default handling if the call returns an optional
587*6761b24aSJan Voung   // but wasn't handled above (if RecordLoc is nullptr).
588*6761b24aSJan Voung   if (isSupportedOptionalType(CE->getType())) {
589*6761b24aSJan Voung     transferCallReturningOptional(CE, Result, State);
590*6761b24aSJan Voung   }
591*6761b24aSJan Voung }
592*6761b24aSJan Voung 
593*6761b24aSJan Voung void transferValue_ConstMemberCall(const CXXMemberCallExpr *MCE,
594*6761b24aSJan Voung                                    const MatchFinder::MatchResult &Result,
595*6761b24aSJan Voung                                    LatticeTransferState &State) {
596*6761b24aSJan Voung   handleConstMemberCall(
597*6761b24aSJan Voung       MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
598*6761b24aSJan Voung }
599*6761b24aSJan Voung 
600*6761b24aSJan Voung void handleNonConstMemberCall(const CallExpr *CE,
601*6761b24aSJan Voung                               dataflow::RecordStorageLocation *RecordLoc,
602*6761b24aSJan Voung                               const MatchFinder::MatchResult &Result,
603*6761b24aSJan Voung                               LatticeTransferState &State) {
604*6761b24aSJan Voung   // When a non-const member function is called, reset some state.
605*6761b24aSJan Voung   if (RecordLoc != nullptr) {
606*6761b24aSJan Voung     for (const auto &[Field, FieldLoc] : RecordLoc->children()) {
607*6761b24aSJan Voung       if (isSupportedOptionalType(Field->getType())) {
608*6761b24aSJan Voung         auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
609*6761b24aSJan Voung         if (FieldRecordLoc) {
610*6761b24aSJan Voung           setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),
611*6761b24aSJan Voung                       State.Env);
612*6761b24aSJan Voung         }
613*6761b24aSJan Voung       }
614*6761b24aSJan Voung     }
615*6761b24aSJan Voung     State.Lattice.clearConstMethodReturnValues(*RecordLoc);
616*6761b24aSJan Voung     State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc);
617*6761b24aSJan Voung   }
618*6761b24aSJan Voung 
619*6761b24aSJan Voung   // Perform default handling if the call returns an optional.
620*6761b24aSJan Voung   if (isSupportedOptionalType(CE->getType())) {
621*6761b24aSJan Voung     transferCallReturningOptional(CE, Result, State);
622*6761b24aSJan Voung   }
623*6761b24aSJan Voung }
624*6761b24aSJan Voung 
625*6761b24aSJan Voung void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE,
626*6761b24aSJan Voung                                       const MatchFinder::MatchResult &Result,
627*6761b24aSJan Voung                                       LatticeTransferState &State) {
628*6761b24aSJan Voung   handleNonConstMemberCall(
629*6761b24aSJan Voung       MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
630*6761b24aSJan Voung }
631*6761b24aSJan Voung 
632*6761b24aSJan Voung void transferValue_NonConstMemberOperatorCall(
633*6761b24aSJan Voung     const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result,
634*6761b24aSJan Voung     LatticeTransferState &State) {
635*6761b24aSJan Voung   auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
636*6761b24aSJan Voung       State.Env.getStorageLocation(*OCE->getArg(0)));
637*6761b24aSJan Voung   handleNonConstMemberCall(OCE, RecordLoc, Result, State);
638*6761b24aSJan Voung }
639*6761b24aSJan Voung 
640f653d140SMartin Braenne void constructOptionalValue(const Expr &E, Environment &Env,
641092a530cSStanislav Gatev                             BoolValue &HasValueVal) {
6429ecdbe38SMartin Braenne   RecordStorageLocation &Loc = Env.getResultObjectLocation(E);
643e8fce958Smartinboehme   setHasValue(Loc, HasValueVal, Env);
6449e0fc676SStanislav Gatev }
6459e0fc676SStanislav Gatev 
646b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
647b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
648b000b770SStanislav Gatev /// where `T` is constructible from `U`.
649ae280281Smartinboehme BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E,
650b000b770SStanislav Gatev                                      const MatchFinder::MatchResult &MatchRes,
651b000b770SStanislav Gatev                                      LatticeTransferState &State) {
652ae280281Smartinboehme   const int DestTypeOptionalWrappersCount =
653ae280281Smartinboehme       countOptionalWrappers(*MatchRes.Context, DestType);
654c849843cSMartin Braenne   const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
655c849843cSMartin Braenne       *MatchRes.Context, E.getType().getNonReferenceType());
656b000b770SStanislav Gatev 
657ae280281Smartinboehme   // Is this an constructor of the form `template<class U> optional(U &&)` /
658ae280281Smartinboehme   // assignment of the form `template<class U> optional& operator=(U &&)`
659ae280281Smartinboehme   // (where `T` is assignable / constructible from `U`)?
660ae280281Smartinboehme   // We recognize this because the number of optionals in the optional being
661ae280281Smartinboehme   // assigned to is different from the function argument type.
662ae280281Smartinboehme   if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)
663b000b770SStanislav Gatev     return State.Env.getBoolLiteralValue(true);
664b000b770SStanislav Gatev 
665ae280281Smartinboehme   // Otherwise, this must be a constructor of the form
666ae280281Smartinboehme   // `template <class U> optional<optional<U> &&)` / assignment of the form
667ae280281Smartinboehme   // `template <class U> optional& operator=(optional<U> &&)
668ae280281Smartinboehme   // (where, again, `T` is assignable / constructible from `U`).
6692ee396b0Smartinboehme   auto *Loc = State.Env.get<RecordStorageLocation>(E);
67071f2ec2dSmartinboehme   if (auto *HasValueVal = getHasValue(State.Env, Loc))
671dd38caf3SYitzhak Mandelbaum     return *HasValueVal;
672b000b770SStanislav Gatev   return State.Env.makeAtomicBoolValue();
673b000b770SStanislav Gatev }
674b000b770SStanislav Gatev 
675092a530cSStanislav Gatev void transferValueOrConversionConstructor(
676092a530cSStanislav Gatev     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
6779e0fc676SStanislav Gatev     LatticeTransferState &State) {
678092a530cSStanislav Gatev   assert(E->getNumArgs() > 0);
679092a530cSStanislav Gatev 
680ae280281Smartinboehme   constructOptionalValue(
681ae280281Smartinboehme       *E, State.Env,
682ae280281Smartinboehme       valueOrConversionHasValue(
683ae280281Smartinboehme           E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0),
684ae280281Smartinboehme           MatchRes, State));
685b000b770SStanislav Gatev }
686092a530cSStanislav Gatev 
687b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
688b000b770SStanislav Gatev                         LatticeTransferState &State) {
689b000b770SStanislav Gatev   assert(E->getNumArgs() > 0);
690b000b770SStanislav Gatev 
6912ee396b0Smartinboehme   if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) {
692e8fce958Smartinboehme     setHasValue(*Loc, HasValueVal, State.Env);
693b000b770SStanislav Gatev 
694b000b770SStanislav Gatev     // Assign a storage location for the whole expression.
695b244b6aeSMartin Braenne     State.Env.setStorageLocation(*E, *Loc);
696f653d140SMartin Braenne   }
697b000b770SStanislav Gatev }
698b000b770SStanislav Gatev 
699b000b770SStanislav Gatev void transferValueOrConversionAssignment(
700b000b770SStanislav Gatev     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
701b000b770SStanislav Gatev     LatticeTransferState &State) {
702b000b770SStanislav Gatev   assert(E->getNumArgs() > 1);
703ae280281Smartinboehme   transferAssignment(
704ae280281Smartinboehme       E,
705ae280281Smartinboehme       valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(),
70606decd0bSKazu Hirata                                 *E->getArg(1), MatchRes, State),
707b000b770SStanislav Gatev       State);
708b000b770SStanislav Gatev }
709b000b770SStanislav Gatev 
710b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
711b000b770SStanislav Gatev                                const MatchFinder::MatchResult &,
712b000b770SStanislav Gatev                                LatticeTransferState &State) {
713b000b770SStanislav Gatev   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
7149e0fc676SStanislav Gatev }
7159e0fc676SStanislav Gatev 
7169ecdbe38SMartin Braenne void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
7179ecdbe38SMartin Braenne                   Environment &Env) {
718d4fb829bSYitzhak Mandelbaum   // We account for cases where one or both of the optionals are not modeled,
719d4fb829bSYitzhak Mandelbaum   // either lacking associated storage locations, or lacking values associated
720d4fb829bSYitzhak Mandelbaum   // to such storage locations.
7212ddd57aeSStanislav Gatev 
722d4fb829bSYitzhak Mandelbaum   if (Loc1 == nullptr) {
723d4fb829bSYitzhak Mandelbaum     if (Loc2 != nullptr)
724e8fce958Smartinboehme       setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env);
725d4fb829bSYitzhak Mandelbaum     return;
726d4fb829bSYitzhak Mandelbaum   }
727d4fb829bSYitzhak Mandelbaum   if (Loc2 == nullptr) {
728e8fce958Smartinboehme     setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env);
729d4fb829bSYitzhak Mandelbaum     return;
730d4fb829bSYitzhak Mandelbaum   }
7312ddd57aeSStanislav Gatev 
732d4fb829bSYitzhak Mandelbaum   // Both expressions have locations, though they may not have corresponding
733d4fb829bSYitzhak Mandelbaum   // values. In that case, we create a fresh value at this point. Note that if
734d4fb829bSYitzhak Mandelbaum   // two branches both do this, they will not share the value, but it at least
735d4fb829bSYitzhak Mandelbaum   // allows for local reasoning about the value. To avoid the above, we would
736d4fb829bSYitzhak Mandelbaum   // need *lazy* value allocation.
737d4fb829bSYitzhak Mandelbaum   // FIXME: allocate values lazily, instead of just creating a fresh value.
73871f2ec2dSmartinboehme   BoolValue *BoolVal1 = getHasValue(Env, Loc1);
739f653d140SMartin Braenne   if (BoolVal1 == nullptr)
740f653d140SMartin Braenne     BoolVal1 = &Env.makeAtomicBoolValue();
741d4fb829bSYitzhak Mandelbaum 
74271f2ec2dSmartinboehme   BoolValue *BoolVal2 = getHasValue(Env, Loc2);
743f653d140SMartin Braenne   if (BoolVal2 == nullptr)
744f653d140SMartin Braenne     BoolVal2 = &Env.makeAtomicBoolValue();
745d4fb829bSYitzhak Mandelbaum 
746e8fce958Smartinboehme   setHasValue(*Loc1, *BoolVal2, Env);
747e8fce958Smartinboehme   setHasValue(*Loc2, *BoolVal1, Env);
7482ddd57aeSStanislav Gatev }
7492ddd57aeSStanislav Gatev 
7502ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
7512ddd57aeSStanislav Gatev                       const MatchFinder::MatchResult &,
7522ddd57aeSStanislav Gatev                       LatticeTransferState &State) {
7532ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 1);
7542ee396b0Smartinboehme   auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
755f653d140SMartin Braenne   transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
7562ddd57aeSStanislav Gatev }
7572ddd57aeSStanislav Gatev 
7582ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
7592ddd57aeSStanislav Gatev                          LatticeTransferState &State) {
7602ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 2);
7612ee396b0Smartinboehme   auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
7622ee396b0Smartinboehme   auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1));
763f653d140SMartin Braenne   transferSwap(Arg0Loc, Arg1Loc, State.Env);
7642ddd57aeSStanislav Gatev }
7652ddd57aeSStanislav Gatev 
76625956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
76725956d55SAMS21                             LatticeTransferState &State) {
76825956d55SAMS21   assert(E->getNumArgs() == 1);
76925956d55SAMS21 
770b244b6aeSMartin Braenne   if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
771b244b6aeSMartin Braenne     State.Env.setStorageLocation(*E, *Loc);
77225956d55SAMS21 }
77325956d55SAMS21 
7746272226bSSam McCall const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
7756272226bSSam McCall                                 const Formula &LHS, const Formula &RHS) {
776390029beSYitzhak Mandelbaum   // Logically, an optional<T> object is composed of two values - a `has_value`
777390029beSYitzhak Mandelbaum   // bit and a value of type T. Equality of optional objects compares both
778390029beSYitzhak Mandelbaum   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
779390029beSYitzhak Mandelbaum   // when two optional objects are engaged, the equality of their respective
780390029beSYitzhak Mandelbaum   // values of type T matters. Since we only track the `has_value` bits, we
781390029beSYitzhak Mandelbaum   // can't make any conclusions about equality when we know that two optional
782390029beSYitzhak Mandelbaum   // objects are engaged.
783390029beSYitzhak Mandelbaum   //
784390029beSYitzhak Mandelbaum   // We express this as two facts about the equality:
785390029beSYitzhak Mandelbaum   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
786390029beSYitzhak Mandelbaum   //    If they are equal, then either both are set or both are unset.
787390029beSYitzhak Mandelbaum   // b) (!LHS & !RHS) => EqVal
788390029beSYitzhak Mandelbaum   //    If neither is set, then they are equal.
789390029beSYitzhak Mandelbaum   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
7906272226bSSam McCall   return A.makeAnd(
7916272226bSSam McCall       A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
7926272226bSSam McCall                                     A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
7936272226bSSam McCall       A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
794390029beSYitzhak Mandelbaum }
795390029beSYitzhak Mandelbaum 
796390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
797390029beSYitzhak Mandelbaum                                     const MatchFinder::MatchResult &,
798390029beSYitzhak Mandelbaum                                     LatticeTransferState &State) {
799390029beSYitzhak Mandelbaum   Environment &Env = State.Env;
8006272226bSSam McCall   auto &A = Env.arena();
801390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
8022ee396b0Smartinboehme   auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0));
80371f2ec2dSmartinboehme   if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {
8042ee396b0Smartinboehme     auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1));
80571f2ec2dSmartinboehme     if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {
806390029beSYitzhak Mandelbaum       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
8076272226bSSam McCall         CmpValue = &A.makeNot(*CmpValue);
808526c9b7eSmartinboehme       Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
8096272226bSSam McCall                                   RHasVal->formula()));
810390029beSYitzhak Mandelbaum     }
811390029beSYitzhak Mandelbaum   }
81271f2ec2dSmartinboehme }
813390029beSYitzhak Mandelbaum 
814390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
815390029beSYitzhak Mandelbaum                                  const clang::Expr *E, Environment &Env) {
8166272226bSSam McCall   auto &A = Env.arena();
817390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
8182ee396b0Smartinboehme   auto *Loc = Env.get<RecordStorageLocation>(*E);
81971f2ec2dSmartinboehme   if (auto *HasVal = getHasValue(Env, Loc)) {
820390029beSYitzhak Mandelbaum     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
8216272226bSSam McCall       CmpValue = &A.makeNot(*CmpValue);
822526c9b7eSmartinboehme     Env.assume(
8236272226bSSam McCall         evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
824390029beSYitzhak Mandelbaum   }
825390029beSYitzhak Mandelbaum }
826390029beSYitzhak Mandelbaum 
82771f2ec2dSmartinboehme void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,
82871f2ec2dSmartinboehme                                    const clang::Expr *E, Environment &Env) {
82971f2ec2dSmartinboehme   auto &A = Env.arena();
83071f2ec2dSmartinboehme   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
8312ee396b0Smartinboehme   auto *Loc = Env.get<RecordStorageLocation>(*E);
83271f2ec2dSmartinboehme   if (auto *HasVal = getHasValue(Env, Loc)) {
83371f2ec2dSmartinboehme     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
83471f2ec2dSmartinboehme       CmpValue = &A.makeNot(*CmpValue);
83571f2ec2dSmartinboehme     Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
83671f2ec2dSmartinboehme                                 A.makeLiteral(false)));
83771f2ec2dSmartinboehme   }
83871f2ec2dSmartinboehme }
83971f2ec2dSmartinboehme 
8406ad0788cSKazu Hirata std::optional<StatementMatcher>
841a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
8425d22d1f5SYitzhak Mandelbaum   if (Options.IgnoreSmartPointerDereference) {
8435d22d1f5SYitzhak Mandelbaum     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
8445d22d1f5SYitzhak Mandelbaum         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
8455d22d1f5SYitzhak Mandelbaum         unless(hasArgument(0, expr(hasOptionalType()))))));
8465d22d1f5SYitzhak Mandelbaum     return expr(
8475d22d1f5SYitzhak Mandelbaum         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
8485d22d1f5SYitzhak Mandelbaum   }
84934e0d057SKazu Hirata   return std::nullopt;
850a184a0d8SYitzhak Mandelbaum }
851a184a0d8SYitzhak Mandelbaum 
85258fe7f96SSam Estep StatementMatcher
8536ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
8542f0630f8SAnton Dukeman   return isOptionalMemberCallWithNameMatcher(hasName("value"),
8552f0630f8SAnton Dukeman                                              IgnorableOptional);
85658fe7f96SSam Estep }
85758fe7f96SSam Estep 
85858fe7f96SSam Estep StatementMatcher
8596ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
86058fe7f96SSam Estep   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
86158fe7f96SSam Estep                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
86258fe7f96SSam Estep }
86358fe7f96SSam Estep 
8645d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() {
865b000b770SStanislav Gatev   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
866b000b770SStanislav Gatev   // lot of duplicated work (e.g. string comparisons), consider providing APIs
867b000b770SStanislav Gatev   // that avoid it through memoization.
8687538b360SWei Yi Tee   return CFGMatchSwitchBuilder<LatticeTransferState>()
8699e0fc676SStanislav Gatev       // make_optional
8707538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
871092a530cSStanislav Gatev 
8720e8d4a6dSYitzhak Mandelbaum       // optional::optional (in place)
8737538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
874092a530cSStanislav Gatev           isOptionalInPlaceConstructor(),
875092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
876092a530cSStanislav Gatev              LatticeTransferState &State) {
877f653d140SMartin Braenne             constructOptionalValue(*E, State.Env,
8780e8d4a6dSYitzhak Mandelbaum                                    State.Env.getBoolLiteralValue(true));
879092a530cSStanislav Gatev           })
8800e8d4a6dSYitzhak Mandelbaum       // optional::optional(nullopt_t)
881390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXConstructExpr>(
882390029beSYitzhak Mandelbaum           isOptionalNulloptConstructor(),
883390029beSYitzhak Mandelbaum           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
884390029beSYitzhak Mandelbaum              LatticeTransferState &State) {
885f653d140SMartin Braenne             constructOptionalValue(*E, State.Env,
8860e8d4a6dSYitzhak Mandelbaum                                    State.Env.getBoolLiteralValue(false));
887390029beSYitzhak Mandelbaum           })
8880e8d4a6dSYitzhak Mandelbaum       // optional::optional (value/conversion)
8897538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
890092a530cSStanislav Gatev                                        transferValueOrConversionConstructor)
8919e0fc676SStanislav Gatev 
892b000b770SStanislav Gatev       // optional::operator=
8937538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(
8947538b360SWei Yi Tee           isOptionalValueOrConversionAssignment(),
895b000b770SStanislav Gatev           transferValueOrConversionAssignment)
8967538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
897b000b770SStanislav Gatev                                           transferNulloptAssignment)
898b000b770SStanislav Gatev 
899af98b0afSStanislav Gatev       // optional::value
9007538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
9015d22d1f5SYitzhak Mandelbaum           valueCall(std::nullopt),
902092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
903092a530cSStanislav Gatev              LatticeTransferState &State) {
904af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
905af98b0afSStanislav Gatev           })
906af98b0afSStanislav Gatev 
9073bc1ea5bSMartin Braenne       // optional::operator*
9083bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
9097538b360SWei Yi Tee                                [](const CallExpr *E,
9107538b360SWei Yi Tee                                   const MatchFinder::MatchResult &,
911092a530cSStanislav Gatev                                   LatticeTransferState &State) {
912af98b0afSStanislav Gatev                                  transferUnwrapCall(E, E->getArg(0), State);
913af98b0afSStanislav Gatev                                })
914af98b0afSStanislav Gatev 
9153bc1ea5bSMartin Braenne       // optional::operator->
9163bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
9173bc1ea5bSMartin Braenne                                [](const CallExpr *E,
9183bc1ea5bSMartin Braenne                                   const MatchFinder::MatchResult &,
9193bc1ea5bSMartin Braenne                                   LatticeTransferState &State) {
9203bc1ea5bSMartin Braenne                                  transferArrowOpCall(E, E->getArg(0), State);
9213bc1ea5bSMartin Braenne                                })
9223bc1ea5bSMartin Braenne 
9232f0630f8SAnton Dukeman       // optional::has_value, optional::hasValue
9242f0630f8SAnton Dukeman       // Of the supported optionals only folly::Optional uses hasValue, but this
9252f0630f8SAnton Dukeman       // will also pass for other types
9267538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
9272f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(
9282f0630f8SAnton Dukeman               hasAnyName("has_value", "hasValue")),
929af98b0afSStanislav Gatev           transferOptionalHasValueCall)
930af98b0afSStanislav Gatev 
9319e0fc676SStanislav Gatev       // optional::operator bool
9327538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
9332f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
9349e0fc676SStanislav Gatev           transferOptionalHasValueCall)
9359e0fc676SStanislav Gatev 
93611c423f9SChris Cotter       // NullableValue::isNull
93711c423f9SChris Cotter       // Only NullableValue has isNull
93811c423f9SChris Cotter       .CaseOfCFGStmt<CXXMemberCallExpr>(
93911c423f9SChris Cotter           isOptionalMemberCallWithNameMatcher(hasName("isNull")),
94011c423f9SChris Cotter           transferOptionalIsNullCall)
94111c423f9SChris Cotter 
9429e0fc676SStanislav Gatev       // optional::emplace
9437538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
9442f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("emplace")),
945092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
946092a530cSStanislav Gatev              LatticeTransferState &State) {
9479ecdbe38SMartin Braenne             if (RecordStorageLocation *Loc =
948f653d140SMartin Braenne                     getImplicitObjectLocation(*E, State.Env)) {
949e8fce958Smartinboehme               setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);
950f653d140SMartin Braenne             }
951092a530cSStanislav Gatev           })
9529e0fc676SStanislav Gatev 
9539e0fc676SStanislav Gatev       // optional::reset
9547538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
9552f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("reset")),
956092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
957092a530cSStanislav Gatev              LatticeTransferState &State) {
9589ecdbe38SMartin Braenne             if (RecordStorageLocation *Loc =
959f653d140SMartin Braenne                     getImplicitObjectLocation(*E, State.Env)) {
960e8fce958Smartinboehme               setHasValue(*Loc, State.Env.getBoolLiteralValue(false),
961f653d140SMartin Braenne                           State.Env);
962f653d140SMartin Braenne             }
963092a530cSStanislav Gatev           })
9649e0fc676SStanislav Gatev 
9652ddd57aeSStanislav Gatev       // optional::swap
9662f0630f8SAnton Dukeman       .CaseOfCFGStmt<CXXMemberCallExpr>(
9672f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("swap")),
9682ddd57aeSStanislav Gatev           transferSwapCall)
9692ddd57aeSStanislav Gatev 
9702ddd57aeSStanislav Gatev       // std::swap
9717538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
9722ddd57aeSStanislav Gatev 
97325956d55SAMS21       // std::forward
97425956d55SAMS21       .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
97525956d55SAMS21 
9767f076004SYitzhak Mandelbaum       // opt.value_or("").empty()
9777538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
9787538b360SWei Yi Tee                            transferValueOrStringEmptyCall)
9797f076004SYitzhak Mandelbaum 
9807f076004SYitzhak Mandelbaum       // opt.value_or(X) != X
9817538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
9827f076004SYitzhak Mandelbaum 
983390029beSYitzhak Mandelbaum       // Comparisons (==, !=):
984390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
98571f2ec2dSmartinboehme           isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
986390029beSYitzhak Mandelbaum           transferOptionalAndOptionalCmp)
987390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
98871f2ec2dSmartinboehme           isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
98971f2ec2dSmartinboehme           [](const clang::CXXOperatorCallExpr *Cmp,
99071f2ec2dSmartinboehme              const MatchFinder::MatchResult &, LatticeTransferState &State) {
99171f2ec2dSmartinboehme             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);
99271f2ec2dSmartinboehme           })
99371f2ec2dSmartinboehme       .CaseOfCFGStmt<CXXOperatorCallExpr>(
99471f2ec2dSmartinboehme           isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
99571f2ec2dSmartinboehme           [](const clang::CXXOperatorCallExpr *Cmp,
99671f2ec2dSmartinboehme              const MatchFinder::MatchResult &, LatticeTransferState &State) {
99771f2ec2dSmartinboehme             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);
99871f2ec2dSmartinboehme           })
99971f2ec2dSmartinboehme       .CaseOfCFGStmt<CXXOperatorCallExpr>(
100071f2ec2dSmartinboehme           isComparisonOperatorCall(
100171f2ec2dSmartinboehme               hasOptionalType(),
100271f2ec2dSmartinboehme               unless(anyOf(hasOptionalType(), hasNulloptType()))),
1003390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
1004390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
1005390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
1006390029beSYitzhak Mandelbaum           })
1007390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
100871f2ec2dSmartinboehme           isComparisonOperatorCall(
100971f2ec2dSmartinboehme               unless(anyOf(hasOptionalType(), hasNulloptType())),
1010390029beSYitzhak Mandelbaum               hasOptionalType()),
1011390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
1012390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
1013390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
1014390029beSYitzhak Mandelbaum           })
1015390029beSYitzhak Mandelbaum 
1016*6761b24aSJan Voung       // const accessor calls
1017*6761b24aSJan Voung       .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),
1018*6761b24aSJan Voung                                         transferValue_ConstMemberCall)
1019*6761b24aSJan Voung       // non-const member calls that may modify the state of an object.
1020*6761b24aSJan Voung       .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
1021*6761b24aSJan Voung                                         transferValue_NonConstMemberCall)
1022*6761b24aSJan Voung       .CaseOfCFGStmt<CXXOperatorCallExpr>(
1023*6761b24aSJan Voung           isNonConstMemberOperatorCall(),
1024*6761b24aSJan Voung           transferValue_NonConstMemberOperatorCall)
1025*6761b24aSJan Voung 
1026*6761b24aSJan Voung       // other cases of returning optional
10277538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
102865e710c3SStanislav Gatev                                transferCallReturningOptional)
102965e710c3SStanislav Gatev 
1030af98b0afSStanislav Gatev       .Build();
1031af98b0afSStanislav Gatev }
1032af98b0afSStanislav Gatev 
1033004a7ceaSYitzhak Mandelbaum llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
103458fe7f96SSam Estep                                                      const Environment &Env) {
103571f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
103671f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, Env))) {
103771f2ec2dSmartinboehme     auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
103858fe7f96SSam Estep     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
1039526c9b7eSmartinboehme       if (Env.proves(HasValueVal->formula()))
104058fe7f96SSam Estep         return {};
104158fe7f96SSam Estep     }
104258fe7f96SSam Estep   }
104358fe7f96SSam Estep 
104458fe7f96SSam Estep   // Record that this unwrap is *not* provably safe.
104558fe7f96SSam Estep   // FIXME: include either the name of the optional (if applicable) or a source
104658fe7f96SSam Estep   // range of the access for easier interpretation of the result.
104758fe7f96SSam Estep   return {ObjectExpr->getBeginLoc()};
104858fe7f96SSam Estep }
104958fe7f96SSam Estep 
105058fe7f96SSam Estep auto buildDiagnoseMatchSwitch(
105158fe7f96SSam Estep     const UncheckedOptionalAccessModelOptions &Options) {
105258fe7f96SSam Estep   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
105358fe7f96SSam Estep   // lot of duplicated work (e.g. string comparisons), consider providing APIs
105458fe7f96SSam Estep   // that avoid it through memoization.
105558fe7f96SSam Estep   auto IgnorableOptional = ignorableOptional(Options);
1056004a7ceaSYitzhak Mandelbaum   return CFGMatchSwitchBuilder<const Environment,
1057004a7ceaSYitzhak Mandelbaum                                llvm::SmallVector<SourceLocation>>()
105858fe7f96SSam Estep       // optional::value
10597538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
106058fe7f96SSam Estep           valueCall(IgnorableOptional),
106158fe7f96SSam Estep           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
106258fe7f96SSam Estep              const Environment &Env) {
10636a81e694SMartin Braenne             return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
106458fe7f96SSam Estep           })
106558fe7f96SSam Estep 
106658fe7f96SSam Estep       // optional::operator*, optional::operator->
10676a81e694SMartin Braenne       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
10686a81e694SMartin Braenne                                [](const CallExpr *E,
10696a81e694SMartin Braenne                                   const MatchFinder::MatchResult &,
107058fe7f96SSam Estep                                   const Environment &Env) {
10716a81e694SMartin Braenne                                  return diagnoseUnwrapCall(E->getArg(0), Env);
107258fe7f96SSam Estep                                })
107358fe7f96SSam Estep       .Build();
107458fe7f96SSam Estep }
107558fe7f96SSam Estep 
1076af98b0afSStanislav Gatev } // namespace
1077af98b0afSStanislav Gatev 
10787e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher
10797e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() {
1080d712c5edSmartinboehme   return cxxRecordDecl(optionalClass());
108171f2ec2dSmartinboehme }
108271f2ec2dSmartinboehme 
108371f2ec2dSmartinboehme UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
108471f2ec2dSmartinboehme                                                            Environment &Env)
1085*6761b24aSJan Voung     : DataflowAnalysis<UncheckedOptionalAccessModel,
1086*6761b24aSJan Voung                        UncheckedOptionalAccessLattice>(Ctx),
108771f2ec2dSmartinboehme       TransferMatchSwitch(buildTransferMatchSwitch()) {
108871f2ec2dSmartinboehme   Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
108971f2ec2dSmartinboehme       [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
1090d712c5edSmartinboehme         const CXXRecordDecl *Optional =
1091d712c5edSmartinboehme             getOptionalBaseClass(Ty->getAsCXXRecordDecl());
1092d712c5edSmartinboehme         if (Optional == nullptr)
109371f2ec2dSmartinboehme           return {};
1094d712c5edSmartinboehme         return {{"value", valueTypeFromOptionalDecl(*Optional)},
109571f2ec2dSmartinboehme                 {"has_value", Ctx.BoolTy}};
109671f2ec2dSmartinboehme       });
109771f2ec2dSmartinboehme }
1098af98b0afSStanislav Gatev 
10996b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
1100*6761b24aSJan Voung                                             UncheckedOptionalAccessLattice &L,
1101*6761b24aSJan Voung                                             Environment &Env) {
1102af98b0afSStanislav Gatev   LatticeTransferState State(L, Env);
11036b991ba4SYitzhak Mandelbaum   TransferMatchSwitch(Elt, getASTContext(), State);
1104af98b0afSStanislav Gatev }
1105af98b0afSStanislav Gatev 
110658fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
110758fe7f96SSam Estep     UncheckedOptionalAccessModelOptions Options)
110858fe7f96SSam Estep     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
110958fe7f96SSam Estep 
1110af98b0afSStanislav Gatev } // namespace dataflow
1111af98b0afSStanislav Gatev } // namespace clang
1112