xref: /llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (revision 72a28a3bf0b539bcdfd8f41905675ce6a890c0ac)
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"
206761b24aSJan 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"
276761b24aSJan Voung #include "clang/Analysis/FlowSensitive/RecordOps.h"
28*72a28a3bSJan Voung #include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h"
290086a355SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/StorageLocation.h"
30af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h"
31*72a28a3bSJan Voung #include "clang/Basic/OperatorKinds.h"
3258fe7f96SSam Estep #include "clang/Basic/SourceLocation.h"
33af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h"
34af98b0afSStanislav Gatev #include "llvm/Support/Casting.h"
35d34fbf2dSYitzhak Mandelbaum #include "llvm/Support/ErrorHandling.h"
36af98b0afSStanislav Gatev #include <cassert>
379e0fc676SStanislav Gatev #include <memory>
38a1580d7bSKazu Hirata #include <optional>
399e0fc676SStanislav Gatev #include <utility>
40af98b0afSStanislav Gatev 
41af98b0afSStanislav Gatev namespace clang {
42af98b0afSStanislav Gatev namespace dataflow {
4309b462efSYitzhak Mandelbaum 
4411c423f9SChris Cotter // Note: the Names appear in reverse order. E.g., to check
4511c423f9SChris Cotter // if NS is foo::bar::, call isFullyQualifiedNamespaceEqualTo(NS, "bar", "foo")
4611c423f9SChris Cotter template <class... NameTypes>
4711c423f9SChris Cotter static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS,
4811c423f9SChris Cotter                                              llvm::StringRef Name,
4911c423f9SChris Cotter                                              NameTypes... Names) {
5011c423f9SChris Cotter   if (!(NS.getDeclName().isIdentifier() && NS.getName() == Name &&
5111c423f9SChris Cotter         NS.getParent() != nullptr))
5211c423f9SChris Cotter     return false;
5311c423f9SChris Cotter 
5411c423f9SChris Cotter   if constexpr (sizeof...(NameTypes) > 0) {
5511c423f9SChris Cotter     if (NS.getParent()->isTranslationUnit())
5611c423f9SChris Cotter       return false;
5711c423f9SChris Cotter     if (const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.getParent()))
5811c423f9SChris Cotter       return isFullyQualifiedNamespaceEqualTo(*NextNS, Names...);
5911c423f9SChris Cotter     return false;
6011c423f9SChris Cotter   } else {
6111c423f9SChris Cotter     return NS.getParent()->isTranslationUnit();
6211c423f9SChris Cotter   }
6309b462efSYitzhak Mandelbaum }
6409b462efSYitzhak Mandelbaum 
6509b462efSYitzhak Mandelbaum static bool hasOptionalClassName(const CXXRecordDecl &RD) {
6609b462efSYitzhak Mandelbaum   if (!RD.getDeclName().isIdentifier())
6709b462efSYitzhak Mandelbaum     return false;
6809b462efSYitzhak Mandelbaum 
6909b462efSYitzhak Mandelbaum   if (RD.getName() == "optional") {
7009b462efSYitzhak Mandelbaum     if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
7111c423f9SChris Cotter       return N->isStdNamespace() ||
7211c423f9SChris Cotter              isFullyQualifiedNamespaceEqualTo(*N, "absl") ||
7311c423f9SChris Cotter              isFullyQualifiedNamespaceEqualTo(*N, "bsl");
7409b462efSYitzhak Mandelbaum     return false;
7509b462efSYitzhak Mandelbaum   }
7609b462efSYitzhak Mandelbaum 
7709b462efSYitzhak Mandelbaum   if (RD.getName() == "Optional") {
782f0630f8SAnton Dukeman     // Check whether namespace is "::base" or "::folly".
7909b462efSYitzhak Mandelbaum     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
8011c423f9SChris Cotter     return N != nullptr && (isFullyQualifiedNamespaceEqualTo(*N, "base") ||
8111c423f9SChris Cotter                             isFullyQualifiedNamespaceEqualTo(*N, "folly"));
8211c423f9SChris Cotter   }
8311c423f9SChris Cotter 
8411c423f9SChris Cotter   if (RD.getName() == "NullableValue") {
8511c423f9SChris Cotter     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
8611c423f9SChris Cotter     return N != nullptr &&
8711c423f9SChris Cotter            isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP");
8809b462efSYitzhak Mandelbaum   }
8909b462efSYitzhak Mandelbaum 
9009b462efSYitzhak Mandelbaum   return false;
9109b462efSYitzhak Mandelbaum }
9209b462efSYitzhak Mandelbaum 
93d712c5edSmartinboehme static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) {
94d712c5edSmartinboehme   if (RD == nullptr)
95d712c5edSmartinboehme     return nullptr;
96d712c5edSmartinboehme   if (hasOptionalClassName(*RD))
97d712c5edSmartinboehme     return RD;
98d712c5edSmartinboehme 
99d712c5edSmartinboehme   if (!RD->hasDefinition())
100d712c5edSmartinboehme     return nullptr;
101d712c5edSmartinboehme 
102d712c5edSmartinboehme   for (const CXXBaseSpecifier &Base : RD->bases())
103d712c5edSmartinboehme     if (const CXXRecordDecl *BaseClass =
104d712c5edSmartinboehme             getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl()))
105d712c5edSmartinboehme       return BaseClass;
106d712c5edSmartinboehme 
107d712c5edSmartinboehme   return nullptr;
108d712c5edSmartinboehme }
109d712c5edSmartinboehme 
1106761b24aSJan Voung static bool isSupportedOptionalType(QualType Ty) {
1116761b24aSJan Voung   const CXXRecordDecl *Optional =
1126761b24aSJan Voung       getOptionalBaseClass(Ty->getAsCXXRecordDecl());
1136761b24aSJan Voung   return Optional != nullptr;
1146761b24aSJan Voung }
1156761b24aSJan Voung 
116af98b0afSStanislav Gatev namespace {
117af98b0afSStanislav Gatev 
118af98b0afSStanislav Gatev using namespace ::clang::ast_matchers;
1196761b24aSJan Voung 
1206761b24aSJan Voung using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>;
121af98b0afSStanislav Gatev 
122d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); }
123d712c5edSmartinboehme 
124d712c5edSmartinboehme AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
125d712c5edSmartinboehme   return getOptionalBaseClass(&Node) != nullptr;
12609b462efSYitzhak Mandelbaum }
12709b462efSYitzhak Mandelbaum 
128d712c5edSmartinboehme auto desugarsToOptionalType() {
12965e710c3SStanislav Gatev   return hasUnqualifiedDesugaredType(
130d712c5edSmartinboehme       recordType(hasDeclaration(cxxRecordDecl(optionalClass()))));
13165e710c3SStanislav Gatev }
13265e710c3SStanislav Gatev 
133d712c5edSmartinboehme auto desugarsToOptionalOrDerivedType() {
134d712c5edSmartinboehme   return hasUnqualifiedDesugaredType(
135d712c5edSmartinboehme       recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass()))));
136d712c5edSmartinboehme }
137d712c5edSmartinboehme 
138d712c5edSmartinboehme auto hasOptionalType() { return hasType(desugarsToOptionalType()); }
139d712c5edSmartinboehme 
140d712c5edSmartinboehme /// Matches any of the spellings of the optional types and sugar, aliases,
141d712c5edSmartinboehme /// derived classes, etc.
142d712c5edSmartinboehme auto hasOptionalOrDerivedType() {
143d712c5edSmartinboehme   return hasType(desugarsToOptionalOrDerivedType());
144d712c5edSmartinboehme }
145d712c5edSmartinboehme 
146d712c5edSmartinboehme QualType getPublicType(const Expr *E) {
147d712c5edSmartinboehme   auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());
148d712c5edSmartinboehme   if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) {
149d712c5edSmartinboehme     QualType Ty = E->getType();
150d712c5edSmartinboehme     if (Ty->isPointerType())
151d712c5edSmartinboehme       return Ty->getPointeeType();
152d712c5edSmartinboehme     return Ty;
153d712c5edSmartinboehme   }
154d712c5edSmartinboehme 
155d712c5edSmartinboehme   // Is the derived type that we're casting from the type of `*this`? In this
156d712c5edSmartinboehme   // special case, we can upcast to the base class even if the base is
157d712c5edSmartinboehme   // non-public.
158d712c5edSmartinboehme   bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr());
159d712c5edSmartinboehme 
160d712c5edSmartinboehme   // Find the least-derived type in the path (i.e. the last entry in the list)
161d712c5edSmartinboehme   // that we can access.
162d712c5edSmartinboehme   const CXXBaseSpecifier *PublicBase = nullptr;
163d712c5edSmartinboehme   for (const CXXBaseSpecifier *Base : Cast->path()) {
164d712c5edSmartinboehme     if (Base->getAccessSpecifier() != AS_public && !CastingFromThis)
165d712c5edSmartinboehme       break;
166d712c5edSmartinboehme     PublicBase = Base;
167d712c5edSmartinboehme     CastingFromThis = false;
168d712c5edSmartinboehme   }
169d712c5edSmartinboehme 
170d712c5edSmartinboehme   if (PublicBase != nullptr)
171d712c5edSmartinboehme     return PublicBase->getType();
172d712c5edSmartinboehme 
173d712c5edSmartinboehme   // We didn't find any public type that we could cast to. There may be more
174d712c5edSmartinboehme   // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this
175d712c5edSmartinboehme   // will return the type of `getSubExpr()`.)
176d712c5edSmartinboehme   return getPublicType(Cast->getSubExpr());
177d712c5edSmartinboehme }
178d712c5edSmartinboehme 
179d712c5edSmartinboehme // Returns the least-derived type for the receiver of `MCE` that
180d712c5edSmartinboehme // `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to.
181d712c5edSmartinboehme // Effectively, we upcast until we reach a non-public base class, unless that
182d712c5edSmartinboehme // base is a base of `*this`.
183d712c5edSmartinboehme //
184d712c5edSmartinboehme // This is needed to correctly match methods called on types derived from
185d712c5edSmartinboehme // `std::optional`.
186d712c5edSmartinboehme //
187d712c5edSmartinboehme // Say we have a `struct Derived : public std::optional<int> {} d;` For a call
188d712c5edSmartinboehme // `d.has_value()`, the `getImplicitObjectArgument()` looks like this:
189d712c5edSmartinboehme //
190d712c5edSmartinboehme //   ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue
191d712c5edSmartinboehme //   |            <UncheckedDerivedToBase (optional -> __optional_storage_base)>
192d712c5edSmartinboehme //   `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived'
193d712c5edSmartinboehme //
194d712c5edSmartinboehme // The type of the implicit object argument is `__optional_storage_base`
195d712c5edSmartinboehme // (since this is the internal type that `has_value()` is declared on). If we
196d712c5edSmartinboehme // call `IgnoreParenImpCasts()` on the implicit object argument, we get the
197d712c5edSmartinboehme // `DeclRefExpr`, which has type `Derived`. Neither of these types is
198d712c5edSmartinboehme // `optional`, and hence neither is sufficient for querying whether we are
199d712c5edSmartinboehme // calling a method on `optional`.
200d712c5edSmartinboehme //
201d712c5edSmartinboehme // Instead, starting with the most derived type, we need to follow the chain of
202d712c5edSmartinboehme // casts
203d712c5edSmartinboehme QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) {
204d712c5edSmartinboehme   return getPublicType(MCE.getImplicitObjectArgument());
205d712c5edSmartinboehme }
206d712c5edSmartinboehme 
207d712c5edSmartinboehme AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType,
208d712c5edSmartinboehme               ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
209d712c5edSmartinboehme   return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder);
210d712c5edSmartinboehme }
2116adfc64eSYitzhak Mandelbaum 
2122f0630f8SAnton Dukeman auto isOptionalMemberCallWithNameMatcher(
2132f0630f8SAnton Dukeman     ast_matchers::internal::Matcher<NamedDecl> matcher,
2146ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
215d712c5edSmartinboehme   return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable)))
216d712c5edSmartinboehme                                      : anything(),
217d712c5edSmartinboehme                            publicReceiverType(desugarsToOptionalType()),
2182f0630f8SAnton Dukeman                            callee(cxxMethodDecl(matcher)));
219af98b0afSStanislav Gatev }
220af98b0afSStanislav Gatev 
221a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName(
222a184a0d8SYitzhak Mandelbaum     llvm::StringRef operator_name,
2236ad0788cSKazu Hirata     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
224a184a0d8SYitzhak Mandelbaum   return cxxOperatorCallExpr(
225a184a0d8SYitzhak Mandelbaum       hasOverloadedOperatorName(operator_name),
226a184a0d8SYitzhak Mandelbaum       callee(cxxMethodDecl(ofClass(optionalClass()))),
227a184a0d8SYitzhak Mandelbaum       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
228af98b0afSStanislav Gatev }
229af98b0afSStanislav Gatev 
230092a530cSStanislav Gatev auto isMakeOptionalCall() {
23111c423f9SChris Cotter   return callExpr(
23211c423f9SChris Cotter       callee(functionDecl(hasAnyName(
23311c423f9SChris Cotter           "std::make_optional", "base::make_optional", "absl::make_optional",
23411c423f9SChris Cotter           "folly::make_optional", "bsl::make_optional"))),
2359e0fc676SStanislav Gatev       hasOptionalType());
2369e0fc676SStanislav Gatev }
2379e0fc676SStanislav Gatev 
238390029beSYitzhak Mandelbaum auto nulloptTypeDecl() {
2392f0630f8SAnton Dukeman   return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",
24011c423f9SChris Cotter                               "base::nullopt_t", "folly::None",
24111c423f9SChris Cotter                               "bsl::nullopt_t"));
242092a530cSStanislav Gatev }
243092a530cSStanislav Gatev 
244390029beSYitzhak Mandelbaum auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
245390029beSYitzhak Mandelbaum 
246092a530cSStanislav Gatev auto inPlaceClass() {
2472f0630f8SAnton Dukeman   return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
24811c423f9SChris Cotter                                "base::in_place_t", "folly::in_place_t",
24911c423f9SChris Cotter                                "bsl::in_place_t"));
250092a530cSStanislav Gatev }
251092a530cSStanislav Gatev 
252092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
2530086a355SYitzhak Mandelbaum   return cxxConstructExpr(
2540086a355SYitzhak Mandelbaum       hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
255d712c5edSmartinboehme                                         hasParameter(0, hasNulloptType()))),
256d712c5edSmartinboehme       hasOptionalOrDerivedType());
257092a530cSStanislav Gatev }
258092a530cSStanislav Gatev 
259092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
260d712c5edSmartinboehme   return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())),
261d712c5edSmartinboehme                           hasOptionalOrDerivedType());
262092a530cSStanislav Gatev }
263092a530cSStanislav Gatev 
264092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
265092a530cSStanislav Gatev   return cxxConstructExpr(
266092a530cSStanislav Gatev       unless(hasDeclaration(
267092a530cSStanislav Gatev           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
268d712c5edSmartinboehme       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())),
269d712c5edSmartinboehme       hasOptionalOrDerivedType());
270092a530cSStanislav Gatev }
271092a530cSStanislav Gatev 
272b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
273b000b770SStanislav Gatev   return cxxOperatorCallExpr(
274b000b770SStanislav Gatev       hasOverloadedOperatorName("="),
275d712c5edSmartinboehme       callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
276b000b770SStanislav Gatev       unless(hasDeclaration(cxxMethodDecl(
277b000b770SStanislav Gatev           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
278b000b770SStanislav Gatev       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
279b000b770SStanislav Gatev }
280b000b770SStanislav Gatev 
281b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
282d712c5edSmartinboehme   return cxxOperatorCallExpr(
283d712c5edSmartinboehme       hasOverloadedOperatorName("="),
284d712c5edSmartinboehme       callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
285d712c5edSmartinboehme       argumentCountIs(2), hasArgument(1, hasNulloptType()));
286b000b770SStanislav Gatev }
287b000b770SStanislav Gatev 
2882ddd57aeSStanislav Gatev auto isStdSwapCall() {
2892ddd57aeSStanislav Gatev   return callExpr(callee(functionDecl(hasName("std::swap"))),
290d712c5edSmartinboehme                   argumentCountIs(2),
291d712c5edSmartinboehme                   hasArgument(0, hasOptionalOrDerivedType()),
292d712c5edSmartinboehme                   hasArgument(1, hasOptionalOrDerivedType()));
2932ddd57aeSStanislav Gatev }
2942ddd57aeSStanislav Gatev 
29525956d55SAMS21 auto isStdForwardCall() {
29625956d55SAMS21   return callExpr(callee(functionDecl(hasName("std::forward"))),
297d712c5edSmartinboehme                   argumentCountIs(1),
298d712c5edSmartinboehme                   hasArgument(0, hasOptionalOrDerivedType()));
29925956d55SAMS21 }
30025956d55SAMS21 
3017f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
3027f076004SYitzhak Mandelbaum 
3037f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() {
3047f076004SYitzhak Mandelbaum   // `opt.value_or("").empty()`
3057f076004SYitzhak Mandelbaum   return cxxMemberCallExpr(
3067f076004SYitzhak Mandelbaum       callee(cxxMethodDecl(hasName("empty"))),
3077f076004SYitzhak Mandelbaum       onImplicitObjectArgument(ignoringImplicit(
3087f076004SYitzhak Mandelbaum           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
3097f076004SYitzhak Mandelbaum                             callee(cxxMethodDecl(hasName("value_or"),
3107f076004SYitzhak Mandelbaum                                                  ofClass(optionalClass()))),
3117f076004SYitzhak Mandelbaum                             hasArgument(0, stringLiteral(hasSize(0))))
3127f076004SYitzhak Mandelbaum               .bind(ValueOrCallID))));
3137f076004SYitzhak Mandelbaum }
3147f076004SYitzhak Mandelbaum 
3157f076004SYitzhak Mandelbaum auto isValueOrNotEqX() {
3167f076004SYitzhak Mandelbaum   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
3177f076004SYitzhak Mandelbaum     return hasOperands(
3187f076004SYitzhak Mandelbaum         ignoringImplicit(
3197f076004SYitzhak Mandelbaum             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
3207f076004SYitzhak Mandelbaum                               callee(cxxMethodDecl(hasName("value_or"),
3217f076004SYitzhak Mandelbaum                                                    ofClass(optionalClass()))),
3227f076004SYitzhak Mandelbaum                               hasArgument(0, Arg))
3237f076004SYitzhak Mandelbaum                 .bind(ValueOrCallID)),
3247f076004SYitzhak Mandelbaum         ignoringImplicit(Arg));
3257f076004SYitzhak Mandelbaum   };
3267f076004SYitzhak Mandelbaum 
3277f076004SYitzhak Mandelbaum   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
3287f076004SYitzhak Mandelbaum   // support this pattern for any expression, but the AST does not have a
3297f076004SYitzhak Mandelbaum   // generic expression comparison facility, so we specialize to common cases
3307f076004SYitzhak Mandelbaum   // seen in practice.  FIXME: define a matcher that compares values across
3317f076004SYitzhak Mandelbaum   // nodes, which would let us generalize this to any `X`.
3327f076004SYitzhak Mandelbaum   return binaryOperation(hasOperatorName("!="),
3337f076004SYitzhak Mandelbaum                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
3347f076004SYitzhak Mandelbaum                                ComparesToSame(stringLiteral(hasSize(0))),
3357f076004SYitzhak Mandelbaum                                ComparesToSame(integerLiteral(equals(0)))));
3367f076004SYitzhak Mandelbaum }
3377f076004SYitzhak Mandelbaum 
3386761b24aSJan Voung auto isZeroParamConstMemberCall() {
3396761b24aSJan Voung   return cxxMemberCallExpr(
3406761b24aSJan Voung       callee(cxxMethodDecl(parameterCountIs(0), isConst())));
3416761b24aSJan Voung }
3426761b24aSJan Voung 
34366bbbf2eSJan Voung auto isZeroParamConstMemberOperatorCall() {
34466bbbf2eSJan Voung   return cxxOperatorCallExpr(
34566bbbf2eSJan Voung       callee(cxxMethodDecl(parameterCountIs(0), isConst())));
34666bbbf2eSJan Voung }
34766bbbf2eSJan Voung 
3486761b24aSJan Voung auto isNonConstMemberCall() {
3496761b24aSJan Voung   return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst()))));
3506761b24aSJan Voung }
3516761b24aSJan Voung 
3526761b24aSJan Voung auto isNonConstMemberOperatorCall() {
3536761b24aSJan Voung   return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));
3546761b24aSJan Voung }
3556761b24aSJan Voung 
35665e710c3SStanislav Gatev auto isCallReturningOptional() {
357d712c5edSmartinboehme   return callExpr(hasType(qualType(
358d712c5edSmartinboehme       anyOf(desugarsToOptionalOrDerivedType(),
359d712c5edSmartinboehme             referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
36065e710c3SStanislav Gatev }
36165e710c3SStanislav Gatev 
362390029beSYitzhak Mandelbaum template <typename L, typename R>
363390029beSYitzhak Mandelbaum auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
364390029beSYitzhak Mandelbaum   return cxxOperatorCallExpr(
365390029beSYitzhak Mandelbaum       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
366390029beSYitzhak Mandelbaum       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
367390029beSYitzhak Mandelbaum       hasArgument(1, rhs_arg_matcher));
368390029beSYitzhak Mandelbaum }
369390029beSYitzhak Mandelbaum 
3706272226bSSam McCall /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
3716272226bSSam McCall const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
3722ee396b0Smartinboehme   auto *Value = Env.get<BoolValue>(Expr);
373390029beSYitzhak Mandelbaum   if (Value != nullptr)
3746272226bSSam McCall     return Value->formula();
375390029beSYitzhak Mandelbaum 
376390029beSYitzhak Mandelbaum   Value = &Env.makeAtomicBoolValue();
377b244b6aeSMartin Braenne   Env.setValue(Expr, *Value);
3786272226bSSam McCall   return Value->formula();
379390029beSYitzhak Mandelbaum }
380390029beSYitzhak Mandelbaum 
38171f2ec2dSmartinboehme StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) {
38271f2ec2dSmartinboehme   return OptionalLoc.getSyntheticField("has_value");
38371f2ec2dSmartinboehme }
38471f2ec2dSmartinboehme 
38571f2ec2dSmartinboehme StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) {
38671f2ec2dSmartinboehme   return OptionalLoc.getSyntheticField("value");
38771f2ec2dSmartinboehme }
38871f2ec2dSmartinboehme 
3898fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
39071f2ec2dSmartinboehme /// property of the optional at `OptionalLoc`.
39171f2ec2dSmartinboehme void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,
39271f2ec2dSmartinboehme                  Environment &Env) {
39371f2ec2dSmartinboehme   Env.setValue(locForHasValue(OptionalLoc), HasValueVal);
3948fcdd625SStanislav Gatev }
3958fcdd625SStanislav Gatev 
396af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
39771f2ec2dSmartinboehme /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null.
39871f2ec2dSmartinboehme BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {
39971f2ec2dSmartinboehme   if (OptionalLoc == nullptr)
40071f2ec2dSmartinboehme     return nullptr;
40171f2ec2dSmartinboehme   StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
4022ee396b0Smartinboehme   auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);
403dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr) {
404dd38caf3SYitzhak Mandelbaum     HasValueVal = &Env.makeAtomicBoolValue();
40571f2ec2dSmartinboehme     Env.setValue(HasValueLoc, *HasValueVal);
406dd38caf3SYitzhak Mandelbaum   }
407dd38caf3SYitzhak Mandelbaum   return HasValueVal;
408af98b0afSStanislav Gatev }
409af98b0afSStanislav Gatev 
410d712c5edSmartinboehme QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {
411d712c5edSmartinboehme   auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);
412d712c5edSmartinboehme   return CTSD.getTemplateArgs()[0].getAsType();
413092a530cSStanislav Gatev }
414092a530cSStanislav Gatev 
415092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
416092a530cSStanislav Gatev ///
417092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
418092a530cSStanislav Gatev /// function will be 2.
419092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
420d712c5edSmartinboehme   const CXXRecordDecl *Optional =
421d712c5edSmartinboehme       getOptionalBaseClass(Type->getAsCXXRecordDecl());
422d712c5edSmartinboehme   if (Optional == nullptr)
423092a530cSStanislav Gatev     return 0;
424092a530cSStanislav Gatev   return 1 + countOptionalWrappers(
425092a530cSStanislav Gatev                  ASTCtx,
426d712c5edSmartinboehme                  valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
427092a530cSStanislav Gatev }
428092a530cSStanislav Gatev 
42971f2ec2dSmartinboehme StorageLocation *getLocBehindPossiblePointer(const Expr &E,
43071f2ec2dSmartinboehme                                              const Environment &Env) {
43171f2ec2dSmartinboehme   if (E.isPRValue()) {
43271f2ec2dSmartinboehme     if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
43371f2ec2dSmartinboehme       return &PointerVal->getPointeeLoc();
434dd38caf3SYitzhak Mandelbaum     return nullptr;
435dd38caf3SYitzhak Mandelbaum   }
43671f2ec2dSmartinboehme   return Env.getStorageLocation(E);
43748bc7150SMartin Braenne }
43848bc7150SMartin Braenne 
439092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
440af98b0afSStanislav Gatev                         LatticeTransferState &State) {
44171f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
44271f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
443b244b6aeSMartin Braenne     if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)
44471f2ec2dSmartinboehme       State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
445af98b0afSStanislav Gatev   }
446dd38caf3SYitzhak Mandelbaum }
447af98b0afSStanislav Gatev 
4483bc1ea5bSMartin Braenne void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
4493bc1ea5bSMartin Braenne                          LatticeTransferState &State) {
45071f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
45171f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
45271f2ec2dSmartinboehme     State.Env.setValue(
45371f2ec2dSmartinboehme         *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));
4543bc1ea5bSMartin Braenne }
4553bc1ea5bSMartin Braenne 
456092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
457092a530cSStanislav Gatev                               const MatchFinder::MatchResult &,
458092a530cSStanislav Gatev                               LatticeTransferState &State) {
459e8fce958Smartinboehme   setHasValue(State.Env.getResultObjectLocation(*E),
460e8fce958Smartinboehme               State.Env.getBoolLiteralValue(true), State.Env);
4619e0fc676SStanislav Gatev }
4629e0fc676SStanislav Gatev 
463092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
464092a530cSStanislav Gatev                                   const MatchFinder::MatchResult &,
465af98b0afSStanislav Gatev                                   LatticeTransferState &State) {
466dd38caf3SYitzhak Mandelbaum   if (auto *HasValueVal = getHasValue(
46771f2ec2dSmartinboehme           State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
468b244b6aeSMartin Braenne     State.Env.setValue(*CallExpr, *HasValueVal);
469af98b0afSStanislav Gatev   }
470af98b0afSStanislav Gatev }
471af98b0afSStanislav Gatev 
47211c423f9SChris Cotter void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr,
47311c423f9SChris Cotter                                 const MatchFinder::MatchResult &,
47411c423f9SChris Cotter                                 LatticeTransferState &State) {
47511c423f9SChris Cotter   if (auto *HasValueVal = getHasValue(
47611c423f9SChris Cotter           State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
47711c423f9SChris Cotter     State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal));
47811c423f9SChris Cotter   }
47911c423f9SChris Cotter }
48011c423f9SChris Cotter 
4817f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in
4827f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property.
4836272226bSSam McCall void transferValueOrImpl(
4846272226bSSam McCall     const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
4857f076004SYitzhak Mandelbaum     LatticeTransferState &State,
4866272226bSSam McCall     const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
4876272226bSSam McCall                                 const Formula &HasValueVal)) {
4887f076004SYitzhak Mandelbaum   auto &Env = State.Env;
4897f076004SYitzhak Mandelbaum 
49071f2ec2dSmartinboehme   const auto *MCE =
49171f2ec2dSmartinboehme       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);
4927f076004SYitzhak Mandelbaum 
49371f2ec2dSmartinboehme   auto *HasValueVal =
49471f2ec2dSmartinboehme       getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env));
495dd38caf3SYitzhak Mandelbaum   if (HasValueVal == nullptr)
4967f076004SYitzhak Mandelbaum     return;
4977f076004SYitzhak Mandelbaum 
498526c9b7eSmartinboehme   Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
4996272226bSSam McCall                        HasValueVal->formula()));
5007f076004SYitzhak Mandelbaum }
5017f076004SYitzhak Mandelbaum 
5027f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
5037f076004SYitzhak Mandelbaum                                     const MatchFinder::MatchResult &Result,
5047f076004SYitzhak Mandelbaum                                     LatticeTransferState &State) {
5057f076004SYitzhak Mandelbaum   return transferValueOrImpl(ComparisonExpr, Result, State,
5066272226bSSam McCall                              [](Environment &Env, const Formula &ExprVal,
5076272226bSSam McCall                                 const Formula &HasValueVal) -> const Formula & {
5086272226bSSam McCall                                auto &A = Env.arena();
5097f076004SYitzhak Mandelbaum                                // If the result is *not* empty, then we know the
5107f076004SYitzhak Mandelbaum                                // optional must have been holding a value. If
5117f076004SYitzhak Mandelbaum                                // `ExprVal` is true, though, we don't learn
5127f076004SYitzhak Mandelbaum                                // anything definite about `has_value`, so we
5137f076004SYitzhak Mandelbaum                                // don't add any corresponding implications to
5147f076004SYitzhak Mandelbaum                                // the flow condition.
5156272226bSSam McCall                                return A.makeImplies(A.makeNot(ExprVal),
5167f076004SYitzhak Mandelbaum                                                     HasValueVal);
5177f076004SYitzhak Mandelbaum                              });
5187f076004SYitzhak Mandelbaum }
5197f076004SYitzhak Mandelbaum 
5207f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr,
5217f076004SYitzhak Mandelbaum                            const MatchFinder::MatchResult &Result,
5227f076004SYitzhak Mandelbaum                            LatticeTransferState &State) {
5237f076004SYitzhak Mandelbaum   transferValueOrImpl(ComparisonExpr, Result, State,
5246272226bSSam McCall                       [](Environment &Env, const Formula &ExprVal,
5256272226bSSam McCall                          const Formula &HasValueVal) -> const Formula & {
5266272226bSSam McCall                         auto &A = Env.arena();
5277f076004SYitzhak Mandelbaum                         // We know that if `(opt.value_or(X) != X)` then
5287f076004SYitzhak Mandelbaum                         // `opt.hasValue()`, even without knowing further
5297f076004SYitzhak Mandelbaum                         // details about the contents of `opt`.
5306272226bSSam McCall                         return A.makeImplies(ExprVal, HasValueVal);
5317f076004SYitzhak Mandelbaum                       });
5327f076004SYitzhak Mandelbaum }
5337f076004SYitzhak Mandelbaum 
53465e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E,
53565e710c3SStanislav Gatev                                    const MatchFinder::MatchResult &Result,
53665e710c3SStanislav Gatev                                    LatticeTransferState &State) {
5379ecdbe38SMartin Braenne   RecordStorageLocation *Loc = nullptr;
53844f98d01SMartin Braenne   if (E->isPRValue()) {
53944f98d01SMartin Braenne     Loc = &State.Env.getResultObjectLocation(*E);
54044f98d01SMartin Braenne   } else {
5412ee396b0Smartinboehme     Loc = State.Env.get<RecordStorageLocation>(*E);
542f76f6674SMartin Braenne     if (Loc == nullptr) {
5439ecdbe38SMartin Braenne       Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));
544b244b6aeSMartin Braenne       State.Env.setStorageLocation(*E, *Loc);
54544f98d01SMartin Braenne     }
546f76f6674SMartin Braenne   }
54744f98d01SMartin Braenne 
548e8fce958Smartinboehme   if (State.Env.getValue(locForHasValue(*Loc)) != nullptr)
549e8fce958Smartinboehme     return;
550e8fce958Smartinboehme 
551e8fce958Smartinboehme   setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
55265e710c3SStanislav Gatev }
55365e710c3SStanislav Gatev 
5546761b24aSJan Voung void handleConstMemberCall(const CallExpr *CE,
5556761b24aSJan Voung                            dataflow::RecordStorageLocation *RecordLoc,
5566761b24aSJan Voung                            const MatchFinder::MatchResult &Result,
5576761b24aSJan Voung                            LatticeTransferState &State) {
5586761b24aSJan Voung   // If the const method returns an optional or reference to an optional.
5596761b24aSJan Voung   if (RecordLoc != nullptr && isSupportedOptionalType(CE->getType())) {
560*72a28a3bSJan Voung     const FunctionDecl *DirectCallee = CE->getDirectCallee();
561*72a28a3bSJan Voung     if (DirectCallee == nullptr)
562*72a28a3bSJan Voung       return;
563*72a28a3bSJan Voung     StorageLocation &Loc =
5646761b24aSJan Voung         State.Lattice.getOrCreateConstMethodReturnStorageLocation(
565*72a28a3bSJan Voung             *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
5666761b24aSJan Voung               setHasValue(cast<RecordStorageLocation>(Loc),
5676761b24aSJan Voung                           State.Env.makeAtomicBoolValue(), State.Env);
5686761b24aSJan Voung             });
5696761b24aSJan Voung     if (CE->isGLValue()) {
5706761b24aSJan Voung       // If the call to the const method returns a reference to an optional,
5716761b24aSJan Voung       // link the call expression to the cached StorageLocation.
572*72a28a3bSJan Voung       State.Env.setStorageLocation(*CE, Loc);
5736761b24aSJan Voung     } else {
5746761b24aSJan Voung       // If the call to the const method returns an optional by value, we
5756761b24aSJan Voung       // need to use CopyRecord to link the optional to the result object
5766761b24aSJan Voung       // of the call expression.
5776761b24aSJan Voung       auto &ResultLoc = State.Env.getResultObjectLocation(*CE);
578*72a28a3bSJan Voung       copyRecord(cast<RecordStorageLocation>(Loc), ResultLoc, State.Env);
5796761b24aSJan Voung     }
5806761b24aSJan Voung     return;
5816761b24aSJan Voung   }
5826761b24aSJan Voung 
58366bbbf2eSJan Voung   // Cache if the const method returns a boolean or pointer type.
5846761b24aSJan Voung   // We may decide to cache other return types in the future.
58566bbbf2eSJan Voung   if (RecordLoc != nullptr &&
58666bbbf2eSJan Voung       (CE->getType()->isBooleanType() || CE->getType()->isPointerType())) {
5876761b24aSJan Voung     Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE,
5886761b24aSJan Voung                                                                  State.Env);
5896761b24aSJan Voung     if (Val == nullptr)
5906761b24aSJan Voung       return;
5916761b24aSJan Voung     State.Env.setValue(*CE, *Val);
5926761b24aSJan Voung     return;
5936761b24aSJan Voung   }
5946761b24aSJan Voung 
5956761b24aSJan Voung   // Perform default handling if the call returns an optional
5966761b24aSJan Voung   // but wasn't handled above (if RecordLoc is nullptr).
5976761b24aSJan Voung   if (isSupportedOptionalType(CE->getType())) {
5986761b24aSJan Voung     transferCallReturningOptional(CE, Result, State);
5996761b24aSJan Voung   }
6006761b24aSJan Voung }
6016761b24aSJan Voung 
6026761b24aSJan Voung void transferValue_ConstMemberCall(const CXXMemberCallExpr *MCE,
6036761b24aSJan Voung                                    const MatchFinder::MatchResult &Result,
6046761b24aSJan Voung                                    LatticeTransferState &State) {
6056761b24aSJan Voung   handleConstMemberCall(
6066761b24aSJan Voung       MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
6076761b24aSJan Voung }
6086761b24aSJan Voung 
60966bbbf2eSJan Voung void transferValue_ConstMemberOperatorCall(
61066bbbf2eSJan Voung     const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result,
61166bbbf2eSJan Voung     LatticeTransferState &State) {
61266bbbf2eSJan Voung   auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
61366bbbf2eSJan Voung       State.Env.getStorageLocation(*OCE->getArg(0)));
61466bbbf2eSJan Voung   handleConstMemberCall(OCE, RecordLoc, Result, State);
61566bbbf2eSJan Voung }
61666bbbf2eSJan Voung 
6176761b24aSJan Voung void handleNonConstMemberCall(const CallExpr *CE,
6186761b24aSJan Voung                               dataflow::RecordStorageLocation *RecordLoc,
6196761b24aSJan Voung                               const MatchFinder::MatchResult &Result,
6206761b24aSJan Voung                               LatticeTransferState &State) {
6216761b24aSJan Voung   if (RecordLoc != nullptr) {
6221f6741c1SJan Voung     // When a non-const member function is called, clear all (non-const)
6231f6741c1SJan Voung     // optional fields of the receiver. Const-qualified fields can't be
6241f6741c1SJan Voung     // changed (at least, not without UB).
6256761b24aSJan Voung     for (const auto &[Field, FieldLoc] : RecordLoc->children()) {
6261f6741c1SJan Voung       QualType FieldType = Field->getType();
6271f6741c1SJan Voung       if (!FieldType.isConstQualified() &&
6281f6741c1SJan Voung           isSupportedOptionalType(Field->getType())) {
6296761b24aSJan Voung         auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
6306761b24aSJan Voung         if (FieldRecordLoc) {
6316761b24aSJan Voung           setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),
6326761b24aSJan Voung                       State.Env);
6336761b24aSJan Voung         }
6346761b24aSJan Voung       }
6356761b24aSJan Voung     }
6366761b24aSJan Voung     State.Lattice.clearConstMethodReturnValues(*RecordLoc);
6376761b24aSJan Voung     State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc);
6386761b24aSJan Voung   }
6396761b24aSJan Voung 
6406761b24aSJan Voung   // Perform default handling if the call returns an optional.
6416761b24aSJan Voung   if (isSupportedOptionalType(CE->getType())) {
6426761b24aSJan Voung     transferCallReturningOptional(CE, Result, State);
6436761b24aSJan Voung   }
6446761b24aSJan Voung }
6456761b24aSJan Voung 
6466761b24aSJan Voung void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE,
6476761b24aSJan Voung                                       const MatchFinder::MatchResult &Result,
6486761b24aSJan Voung                                       LatticeTransferState &State) {
6496761b24aSJan Voung   handleNonConstMemberCall(
6506761b24aSJan Voung       MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
6516761b24aSJan Voung }
6526761b24aSJan Voung 
6536761b24aSJan Voung void transferValue_NonConstMemberOperatorCall(
6546761b24aSJan Voung     const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result,
6556761b24aSJan Voung     LatticeTransferState &State) {
6566761b24aSJan Voung   auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
6576761b24aSJan Voung       State.Env.getStorageLocation(*OCE->getArg(0)));
6586761b24aSJan Voung   handleNonConstMemberCall(OCE, RecordLoc, Result, State);
6596761b24aSJan Voung }
6606761b24aSJan Voung 
661f653d140SMartin Braenne void constructOptionalValue(const Expr &E, Environment &Env,
662092a530cSStanislav Gatev                             BoolValue &HasValueVal) {
6639ecdbe38SMartin Braenne   RecordStorageLocation &Loc = Env.getResultObjectLocation(E);
664e8fce958Smartinboehme   setHasValue(Loc, HasValueVal, Env);
6659e0fc676SStanislav Gatev }
6669e0fc676SStanislav Gatev 
667b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
668b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
669b000b770SStanislav Gatev /// where `T` is constructible from `U`.
670ae280281Smartinboehme BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E,
671b000b770SStanislav Gatev                                      const MatchFinder::MatchResult &MatchRes,
672b000b770SStanislav Gatev                                      LatticeTransferState &State) {
673ae280281Smartinboehme   const int DestTypeOptionalWrappersCount =
674ae280281Smartinboehme       countOptionalWrappers(*MatchRes.Context, DestType);
675c849843cSMartin Braenne   const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
676c849843cSMartin Braenne       *MatchRes.Context, E.getType().getNonReferenceType());
677b000b770SStanislav Gatev 
678ae280281Smartinboehme   // Is this an constructor of the form `template<class U> optional(U &&)` /
679ae280281Smartinboehme   // assignment of the form `template<class U> optional& operator=(U &&)`
680ae280281Smartinboehme   // (where `T` is assignable / constructible from `U`)?
681ae280281Smartinboehme   // We recognize this because the number of optionals in the optional being
682ae280281Smartinboehme   // assigned to is different from the function argument type.
683ae280281Smartinboehme   if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)
684b000b770SStanislav Gatev     return State.Env.getBoolLiteralValue(true);
685b000b770SStanislav Gatev 
686ae280281Smartinboehme   // Otherwise, this must be a constructor of the form
687ae280281Smartinboehme   // `template <class U> optional<optional<U> &&)` / assignment of the form
688ae280281Smartinboehme   // `template <class U> optional& operator=(optional<U> &&)
689ae280281Smartinboehme   // (where, again, `T` is assignable / constructible from `U`).
6902ee396b0Smartinboehme   auto *Loc = State.Env.get<RecordStorageLocation>(E);
69171f2ec2dSmartinboehme   if (auto *HasValueVal = getHasValue(State.Env, Loc))
692dd38caf3SYitzhak Mandelbaum     return *HasValueVal;
693b000b770SStanislav Gatev   return State.Env.makeAtomicBoolValue();
694b000b770SStanislav Gatev }
695b000b770SStanislav Gatev 
696092a530cSStanislav Gatev void transferValueOrConversionConstructor(
697092a530cSStanislav Gatev     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
6989e0fc676SStanislav Gatev     LatticeTransferState &State) {
699092a530cSStanislav Gatev   assert(E->getNumArgs() > 0);
700092a530cSStanislav Gatev 
701ae280281Smartinboehme   constructOptionalValue(
702ae280281Smartinboehme       *E, State.Env,
703ae280281Smartinboehme       valueOrConversionHasValue(
704ae280281Smartinboehme           E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0),
705ae280281Smartinboehme           MatchRes, State));
706b000b770SStanislav Gatev }
707092a530cSStanislav Gatev 
708b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
709b000b770SStanislav Gatev                         LatticeTransferState &State) {
710b000b770SStanislav Gatev   assert(E->getNumArgs() > 0);
711b000b770SStanislav Gatev 
7122ee396b0Smartinboehme   if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) {
713e8fce958Smartinboehme     setHasValue(*Loc, HasValueVal, State.Env);
714b000b770SStanislav Gatev 
715b000b770SStanislav Gatev     // Assign a storage location for the whole expression.
716b244b6aeSMartin Braenne     State.Env.setStorageLocation(*E, *Loc);
717f653d140SMartin Braenne   }
718b000b770SStanislav Gatev }
719b000b770SStanislav Gatev 
720b000b770SStanislav Gatev void transferValueOrConversionAssignment(
721b000b770SStanislav Gatev     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
722b000b770SStanislav Gatev     LatticeTransferState &State) {
723b000b770SStanislav Gatev   assert(E->getNumArgs() > 1);
724ae280281Smartinboehme   transferAssignment(
725ae280281Smartinboehme       E,
726ae280281Smartinboehme       valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(),
72706decd0bSKazu Hirata                                 *E->getArg(1), MatchRes, State),
728b000b770SStanislav Gatev       State);
729b000b770SStanislav Gatev }
730b000b770SStanislav Gatev 
731b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
732b000b770SStanislav Gatev                                const MatchFinder::MatchResult &,
733b000b770SStanislav Gatev                                LatticeTransferState &State) {
734b000b770SStanislav Gatev   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
7359e0fc676SStanislav Gatev }
7369e0fc676SStanislav Gatev 
7379ecdbe38SMartin Braenne void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
7389ecdbe38SMartin Braenne                   Environment &Env) {
739d4fb829bSYitzhak Mandelbaum   // We account for cases where one or both of the optionals are not modeled,
740d4fb829bSYitzhak Mandelbaum   // either lacking associated storage locations, or lacking values associated
741d4fb829bSYitzhak Mandelbaum   // to such storage locations.
7422ddd57aeSStanislav Gatev 
743d4fb829bSYitzhak Mandelbaum   if (Loc1 == nullptr) {
744d4fb829bSYitzhak Mandelbaum     if (Loc2 != nullptr)
745e8fce958Smartinboehme       setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env);
746d4fb829bSYitzhak Mandelbaum     return;
747d4fb829bSYitzhak Mandelbaum   }
748d4fb829bSYitzhak Mandelbaum   if (Loc2 == nullptr) {
749e8fce958Smartinboehme     setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env);
750d4fb829bSYitzhak Mandelbaum     return;
751d4fb829bSYitzhak Mandelbaum   }
7522ddd57aeSStanislav Gatev 
753d4fb829bSYitzhak Mandelbaum   // Both expressions have locations, though they may not have corresponding
754d4fb829bSYitzhak Mandelbaum   // values. In that case, we create a fresh value at this point. Note that if
755d4fb829bSYitzhak Mandelbaum   // two branches both do this, they will not share the value, but it at least
756d4fb829bSYitzhak Mandelbaum   // allows for local reasoning about the value. To avoid the above, we would
757d4fb829bSYitzhak Mandelbaum   // need *lazy* value allocation.
758d4fb829bSYitzhak Mandelbaum   // FIXME: allocate values lazily, instead of just creating a fresh value.
75971f2ec2dSmartinboehme   BoolValue *BoolVal1 = getHasValue(Env, Loc1);
760f653d140SMartin Braenne   if (BoolVal1 == nullptr)
761f653d140SMartin Braenne     BoolVal1 = &Env.makeAtomicBoolValue();
762d4fb829bSYitzhak Mandelbaum 
76371f2ec2dSmartinboehme   BoolValue *BoolVal2 = getHasValue(Env, Loc2);
764f653d140SMartin Braenne   if (BoolVal2 == nullptr)
765f653d140SMartin Braenne     BoolVal2 = &Env.makeAtomicBoolValue();
766d4fb829bSYitzhak Mandelbaum 
767e8fce958Smartinboehme   setHasValue(*Loc1, *BoolVal2, Env);
768e8fce958Smartinboehme   setHasValue(*Loc2, *BoolVal1, Env);
7692ddd57aeSStanislav Gatev }
7702ddd57aeSStanislav Gatev 
7712ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
7722ddd57aeSStanislav Gatev                       const MatchFinder::MatchResult &,
7732ddd57aeSStanislav Gatev                       LatticeTransferState &State) {
7742ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 1);
7752ee396b0Smartinboehme   auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
776f653d140SMartin Braenne   transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
7772ddd57aeSStanislav Gatev }
7782ddd57aeSStanislav Gatev 
7792ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
7802ddd57aeSStanislav Gatev                          LatticeTransferState &State) {
7812ddd57aeSStanislav Gatev   assert(E->getNumArgs() == 2);
7822ee396b0Smartinboehme   auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
7832ee396b0Smartinboehme   auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1));
784f653d140SMartin Braenne   transferSwap(Arg0Loc, Arg1Loc, State.Env);
7852ddd57aeSStanislav Gatev }
7862ddd57aeSStanislav Gatev 
78725956d55SAMS21 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
78825956d55SAMS21                             LatticeTransferState &State) {
78925956d55SAMS21   assert(E->getNumArgs() == 1);
79025956d55SAMS21 
791b244b6aeSMartin Braenne   if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
792b244b6aeSMartin Braenne     State.Env.setStorageLocation(*E, *Loc);
79325956d55SAMS21 }
79425956d55SAMS21 
7956272226bSSam McCall const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
7966272226bSSam McCall                                 const Formula &LHS, const Formula &RHS) {
797390029beSYitzhak Mandelbaum   // Logically, an optional<T> object is composed of two values - a `has_value`
798390029beSYitzhak Mandelbaum   // bit and a value of type T. Equality of optional objects compares both
799390029beSYitzhak Mandelbaum   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
800390029beSYitzhak Mandelbaum   // when two optional objects are engaged, the equality of their respective
801390029beSYitzhak Mandelbaum   // values of type T matters. Since we only track the `has_value` bits, we
802390029beSYitzhak Mandelbaum   // can't make any conclusions about equality when we know that two optional
803390029beSYitzhak Mandelbaum   // objects are engaged.
804390029beSYitzhak Mandelbaum   //
805390029beSYitzhak Mandelbaum   // We express this as two facts about the equality:
806390029beSYitzhak Mandelbaum   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
807390029beSYitzhak Mandelbaum   //    If they are equal, then either both are set or both are unset.
808390029beSYitzhak Mandelbaum   // b) (!LHS & !RHS) => EqVal
809390029beSYitzhak Mandelbaum   //    If neither is set, then they are equal.
810390029beSYitzhak Mandelbaum   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
8116272226bSSam McCall   return A.makeAnd(
8126272226bSSam McCall       A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
8136272226bSSam McCall                                     A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
8146272226bSSam McCall       A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
815390029beSYitzhak Mandelbaum }
816390029beSYitzhak Mandelbaum 
817390029beSYitzhak Mandelbaum void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
818390029beSYitzhak Mandelbaum                                     const MatchFinder::MatchResult &,
819390029beSYitzhak Mandelbaum                                     LatticeTransferState &State) {
820390029beSYitzhak Mandelbaum   Environment &Env = State.Env;
8216272226bSSam McCall   auto &A = Env.arena();
822390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
8232ee396b0Smartinboehme   auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0));
82471f2ec2dSmartinboehme   if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {
8252ee396b0Smartinboehme     auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1));
82671f2ec2dSmartinboehme     if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {
827390029beSYitzhak Mandelbaum       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
8286272226bSSam McCall         CmpValue = &A.makeNot(*CmpValue);
829526c9b7eSmartinboehme       Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
8306272226bSSam McCall                                   RHasVal->formula()));
831390029beSYitzhak Mandelbaum     }
832390029beSYitzhak Mandelbaum   }
83371f2ec2dSmartinboehme }
834390029beSYitzhak Mandelbaum 
835390029beSYitzhak Mandelbaum void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
836390029beSYitzhak Mandelbaum                                  const clang::Expr *E, Environment &Env) {
8376272226bSSam McCall   auto &A = Env.arena();
838390029beSYitzhak Mandelbaum   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
8392ee396b0Smartinboehme   auto *Loc = Env.get<RecordStorageLocation>(*E);
84071f2ec2dSmartinboehme   if (auto *HasVal = getHasValue(Env, Loc)) {
841390029beSYitzhak Mandelbaum     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
8426272226bSSam McCall       CmpValue = &A.makeNot(*CmpValue);
843526c9b7eSmartinboehme     Env.assume(
8446272226bSSam McCall         evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
845390029beSYitzhak Mandelbaum   }
846390029beSYitzhak Mandelbaum }
847390029beSYitzhak Mandelbaum 
84871f2ec2dSmartinboehme void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,
84971f2ec2dSmartinboehme                                    const clang::Expr *E, Environment &Env) {
85071f2ec2dSmartinboehme   auto &A = Env.arena();
85171f2ec2dSmartinboehme   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
8522ee396b0Smartinboehme   auto *Loc = Env.get<RecordStorageLocation>(*E);
85371f2ec2dSmartinboehme   if (auto *HasVal = getHasValue(Env, Loc)) {
85471f2ec2dSmartinboehme     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
85571f2ec2dSmartinboehme       CmpValue = &A.makeNot(*CmpValue);
85671f2ec2dSmartinboehme     Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
85771f2ec2dSmartinboehme                                 A.makeLiteral(false)));
85871f2ec2dSmartinboehme   }
85971f2ec2dSmartinboehme }
86071f2ec2dSmartinboehme 
8616ad0788cSKazu Hirata std::optional<StatementMatcher>
862a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
8635d22d1f5SYitzhak Mandelbaum   if (Options.IgnoreSmartPointerDereference) {
8645d22d1f5SYitzhak Mandelbaum     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
8655d22d1f5SYitzhak Mandelbaum         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
8665d22d1f5SYitzhak Mandelbaum         unless(hasArgument(0, expr(hasOptionalType()))))));
8675d22d1f5SYitzhak Mandelbaum     return expr(
8685d22d1f5SYitzhak Mandelbaum         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
8695d22d1f5SYitzhak Mandelbaum   }
87034e0d057SKazu Hirata   return std::nullopt;
871a184a0d8SYitzhak Mandelbaum }
872a184a0d8SYitzhak Mandelbaum 
87358fe7f96SSam Estep StatementMatcher
8746ad0788cSKazu Hirata valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
8752f0630f8SAnton Dukeman   return isOptionalMemberCallWithNameMatcher(hasName("value"),
8762f0630f8SAnton Dukeman                                              IgnorableOptional);
87758fe7f96SSam Estep }
87858fe7f96SSam Estep 
87958fe7f96SSam Estep StatementMatcher
8806ad0788cSKazu Hirata valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
88158fe7f96SSam Estep   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
88258fe7f96SSam Estep                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
88358fe7f96SSam Estep }
88458fe7f96SSam Estep 
8855d22d1f5SYitzhak Mandelbaum auto buildTransferMatchSwitch() {
886b000b770SStanislav Gatev   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
887b000b770SStanislav Gatev   // lot of duplicated work (e.g. string comparisons), consider providing APIs
888b000b770SStanislav Gatev   // that avoid it through memoization.
8897538b360SWei Yi Tee   return CFGMatchSwitchBuilder<LatticeTransferState>()
8909e0fc676SStanislav Gatev       // make_optional
8917538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
892092a530cSStanislav Gatev 
8930e8d4a6dSYitzhak Mandelbaum       // optional::optional (in place)
8947538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(
895092a530cSStanislav Gatev           isOptionalInPlaceConstructor(),
896092a530cSStanislav Gatev           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
897092a530cSStanislav Gatev              LatticeTransferState &State) {
898f653d140SMartin Braenne             constructOptionalValue(*E, State.Env,
8990e8d4a6dSYitzhak Mandelbaum                                    State.Env.getBoolLiteralValue(true));
900092a530cSStanislav Gatev           })
9010e8d4a6dSYitzhak Mandelbaum       // optional::optional(nullopt_t)
902390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXConstructExpr>(
903390029beSYitzhak Mandelbaum           isOptionalNulloptConstructor(),
904390029beSYitzhak Mandelbaum           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
905390029beSYitzhak Mandelbaum              LatticeTransferState &State) {
906f653d140SMartin Braenne             constructOptionalValue(*E, State.Env,
9070e8d4a6dSYitzhak Mandelbaum                                    State.Env.getBoolLiteralValue(false));
908390029beSYitzhak Mandelbaum           })
9090e8d4a6dSYitzhak Mandelbaum       // optional::optional (value/conversion)
9107538b360SWei Yi Tee       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
911092a530cSStanislav Gatev                                        transferValueOrConversionConstructor)
9129e0fc676SStanislav Gatev 
913b000b770SStanislav Gatev       // optional::operator=
9147538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(
9157538b360SWei Yi Tee           isOptionalValueOrConversionAssignment(),
916b000b770SStanislav Gatev           transferValueOrConversionAssignment)
9177538b360SWei Yi Tee       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
918b000b770SStanislav Gatev                                           transferNulloptAssignment)
919b000b770SStanislav Gatev 
920af98b0afSStanislav Gatev       // optional::value
9217538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
9225d22d1f5SYitzhak Mandelbaum           valueCall(std::nullopt),
923092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
924092a530cSStanislav Gatev              LatticeTransferState &State) {
925af98b0afSStanislav Gatev             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
926af98b0afSStanislav Gatev           })
927af98b0afSStanislav Gatev 
9283bc1ea5bSMartin Braenne       // optional::operator*
9293bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
9307538b360SWei Yi Tee                                [](const CallExpr *E,
9317538b360SWei Yi Tee                                   const MatchFinder::MatchResult &,
932092a530cSStanislav Gatev                                   LatticeTransferState &State) {
933af98b0afSStanislav Gatev                                  transferUnwrapCall(E, E->getArg(0), State);
934af98b0afSStanislav Gatev                                })
935af98b0afSStanislav Gatev 
9363bc1ea5bSMartin Braenne       // optional::operator->
9373bc1ea5bSMartin Braenne       .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
9383bc1ea5bSMartin Braenne                                [](const CallExpr *E,
9393bc1ea5bSMartin Braenne                                   const MatchFinder::MatchResult &,
9403bc1ea5bSMartin Braenne                                   LatticeTransferState &State) {
9413bc1ea5bSMartin Braenne                                  transferArrowOpCall(E, E->getArg(0), State);
9423bc1ea5bSMartin Braenne                                })
9433bc1ea5bSMartin Braenne 
9442f0630f8SAnton Dukeman       // optional::has_value, optional::hasValue
9452f0630f8SAnton Dukeman       // Of the supported optionals only folly::Optional uses hasValue, but this
9462f0630f8SAnton Dukeman       // will also pass for other types
9477538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
9482f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(
9492f0630f8SAnton Dukeman               hasAnyName("has_value", "hasValue")),
950af98b0afSStanislav Gatev           transferOptionalHasValueCall)
951af98b0afSStanislav Gatev 
9529e0fc676SStanislav Gatev       // optional::operator bool
9537538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
9542f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
9559e0fc676SStanislav Gatev           transferOptionalHasValueCall)
9569e0fc676SStanislav Gatev 
95711c423f9SChris Cotter       // NullableValue::isNull
95811c423f9SChris Cotter       // Only NullableValue has isNull
95911c423f9SChris Cotter       .CaseOfCFGStmt<CXXMemberCallExpr>(
96011c423f9SChris Cotter           isOptionalMemberCallWithNameMatcher(hasName("isNull")),
96111c423f9SChris Cotter           transferOptionalIsNullCall)
96211c423f9SChris Cotter 
9639e0fc676SStanislav Gatev       // optional::emplace
9647538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
9652f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("emplace")),
966092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
967092a530cSStanislav Gatev              LatticeTransferState &State) {
9689ecdbe38SMartin Braenne             if (RecordStorageLocation *Loc =
969f653d140SMartin Braenne                     getImplicitObjectLocation(*E, State.Env)) {
970e8fce958Smartinboehme               setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);
971f653d140SMartin Braenne             }
972092a530cSStanislav Gatev           })
9739e0fc676SStanislav Gatev 
9749e0fc676SStanislav Gatev       // optional::reset
9757538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
9762f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("reset")),
977092a530cSStanislav Gatev           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
978092a530cSStanislav Gatev              LatticeTransferState &State) {
9799ecdbe38SMartin Braenne             if (RecordStorageLocation *Loc =
980f653d140SMartin Braenne                     getImplicitObjectLocation(*E, State.Env)) {
981e8fce958Smartinboehme               setHasValue(*Loc, State.Env.getBoolLiteralValue(false),
982f653d140SMartin Braenne                           State.Env);
983f653d140SMartin Braenne             }
984092a530cSStanislav Gatev           })
9859e0fc676SStanislav Gatev 
9862ddd57aeSStanislav Gatev       // optional::swap
9872f0630f8SAnton Dukeman       .CaseOfCFGStmt<CXXMemberCallExpr>(
9882f0630f8SAnton Dukeman           isOptionalMemberCallWithNameMatcher(hasName("swap")),
9892ddd57aeSStanislav Gatev           transferSwapCall)
9902ddd57aeSStanislav Gatev 
9912ddd57aeSStanislav Gatev       // std::swap
9927538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
9932ddd57aeSStanislav Gatev 
99425956d55SAMS21       // std::forward
99525956d55SAMS21       .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
99625956d55SAMS21 
9977f076004SYitzhak Mandelbaum       // opt.value_or("").empty()
9987538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
9997538b360SWei Yi Tee                            transferValueOrStringEmptyCall)
10007f076004SYitzhak Mandelbaum 
10017f076004SYitzhak Mandelbaum       // opt.value_or(X) != X
10027538b360SWei Yi Tee       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
10037f076004SYitzhak Mandelbaum 
1004390029beSYitzhak Mandelbaum       // Comparisons (==, !=):
1005390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
100671f2ec2dSmartinboehme           isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
1007390029beSYitzhak Mandelbaum           transferOptionalAndOptionalCmp)
1008390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
100971f2ec2dSmartinboehme           isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
101071f2ec2dSmartinboehme           [](const clang::CXXOperatorCallExpr *Cmp,
101171f2ec2dSmartinboehme              const MatchFinder::MatchResult &, LatticeTransferState &State) {
101271f2ec2dSmartinboehme             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);
101371f2ec2dSmartinboehme           })
101471f2ec2dSmartinboehme       .CaseOfCFGStmt<CXXOperatorCallExpr>(
101571f2ec2dSmartinboehme           isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
101671f2ec2dSmartinboehme           [](const clang::CXXOperatorCallExpr *Cmp,
101771f2ec2dSmartinboehme              const MatchFinder::MatchResult &, LatticeTransferState &State) {
101871f2ec2dSmartinboehme             transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);
101971f2ec2dSmartinboehme           })
102071f2ec2dSmartinboehme       .CaseOfCFGStmt<CXXOperatorCallExpr>(
102171f2ec2dSmartinboehme           isComparisonOperatorCall(
102271f2ec2dSmartinboehme               hasOptionalType(),
102371f2ec2dSmartinboehme               unless(anyOf(hasOptionalType(), hasNulloptType()))),
1024390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
1025390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
1026390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
1027390029beSYitzhak Mandelbaum           })
1028390029beSYitzhak Mandelbaum       .CaseOfCFGStmt<CXXOperatorCallExpr>(
102971f2ec2dSmartinboehme           isComparisonOperatorCall(
103071f2ec2dSmartinboehme               unless(anyOf(hasOptionalType(), hasNulloptType())),
1031390029beSYitzhak Mandelbaum               hasOptionalType()),
1032390029beSYitzhak Mandelbaum           [](const clang::CXXOperatorCallExpr *Cmp,
1033390029beSYitzhak Mandelbaum              const MatchFinder::MatchResult &, LatticeTransferState &State) {
1034390029beSYitzhak Mandelbaum             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
1035390029beSYitzhak Mandelbaum           })
1036390029beSYitzhak Mandelbaum 
1037*72a28a3bSJan Voung       // Smart-pointer-like operator* and operator-> calls that may look like
1038*72a28a3bSJan Voung       // const accessors (below) but need special handling to allow mixing
1039*72a28a3bSJan Voung       // the accessor calls.
1040*72a28a3bSJan Voung       .CaseOfCFGStmt<CXXOperatorCallExpr>(
1041*72a28a3bSJan Voung           isSmartPointerLikeOperatorStar(),
1042*72a28a3bSJan Voung           [](const CXXOperatorCallExpr *E,
1043*72a28a3bSJan Voung              const MatchFinder::MatchResult &Result,
1044*72a28a3bSJan Voung              LatticeTransferState &State) {
1045*72a28a3bSJan Voung             transferSmartPointerLikeCachedDeref(
1046*72a28a3bSJan Voung                 E,
1047*72a28a3bSJan Voung                 dyn_cast_or_null<RecordStorageLocation>(
1048*72a28a3bSJan Voung                     getLocBehindPossiblePointer(*E->getArg(0), State.Env)),
1049*72a28a3bSJan Voung                 State, [](StorageLocation &Loc) {});
1050*72a28a3bSJan Voung           })
1051*72a28a3bSJan Voung       .CaseOfCFGStmt<CXXOperatorCallExpr>(
1052*72a28a3bSJan Voung           isSmartPointerLikeOperatorArrow(),
1053*72a28a3bSJan Voung           [](const CXXOperatorCallExpr *E,
1054*72a28a3bSJan Voung              const MatchFinder::MatchResult &Result,
1055*72a28a3bSJan Voung              LatticeTransferState &State) {
1056*72a28a3bSJan Voung             transferSmartPointerLikeCachedGet(
1057*72a28a3bSJan Voung                 E,
1058*72a28a3bSJan Voung                 dyn_cast_or_null<RecordStorageLocation>(
1059*72a28a3bSJan Voung                     getLocBehindPossiblePointer(*E->getArg(0), State.Env)),
1060*72a28a3bSJan Voung                 State, [](StorageLocation &Loc) {});
1061*72a28a3bSJan Voung           })
1062*72a28a3bSJan Voung       .CaseOfCFGStmt<CXXMemberCallExpr>(
1063*72a28a3bSJan Voung           isSmartPointerLikeValueMethodCall(),
1064*72a28a3bSJan Voung           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
1065*72a28a3bSJan Voung              LatticeTransferState &State) {
1066*72a28a3bSJan Voung             transferSmartPointerLikeCachedDeref(
1067*72a28a3bSJan Voung                 E, getImplicitObjectLocation(*E, State.Env), State,
1068*72a28a3bSJan Voung                 [](StorageLocation &Loc) {});
1069*72a28a3bSJan Voung           })
1070*72a28a3bSJan Voung       .CaseOfCFGStmt<CXXMemberCallExpr>(
1071*72a28a3bSJan Voung           isSmartPointerLikeGetMethodCall(),
1072*72a28a3bSJan Voung           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
1073*72a28a3bSJan Voung              LatticeTransferState &State) {
1074*72a28a3bSJan Voung             transferSmartPointerLikeCachedGet(
1075*72a28a3bSJan Voung                 E, getImplicitObjectLocation(*E, State.Env), State,
1076*72a28a3bSJan Voung                 [](StorageLocation &Loc) {});
1077*72a28a3bSJan Voung           })
1078*72a28a3bSJan Voung 
10796761b24aSJan Voung       // const accessor calls
10806761b24aSJan Voung       .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),
10816761b24aSJan Voung                                         transferValue_ConstMemberCall)
108266bbbf2eSJan Voung       .CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(),
108366bbbf2eSJan Voung                                           transferValue_ConstMemberOperatorCall)
10846761b24aSJan Voung       // non-const member calls that may modify the state of an object.
10856761b24aSJan Voung       .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
10866761b24aSJan Voung                                         transferValue_NonConstMemberCall)
10876761b24aSJan Voung       .CaseOfCFGStmt<CXXOperatorCallExpr>(
10886761b24aSJan Voung           isNonConstMemberOperatorCall(),
10896761b24aSJan Voung           transferValue_NonConstMemberOperatorCall)
10906761b24aSJan Voung 
10916761b24aSJan Voung       // other cases of returning optional
10927538b360SWei Yi Tee       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
109365e710c3SStanislav Gatev                                transferCallReturningOptional)
109465e710c3SStanislav Gatev 
1095af98b0afSStanislav Gatev       .Build();
1096af98b0afSStanislav Gatev }
1097af98b0afSStanislav Gatev 
1098004a7ceaSYitzhak Mandelbaum llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
109958fe7f96SSam Estep                                                      const Environment &Env) {
110071f2ec2dSmartinboehme   if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
110171f2ec2dSmartinboehme           getLocBehindPossiblePointer(*ObjectExpr, Env))) {
110271f2ec2dSmartinboehme     auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
110358fe7f96SSam Estep     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
1104526c9b7eSmartinboehme       if (Env.proves(HasValueVal->formula()))
110558fe7f96SSam Estep         return {};
110658fe7f96SSam Estep     }
110758fe7f96SSam Estep   }
110858fe7f96SSam Estep 
110958fe7f96SSam Estep   // Record that this unwrap is *not* provably safe.
111058fe7f96SSam Estep   // FIXME: include either the name of the optional (if applicable) or a source
111158fe7f96SSam Estep   // range of the access for easier interpretation of the result.
111258fe7f96SSam Estep   return {ObjectExpr->getBeginLoc()};
111358fe7f96SSam Estep }
111458fe7f96SSam Estep 
111558fe7f96SSam Estep auto buildDiagnoseMatchSwitch(
111658fe7f96SSam Estep     const UncheckedOptionalAccessModelOptions &Options) {
111758fe7f96SSam Estep   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
111858fe7f96SSam Estep   // lot of duplicated work (e.g. string comparisons), consider providing APIs
111958fe7f96SSam Estep   // that avoid it through memoization.
112058fe7f96SSam Estep   auto IgnorableOptional = ignorableOptional(Options);
1121004a7ceaSYitzhak Mandelbaum   return CFGMatchSwitchBuilder<const Environment,
1122004a7ceaSYitzhak Mandelbaum                                llvm::SmallVector<SourceLocation>>()
112358fe7f96SSam Estep       // optional::value
11247538b360SWei Yi Tee       .CaseOfCFGStmt<CXXMemberCallExpr>(
112558fe7f96SSam Estep           valueCall(IgnorableOptional),
112658fe7f96SSam Estep           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
112758fe7f96SSam Estep              const Environment &Env) {
11286a81e694SMartin Braenne             return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
112958fe7f96SSam Estep           })
113058fe7f96SSam Estep 
113158fe7f96SSam Estep       // optional::operator*, optional::operator->
11326a81e694SMartin Braenne       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
11336a81e694SMartin Braenne                                [](const CallExpr *E,
11346a81e694SMartin Braenne                                   const MatchFinder::MatchResult &,
113558fe7f96SSam Estep                                   const Environment &Env) {
11366a81e694SMartin Braenne                                  return diagnoseUnwrapCall(E->getArg(0), Env);
113758fe7f96SSam Estep                                })
113858fe7f96SSam Estep       .Build();
113958fe7f96SSam Estep }
114058fe7f96SSam Estep 
1141af98b0afSStanislav Gatev } // namespace
1142af98b0afSStanislav Gatev 
11437e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher
11447e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() {
1145d712c5edSmartinboehme   return cxxRecordDecl(optionalClass());
114671f2ec2dSmartinboehme }
114771f2ec2dSmartinboehme 
114871f2ec2dSmartinboehme UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
114971f2ec2dSmartinboehme                                                            Environment &Env)
11506761b24aSJan Voung     : DataflowAnalysis<UncheckedOptionalAccessModel,
11516761b24aSJan Voung                        UncheckedOptionalAccessLattice>(Ctx),
115271f2ec2dSmartinboehme       TransferMatchSwitch(buildTransferMatchSwitch()) {
115371f2ec2dSmartinboehme   Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
115471f2ec2dSmartinboehme       [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
1155d712c5edSmartinboehme         const CXXRecordDecl *Optional =
1156d712c5edSmartinboehme             getOptionalBaseClass(Ty->getAsCXXRecordDecl());
1157d712c5edSmartinboehme         if (Optional == nullptr)
115871f2ec2dSmartinboehme           return {};
1159d712c5edSmartinboehme         return {{"value", valueTypeFromOptionalDecl(*Optional)},
116071f2ec2dSmartinboehme                 {"has_value", Ctx.BoolTy}};
116171f2ec2dSmartinboehme       });
116271f2ec2dSmartinboehme }
1163af98b0afSStanislav Gatev 
11646b991ba4SYitzhak Mandelbaum void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
11656761b24aSJan Voung                                             UncheckedOptionalAccessLattice &L,
11666761b24aSJan Voung                                             Environment &Env) {
1167af98b0afSStanislav Gatev   LatticeTransferState State(L, Env);
11686b991ba4SYitzhak Mandelbaum   TransferMatchSwitch(Elt, getASTContext(), State);
1169af98b0afSStanislav Gatev }
1170af98b0afSStanislav Gatev 
117158fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
117258fe7f96SSam Estep     UncheckedOptionalAccessModelOptions Options)
117358fe7f96SSam Estep     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
117458fe7f96SSam Estep 
1175af98b0afSStanislav Gatev } // namespace dataflow
1176af98b0afSStanislav Gatev } // namespace clang
1177