1b000b770SStanislav Gatev //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//
2b000b770SStanislav Gatev //
3b000b770SStanislav Gatev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b000b770SStanislav Gatev // See https://llvm.org/LICENSE.txt for license information.
5b000b770SStanislav Gatev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b000b770SStanislav Gatev //
7b000b770SStanislav Gatev //===----------------------------------------------------------------------===//
8b000b770SStanislav Gatev //
9b000b770SStanislav Gatev // This file defines a dataflow analysis that detects unsafe uses of optional
10b000b770SStanislav Gatev // values.
11b000b770SStanislav Gatev //
12b000b770SStanislav Gatev //===----------------------------------------------------------------------===//
13b000b770SStanislav Gatev
14af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
15af98b0afSStanislav Gatev #include "clang/AST/ASTContext.h"
167e63a0d4SYitzhak Mandelbaum #include "clang/AST/DeclCXX.h"
17af98b0afSStanislav Gatev #include "clang/AST/Expr.h"
18af98b0afSStanislav Gatev #include "clang/AST/ExprCXX.h"
19af98b0afSStanislav Gatev #include "clang/AST/Stmt.h"
20af98b0afSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h"
21af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
22af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
23*cf1f978dSSam Estep #include "clang/Analysis/FlowSensitive/NoopLattice.h"
24af98b0afSStanislav Gatev #include "clang/Analysis/FlowSensitive/Value.h"
2558fe7f96SSam Estep #include "clang/Basic/SourceLocation.h"
26af98b0afSStanislav Gatev #include "llvm/ADT/StringRef.h"
27af98b0afSStanislav Gatev #include "llvm/Support/Casting.h"
28af98b0afSStanislav Gatev #include <cassert>
299e0fc676SStanislav Gatev #include <memory>
309e0fc676SStanislav Gatev #include <utility>
3158fe7f96SSam Estep #include <vector>
32af98b0afSStanislav Gatev
33af98b0afSStanislav Gatev namespace clang {
34af98b0afSStanislav Gatev namespace dataflow {
35af98b0afSStanislav Gatev namespace {
36af98b0afSStanislav Gatev
37af98b0afSStanislav Gatev using namespace ::clang::ast_matchers;
38*cf1f978dSSam Estep using LatticeTransferState = TransferState<NoopLattice>;
39af98b0afSStanislav Gatev
optionalClass()407e63a0d4SYitzhak Mandelbaum DeclarationMatcher optionalClass() {
41af98b0afSStanislav Gatev return classTemplateSpecializationDecl(
42af98b0afSStanislav Gatev anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
43af98b0afSStanislav Gatev hasName("__optional_destruct_base"), hasName("absl::optional"),
44af98b0afSStanislav Gatev hasName("base::Optional")),
45af98b0afSStanislav Gatev hasTemplateArgument(0, refersToType(type().bind("T"))));
46af98b0afSStanislav Gatev }
47af98b0afSStanislav Gatev
optionalOrAliasType()486adfc64eSYitzhak Mandelbaum auto optionalOrAliasType() {
4965e710c3SStanislav Gatev return hasUnqualifiedDesugaredType(
5065e710c3SStanislav Gatev recordType(hasDeclaration(optionalClass())));
5165e710c3SStanislav Gatev }
5265e710c3SStanislav Gatev
536adfc64eSYitzhak Mandelbaum /// Matches any of the spellings of the optional types and sugar, aliases, etc.
hasOptionalType()546adfc64eSYitzhak Mandelbaum auto hasOptionalType() { return hasType(optionalOrAliasType()); }
556adfc64eSYitzhak Mandelbaum
isOptionalMemberCallWithName(llvm::StringRef MemberName,llvm::Optional<StatementMatcher> Ignorable=llvm::None)56a184a0d8SYitzhak Mandelbaum auto isOptionalMemberCallWithName(
57a184a0d8SYitzhak Mandelbaum llvm::StringRef MemberName,
58a184a0d8SYitzhak Mandelbaum llvm::Optional<StatementMatcher> Ignorable = llvm::None) {
59a184a0d8SYitzhak Mandelbaum auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr()))
60a184a0d8SYitzhak Mandelbaum : cxxThisExpr());
61af98b0afSStanislav Gatev return cxxMemberCallExpr(
62a184a0d8SYitzhak Mandelbaum on(expr(Exception)),
63af98b0afSStanislav Gatev callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
64af98b0afSStanislav Gatev }
65af98b0afSStanislav Gatev
isOptionalOperatorCallWithName(llvm::StringRef operator_name,llvm::Optional<StatementMatcher> Ignorable=llvm::None)66a184a0d8SYitzhak Mandelbaum auto isOptionalOperatorCallWithName(
67a184a0d8SYitzhak Mandelbaum llvm::StringRef operator_name,
68a184a0d8SYitzhak Mandelbaum llvm::Optional<StatementMatcher> Ignorable = llvm::None) {
69a184a0d8SYitzhak Mandelbaum return cxxOperatorCallExpr(
70a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName(operator_name),
71a184a0d8SYitzhak Mandelbaum callee(cxxMethodDecl(ofClass(optionalClass()))),
72a184a0d8SYitzhak Mandelbaum Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
73af98b0afSStanislav Gatev }
74af98b0afSStanislav Gatev
isMakeOptionalCall()75092a530cSStanislav Gatev auto isMakeOptionalCall() {
769e0fc676SStanislav Gatev return callExpr(
779e0fc676SStanislav Gatev callee(functionDecl(hasAnyName(
789e0fc676SStanislav Gatev "std::make_optional", "base::make_optional", "absl::make_optional"))),
799e0fc676SStanislav Gatev hasOptionalType());
809e0fc676SStanislav Gatev }
819e0fc676SStanislav Gatev
hasNulloptType()82092a530cSStanislav Gatev auto hasNulloptType() {
83092a530cSStanislav Gatev return hasType(namedDecl(
84092a530cSStanislav Gatev hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")));
85092a530cSStanislav Gatev }
86092a530cSStanislav Gatev
inPlaceClass()87092a530cSStanislav Gatev auto inPlaceClass() {
88092a530cSStanislav Gatev return recordDecl(
89092a530cSStanislav Gatev hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
90092a530cSStanislav Gatev }
91092a530cSStanislav Gatev
isOptionalNulloptConstructor()92092a530cSStanislav Gatev auto isOptionalNulloptConstructor() {
93092a530cSStanislav Gatev return cxxConstructExpr(hasOptionalType(), argumentCountIs(1),
94092a530cSStanislav Gatev hasArgument(0, hasNulloptType()));
95092a530cSStanislav Gatev }
96092a530cSStanislav Gatev
isOptionalInPlaceConstructor()97092a530cSStanislav Gatev auto isOptionalInPlaceConstructor() {
98092a530cSStanislav Gatev return cxxConstructExpr(hasOptionalType(),
99092a530cSStanislav Gatev hasArgument(0, hasType(inPlaceClass())));
100092a530cSStanislav Gatev }
101092a530cSStanislav Gatev
isOptionalValueOrConversionConstructor()102092a530cSStanislav Gatev auto isOptionalValueOrConversionConstructor() {
103092a530cSStanislav Gatev return cxxConstructExpr(
104092a530cSStanislav Gatev hasOptionalType(),
105092a530cSStanislav Gatev unless(hasDeclaration(
106092a530cSStanislav Gatev cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
107092a530cSStanislav Gatev argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
108092a530cSStanislav Gatev }
109092a530cSStanislav Gatev
isOptionalValueOrConversionAssignment()110b000b770SStanislav Gatev auto isOptionalValueOrConversionAssignment() {
111b000b770SStanislav Gatev return cxxOperatorCallExpr(
112b000b770SStanislav Gatev hasOverloadedOperatorName("="),
113b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))),
114b000b770SStanislav Gatev unless(hasDeclaration(cxxMethodDecl(
115b000b770SStanislav Gatev anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
116b000b770SStanislav Gatev argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
117b000b770SStanislav Gatev }
118b000b770SStanislav Gatev
isOptionalNulloptAssignment()119b000b770SStanislav Gatev auto isOptionalNulloptAssignment() {
120b000b770SStanislav Gatev return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
121b000b770SStanislav Gatev callee(cxxMethodDecl(ofClass(optionalClass()))),
122b000b770SStanislav Gatev argumentCountIs(2),
123b000b770SStanislav Gatev hasArgument(1, hasNulloptType()));
124b000b770SStanislav Gatev }
125b000b770SStanislav Gatev
isStdSwapCall()1262ddd57aeSStanislav Gatev auto isStdSwapCall() {
1272ddd57aeSStanislav Gatev return callExpr(callee(functionDecl(hasName("std::swap"))),
1282ddd57aeSStanislav Gatev argumentCountIs(2), hasArgument(0, hasOptionalType()),
1292ddd57aeSStanislav Gatev hasArgument(1, hasOptionalType()));
1302ddd57aeSStanislav Gatev }
1312ddd57aeSStanislav Gatev
1327f076004SYitzhak Mandelbaum constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
1337f076004SYitzhak Mandelbaum
isValueOrStringEmptyCall()1347f076004SYitzhak Mandelbaum auto isValueOrStringEmptyCall() {
1357f076004SYitzhak Mandelbaum // `opt.value_or("").empty()`
1367f076004SYitzhak Mandelbaum return cxxMemberCallExpr(
1377f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("empty"))),
1387f076004SYitzhak Mandelbaum onImplicitObjectArgument(ignoringImplicit(
1397f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1407f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"),
1417f076004SYitzhak Mandelbaum ofClass(optionalClass()))),
1427f076004SYitzhak Mandelbaum hasArgument(0, stringLiteral(hasSize(0))))
1437f076004SYitzhak Mandelbaum .bind(ValueOrCallID))));
1447f076004SYitzhak Mandelbaum }
1457f076004SYitzhak Mandelbaum
isValueOrNotEqX()1467f076004SYitzhak Mandelbaum auto isValueOrNotEqX() {
1477f076004SYitzhak Mandelbaum auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
1487f076004SYitzhak Mandelbaum return hasOperands(
1497f076004SYitzhak Mandelbaum ignoringImplicit(
1507f076004SYitzhak Mandelbaum cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
1517f076004SYitzhak Mandelbaum callee(cxxMethodDecl(hasName("value_or"),
1527f076004SYitzhak Mandelbaum ofClass(optionalClass()))),
1537f076004SYitzhak Mandelbaum hasArgument(0, Arg))
1547f076004SYitzhak Mandelbaum .bind(ValueOrCallID)),
1557f076004SYitzhak Mandelbaum ignoringImplicit(Arg));
1567f076004SYitzhak Mandelbaum };
1577f076004SYitzhak Mandelbaum
1587f076004SYitzhak Mandelbaum // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
1597f076004SYitzhak Mandelbaum // support this pattern for any expression, but the AST does not have a
1607f076004SYitzhak Mandelbaum // generic expression comparison facility, so we specialize to common cases
1617f076004SYitzhak Mandelbaum // seen in practice. FIXME: define a matcher that compares values across
1627f076004SYitzhak Mandelbaum // nodes, which would let us generalize this to any `X`.
1637f076004SYitzhak Mandelbaum return binaryOperation(hasOperatorName("!="),
1647f076004SYitzhak Mandelbaum anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
1657f076004SYitzhak Mandelbaum ComparesToSame(stringLiteral(hasSize(0))),
1667f076004SYitzhak Mandelbaum ComparesToSame(integerLiteral(equals(0)))));
1677f076004SYitzhak Mandelbaum }
1687f076004SYitzhak Mandelbaum
isCallReturningOptional()16965e710c3SStanislav Gatev auto isCallReturningOptional() {
170cd0d5261SSam Estep return callExpr(hasType(qualType(anyOf(
171cd0d5261SSam Estep optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
17265e710c3SStanislav Gatev }
17365e710c3SStanislav Gatev
1748fcdd625SStanislav Gatev /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
1758fcdd625SStanislav Gatev /// property of the optional value `OptionalVal`.
setHasValue(Value & OptionalVal,BoolValue & HasValueVal)1768fcdd625SStanislav Gatev void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
1778fcdd625SStanislav Gatev OptionalVal.setProperty("has_value", HasValueVal);
1788fcdd625SStanislav Gatev }
1798fcdd625SStanislav Gatev
1809e0fc676SStanislav Gatev /// Creates a symbolic value for an `optional` value using `HasValueVal` as the
1819e0fc676SStanislav Gatev /// symbolic value of its "has_value" property.
createOptionalValue(Environment & Env,BoolValue & HasValueVal)1829e0fc676SStanislav Gatev StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
1839e0fc676SStanislav Gatev auto OptionalVal = std::make_unique<StructValue>();
1848fcdd625SStanislav Gatev setHasValue(*OptionalVal, HasValueVal);
1859e0fc676SStanislav Gatev return Env.takeOwnership(std::move(OptionalVal));
1869e0fc676SStanislav Gatev }
1879e0fc676SStanislav Gatev
188af98b0afSStanislav Gatev /// Returns the symbolic value that represents the "has_value" property of the
18949ed5bf5SWei Yi Tee /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
getHasValue(Environment & Env,Value * OptionalVal)190dd38caf3SYitzhak Mandelbaum BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
191dd38caf3SYitzhak Mandelbaum if (OptionalVal != nullptr) {
192dd38caf3SYitzhak Mandelbaum auto *HasValueVal =
193dd38caf3SYitzhak Mandelbaum cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
194dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr) {
195dd38caf3SYitzhak Mandelbaum HasValueVal = &Env.makeAtomicBoolValue();
196dd38caf3SYitzhak Mandelbaum OptionalVal->setProperty("has_value", *HasValueVal);
197dd38caf3SYitzhak Mandelbaum }
198dd38caf3SYitzhak Mandelbaum return HasValueVal;
199af98b0afSStanislav Gatev }
200af98b0afSStanislav Gatev return nullptr;
201af98b0afSStanislav Gatev }
202af98b0afSStanislav Gatev
203092a530cSStanislav Gatev /// If `Type` is a reference type, returns the type of its pointee. Otherwise,
204092a530cSStanislav Gatev /// returns `Type` itself.
stripReference(QualType Type)205092a530cSStanislav Gatev QualType stripReference(QualType Type) {
206092a530cSStanislav Gatev return Type->isReferenceType() ? Type->getPointeeType() : Type;
207092a530cSStanislav Gatev }
208092a530cSStanislav Gatev
209092a530cSStanislav Gatev /// Returns true if and only if `Type` is an optional type.
IsOptionalType(QualType Type)210092a530cSStanislav Gatev bool IsOptionalType(QualType Type) {
211092a530cSStanislav Gatev if (!Type->isRecordType())
212092a530cSStanislav Gatev return false;
213092a530cSStanislav Gatev // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call.
214092a530cSStanislav Gatev auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString();
215092a530cSStanislav Gatev return TypeName == "std::optional" || TypeName == "absl::optional" ||
216092a530cSStanislav Gatev TypeName == "base::Optional";
217092a530cSStanislav Gatev }
218092a530cSStanislav Gatev
219092a530cSStanislav Gatev /// Returns the number of optional wrappers in `Type`.
220092a530cSStanislav Gatev ///
221092a530cSStanislav Gatev /// For example, if `Type` is `optional<optional<int>>`, the result of this
222092a530cSStanislav Gatev /// function will be 2.
countOptionalWrappers(const ASTContext & ASTCtx,QualType Type)223092a530cSStanislav Gatev int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
224092a530cSStanislav Gatev if (!IsOptionalType(Type))
225092a530cSStanislav Gatev return 0;
226092a530cSStanislav Gatev return 1 + countOptionalWrappers(
227092a530cSStanislav Gatev ASTCtx,
228092a530cSStanislav Gatev cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
229092a530cSStanislav Gatev ->getTemplateArgs()
230092a530cSStanislav Gatev .get(0)
231092a530cSStanislav Gatev .getAsType()
232092a530cSStanislav Gatev .getDesugaredType(ASTCtx));
233092a530cSStanislav Gatev }
234092a530cSStanislav Gatev
235dd38caf3SYitzhak Mandelbaum /// Tries to initialize the `optional`'s value (that is, contents), and return
236dd38caf3SYitzhak Mandelbaum /// its location. Returns nullptr if the value can't be represented.
maybeInitializeOptionalValueMember(QualType Q,Value & OptionalVal,Environment & Env)237dd38caf3SYitzhak Mandelbaum StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
238dd38caf3SYitzhak Mandelbaum Value &OptionalVal,
239dd38caf3SYitzhak Mandelbaum Environment &Env) {
240dd38caf3SYitzhak Mandelbaum // The "value" property represents a synthetic field. As such, it needs
241dd38caf3SYitzhak Mandelbaum // `StorageLocation`, like normal fields (and other variables). So, we model
242dd38caf3SYitzhak Mandelbaum // it with a `ReferenceValue`, since that includes a storage location. Once
243dd38caf3SYitzhak Mandelbaum // the property is set, it will be shared by all environments that access the
244dd38caf3SYitzhak Mandelbaum // `Value` representing the optional (here, `OptionalVal`).
245dd38caf3SYitzhak Mandelbaum if (auto *ValueProp = OptionalVal.getProperty("value")) {
246dd38caf3SYitzhak Mandelbaum auto *ValueRef = clang::cast<ReferenceValue>(ValueProp);
24797d69cdaSWei Yi Tee auto &ValueLoc = ValueRef->getReferentLoc();
248dd38caf3SYitzhak Mandelbaum if (Env.getValue(ValueLoc) == nullptr) {
249dd38caf3SYitzhak Mandelbaum // The property was previously set, but the value has been lost. This can
250dd38caf3SYitzhak Mandelbaum // happen, for example, because of an environment merge (where the two
251dd38caf3SYitzhak Mandelbaum // environments mapped the property to different values, which resulted in
252dd38caf3SYitzhak Mandelbaum // them both being discarded), or when two blocks in the CFG, with neither
253dd38caf3SYitzhak Mandelbaum // a dominator of the other, visit the same optional value, or even when a
254dd38caf3SYitzhak Mandelbaum // block is revisited during testing to collect per-statement state.
255dd38caf3SYitzhak Mandelbaum // FIXME: This situation means that the optional contents are not shared
256dd38caf3SYitzhak Mandelbaum // between branches and the like. Practically, this lack of sharing
257dd38caf3SYitzhak Mandelbaum // reduces the precision of the model when the contents are relevant to
258dd38caf3SYitzhak Mandelbaum // the check, like another optional or a boolean that influences control
259dd38caf3SYitzhak Mandelbaum // flow.
260dd38caf3SYitzhak Mandelbaum auto *ValueVal = Env.createValue(ValueLoc.getType());
261dd38caf3SYitzhak Mandelbaum if (ValueVal == nullptr)
262dd38caf3SYitzhak Mandelbaum return nullptr;
263dd38caf3SYitzhak Mandelbaum Env.setValue(ValueLoc, *ValueVal);
264dd38caf3SYitzhak Mandelbaum }
265dd38caf3SYitzhak Mandelbaum return &ValueLoc;
266dd38caf3SYitzhak Mandelbaum }
267dd38caf3SYitzhak Mandelbaum
268dd38caf3SYitzhak Mandelbaum auto Ty = stripReference(Q);
269dd38caf3SYitzhak Mandelbaum auto *ValueVal = Env.createValue(Ty);
270dd38caf3SYitzhak Mandelbaum if (ValueVal == nullptr)
271dd38caf3SYitzhak Mandelbaum return nullptr;
272dd38caf3SYitzhak Mandelbaum auto &ValueLoc = Env.createStorageLocation(Ty);
273dd38caf3SYitzhak Mandelbaum Env.setValue(ValueLoc, *ValueVal);
274dd38caf3SYitzhak Mandelbaum auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc);
275dd38caf3SYitzhak Mandelbaum OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef)));
276dd38caf3SYitzhak Mandelbaum return &ValueLoc;
277dd38caf3SYitzhak Mandelbaum }
278dd38caf3SYitzhak Mandelbaum
initializeOptionalReference(const Expr * OptionalExpr,const MatchFinder::MatchResult &,LatticeTransferState & State)279092a530cSStanislav Gatev void initializeOptionalReference(const Expr *OptionalExpr,
280092a530cSStanislav Gatev const MatchFinder::MatchResult &,
281af98b0afSStanislav Gatev LatticeTransferState &State) {
28249ed5bf5SWei Yi Tee if (auto *OptionalVal =
28349ed5bf5SWei Yi Tee State.Env.getValue(*OptionalExpr, SkipPast::Reference)) {
284af98b0afSStanislav Gatev if (OptionalVal->getProperty("has_value") == nullptr) {
2858fcdd625SStanislav Gatev setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
286af98b0afSStanislav Gatev }
287af98b0afSStanislav Gatev }
288af98b0afSStanislav Gatev }
289af98b0afSStanislav Gatev
2908fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
2918fcdd625SStanislav Gatev /// empty in `Env.
isEmptyOptional(const Value & OptionalVal,const Environment & Env)2928fcdd625SStanislav Gatev bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
2938fcdd625SStanislav Gatev auto *HasValueVal =
2948fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
2958fcdd625SStanislav Gatev return HasValueVal != nullptr &&
2968fcdd625SStanislav Gatev Env.flowConditionImplies(Env.makeNot(*HasValueVal));
2978fcdd625SStanislav Gatev }
2988fcdd625SStanislav Gatev
2998fcdd625SStanislav Gatev /// Returns true if and only if `OptionalVal` is initialized and known to be
3008fcdd625SStanislav Gatev /// non-empty in `Env.
isNonEmptyOptional(const Value & OptionalVal,const Environment & Env)3018fcdd625SStanislav Gatev bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
3028fcdd625SStanislav Gatev auto *HasValueVal =
3038fcdd625SStanislav Gatev cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
3048fcdd625SStanislav Gatev return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal);
3058fcdd625SStanislav Gatev }
3068fcdd625SStanislav Gatev
transferUnwrapCall(const Expr * UnwrapExpr,const Expr * ObjectExpr,LatticeTransferState & State)307092a530cSStanislav Gatev void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
308af98b0afSStanislav Gatev LatticeTransferState &State) {
30949ed5bf5SWei Yi Tee if (auto *OptionalVal =
31049ed5bf5SWei Yi Tee State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
311dd38caf3SYitzhak Mandelbaum if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
312dd38caf3SYitzhak Mandelbaum if (auto *Loc = maybeInitializeOptionalValueMember(
313dd38caf3SYitzhak Mandelbaum UnwrapExpr->getType(), *OptionalVal, State.Env))
314dd38caf3SYitzhak Mandelbaum State.Env.setStorageLocation(*UnwrapExpr, *Loc);
315af98b0afSStanislav Gatev }
316dd38caf3SYitzhak Mandelbaum }
317af98b0afSStanislav Gatev
transferMakeOptionalCall(const CallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)318092a530cSStanislav Gatev void transferMakeOptionalCall(const CallExpr *E,
319092a530cSStanislav Gatev const MatchFinder::MatchResult &,
320092a530cSStanislav Gatev LatticeTransferState &State) {
3219e0fc676SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E);
3229e0fc676SStanislav Gatev State.Env.setStorageLocation(*E, Loc);
3239e0fc676SStanislav Gatev State.Env.setValue(
3249e0fc676SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
3259e0fc676SStanislav Gatev }
3269e0fc676SStanislav Gatev
transferOptionalHasValueCall(const CXXMemberCallExpr * CallExpr,const MatchFinder::MatchResult &,LatticeTransferState & State)327092a530cSStanislav Gatev void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
328092a530cSStanislav Gatev const MatchFinder::MatchResult &,
329af98b0afSStanislav Gatev LatticeTransferState &State) {
330dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal = getHasValue(
331dd38caf3SYitzhak Mandelbaum State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
332af98b0afSStanislav Gatev SkipPast::ReferenceThenPointer))) {
333af98b0afSStanislav Gatev auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
334af98b0afSStanislav Gatev State.Env.setValue(CallExprLoc, *HasValueVal);
335af98b0afSStanislav Gatev State.Env.setStorageLocation(*CallExpr, CallExprLoc);
336af98b0afSStanislav Gatev }
337af98b0afSStanislav Gatev }
338af98b0afSStanislav Gatev
3397f076004SYitzhak Mandelbaum /// `ModelPred` builds a logical formula relating the predicate in
3407f076004SYitzhak Mandelbaum /// `ValueOrPredExpr` to the optional's `has_value` property.
transferValueOrImpl(const clang::Expr * ValueOrPredExpr,const MatchFinder::MatchResult & Result,LatticeTransferState & State,BoolValue & (* ModelPred)(Environment & Env,BoolValue & ExprVal,BoolValue & HasValueVal))3417f076004SYitzhak Mandelbaum void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
3427f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result,
3437f076004SYitzhak Mandelbaum LatticeTransferState &State,
3447f076004SYitzhak Mandelbaum BoolValue &(*ModelPred)(Environment &Env,
3457f076004SYitzhak Mandelbaum BoolValue &ExprVal,
3467f076004SYitzhak Mandelbaum BoolValue &HasValueVal)) {
3477f076004SYitzhak Mandelbaum auto &Env = State.Env;
3487f076004SYitzhak Mandelbaum
3497f076004SYitzhak Mandelbaum const auto *ObjectArgumentExpr =
3507f076004SYitzhak Mandelbaum Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
3517f076004SYitzhak Mandelbaum ->getImplicitObjectArgument();
3527f076004SYitzhak Mandelbaum
353dd38caf3SYitzhak Mandelbaum auto *HasValueVal = getHasValue(
354dd38caf3SYitzhak Mandelbaum State.Env,
355dd38caf3SYitzhak Mandelbaum State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
356dd38caf3SYitzhak Mandelbaum if (HasValueVal == nullptr)
3577f076004SYitzhak Mandelbaum return;
3587f076004SYitzhak Mandelbaum
3597f076004SYitzhak Mandelbaum auto *ExprValue = cast_or_null<BoolValue>(
3607f076004SYitzhak Mandelbaum State.Env.getValue(*ValueOrPredExpr, SkipPast::None));
3617f076004SYitzhak Mandelbaum if (ExprValue == nullptr) {
3627f076004SYitzhak Mandelbaum auto &ExprLoc = State.Env.createStorageLocation(*ValueOrPredExpr);
3637f076004SYitzhak Mandelbaum ExprValue = &State.Env.makeAtomicBoolValue();
3647f076004SYitzhak Mandelbaum State.Env.setValue(ExprLoc, *ExprValue);
3657f076004SYitzhak Mandelbaum State.Env.setStorageLocation(*ValueOrPredExpr, ExprLoc);
3667f076004SYitzhak Mandelbaum }
3677f076004SYitzhak Mandelbaum
3687f076004SYitzhak Mandelbaum Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal));
3697f076004SYitzhak Mandelbaum }
3707f076004SYitzhak Mandelbaum
transferValueOrStringEmptyCall(const clang::Expr * ComparisonExpr,const MatchFinder::MatchResult & Result,LatticeTransferState & State)3717f076004SYitzhak Mandelbaum void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
3727f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result,
3737f076004SYitzhak Mandelbaum LatticeTransferState &State) {
3747f076004SYitzhak Mandelbaum return transferValueOrImpl(ComparisonExpr, Result, State,
3757f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal,
3767f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & {
3777f076004SYitzhak Mandelbaum // If the result is *not* empty, then we know the
3787f076004SYitzhak Mandelbaum // optional must have been holding a value. If
3797f076004SYitzhak Mandelbaum // `ExprVal` is true, though, we don't learn
3807f076004SYitzhak Mandelbaum // anything definite about `has_value`, so we
3817f076004SYitzhak Mandelbaum // don't add any corresponding implications to
3827f076004SYitzhak Mandelbaum // the flow condition.
3837f076004SYitzhak Mandelbaum return Env.makeImplication(Env.makeNot(ExprVal),
3847f076004SYitzhak Mandelbaum HasValueVal);
3857f076004SYitzhak Mandelbaum });
3867f076004SYitzhak Mandelbaum }
3877f076004SYitzhak Mandelbaum
transferValueOrNotEqX(const Expr * ComparisonExpr,const MatchFinder::MatchResult & Result,LatticeTransferState & State)3887f076004SYitzhak Mandelbaum void transferValueOrNotEqX(const Expr *ComparisonExpr,
3897f076004SYitzhak Mandelbaum const MatchFinder::MatchResult &Result,
3907f076004SYitzhak Mandelbaum LatticeTransferState &State) {
3917f076004SYitzhak Mandelbaum transferValueOrImpl(ComparisonExpr, Result, State,
3927f076004SYitzhak Mandelbaum [](Environment &Env, BoolValue &ExprVal,
3937f076004SYitzhak Mandelbaum BoolValue &HasValueVal) -> BoolValue & {
3947f076004SYitzhak Mandelbaum // We know that if `(opt.value_or(X) != X)` then
3957f076004SYitzhak Mandelbaum // `opt.hasValue()`, even without knowing further
3967f076004SYitzhak Mandelbaum // details about the contents of `opt`.
3977f076004SYitzhak Mandelbaum return Env.makeImplication(ExprVal, HasValueVal);
3987f076004SYitzhak Mandelbaum });
3997f076004SYitzhak Mandelbaum }
4007f076004SYitzhak Mandelbaum
transferCallReturningOptional(const CallExpr * E,const MatchFinder::MatchResult & Result,LatticeTransferState & State)40165e710c3SStanislav Gatev void transferCallReturningOptional(const CallExpr *E,
40265e710c3SStanislav Gatev const MatchFinder::MatchResult &Result,
40365e710c3SStanislav Gatev LatticeTransferState &State) {
40465e710c3SStanislav Gatev if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
40565e710c3SStanislav Gatev return;
40665e710c3SStanislav Gatev
40765e710c3SStanislav Gatev auto &Loc = State.Env.createStorageLocation(*E);
40865e710c3SStanislav Gatev State.Env.setStorageLocation(*E, Loc);
40965e710c3SStanislav Gatev State.Env.setValue(
41065e710c3SStanislav Gatev Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()));
41165e710c3SStanislav Gatev }
41265e710c3SStanislav Gatev
assignOptionalValue(const Expr & E,LatticeTransferState & State,BoolValue & HasValueVal)413092a530cSStanislav Gatev void assignOptionalValue(const Expr &E, LatticeTransferState &State,
414092a530cSStanislav Gatev BoolValue &HasValueVal) {
415092a530cSStanislav Gatev if (auto *OptionalLoc =
416092a530cSStanislav Gatev State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
417092a530cSStanislav Gatev State.Env.setValue(*OptionalLoc,
418092a530cSStanislav Gatev createOptionalValue(State.Env, HasValueVal));
4199e0fc676SStanislav Gatev }
4209e0fc676SStanislav Gatev }
4219e0fc676SStanislav Gatev
422b000b770SStanislav Gatev /// Returns a symbolic value for the "has_value" property of an `optional<T>`
423b000b770SStanislav Gatev /// value that is constructed/assigned from a value of type `U` or `optional<U>`
424b000b770SStanislav Gatev /// where `T` is constructible from `U`.
value_orConversionHasValue(const FunctionDecl & F,const Expr & E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)42506decd0bSKazu Hirata BoolValue &value_orConversionHasValue(const FunctionDecl &F, const Expr &E,
426b000b770SStanislav Gatev const MatchFinder::MatchResult &MatchRes,
427b000b770SStanislav Gatev LatticeTransferState &State) {
428b000b770SStanislav Gatev assert(F.getTemplateSpecializationArgs()->size() > 0);
429b000b770SStanislav Gatev
430b000b770SStanislav Gatev const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
431b000b770SStanislav Gatev *MatchRes.Context,
432b000b770SStanislav Gatev stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType()));
433b000b770SStanislav Gatev const int ArgTypeOptionalWrappersCount =
434b000b770SStanislav Gatev countOptionalWrappers(*MatchRes.Context, stripReference(E.getType()));
435b000b770SStanislav Gatev
436b000b770SStanislav Gatev // Check if this is a constructor/assignment call for `optional<T>` with
437b000b770SStanislav Gatev // argument of type `U` such that `T` is constructible from `U`.
438b000b770SStanislav Gatev if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
439b000b770SStanislav Gatev return State.Env.getBoolLiteralValue(true);
440b000b770SStanislav Gatev
441b000b770SStanislav Gatev // This is a constructor/assignment call for `optional<T>` with argument of
442b000b770SStanislav Gatev // type `optional<U>` such that `T` is constructible from `U`.
443dd38caf3SYitzhak Mandelbaum if (auto *HasValueVal =
444dd38caf3SYitzhak Mandelbaum getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference)))
445dd38caf3SYitzhak Mandelbaum return *HasValueVal;
446b000b770SStanislav Gatev return State.Env.makeAtomicBoolValue();
447b000b770SStanislav Gatev }
448b000b770SStanislav Gatev
transferValueOrConversionConstructor(const CXXConstructExpr * E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)449092a530cSStanislav Gatev void transferValueOrConversionConstructor(
450092a530cSStanislav Gatev const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
4519e0fc676SStanislav Gatev LatticeTransferState &State) {
452092a530cSStanislav Gatev assert(E->getNumArgs() > 0);
453092a530cSStanislav Gatev
454b000b770SStanislav Gatev assignOptionalValue(*E, State,
45506decd0bSKazu Hirata value_orConversionHasValue(*E->getConstructor(),
456b000b770SStanislav Gatev *E->getArg(0), MatchRes,
457b000b770SStanislav Gatev State));
458b000b770SStanislav Gatev }
459092a530cSStanislav Gatev
transferAssignment(const CXXOperatorCallExpr * E,BoolValue & HasValueVal,LatticeTransferState & State)460b000b770SStanislav Gatev void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
461b000b770SStanislav Gatev LatticeTransferState &State) {
462b000b770SStanislav Gatev assert(E->getNumArgs() > 0);
463b000b770SStanislav Gatev
464b000b770SStanislav Gatev auto *OptionalLoc =
465b000b770SStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
466a9ad689eSSam Estep if (OptionalLoc == nullptr)
467a9ad689eSSam Estep return;
468b000b770SStanislav Gatev
469b000b770SStanislav Gatev State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
470b000b770SStanislav Gatev
471b000b770SStanislav Gatev // Assign a storage location for the whole expression.
472b000b770SStanislav Gatev State.Env.setStorageLocation(*E, *OptionalLoc);
473b000b770SStanislav Gatev }
474b000b770SStanislav Gatev
transferValueOrConversionAssignment(const CXXOperatorCallExpr * E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)475b000b770SStanislav Gatev void transferValueOrConversionAssignment(
476b000b770SStanislav Gatev const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
477b000b770SStanislav Gatev LatticeTransferState &State) {
478b000b770SStanislav Gatev assert(E->getNumArgs() > 1);
479b000b770SStanislav Gatev transferAssignment(E,
48006decd0bSKazu Hirata value_orConversionHasValue(*E->getDirectCallee(),
48106decd0bSKazu Hirata *E->getArg(1), MatchRes, State),
482b000b770SStanislav Gatev State);
483b000b770SStanislav Gatev }
484b000b770SStanislav Gatev
transferNulloptAssignment(const CXXOperatorCallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)485b000b770SStanislav Gatev void transferNulloptAssignment(const CXXOperatorCallExpr *E,
486b000b770SStanislav Gatev const MatchFinder::MatchResult &,
487b000b770SStanislav Gatev LatticeTransferState &State) {
488b000b770SStanislav Gatev transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
4899e0fc676SStanislav Gatev }
4909e0fc676SStanislav Gatev
transferSwap(const StorageLocation & OptionalLoc1,const StorageLocation & OptionalLoc2,LatticeTransferState & State)4912ddd57aeSStanislav Gatev void transferSwap(const StorageLocation &OptionalLoc1,
4922ddd57aeSStanislav Gatev const StorageLocation &OptionalLoc2,
4932ddd57aeSStanislav Gatev LatticeTransferState &State) {
4942ddd57aeSStanislav Gatev auto *OptionalVal1 = State.Env.getValue(OptionalLoc1);
4952ddd57aeSStanislav Gatev assert(OptionalVal1 != nullptr);
4962ddd57aeSStanislav Gatev
4972ddd57aeSStanislav Gatev auto *OptionalVal2 = State.Env.getValue(OptionalLoc2);
4982ddd57aeSStanislav Gatev assert(OptionalVal2 != nullptr);
4992ddd57aeSStanislav Gatev
5002ddd57aeSStanislav Gatev State.Env.setValue(OptionalLoc1, *OptionalVal2);
5012ddd57aeSStanislav Gatev State.Env.setValue(OptionalLoc2, *OptionalVal1);
5022ddd57aeSStanislav Gatev }
5032ddd57aeSStanislav Gatev
transferSwapCall(const CXXMemberCallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)5042ddd57aeSStanislav Gatev void transferSwapCall(const CXXMemberCallExpr *E,
5052ddd57aeSStanislav Gatev const MatchFinder::MatchResult &,
5062ddd57aeSStanislav Gatev LatticeTransferState &State) {
5072ddd57aeSStanislav Gatev assert(E->getNumArgs() == 1);
5082ddd57aeSStanislav Gatev
5092ddd57aeSStanislav Gatev auto *OptionalLoc1 = State.Env.getStorageLocation(
5102ddd57aeSStanislav Gatev *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer);
5112ddd57aeSStanislav Gatev assert(OptionalLoc1 != nullptr);
5122ddd57aeSStanislav Gatev
5132ddd57aeSStanislav Gatev auto *OptionalLoc2 =
5142ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
5152ddd57aeSStanislav Gatev assert(OptionalLoc2 != nullptr);
5162ddd57aeSStanislav Gatev
5172ddd57aeSStanislav Gatev transferSwap(*OptionalLoc1, *OptionalLoc2, State);
5182ddd57aeSStanislav Gatev }
5192ddd57aeSStanislav Gatev
transferStdSwapCall(const CallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)5202ddd57aeSStanislav Gatev void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
5212ddd57aeSStanislav Gatev LatticeTransferState &State) {
5222ddd57aeSStanislav Gatev assert(E->getNumArgs() == 2);
5232ddd57aeSStanislav Gatev
5242ddd57aeSStanislav Gatev auto *OptionalLoc1 =
5252ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
5262ddd57aeSStanislav Gatev assert(OptionalLoc1 != nullptr);
5272ddd57aeSStanislav Gatev
5282ddd57aeSStanislav Gatev auto *OptionalLoc2 =
5292ddd57aeSStanislav Gatev State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference);
5302ddd57aeSStanislav Gatev assert(OptionalLoc2 != nullptr);
5312ddd57aeSStanislav Gatev
5322ddd57aeSStanislav Gatev transferSwap(*OptionalLoc1, *OptionalLoc2, State);
5332ddd57aeSStanislav Gatev }
5342ddd57aeSStanislav Gatev
535a184a0d8SYitzhak Mandelbaum llvm::Optional<StatementMatcher>
ignorableOptional(const UncheckedOptionalAccessModelOptions & Options)536a184a0d8SYitzhak Mandelbaum ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
537a184a0d8SYitzhak Mandelbaum if (Options.IgnoreSmartPointerDereference)
538a184a0d8SYitzhak Mandelbaum return memberExpr(hasObjectExpression(ignoringParenImpCasts(
539a184a0d8SYitzhak Mandelbaum cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("->"),
540a184a0d8SYitzhak Mandelbaum hasOverloadedOperatorName("*")),
541a184a0d8SYitzhak Mandelbaum unless(hasArgument(0, expr(hasOptionalType())))))));
542a184a0d8SYitzhak Mandelbaum return llvm::None;
543a184a0d8SYitzhak Mandelbaum }
544a184a0d8SYitzhak Mandelbaum
54558fe7f96SSam Estep StatementMatcher
valueCall(llvm::Optional<StatementMatcher> & IgnorableOptional)54658fe7f96SSam Estep valueCall(llvm::Optional<StatementMatcher> &IgnorableOptional) {
54758fe7f96SSam Estep return isOptionalMemberCallWithName("value", IgnorableOptional);
54858fe7f96SSam Estep }
54958fe7f96SSam Estep
55058fe7f96SSam Estep StatementMatcher
valueOperatorCall(llvm::Optional<StatementMatcher> & IgnorableOptional)55158fe7f96SSam Estep valueOperatorCall(llvm::Optional<StatementMatcher> &IgnorableOptional) {
55258fe7f96SSam Estep return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
55358fe7f96SSam Estep isOptionalOperatorCallWithName("->", IgnorableOptional)));
55458fe7f96SSam Estep }
55558fe7f96SSam Estep
buildTransferMatchSwitch(const UncheckedOptionalAccessModelOptions & Options)556a184a0d8SYitzhak Mandelbaum auto buildTransferMatchSwitch(
557a184a0d8SYitzhak Mandelbaum const UncheckedOptionalAccessModelOptions &Options) {
558b000b770SStanislav Gatev // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
559b000b770SStanislav Gatev // lot of duplicated work (e.g. string comparisons), consider providing APIs
560b000b770SStanislav Gatev // that avoid it through memoization.
561a184a0d8SYitzhak Mandelbaum auto IgnorableOptional = ignorableOptional(Options);
562af98b0afSStanislav Gatev return MatchSwitchBuilder<LatticeTransferState>()
563af98b0afSStanislav Gatev // Attach a symbolic "has_value" state to optional values that we see for
564af98b0afSStanislav Gatev // the first time.
5656adfc64eSYitzhak Mandelbaum .CaseOf<Expr>(
5666adfc64eSYitzhak Mandelbaum expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
567af98b0afSStanislav Gatev initializeOptionalReference)
568af98b0afSStanislav Gatev
5699e0fc676SStanislav Gatev // make_optional
570092a530cSStanislav Gatev .CaseOf<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
571092a530cSStanislav Gatev
572b000b770SStanislav Gatev // optional::optional
573092a530cSStanislav Gatev .CaseOf<CXXConstructExpr>(
574092a530cSStanislav Gatev isOptionalInPlaceConstructor(),
575092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
576092a530cSStanislav Gatev LatticeTransferState &State) {
577092a530cSStanislav Gatev assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true));
578092a530cSStanislav Gatev })
579092a530cSStanislav Gatev .CaseOf<CXXConstructExpr>(
580092a530cSStanislav Gatev isOptionalNulloptConstructor(),
581092a530cSStanislav Gatev [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
582092a530cSStanislav Gatev LatticeTransferState &State) {
583092a530cSStanislav Gatev assignOptionalValue(*E, State,
584092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false));
585092a530cSStanislav Gatev })
586092a530cSStanislav Gatev .CaseOf<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
587092a530cSStanislav Gatev transferValueOrConversionConstructor)
5889e0fc676SStanislav Gatev
589b000b770SStanislav Gatev // optional::operator=
590b000b770SStanislav Gatev .CaseOf<CXXOperatorCallExpr>(isOptionalValueOrConversionAssignment(),
591b000b770SStanislav Gatev transferValueOrConversionAssignment)
592b000b770SStanislav Gatev .CaseOf<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
593b000b770SStanislav Gatev transferNulloptAssignment)
594b000b770SStanislav Gatev
595af98b0afSStanislav Gatev // optional::value
596092a530cSStanislav Gatev .CaseOf<CXXMemberCallExpr>(
59758fe7f96SSam Estep valueCall(IgnorableOptional),
598092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
599092a530cSStanislav Gatev LatticeTransferState &State) {
600af98b0afSStanislav Gatev transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
601af98b0afSStanislav Gatev })
602af98b0afSStanislav Gatev
603af98b0afSStanislav Gatev // optional::operator*, optional::operator->
60458fe7f96SSam Estep .CaseOf<CallExpr>(valueOperatorCall(IgnorableOptional),
605092a530cSStanislav Gatev [](const CallExpr *E, const MatchFinder::MatchResult &,
606092a530cSStanislav Gatev LatticeTransferState &State) {
607af98b0afSStanislav Gatev transferUnwrapCall(E, E->getArg(0), State);
608af98b0afSStanislav Gatev })
609af98b0afSStanislav Gatev
610af98b0afSStanislav Gatev // optional::has_value
611092a530cSStanislav Gatev .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("has_value"),
612af98b0afSStanislav Gatev transferOptionalHasValueCall)
613af98b0afSStanislav Gatev
6149e0fc676SStanislav Gatev // optional::operator bool
615092a530cSStanislav Gatev .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("operator bool"),
6169e0fc676SStanislav Gatev transferOptionalHasValueCall)
6179e0fc676SStanislav Gatev
6189e0fc676SStanislav Gatev // optional::emplace
619092a530cSStanislav Gatev .CaseOf<CXXMemberCallExpr>(
620092a530cSStanislav Gatev isOptionalMemberCallWithName("emplace"),
621092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
622092a530cSStanislav Gatev LatticeTransferState &State) {
623092a530cSStanislav Gatev assignOptionalValue(*E->getImplicitObjectArgument(), State,
624092a530cSStanislav Gatev State.Env.getBoolLiteralValue(true));
625092a530cSStanislav Gatev })
6269e0fc676SStanislav Gatev
6279e0fc676SStanislav Gatev // optional::reset
628092a530cSStanislav Gatev .CaseOf<CXXMemberCallExpr>(
629092a530cSStanislav Gatev isOptionalMemberCallWithName("reset"),
630092a530cSStanislav Gatev [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
631092a530cSStanislav Gatev LatticeTransferState &State) {
632092a530cSStanislav Gatev assignOptionalValue(*E->getImplicitObjectArgument(), State,
633092a530cSStanislav Gatev State.Env.getBoolLiteralValue(false));
634092a530cSStanislav Gatev })
6359e0fc676SStanislav Gatev
6362ddd57aeSStanislav Gatev // optional::swap
6372ddd57aeSStanislav Gatev .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
6382ddd57aeSStanislav Gatev transferSwapCall)
6392ddd57aeSStanislav Gatev
6402ddd57aeSStanislav Gatev // std::swap
6412ddd57aeSStanislav Gatev .CaseOf<CallExpr>(isStdSwapCall(), transferStdSwapCall)
6422ddd57aeSStanislav Gatev
6437f076004SYitzhak Mandelbaum // opt.value_or("").empty()
6447f076004SYitzhak Mandelbaum .CaseOf<Expr>(isValueOrStringEmptyCall(), transferValueOrStringEmptyCall)
6457f076004SYitzhak Mandelbaum
6467f076004SYitzhak Mandelbaum // opt.value_or(X) != X
6477f076004SYitzhak Mandelbaum .CaseOf<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
6487f076004SYitzhak Mandelbaum
64965e710c3SStanislav Gatev // returns optional
65065e710c3SStanislav Gatev .CaseOf<CallExpr>(isCallReturningOptional(),
65165e710c3SStanislav Gatev transferCallReturningOptional)
65265e710c3SStanislav Gatev
653af98b0afSStanislav Gatev .Build();
654af98b0afSStanislav Gatev }
655af98b0afSStanislav Gatev
diagnoseUnwrapCall(const Expr * UnwrapExpr,const Expr * ObjectExpr,const Environment & Env)65658fe7f96SSam Estep std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr,
65758fe7f96SSam Estep const Expr *ObjectExpr,
65858fe7f96SSam Estep const Environment &Env) {
65958fe7f96SSam Estep if (auto *OptionalVal =
66058fe7f96SSam Estep Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
66158fe7f96SSam Estep auto *Prop = OptionalVal->getProperty("has_value");
66258fe7f96SSam Estep if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
66358fe7f96SSam Estep if (Env.flowConditionImplies(*HasValueVal))
66458fe7f96SSam Estep return {};
66558fe7f96SSam Estep }
66658fe7f96SSam Estep }
66758fe7f96SSam Estep
66858fe7f96SSam Estep // Record that this unwrap is *not* provably safe.
66958fe7f96SSam Estep // FIXME: include either the name of the optional (if applicable) or a source
67058fe7f96SSam Estep // range of the access for easier interpretation of the result.
67158fe7f96SSam Estep return {ObjectExpr->getBeginLoc()};
67258fe7f96SSam Estep }
67358fe7f96SSam Estep
buildDiagnoseMatchSwitch(const UncheckedOptionalAccessModelOptions & Options)67458fe7f96SSam Estep auto buildDiagnoseMatchSwitch(
67558fe7f96SSam Estep const UncheckedOptionalAccessModelOptions &Options) {
67658fe7f96SSam Estep // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
67758fe7f96SSam Estep // lot of duplicated work (e.g. string comparisons), consider providing APIs
67858fe7f96SSam Estep // that avoid it through memoization.
67958fe7f96SSam Estep auto IgnorableOptional = ignorableOptional(Options);
68058fe7f96SSam Estep return MatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
68158fe7f96SSam Estep // optional::value
68258fe7f96SSam Estep .CaseOf<CXXMemberCallExpr>(
68358fe7f96SSam Estep valueCall(IgnorableOptional),
68458fe7f96SSam Estep [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
68558fe7f96SSam Estep const Environment &Env) {
68658fe7f96SSam Estep return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env);
68758fe7f96SSam Estep })
68858fe7f96SSam Estep
68958fe7f96SSam Estep // optional::operator*, optional::operator->
69058fe7f96SSam Estep .CaseOf<CallExpr>(
69158fe7f96SSam Estep valueOperatorCall(IgnorableOptional),
69258fe7f96SSam Estep [](const CallExpr *E, const MatchFinder::MatchResult &,
69358fe7f96SSam Estep const Environment &Env) {
69458fe7f96SSam Estep return diagnoseUnwrapCall(E, E->getArg(0), Env);
69558fe7f96SSam Estep })
69658fe7f96SSam Estep .Build();
69758fe7f96SSam Estep }
69858fe7f96SSam Estep
699af98b0afSStanislav Gatev } // namespace
700af98b0afSStanislav Gatev
7017e63a0d4SYitzhak Mandelbaum ast_matchers::DeclarationMatcher
optionalClassDecl()7027e63a0d4SYitzhak Mandelbaum UncheckedOptionalAccessModel::optionalClassDecl() {
7037e63a0d4SYitzhak Mandelbaum return optionalClass();
7047e63a0d4SYitzhak Mandelbaum }
7057e63a0d4SYitzhak Mandelbaum
UncheckedOptionalAccessModel(ASTContext & Ctx,UncheckedOptionalAccessModelOptions Options)706a184a0d8SYitzhak Mandelbaum UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(
707a184a0d8SYitzhak Mandelbaum ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options)
708*cf1f978dSSam Estep : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
709a184a0d8SYitzhak Mandelbaum TransferMatchSwitch(buildTransferMatchSwitch(Options)) {}
710af98b0afSStanislav Gatev
transfer(const Stmt * S,NoopLattice & L,Environment & Env)711*cf1f978dSSam Estep void UncheckedOptionalAccessModel::transfer(const Stmt *S, NoopLattice &L,
712af98b0afSStanislav Gatev Environment &Env) {
713af98b0afSStanislav Gatev LatticeTransferState State(L, Env);
714af98b0afSStanislav Gatev TransferMatchSwitch(*S, getASTContext(), State);
715af98b0afSStanislav Gatev }
716af98b0afSStanislav Gatev
compareEquivalent(QualType Type,const Value & Val1,const Environment & Env1,const Value & Val2,const Environment & Env2)7178fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::compareEquivalent(QualType Type,
7188fcdd625SStanislav Gatev const Value &Val1,
7198fcdd625SStanislav Gatev const Environment &Env1,
7208fcdd625SStanislav Gatev const Value &Val2,
7218fcdd625SStanislav Gatev const Environment &Env2) {
7228fcdd625SStanislav Gatev return isNonEmptyOptional(Val1, Env1) == isNonEmptyOptional(Val2, Env2);
7238fcdd625SStanislav Gatev }
7248fcdd625SStanislav Gatev
merge(QualType Type,const Value & Val1,const Environment & Env1,const Value & Val2,const Environment & Env2,Value & MergedVal,Environment & MergedEnv)7258fcdd625SStanislav Gatev bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
7268fcdd625SStanislav Gatev const Environment &Env1,
7278fcdd625SStanislav Gatev const Value &Val2,
7288fcdd625SStanislav Gatev const Environment &Env2,
7298fcdd625SStanislav Gatev Value &MergedVal,
7308fcdd625SStanislav Gatev Environment &MergedEnv) {
7318fcdd625SStanislav Gatev if (!IsOptionalType(Type))
7328fcdd625SStanislav Gatev return true;
7338fcdd625SStanislav Gatev
7348fcdd625SStanislav Gatev auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
7358fcdd625SStanislav Gatev if (isNonEmptyOptional(Val1, Env1) && isNonEmptyOptional(Val2, Env2))
7368fcdd625SStanislav Gatev MergedEnv.addToFlowCondition(HasValueVal);
7378fcdd625SStanislav Gatev else if (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
7388fcdd625SStanislav Gatev MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal));
7398fcdd625SStanislav Gatev setHasValue(MergedVal, HasValueVal);
7408fcdd625SStanislav Gatev return true;
7418fcdd625SStanislav Gatev }
7428fcdd625SStanislav Gatev
UncheckedOptionalAccessDiagnoser(UncheckedOptionalAccessModelOptions Options)74358fe7f96SSam Estep UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
74458fe7f96SSam Estep UncheckedOptionalAccessModelOptions Options)
74558fe7f96SSam Estep : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
74658fe7f96SSam Estep
diagnose(ASTContext & Context,const Stmt * Stmt,const Environment & Env)74758fe7f96SSam Estep std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose(
74858fe7f96SSam Estep ASTContext &Context, const Stmt *Stmt, const Environment &Env) {
74958fe7f96SSam Estep return DiagnoseMatchSwitch(*Stmt, Context, Env);
75058fe7f96SSam Estep }
75158fe7f96SSam Estep
752af98b0afSStanislav Gatev } // namespace dataflow
753af98b0afSStanislav Gatev } // namespace clang
754