181ad6265SDimitry Andric //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric //
981ad6265SDimitry Andric //  This file defines a dataflow analysis that detects unsafe uses of optional
1081ad6265SDimitry Andric //  values.
1181ad6265SDimitry Andric //
1281ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1381ad6265SDimitry Andric 
1481ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
1581ad6265SDimitry Andric #include "clang/AST/ASTContext.h"
1681ad6265SDimitry Andric #include "clang/AST/DeclCXX.h"
1781ad6265SDimitry Andric #include "clang/AST/Expr.h"
1881ad6265SDimitry Andric #include "clang/AST/ExprCXX.h"
1981ad6265SDimitry Andric #include "clang/AST/Stmt.h"
2081ad6265SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h"
21*bdd1243dSDimitry Andric #include "clang/Analysis/CFG.h"
22*bdd1243dSDimitry Andric #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
2381ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
2481ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/NoopLattice.h"
25*bdd1243dSDimitry Andric #include "clang/Analysis/FlowSensitive/StorageLocation.h"
2681ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/Value.h"
2781ad6265SDimitry Andric #include "clang/Basic/SourceLocation.h"
2881ad6265SDimitry Andric #include "llvm/ADT/StringRef.h"
2981ad6265SDimitry Andric #include "llvm/Support/Casting.h"
30*bdd1243dSDimitry Andric #include "llvm/Support/ErrorHandling.h"
3181ad6265SDimitry Andric #include <cassert>
3281ad6265SDimitry Andric #include <memory>
33*bdd1243dSDimitry Andric #include <optional>
3481ad6265SDimitry Andric #include <utility>
3581ad6265SDimitry Andric #include <vector>
3681ad6265SDimitry Andric 
3781ad6265SDimitry Andric namespace clang {
3881ad6265SDimitry Andric namespace dataflow {
3981ad6265SDimitry Andric namespace {
4081ad6265SDimitry Andric 
4181ad6265SDimitry Andric using namespace ::clang::ast_matchers;
4281ad6265SDimitry Andric using LatticeTransferState = TransferState<NoopLattice>;
4381ad6265SDimitry Andric 
4481ad6265SDimitry Andric DeclarationMatcher optionalClass() {
4581ad6265SDimitry Andric   return classTemplateSpecializationDecl(
4681ad6265SDimitry Andric       anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
4781ad6265SDimitry Andric             hasName("__optional_destruct_base"), hasName("absl::optional"),
4881ad6265SDimitry Andric             hasName("base::Optional")),
4981ad6265SDimitry Andric       hasTemplateArgument(0, refersToType(type().bind("T"))));
5081ad6265SDimitry Andric }
5181ad6265SDimitry Andric 
5281ad6265SDimitry Andric auto optionalOrAliasType() {
5381ad6265SDimitry Andric   return hasUnqualifiedDesugaredType(
5481ad6265SDimitry Andric       recordType(hasDeclaration(optionalClass())));
5581ad6265SDimitry Andric }
5681ad6265SDimitry Andric 
5781ad6265SDimitry Andric /// Matches any of the spellings of the optional types and sugar, aliases, etc.
5881ad6265SDimitry Andric auto hasOptionalType() { return hasType(optionalOrAliasType()); }
5981ad6265SDimitry Andric 
6081ad6265SDimitry Andric auto isOptionalMemberCallWithName(
6181ad6265SDimitry Andric     llvm::StringRef MemberName,
62*bdd1243dSDimitry Andric     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
6381ad6265SDimitry Andric   auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr()))
6481ad6265SDimitry Andric                                     : cxxThisExpr());
6581ad6265SDimitry Andric   return cxxMemberCallExpr(
6681ad6265SDimitry Andric       on(expr(Exception)),
6781ad6265SDimitry Andric       callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
6881ad6265SDimitry Andric }
6981ad6265SDimitry Andric 
7081ad6265SDimitry Andric auto isOptionalOperatorCallWithName(
7181ad6265SDimitry Andric     llvm::StringRef operator_name,
72*bdd1243dSDimitry Andric     const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
7381ad6265SDimitry Andric   return cxxOperatorCallExpr(
7481ad6265SDimitry Andric       hasOverloadedOperatorName(operator_name),
7581ad6265SDimitry Andric       callee(cxxMethodDecl(ofClass(optionalClass()))),
7681ad6265SDimitry Andric       Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
7781ad6265SDimitry Andric }
7881ad6265SDimitry Andric 
7981ad6265SDimitry Andric auto isMakeOptionalCall() {
8081ad6265SDimitry Andric   return callExpr(
8181ad6265SDimitry Andric       callee(functionDecl(hasAnyName(
8281ad6265SDimitry Andric           "std::make_optional", "base::make_optional", "absl::make_optional"))),
8381ad6265SDimitry Andric       hasOptionalType());
8481ad6265SDimitry Andric }
8581ad6265SDimitry Andric 
86*bdd1243dSDimitry Andric auto nulloptTypeDecl() {
87*bdd1243dSDimitry Andric   return namedDecl(
88*bdd1243dSDimitry Andric       hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t"));
8981ad6265SDimitry Andric }
9081ad6265SDimitry Andric 
91*bdd1243dSDimitry Andric auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
92*bdd1243dSDimitry Andric 
93*bdd1243dSDimitry Andric // `optional` or `nullopt_t`
94*bdd1243dSDimitry Andric auto hasAnyOptionalType() {
95*bdd1243dSDimitry Andric   return hasType(hasUnqualifiedDesugaredType(
96*bdd1243dSDimitry Andric       recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass())))));
97*bdd1243dSDimitry Andric }
98*bdd1243dSDimitry Andric 
99*bdd1243dSDimitry Andric 
10081ad6265SDimitry Andric auto inPlaceClass() {
10181ad6265SDimitry Andric   return recordDecl(
10281ad6265SDimitry Andric       hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
10381ad6265SDimitry Andric }
10481ad6265SDimitry Andric 
10581ad6265SDimitry Andric auto isOptionalNulloptConstructor() {
106*bdd1243dSDimitry Andric   return cxxConstructExpr(
107*bdd1243dSDimitry Andric       hasOptionalType(),
108*bdd1243dSDimitry Andric       hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
109*bdd1243dSDimitry Andric                                         hasParameter(0, hasNulloptType()))));
11081ad6265SDimitry Andric }
11181ad6265SDimitry Andric 
11281ad6265SDimitry Andric auto isOptionalInPlaceConstructor() {
11381ad6265SDimitry Andric   return cxxConstructExpr(hasOptionalType(),
11481ad6265SDimitry Andric                           hasArgument(0, hasType(inPlaceClass())));
11581ad6265SDimitry Andric }
11681ad6265SDimitry Andric 
11781ad6265SDimitry Andric auto isOptionalValueOrConversionConstructor() {
11881ad6265SDimitry Andric   return cxxConstructExpr(
11981ad6265SDimitry Andric       hasOptionalType(),
12081ad6265SDimitry Andric       unless(hasDeclaration(
12181ad6265SDimitry Andric           cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
12281ad6265SDimitry Andric       argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
12381ad6265SDimitry Andric }
12481ad6265SDimitry Andric 
12581ad6265SDimitry Andric auto isOptionalValueOrConversionAssignment() {
12681ad6265SDimitry Andric   return cxxOperatorCallExpr(
12781ad6265SDimitry Andric       hasOverloadedOperatorName("="),
12881ad6265SDimitry Andric       callee(cxxMethodDecl(ofClass(optionalClass()))),
12981ad6265SDimitry Andric       unless(hasDeclaration(cxxMethodDecl(
13081ad6265SDimitry Andric           anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
13181ad6265SDimitry Andric       argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
13281ad6265SDimitry Andric }
13381ad6265SDimitry Andric 
134*bdd1243dSDimitry Andric auto isNulloptConstructor() {
135*bdd1243dSDimitry Andric   return cxxConstructExpr(hasNulloptType(), argumentCountIs(1),
136*bdd1243dSDimitry Andric                           hasArgument(0, hasNulloptType()));
137*bdd1243dSDimitry Andric }
138*bdd1243dSDimitry Andric 
13981ad6265SDimitry Andric auto isOptionalNulloptAssignment() {
14081ad6265SDimitry Andric   return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
14181ad6265SDimitry Andric                              callee(cxxMethodDecl(ofClass(optionalClass()))),
14281ad6265SDimitry Andric                              argumentCountIs(2),
14381ad6265SDimitry Andric                              hasArgument(1, hasNulloptType()));
14481ad6265SDimitry Andric }
14581ad6265SDimitry Andric 
14681ad6265SDimitry Andric auto isStdSwapCall() {
14781ad6265SDimitry Andric   return callExpr(callee(functionDecl(hasName("std::swap"))),
14881ad6265SDimitry Andric                   argumentCountIs(2), hasArgument(0, hasOptionalType()),
14981ad6265SDimitry Andric                   hasArgument(1, hasOptionalType()));
15081ad6265SDimitry Andric }
15181ad6265SDimitry Andric 
15281ad6265SDimitry Andric constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
15381ad6265SDimitry Andric 
15481ad6265SDimitry Andric auto isValueOrStringEmptyCall() {
15581ad6265SDimitry Andric   // `opt.value_or("").empty()`
15681ad6265SDimitry Andric   return cxxMemberCallExpr(
15781ad6265SDimitry Andric       callee(cxxMethodDecl(hasName("empty"))),
15881ad6265SDimitry Andric       onImplicitObjectArgument(ignoringImplicit(
15981ad6265SDimitry Andric           cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
16081ad6265SDimitry Andric                             callee(cxxMethodDecl(hasName("value_or"),
16181ad6265SDimitry Andric                                                  ofClass(optionalClass()))),
16281ad6265SDimitry Andric                             hasArgument(0, stringLiteral(hasSize(0))))
16381ad6265SDimitry Andric               .bind(ValueOrCallID))));
16481ad6265SDimitry Andric }
16581ad6265SDimitry Andric 
16681ad6265SDimitry Andric auto isValueOrNotEqX() {
16781ad6265SDimitry Andric   auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
16881ad6265SDimitry Andric     return hasOperands(
16981ad6265SDimitry Andric         ignoringImplicit(
17081ad6265SDimitry Andric             cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
17181ad6265SDimitry Andric                               callee(cxxMethodDecl(hasName("value_or"),
17281ad6265SDimitry Andric                                                    ofClass(optionalClass()))),
17381ad6265SDimitry Andric                               hasArgument(0, Arg))
17481ad6265SDimitry Andric                 .bind(ValueOrCallID)),
17581ad6265SDimitry Andric         ignoringImplicit(Arg));
17681ad6265SDimitry Andric   };
17781ad6265SDimitry Andric 
17881ad6265SDimitry Andric   // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
17981ad6265SDimitry Andric   // support this pattern for any expression, but the AST does not have a
18081ad6265SDimitry Andric   // generic expression comparison facility, so we specialize to common cases
18181ad6265SDimitry Andric   // seen in practice.  FIXME: define a matcher that compares values across
18281ad6265SDimitry Andric   // nodes, which would let us generalize this to any `X`.
18381ad6265SDimitry Andric   return binaryOperation(hasOperatorName("!="),
18481ad6265SDimitry Andric                          anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
18581ad6265SDimitry Andric                                ComparesToSame(stringLiteral(hasSize(0))),
18681ad6265SDimitry Andric                                ComparesToSame(integerLiteral(equals(0)))));
18781ad6265SDimitry Andric }
18881ad6265SDimitry Andric 
18981ad6265SDimitry Andric auto isCallReturningOptional() {
19081ad6265SDimitry Andric   return callExpr(hasType(qualType(anyOf(
19181ad6265SDimitry Andric       optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
19281ad6265SDimitry Andric }
19381ad6265SDimitry Andric 
194*bdd1243dSDimitry Andric template <typename L, typename R>
195*bdd1243dSDimitry Andric auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
196*bdd1243dSDimitry Andric   return cxxOperatorCallExpr(
197*bdd1243dSDimitry Andric       anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
198*bdd1243dSDimitry Andric       argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
199*bdd1243dSDimitry Andric       hasArgument(1, rhs_arg_matcher));
200*bdd1243dSDimitry Andric }
201*bdd1243dSDimitry Andric 
202*bdd1243dSDimitry Andric // Ensures that `Expr` is mapped to a `BoolValue` and returns it.
203*bdd1243dSDimitry Andric BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) {
204*bdd1243dSDimitry Andric   auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None));
205*bdd1243dSDimitry Andric   if (Value != nullptr)
206*bdd1243dSDimitry Andric     return *Value;
207*bdd1243dSDimitry Andric 
208*bdd1243dSDimitry Andric   auto &Loc = Env.createStorageLocation(Expr);
209*bdd1243dSDimitry Andric   Value = &Env.makeAtomicBoolValue();
210*bdd1243dSDimitry Andric   Env.setValue(Loc, *Value);
211*bdd1243dSDimitry Andric   Env.setStorageLocation(Expr, Loc);
212*bdd1243dSDimitry Andric   return *Value;
213*bdd1243dSDimitry Andric }
214*bdd1243dSDimitry Andric 
21581ad6265SDimitry Andric /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
21681ad6265SDimitry Andric /// property of the optional value `OptionalVal`.
21781ad6265SDimitry Andric void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
21881ad6265SDimitry Andric   OptionalVal.setProperty("has_value", HasValueVal);
21981ad6265SDimitry Andric }
22081ad6265SDimitry Andric 
22181ad6265SDimitry Andric /// Creates a symbolic value for an `optional` value using `HasValueVal` as the
22281ad6265SDimitry Andric /// symbolic value of its "has_value" property.
22381ad6265SDimitry Andric StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
22481ad6265SDimitry Andric   auto OptionalVal = std::make_unique<StructValue>();
22581ad6265SDimitry Andric   setHasValue(*OptionalVal, HasValueVal);
22681ad6265SDimitry Andric   return Env.takeOwnership(std::move(OptionalVal));
22781ad6265SDimitry Andric }
22881ad6265SDimitry Andric 
22981ad6265SDimitry Andric /// Returns the symbolic value that represents the "has_value" property of the
23081ad6265SDimitry Andric /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
23181ad6265SDimitry Andric BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
23281ad6265SDimitry Andric   if (OptionalVal != nullptr) {
23381ad6265SDimitry Andric     auto *HasValueVal =
23481ad6265SDimitry Andric         cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
23581ad6265SDimitry Andric     if (HasValueVal == nullptr) {
23681ad6265SDimitry Andric       HasValueVal = &Env.makeAtomicBoolValue();
23781ad6265SDimitry Andric       OptionalVal->setProperty("has_value", *HasValueVal);
23881ad6265SDimitry Andric     }
23981ad6265SDimitry Andric     return HasValueVal;
24081ad6265SDimitry Andric   }
24181ad6265SDimitry Andric   return nullptr;
24281ad6265SDimitry Andric }
24381ad6265SDimitry Andric 
24481ad6265SDimitry Andric /// If `Type` is a reference type, returns the type of its pointee. Otherwise,
24581ad6265SDimitry Andric /// returns `Type` itself.
24681ad6265SDimitry Andric QualType stripReference(QualType Type) {
24781ad6265SDimitry Andric   return Type->isReferenceType() ? Type->getPointeeType() : Type;
24881ad6265SDimitry Andric }
24981ad6265SDimitry Andric 
25081ad6265SDimitry Andric /// Returns true if and only if `Type` is an optional type.
251*bdd1243dSDimitry Andric bool isOptionalType(QualType Type) {
25281ad6265SDimitry Andric   if (!Type->isRecordType())
25381ad6265SDimitry Andric     return false;
25481ad6265SDimitry Andric   // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call.
25581ad6265SDimitry Andric   auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString();
25681ad6265SDimitry Andric   return TypeName == "std::optional" || TypeName == "absl::optional" ||
25781ad6265SDimitry Andric          TypeName == "base::Optional";
25881ad6265SDimitry Andric }
25981ad6265SDimitry Andric 
26081ad6265SDimitry Andric /// Returns the number of optional wrappers in `Type`.
26181ad6265SDimitry Andric ///
26281ad6265SDimitry Andric /// For example, if `Type` is `optional<optional<int>>`, the result of this
26381ad6265SDimitry Andric /// function will be 2.
26481ad6265SDimitry Andric int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
265*bdd1243dSDimitry Andric   if (!isOptionalType(Type))
26681ad6265SDimitry Andric     return 0;
26781ad6265SDimitry Andric   return 1 + countOptionalWrappers(
26881ad6265SDimitry Andric                  ASTCtx,
26981ad6265SDimitry Andric                  cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
27081ad6265SDimitry Andric                      ->getTemplateArgs()
27181ad6265SDimitry Andric                      .get(0)
27281ad6265SDimitry Andric                      .getAsType()
27381ad6265SDimitry Andric                      .getDesugaredType(ASTCtx));
27481ad6265SDimitry Andric }
27581ad6265SDimitry Andric 
27681ad6265SDimitry Andric /// Tries to initialize the `optional`'s value (that is, contents), and return
27781ad6265SDimitry Andric /// its location. Returns nullptr if the value can't be represented.
27881ad6265SDimitry Andric StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
27981ad6265SDimitry Andric                                                     Value &OptionalVal,
28081ad6265SDimitry Andric                                                     Environment &Env) {
28181ad6265SDimitry Andric   // The "value" property represents a synthetic field. As such, it needs
28281ad6265SDimitry Andric   // `StorageLocation`, like normal fields (and other variables). So, we model
28381ad6265SDimitry Andric   // it with a `ReferenceValue`, since that includes a storage location.  Once
28481ad6265SDimitry Andric   // the property is set, it will be shared by all environments that access the
28581ad6265SDimitry Andric   // `Value` representing the optional (here, `OptionalVal`).
28681ad6265SDimitry Andric   if (auto *ValueProp = OptionalVal.getProperty("value")) {
28781ad6265SDimitry Andric     auto *ValueRef = clang::cast<ReferenceValue>(ValueProp);
28881ad6265SDimitry Andric     auto &ValueLoc = ValueRef->getReferentLoc();
28981ad6265SDimitry Andric     if (Env.getValue(ValueLoc) == nullptr) {
29081ad6265SDimitry Andric       // The property was previously set, but the value has been lost. This can
29181ad6265SDimitry Andric       // happen, for example, because of an environment merge (where the two
29281ad6265SDimitry Andric       // environments mapped the property to different values, which resulted in
29381ad6265SDimitry Andric       // them both being discarded), or when two blocks in the CFG, with neither
29481ad6265SDimitry Andric       // a dominator of the other, visit the same optional value, or even when a
29581ad6265SDimitry Andric       // block is revisited during testing to collect per-statement state.
29681ad6265SDimitry Andric       // FIXME: This situation means that the optional contents are not shared
29781ad6265SDimitry Andric       // between branches and the like. Practically, this lack of sharing
29881ad6265SDimitry Andric       // reduces the precision of the model when the contents are relevant to
29981ad6265SDimitry Andric       // the check, like another optional or a boolean that influences control
30081ad6265SDimitry Andric       // flow.
30181ad6265SDimitry Andric       auto *ValueVal = Env.createValue(ValueLoc.getType());
30281ad6265SDimitry Andric       if (ValueVal == nullptr)
30381ad6265SDimitry Andric         return nullptr;
30481ad6265SDimitry Andric       Env.setValue(ValueLoc, *ValueVal);
30581ad6265SDimitry Andric     }
30681ad6265SDimitry Andric     return &ValueLoc;
30781ad6265SDimitry Andric   }
30881ad6265SDimitry Andric 
30981ad6265SDimitry Andric   auto Ty = stripReference(Q);
31081ad6265SDimitry Andric   auto *ValueVal = Env.createValue(Ty);
31181ad6265SDimitry Andric   if (ValueVal == nullptr)
31281ad6265SDimitry Andric     return nullptr;
31381ad6265SDimitry Andric   auto &ValueLoc = Env.createStorageLocation(Ty);
31481ad6265SDimitry Andric   Env.setValue(ValueLoc, *ValueVal);
31581ad6265SDimitry Andric   auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc);
31681ad6265SDimitry Andric   OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef)));
31781ad6265SDimitry Andric   return &ValueLoc;
31881ad6265SDimitry Andric }
31981ad6265SDimitry Andric 
32081ad6265SDimitry Andric void initializeOptionalReference(const Expr *OptionalExpr,
32181ad6265SDimitry Andric                                  const MatchFinder::MatchResult &,
32281ad6265SDimitry Andric                                  LatticeTransferState &State) {
32381ad6265SDimitry Andric   if (auto *OptionalVal =
32481ad6265SDimitry Andric           State.Env.getValue(*OptionalExpr, SkipPast::Reference)) {
32581ad6265SDimitry Andric     if (OptionalVal->getProperty("has_value") == nullptr) {
32681ad6265SDimitry Andric       setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
32781ad6265SDimitry Andric     }
32881ad6265SDimitry Andric   }
32981ad6265SDimitry Andric }
33081ad6265SDimitry Andric 
33181ad6265SDimitry Andric /// Returns true if and only if `OptionalVal` is initialized and known to be
33281ad6265SDimitry Andric /// empty in `Env.
33381ad6265SDimitry Andric bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
33481ad6265SDimitry Andric   auto *HasValueVal =
33581ad6265SDimitry Andric       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
33681ad6265SDimitry Andric   return HasValueVal != nullptr &&
33781ad6265SDimitry Andric          Env.flowConditionImplies(Env.makeNot(*HasValueVal));
33881ad6265SDimitry Andric }
33981ad6265SDimitry Andric 
34081ad6265SDimitry Andric /// Returns true if and only if `OptionalVal` is initialized and known to be
34181ad6265SDimitry Andric /// non-empty in `Env.
34281ad6265SDimitry Andric bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
34381ad6265SDimitry Andric   auto *HasValueVal =
34481ad6265SDimitry Andric       cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
34581ad6265SDimitry Andric   return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal);
34681ad6265SDimitry Andric }
34781ad6265SDimitry Andric 
34881ad6265SDimitry Andric void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
34981ad6265SDimitry Andric                         LatticeTransferState &State) {
35081ad6265SDimitry Andric   if (auto *OptionalVal =
35181ad6265SDimitry Andric           State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
35281ad6265SDimitry Andric     if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
35381ad6265SDimitry Andric       if (auto *Loc = maybeInitializeOptionalValueMember(
35481ad6265SDimitry Andric               UnwrapExpr->getType(), *OptionalVal, State.Env))
35581ad6265SDimitry Andric         State.Env.setStorageLocation(*UnwrapExpr, *Loc);
35681ad6265SDimitry Andric   }
35781ad6265SDimitry Andric }
35881ad6265SDimitry Andric 
35981ad6265SDimitry Andric void transferMakeOptionalCall(const CallExpr *E,
36081ad6265SDimitry Andric                               const MatchFinder::MatchResult &,
36181ad6265SDimitry Andric                               LatticeTransferState &State) {
36281ad6265SDimitry Andric   auto &Loc = State.Env.createStorageLocation(*E);
36381ad6265SDimitry Andric   State.Env.setStorageLocation(*E, Loc);
36481ad6265SDimitry Andric   State.Env.setValue(
36581ad6265SDimitry Andric       Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
36681ad6265SDimitry Andric }
36781ad6265SDimitry Andric 
36881ad6265SDimitry Andric void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
36981ad6265SDimitry Andric                                   const MatchFinder::MatchResult &,
37081ad6265SDimitry Andric                                   LatticeTransferState &State) {
37181ad6265SDimitry Andric   if (auto *HasValueVal = getHasValue(
37281ad6265SDimitry Andric           State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
37381ad6265SDimitry Andric                                         SkipPast::ReferenceThenPointer))) {
37481ad6265SDimitry Andric     auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
37581ad6265SDimitry Andric     State.Env.setValue(CallExprLoc, *HasValueVal);
37681ad6265SDimitry Andric     State.Env.setStorageLocation(*CallExpr, CallExprLoc);
37781ad6265SDimitry Andric   }
37881ad6265SDimitry Andric }
37981ad6265SDimitry Andric 
38081ad6265SDimitry Andric /// `ModelPred` builds a logical formula relating the predicate in
38181ad6265SDimitry Andric /// `ValueOrPredExpr` to the optional's `has_value` property.
38281ad6265SDimitry Andric void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
38381ad6265SDimitry Andric                          const MatchFinder::MatchResult &Result,
38481ad6265SDimitry Andric                          LatticeTransferState &State,
38581ad6265SDimitry Andric                          BoolValue &(*ModelPred)(Environment &Env,
38681ad6265SDimitry Andric                                                  BoolValue &ExprVal,
38781ad6265SDimitry Andric                                                  BoolValue &HasValueVal)) {
38881ad6265SDimitry Andric   auto &Env = State.Env;
38981ad6265SDimitry Andric 
39081ad6265SDimitry Andric   const auto *ObjectArgumentExpr =
39181ad6265SDimitry Andric       Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
39281ad6265SDimitry Andric           ->getImplicitObjectArgument();
39381ad6265SDimitry Andric 
39481ad6265SDimitry Andric   auto *HasValueVal = getHasValue(
39581ad6265SDimitry Andric       State.Env,
39681ad6265SDimitry Andric       State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
39781ad6265SDimitry Andric   if (HasValueVal == nullptr)
39881ad6265SDimitry Andric     return;
39981ad6265SDimitry Andric 
400*bdd1243dSDimitry Andric   Env.addToFlowCondition(
401*bdd1243dSDimitry Andric       ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal));
40281ad6265SDimitry Andric }
40381ad6265SDimitry Andric 
40481ad6265SDimitry Andric void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
40581ad6265SDimitry Andric                                     const MatchFinder::MatchResult &Result,
40681ad6265SDimitry Andric                                     LatticeTransferState &State) {
40781ad6265SDimitry Andric   return transferValueOrImpl(ComparisonExpr, Result, State,
40881ad6265SDimitry Andric                              [](Environment &Env, BoolValue &ExprVal,
40981ad6265SDimitry Andric                                 BoolValue &HasValueVal) -> BoolValue & {
41081ad6265SDimitry Andric                                // If the result is *not* empty, then we know the
41181ad6265SDimitry Andric                                // optional must have been holding a value. If
41281ad6265SDimitry Andric                                // `ExprVal` is true, though, we don't learn
41381ad6265SDimitry Andric                                // anything definite about `has_value`, so we
41481ad6265SDimitry Andric                                // don't add any corresponding implications to
41581ad6265SDimitry Andric                                // the flow condition.
41681ad6265SDimitry Andric                                return Env.makeImplication(Env.makeNot(ExprVal),
41781ad6265SDimitry Andric                                                           HasValueVal);
41881ad6265SDimitry Andric                              });
41981ad6265SDimitry Andric }
42081ad6265SDimitry Andric 
42181ad6265SDimitry Andric void transferValueOrNotEqX(const Expr *ComparisonExpr,
42281ad6265SDimitry Andric                            const MatchFinder::MatchResult &Result,
42381ad6265SDimitry Andric                            LatticeTransferState &State) {
42481ad6265SDimitry Andric   transferValueOrImpl(ComparisonExpr, Result, State,
42581ad6265SDimitry Andric                       [](Environment &Env, BoolValue &ExprVal,
42681ad6265SDimitry Andric                          BoolValue &HasValueVal) -> BoolValue & {
42781ad6265SDimitry Andric                         // We know that if `(opt.value_or(X) != X)` then
42881ad6265SDimitry Andric                         // `opt.hasValue()`, even without knowing further
42981ad6265SDimitry Andric                         // details about the contents of `opt`.
43081ad6265SDimitry Andric                         return Env.makeImplication(ExprVal, HasValueVal);
43181ad6265SDimitry Andric                       });
43281ad6265SDimitry Andric }
43381ad6265SDimitry Andric 
43481ad6265SDimitry Andric void transferCallReturningOptional(const CallExpr *E,
43581ad6265SDimitry Andric                                    const MatchFinder::MatchResult &Result,
43681ad6265SDimitry Andric                                    LatticeTransferState &State) {
43781ad6265SDimitry Andric   if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
43881ad6265SDimitry Andric     return;
43981ad6265SDimitry Andric 
44081ad6265SDimitry Andric   auto &Loc = State.Env.createStorageLocation(*E);
44181ad6265SDimitry Andric   State.Env.setStorageLocation(*E, Loc);
44281ad6265SDimitry Andric   State.Env.setValue(
44381ad6265SDimitry Andric       Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()));
44481ad6265SDimitry Andric }
44581ad6265SDimitry Andric 
446*bdd1243dSDimitry Andric void assignOptionalValue(const Expr &E, Environment &Env,
44781ad6265SDimitry Andric                          BoolValue &HasValueVal) {
44881ad6265SDimitry Andric   if (auto *OptionalLoc =
449*bdd1243dSDimitry Andric           Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
450*bdd1243dSDimitry Andric     Env.setValue(*OptionalLoc, createOptionalValue(Env, HasValueVal));
45181ad6265SDimitry Andric   }
45281ad6265SDimitry Andric }
45381ad6265SDimitry Andric 
45481ad6265SDimitry Andric /// Returns a symbolic value for the "has_value" property of an `optional<T>`
45581ad6265SDimitry Andric /// value that is constructed/assigned from a value of type `U` or `optional<U>`
45681ad6265SDimitry Andric /// where `T` is constructible from `U`.
457*bdd1243dSDimitry Andric BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
45881ad6265SDimitry Andric                                      const MatchFinder::MatchResult &MatchRes,
45981ad6265SDimitry Andric                                      LatticeTransferState &State) {
460*bdd1243dSDimitry Andric   assert(F.getTemplateSpecializationArgs() != nullptr);
46181ad6265SDimitry Andric   assert(F.getTemplateSpecializationArgs()->size() > 0);
46281ad6265SDimitry Andric 
46381ad6265SDimitry Andric   const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
46481ad6265SDimitry Andric       *MatchRes.Context,
46581ad6265SDimitry Andric       stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType()));
46681ad6265SDimitry Andric   const int ArgTypeOptionalWrappersCount =
46781ad6265SDimitry Andric       countOptionalWrappers(*MatchRes.Context, stripReference(E.getType()));
46881ad6265SDimitry Andric 
46981ad6265SDimitry Andric   // Check if this is a constructor/assignment call for `optional<T>` with
47081ad6265SDimitry Andric   // argument of type `U` such that `T` is constructible from `U`.
47181ad6265SDimitry Andric   if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
47281ad6265SDimitry Andric     return State.Env.getBoolLiteralValue(true);
47381ad6265SDimitry Andric 
47481ad6265SDimitry Andric   // This is a constructor/assignment call for `optional<T>` with argument of
47581ad6265SDimitry Andric   // type `optional<U>` such that `T` is constructible from `U`.
47681ad6265SDimitry Andric   if (auto *HasValueVal =
47781ad6265SDimitry Andric           getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference)))
47881ad6265SDimitry Andric     return *HasValueVal;
47981ad6265SDimitry Andric   return State.Env.makeAtomicBoolValue();
48081ad6265SDimitry Andric }
48181ad6265SDimitry Andric 
48281ad6265SDimitry Andric void transferValueOrConversionConstructor(
48381ad6265SDimitry Andric     const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
48481ad6265SDimitry Andric     LatticeTransferState &State) {
48581ad6265SDimitry Andric   assert(E->getNumArgs() > 0);
48681ad6265SDimitry Andric 
487*bdd1243dSDimitry Andric   assignOptionalValue(*E, State.Env,
488*bdd1243dSDimitry Andric                       valueOrConversionHasValue(*E->getConstructor(),
48981ad6265SDimitry Andric                                                 *E->getArg(0), MatchRes,
49081ad6265SDimitry Andric                                                 State));
49181ad6265SDimitry Andric }
49281ad6265SDimitry Andric 
49381ad6265SDimitry Andric void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
49481ad6265SDimitry Andric                         LatticeTransferState &State) {
49581ad6265SDimitry Andric   assert(E->getNumArgs() > 0);
49681ad6265SDimitry Andric 
49781ad6265SDimitry Andric   auto *OptionalLoc =
49881ad6265SDimitry Andric       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
49981ad6265SDimitry Andric   if (OptionalLoc == nullptr)
50081ad6265SDimitry Andric     return;
50181ad6265SDimitry Andric 
50281ad6265SDimitry Andric   State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
50381ad6265SDimitry Andric 
50481ad6265SDimitry Andric   // Assign a storage location for the whole expression.
50581ad6265SDimitry Andric   State.Env.setStorageLocation(*E, *OptionalLoc);
50681ad6265SDimitry Andric }
50781ad6265SDimitry Andric 
50881ad6265SDimitry Andric void transferValueOrConversionAssignment(
50981ad6265SDimitry Andric     const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
51081ad6265SDimitry Andric     LatticeTransferState &State) {
51181ad6265SDimitry Andric   assert(E->getNumArgs() > 1);
51281ad6265SDimitry Andric   transferAssignment(E,
513*bdd1243dSDimitry Andric                      valueOrConversionHasValue(*E->getDirectCallee(),
51481ad6265SDimitry Andric                                                *E->getArg(1), MatchRes, State),
51581ad6265SDimitry Andric                      State);
51681ad6265SDimitry Andric }
51781ad6265SDimitry Andric 
51881ad6265SDimitry Andric void transferNulloptAssignment(const CXXOperatorCallExpr *E,
51981ad6265SDimitry Andric                                const MatchFinder::MatchResult &,
52081ad6265SDimitry Andric                                LatticeTransferState &State) {
52181ad6265SDimitry Andric   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
52281ad6265SDimitry Andric }
52381ad6265SDimitry Andric 
52481ad6265SDimitry Andric void transferSwap(const StorageLocation &OptionalLoc1,
52581ad6265SDimitry Andric                   const StorageLocation &OptionalLoc2,
52681ad6265SDimitry Andric                   LatticeTransferState &State) {
52781ad6265SDimitry Andric   auto *OptionalVal1 = State.Env.getValue(OptionalLoc1);
52881ad6265SDimitry Andric   assert(OptionalVal1 != nullptr);
52981ad6265SDimitry Andric 
53081ad6265SDimitry Andric   auto *OptionalVal2 = State.Env.getValue(OptionalLoc2);
53181ad6265SDimitry Andric   assert(OptionalVal2 != nullptr);
53281ad6265SDimitry Andric 
53381ad6265SDimitry Andric   State.Env.setValue(OptionalLoc1, *OptionalVal2);
53481ad6265SDimitry Andric   State.Env.setValue(OptionalLoc2, *OptionalVal1);
53581ad6265SDimitry Andric }
53681ad6265SDimitry Andric 
53781ad6265SDimitry Andric void transferSwapCall(const CXXMemberCallExpr *E,
53881ad6265SDimitry Andric                       const MatchFinder::MatchResult &,
53981ad6265SDimitry Andric                       LatticeTransferState &State) {
54081ad6265SDimitry Andric   assert(E->getNumArgs() == 1);
54181ad6265SDimitry Andric 
54281ad6265SDimitry Andric   auto *OptionalLoc1 = State.Env.getStorageLocation(
54381ad6265SDimitry Andric       *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer);
54481ad6265SDimitry Andric   assert(OptionalLoc1 != nullptr);
54581ad6265SDimitry Andric 
54681ad6265SDimitry Andric   auto *OptionalLoc2 =
54781ad6265SDimitry Andric       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
54881ad6265SDimitry Andric   assert(OptionalLoc2 != nullptr);
54981ad6265SDimitry Andric 
55081ad6265SDimitry Andric   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
55181ad6265SDimitry Andric }
55281ad6265SDimitry Andric 
55381ad6265SDimitry Andric void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
55481ad6265SDimitry Andric                          LatticeTransferState &State) {
55581ad6265SDimitry Andric   assert(E->getNumArgs() == 2);
55681ad6265SDimitry Andric 
55781ad6265SDimitry Andric   auto *OptionalLoc1 =
55881ad6265SDimitry Andric       State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
55981ad6265SDimitry Andric   assert(OptionalLoc1 != nullptr);
56081ad6265SDimitry Andric 
56181ad6265SDimitry Andric   auto *OptionalLoc2 =
56281ad6265SDimitry Andric       State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference);
56381ad6265SDimitry Andric   assert(OptionalLoc2 != nullptr);
56481ad6265SDimitry Andric 
56581ad6265SDimitry Andric   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
56681ad6265SDimitry Andric }
56781ad6265SDimitry Andric 
568*bdd1243dSDimitry Andric BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS,
569*bdd1243dSDimitry Andric                             BoolValue &RHS) {
570*bdd1243dSDimitry Andric   // Logically, an optional<T> object is composed of two values - a `has_value`
571*bdd1243dSDimitry Andric   // bit and a value of type T. Equality of optional objects compares both
572*bdd1243dSDimitry Andric   // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
573*bdd1243dSDimitry Andric   // when two optional objects are engaged, the equality of their respective
574*bdd1243dSDimitry Andric   // values of type T matters. Since we only track the `has_value` bits, we
575*bdd1243dSDimitry Andric   // can't make any conclusions about equality when we know that two optional
576*bdd1243dSDimitry Andric   // objects are engaged.
577*bdd1243dSDimitry Andric   //
578*bdd1243dSDimitry Andric   // We express this as two facts about the equality:
579*bdd1243dSDimitry Andric   // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
580*bdd1243dSDimitry Andric   //    If they are equal, then either both are set or both are unset.
581*bdd1243dSDimitry Andric   // b) (!LHS & !RHS) => EqVal
582*bdd1243dSDimitry Andric   //    If neither is set, then they are equal.
583*bdd1243dSDimitry Andric   // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
584*bdd1243dSDimitry Andric   return Env.makeAnd(
585*bdd1243dSDimitry Andric       Env.makeImplication(
586*bdd1243dSDimitry Andric           EqVal, Env.makeOr(Env.makeAnd(LHS, RHS),
587*bdd1243dSDimitry Andric                             Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))),
588*bdd1243dSDimitry Andric       Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS)));
589*bdd1243dSDimitry Andric }
590*bdd1243dSDimitry Andric 
591*bdd1243dSDimitry Andric void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
592*bdd1243dSDimitry Andric                                     const MatchFinder::MatchResult &,
593*bdd1243dSDimitry Andric                                     LatticeTransferState &State) {
594*bdd1243dSDimitry Andric   Environment &Env = State.Env;
595*bdd1243dSDimitry Andric   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
596*bdd1243dSDimitry Andric   if (auto *LHasVal = getHasValue(
597*bdd1243dSDimitry Andric           Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference)))
598*bdd1243dSDimitry Andric     if (auto *RHasVal = getHasValue(
599*bdd1243dSDimitry Andric             Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) {
600*bdd1243dSDimitry Andric       if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
601*bdd1243dSDimitry Andric         CmpValue = &State.Env.makeNot(*CmpValue);
602*bdd1243dSDimitry Andric       Env.addToFlowCondition(
603*bdd1243dSDimitry Andric           evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal));
604*bdd1243dSDimitry Andric     }
605*bdd1243dSDimitry Andric }
606*bdd1243dSDimitry Andric 
607*bdd1243dSDimitry Andric void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
608*bdd1243dSDimitry Andric                                  const clang::Expr *E, Environment &Env) {
609*bdd1243dSDimitry Andric   auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
610*bdd1243dSDimitry Andric   if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) {
611*bdd1243dSDimitry Andric     if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
612*bdd1243dSDimitry Andric       CmpValue = &Env.makeNot(*CmpValue);
613*bdd1243dSDimitry Andric     Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal,
614*bdd1243dSDimitry Andric                                             Env.getBoolLiteralValue(true)));
615*bdd1243dSDimitry Andric   }
616*bdd1243dSDimitry Andric }
617*bdd1243dSDimitry Andric 
618*bdd1243dSDimitry Andric std::optional<StatementMatcher>
61981ad6265SDimitry Andric ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
620*bdd1243dSDimitry Andric   if (Options.IgnoreSmartPointerDereference) {
621*bdd1243dSDimitry Andric     auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
622*bdd1243dSDimitry Andric         anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
623*bdd1243dSDimitry Andric         unless(hasArgument(0, expr(hasOptionalType()))))));
624*bdd1243dSDimitry Andric     return expr(
625*bdd1243dSDimitry Andric         anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
626*bdd1243dSDimitry Andric   }
627*bdd1243dSDimitry Andric   return std::nullopt;
62881ad6265SDimitry Andric }
62981ad6265SDimitry Andric 
63081ad6265SDimitry Andric StatementMatcher
631*bdd1243dSDimitry Andric valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
63281ad6265SDimitry Andric   return isOptionalMemberCallWithName("value", IgnorableOptional);
63381ad6265SDimitry Andric }
63481ad6265SDimitry Andric 
63581ad6265SDimitry Andric StatementMatcher
636*bdd1243dSDimitry Andric valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
63781ad6265SDimitry Andric   return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
63881ad6265SDimitry Andric                     isOptionalOperatorCallWithName("->", IgnorableOptional)));
63981ad6265SDimitry Andric }
64081ad6265SDimitry Andric 
641*bdd1243dSDimitry Andric auto buildTransferMatchSwitch() {
64281ad6265SDimitry Andric   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
64381ad6265SDimitry Andric   // lot of duplicated work (e.g. string comparisons), consider providing APIs
64481ad6265SDimitry Andric   // that avoid it through memoization.
645*bdd1243dSDimitry Andric   return CFGMatchSwitchBuilder<LatticeTransferState>()
64681ad6265SDimitry Andric       // Attach a symbolic "has_value" state to optional values that we see for
64781ad6265SDimitry Andric       // the first time.
648*bdd1243dSDimitry Andric       .CaseOfCFGStmt<Expr>(
64981ad6265SDimitry Andric           expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
65081ad6265SDimitry Andric           initializeOptionalReference)
65181ad6265SDimitry Andric 
65281ad6265SDimitry Andric       // make_optional
653*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
65481ad6265SDimitry Andric 
655*bdd1243dSDimitry Andric       // optional::optional (in place)
656*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXConstructExpr>(
65781ad6265SDimitry Andric           isOptionalInPlaceConstructor(),
65881ad6265SDimitry Andric           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
65981ad6265SDimitry Andric              LatticeTransferState &State) {
660*bdd1243dSDimitry Andric             assignOptionalValue(*E, State.Env,
661*bdd1243dSDimitry Andric                                 State.Env.getBoolLiteralValue(true));
66281ad6265SDimitry Andric           })
663*bdd1243dSDimitry Andric       // nullopt_t::nullopt_t
664*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXConstructExpr>(
665*bdd1243dSDimitry Andric           isNulloptConstructor(),
666*bdd1243dSDimitry Andric           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
667*bdd1243dSDimitry Andric              LatticeTransferState &State) {
668*bdd1243dSDimitry Andric             assignOptionalValue(*E, State.Env,
669*bdd1243dSDimitry Andric                                 State.Env.getBoolLiteralValue(false));
670*bdd1243dSDimitry Andric           })
671*bdd1243dSDimitry Andric       // optional::optional(nullopt_t)
672*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXConstructExpr>(
67381ad6265SDimitry Andric           isOptionalNulloptConstructor(),
67481ad6265SDimitry Andric           [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
67581ad6265SDimitry Andric              LatticeTransferState &State) {
676*bdd1243dSDimitry Andric             assignOptionalValue(*E, State.Env,
67781ad6265SDimitry Andric                                 State.Env.getBoolLiteralValue(false));
67881ad6265SDimitry Andric           })
679*bdd1243dSDimitry Andric       // optional::optional (value/conversion)
680*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
68181ad6265SDimitry Andric                                        transferValueOrConversionConstructor)
68281ad6265SDimitry Andric 
683*bdd1243dSDimitry Andric 
68481ad6265SDimitry Andric       // optional::operator=
685*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(
686*bdd1243dSDimitry Andric           isOptionalValueOrConversionAssignment(),
68781ad6265SDimitry Andric           transferValueOrConversionAssignment)
688*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
68981ad6265SDimitry Andric                                           transferNulloptAssignment)
69081ad6265SDimitry Andric 
69181ad6265SDimitry Andric       // optional::value
692*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
693*bdd1243dSDimitry Andric           valueCall(std::nullopt),
69481ad6265SDimitry Andric           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
69581ad6265SDimitry Andric              LatticeTransferState &State) {
69681ad6265SDimitry Andric             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
69781ad6265SDimitry Andric           })
69881ad6265SDimitry Andric 
69981ad6265SDimitry Andric       // optional::operator*, optional::operator->
700*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(std::nullopt),
701*bdd1243dSDimitry Andric                                [](const CallExpr *E,
702*bdd1243dSDimitry Andric                                   const MatchFinder::MatchResult &,
70381ad6265SDimitry Andric                                   LatticeTransferState &State) {
70481ad6265SDimitry Andric                                  transferUnwrapCall(E, E->getArg(0), State);
70581ad6265SDimitry Andric                                })
70681ad6265SDimitry Andric 
70781ad6265SDimitry Andric       // optional::has_value
708*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
709*bdd1243dSDimitry Andric           isOptionalMemberCallWithName("has_value"),
71081ad6265SDimitry Andric           transferOptionalHasValueCall)
71181ad6265SDimitry Andric 
71281ad6265SDimitry Andric       // optional::operator bool
713*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
714*bdd1243dSDimitry Andric           isOptionalMemberCallWithName("operator bool"),
71581ad6265SDimitry Andric           transferOptionalHasValueCall)
71681ad6265SDimitry Andric 
71781ad6265SDimitry Andric       // optional::emplace
718*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
71981ad6265SDimitry Andric           isOptionalMemberCallWithName("emplace"),
72081ad6265SDimitry Andric           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
72181ad6265SDimitry Andric              LatticeTransferState &State) {
722*bdd1243dSDimitry Andric             assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
72381ad6265SDimitry Andric                                 State.Env.getBoolLiteralValue(true));
72481ad6265SDimitry Andric           })
72581ad6265SDimitry Andric 
72681ad6265SDimitry Andric       // optional::reset
727*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
72881ad6265SDimitry Andric           isOptionalMemberCallWithName("reset"),
72981ad6265SDimitry Andric           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
73081ad6265SDimitry Andric              LatticeTransferState &State) {
731*bdd1243dSDimitry Andric             assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
73281ad6265SDimitry Andric                                 State.Env.getBoolLiteralValue(false));
73381ad6265SDimitry Andric           })
73481ad6265SDimitry Andric 
73581ad6265SDimitry Andric       // optional::swap
736*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
73781ad6265SDimitry Andric                                         transferSwapCall)
73881ad6265SDimitry Andric 
73981ad6265SDimitry Andric       // std::swap
740*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
74181ad6265SDimitry Andric 
74281ad6265SDimitry Andric       // opt.value_or("").empty()
743*bdd1243dSDimitry Andric       .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
744*bdd1243dSDimitry Andric                            transferValueOrStringEmptyCall)
74581ad6265SDimitry Andric 
74681ad6265SDimitry Andric       // opt.value_or(X) != X
747*bdd1243dSDimitry Andric       .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
748*bdd1243dSDimitry Andric 
749*bdd1243dSDimitry Andric       // Comparisons (==, !=):
750*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(
751*bdd1243dSDimitry Andric           isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()),
752*bdd1243dSDimitry Andric           transferOptionalAndOptionalCmp)
753*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(
754*bdd1243dSDimitry Andric           isComparisonOperatorCall(hasOptionalType(),
755*bdd1243dSDimitry Andric                                    unless(hasAnyOptionalType())),
756*bdd1243dSDimitry Andric           [](const clang::CXXOperatorCallExpr *Cmp,
757*bdd1243dSDimitry Andric              const MatchFinder::MatchResult &, LatticeTransferState &State) {
758*bdd1243dSDimitry Andric             transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
759*bdd1243dSDimitry Andric           })
760*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXOperatorCallExpr>(
761*bdd1243dSDimitry Andric           isComparisonOperatorCall(unless(hasAnyOptionalType()),
762*bdd1243dSDimitry Andric                                    hasOptionalType()),
763*bdd1243dSDimitry Andric           [](const clang::CXXOperatorCallExpr *Cmp,
764*bdd1243dSDimitry Andric              const MatchFinder::MatchResult &, LatticeTransferState &State) {
765*bdd1243dSDimitry Andric             transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
766*bdd1243dSDimitry Andric           })
76781ad6265SDimitry Andric 
76881ad6265SDimitry Andric       // returns optional
769*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
77081ad6265SDimitry Andric                                transferCallReturningOptional)
77181ad6265SDimitry Andric 
77281ad6265SDimitry Andric       .Build();
77381ad6265SDimitry Andric }
77481ad6265SDimitry Andric 
77581ad6265SDimitry Andric std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr,
77681ad6265SDimitry Andric                                                const Expr *ObjectExpr,
77781ad6265SDimitry Andric                                                const Environment &Env) {
77881ad6265SDimitry Andric   if (auto *OptionalVal =
77981ad6265SDimitry Andric           Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
78081ad6265SDimitry Andric     auto *Prop = OptionalVal->getProperty("has_value");
78181ad6265SDimitry Andric     if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
78281ad6265SDimitry Andric       if (Env.flowConditionImplies(*HasValueVal))
78381ad6265SDimitry Andric         return {};
78481ad6265SDimitry Andric     }
78581ad6265SDimitry Andric   }
78681ad6265SDimitry Andric 
78781ad6265SDimitry Andric   // Record that this unwrap is *not* provably safe.
78881ad6265SDimitry Andric   // FIXME: include either the name of the optional (if applicable) or a source
78981ad6265SDimitry Andric   // range of the access for easier interpretation of the result.
79081ad6265SDimitry Andric   return {ObjectExpr->getBeginLoc()};
79181ad6265SDimitry Andric }
79281ad6265SDimitry Andric 
79381ad6265SDimitry Andric auto buildDiagnoseMatchSwitch(
79481ad6265SDimitry Andric     const UncheckedOptionalAccessModelOptions &Options) {
79581ad6265SDimitry Andric   // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
79681ad6265SDimitry Andric   // lot of duplicated work (e.g. string comparisons), consider providing APIs
79781ad6265SDimitry Andric   // that avoid it through memoization.
79881ad6265SDimitry Andric   auto IgnorableOptional = ignorableOptional(Options);
799*bdd1243dSDimitry Andric   return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
80081ad6265SDimitry Andric       // optional::value
801*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CXXMemberCallExpr>(
80281ad6265SDimitry Andric           valueCall(IgnorableOptional),
80381ad6265SDimitry Andric           [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
80481ad6265SDimitry Andric              const Environment &Env) {
80581ad6265SDimitry Andric             return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env);
80681ad6265SDimitry Andric           })
80781ad6265SDimitry Andric 
80881ad6265SDimitry Andric       // optional::operator*, optional::operator->
809*bdd1243dSDimitry Andric       .CaseOfCFGStmt<CallExpr>(
81081ad6265SDimitry Andric           valueOperatorCall(IgnorableOptional),
81181ad6265SDimitry Andric           [](const CallExpr *E, const MatchFinder::MatchResult &,
81281ad6265SDimitry Andric              const Environment &Env) {
81381ad6265SDimitry Andric             return diagnoseUnwrapCall(E, E->getArg(0), Env);
81481ad6265SDimitry Andric           })
81581ad6265SDimitry Andric       .Build();
81681ad6265SDimitry Andric }
81781ad6265SDimitry Andric 
81881ad6265SDimitry Andric } // namespace
81981ad6265SDimitry Andric 
82081ad6265SDimitry Andric ast_matchers::DeclarationMatcher
82181ad6265SDimitry Andric UncheckedOptionalAccessModel::optionalClassDecl() {
82281ad6265SDimitry Andric   return optionalClass();
82381ad6265SDimitry Andric }
82481ad6265SDimitry Andric 
825*bdd1243dSDimitry Andric UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
82681ad6265SDimitry Andric     : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
827*bdd1243dSDimitry Andric       TransferMatchSwitch(buildTransferMatchSwitch()) {}
82881ad6265SDimitry Andric 
829*bdd1243dSDimitry Andric void UncheckedOptionalAccessModel::transfer(const CFGElement *Elt,
830*bdd1243dSDimitry Andric                                             NoopLattice &L, Environment &Env) {
83181ad6265SDimitry Andric   LatticeTransferState State(L, Env);
832*bdd1243dSDimitry Andric   TransferMatchSwitch(*Elt, getASTContext(), State);
83381ad6265SDimitry Andric }
83481ad6265SDimitry Andric 
835*bdd1243dSDimitry Andric ComparisonResult UncheckedOptionalAccessModel::compare(
836*bdd1243dSDimitry Andric     QualType Type, const Value &Val1, const Environment &Env1,
837*bdd1243dSDimitry Andric     const Value &Val2, const Environment &Env2) {
838*bdd1243dSDimitry Andric   if (!isOptionalType(Type))
839*bdd1243dSDimitry Andric     return ComparisonResult::Unknown;
840*bdd1243dSDimitry Andric   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
841*bdd1243dSDimitry Andric   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
842*bdd1243dSDimitry Andric   if (MustNonEmpty1 && MustNonEmpty2) return ComparisonResult::Same;
843*bdd1243dSDimitry Andric   // If exactly one is true, then they're different, no reason to check whether
844*bdd1243dSDimitry Andric   // they're definitely empty.
845*bdd1243dSDimitry Andric   if (MustNonEmpty1 || MustNonEmpty2) return ComparisonResult::Different;
846*bdd1243dSDimitry Andric   // Check if they're both definitely empty.
847*bdd1243dSDimitry Andric   return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
848*bdd1243dSDimitry Andric              ? ComparisonResult::Same
849*bdd1243dSDimitry Andric              : ComparisonResult::Different;
85081ad6265SDimitry Andric }
85181ad6265SDimitry Andric 
85281ad6265SDimitry Andric bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
85381ad6265SDimitry Andric                                          const Environment &Env1,
85481ad6265SDimitry Andric                                          const Value &Val2,
85581ad6265SDimitry Andric                                          const Environment &Env2,
85681ad6265SDimitry Andric                                          Value &MergedVal,
85781ad6265SDimitry Andric                                          Environment &MergedEnv) {
858*bdd1243dSDimitry Andric   if (!isOptionalType(Type))
85981ad6265SDimitry Andric     return true;
860*bdd1243dSDimitry Andric   // FIXME: uses same approach as join for `BoolValues`. Requires non-const
861*bdd1243dSDimitry Andric   // values, though, so will require updating the interface.
86281ad6265SDimitry Andric   auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
863*bdd1243dSDimitry Andric   bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
864*bdd1243dSDimitry Andric   bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
865*bdd1243dSDimitry Andric   if (MustNonEmpty1 && MustNonEmpty2)
86681ad6265SDimitry Andric     MergedEnv.addToFlowCondition(HasValueVal);
867*bdd1243dSDimitry Andric   else if (
868*bdd1243dSDimitry Andric       // Only make the costly calls to `isEmptyOptional` if we got "unknown"
869*bdd1243dSDimitry Andric       // (false) for both calls to `isNonEmptyOptional`.
870*bdd1243dSDimitry Andric       !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) &&
871*bdd1243dSDimitry Andric       isEmptyOptional(Val2, Env2))
87281ad6265SDimitry Andric     MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal));
87381ad6265SDimitry Andric   setHasValue(MergedVal, HasValueVal);
87481ad6265SDimitry Andric   return true;
87581ad6265SDimitry Andric }
87681ad6265SDimitry Andric 
877*bdd1243dSDimitry Andric Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev,
878*bdd1243dSDimitry Andric                                            const Environment &PrevEnv,
879*bdd1243dSDimitry Andric                                            Value &Current,
880*bdd1243dSDimitry Andric                                            Environment &CurrentEnv) {
881*bdd1243dSDimitry Andric   switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) {
882*bdd1243dSDimitry Andric   case ComparisonResult::Same:
883*bdd1243dSDimitry Andric     return &Prev;
884*bdd1243dSDimitry Andric   case ComparisonResult::Different:
885*bdd1243dSDimitry Andric     if (auto *PrevHasVal =
886*bdd1243dSDimitry Andric             cast_or_null<BoolValue>(Prev.getProperty("has_value"))) {
887*bdd1243dSDimitry Andric       if (isa<TopBoolValue>(PrevHasVal))
888*bdd1243dSDimitry Andric         return &Prev;
889*bdd1243dSDimitry Andric     }
890*bdd1243dSDimitry Andric     if (auto *CurrentHasVal =
891*bdd1243dSDimitry Andric             cast_or_null<BoolValue>(Current.getProperty("has_value"))) {
892*bdd1243dSDimitry Andric       if (isa<TopBoolValue>(CurrentHasVal))
893*bdd1243dSDimitry Andric         return &Current;
894*bdd1243dSDimitry Andric     }
895*bdd1243dSDimitry Andric     return &createOptionalValue(CurrentEnv, CurrentEnv.makeTopBoolValue());
896*bdd1243dSDimitry Andric   case ComparisonResult::Unknown:
897*bdd1243dSDimitry Andric     return nullptr;
898*bdd1243dSDimitry Andric   }
899*bdd1243dSDimitry Andric   llvm_unreachable("all cases covered in switch");
900*bdd1243dSDimitry Andric }
901*bdd1243dSDimitry Andric 
90281ad6265SDimitry Andric UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
90381ad6265SDimitry Andric     UncheckedOptionalAccessModelOptions Options)
90481ad6265SDimitry Andric     : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
90581ad6265SDimitry Andric 
90681ad6265SDimitry Andric std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose(
907*bdd1243dSDimitry Andric     ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) {
908*bdd1243dSDimitry Andric   return DiagnoseMatchSwitch(*Elt, Ctx, Env);
90981ad6265SDimitry Andric }
91081ad6265SDimitry Andric 
91181ad6265SDimitry Andric } // namespace dataflow
91281ad6265SDimitry Andric } // namespace clang
913