1*12c85518Srobert //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//
2*12c85518Srobert //
3*12c85518Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*12c85518Srobert // See https://llvm.org/LICENSE.txt for license information.
5*12c85518Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*12c85518Srobert //
7*12c85518Srobert //===----------------------------------------------------------------------===//
8*12c85518Srobert //
9*12c85518Srobert // This file defines a dataflow analysis that detects unsafe uses of optional
10*12c85518Srobert // values.
11*12c85518Srobert //
12*12c85518Srobert //===----------------------------------------------------------------------===//
13*12c85518Srobert
14*12c85518Srobert #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
15*12c85518Srobert #include "clang/AST/ASTContext.h"
16*12c85518Srobert #include "clang/AST/DeclCXX.h"
17*12c85518Srobert #include "clang/AST/Expr.h"
18*12c85518Srobert #include "clang/AST/ExprCXX.h"
19*12c85518Srobert #include "clang/AST/Stmt.h"
20*12c85518Srobert #include "clang/ASTMatchers/ASTMatchers.h"
21*12c85518Srobert #include "clang/Analysis/CFG.h"
22*12c85518Srobert #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
23*12c85518Srobert #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
24*12c85518Srobert #include "clang/Analysis/FlowSensitive/NoopLattice.h"
25*12c85518Srobert #include "clang/Analysis/FlowSensitive/StorageLocation.h"
26*12c85518Srobert #include "clang/Analysis/FlowSensitive/Value.h"
27*12c85518Srobert #include "clang/Basic/SourceLocation.h"
28*12c85518Srobert #include "llvm/ADT/StringRef.h"
29*12c85518Srobert #include "llvm/Support/Casting.h"
30*12c85518Srobert #include "llvm/Support/ErrorHandling.h"
31*12c85518Srobert #include <cassert>
32*12c85518Srobert #include <memory>
33*12c85518Srobert #include <optional>
34*12c85518Srobert #include <utility>
35*12c85518Srobert #include <vector>
36*12c85518Srobert
37*12c85518Srobert namespace clang {
38*12c85518Srobert namespace dataflow {
39*12c85518Srobert namespace {
40*12c85518Srobert
41*12c85518Srobert using namespace ::clang::ast_matchers;
42*12c85518Srobert using LatticeTransferState = TransferState<NoopLattice>;
43*12c85518Srobert
optionalClass()44*12c85518Srobert DeclarationMatcher optionalClass() {
45*12c85518Srobert return classTemplateSpecializationDecl(
46*12c85518Srobert anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
47*12c85518Srobert hasName("__optional_destruct_base"), hasName("absl::optional"),
48*12c85518Srobert hasName("base::Optional")),
49*12c85518Srobert hasTemplateArgument(0, refersToType(type().bind("T"))));
50*12c85518Srobert }
51*12c85518Srobert
optionalOrAliasType()52*12c85518Srobert auto optionalOrAliasType() {
53*12c85518Srobert return hasUnqualifiedDesugaredType(
54*12c85518Srobert recordType(hasDeclaration(optionalClass())));
55*12c85518Srobert }
56*12c85518Srobert
57*12c85518Srobert /// Matches any of the spellings of the optional types and sugar, aliases, etc.
hasOptionalType()58*12c85518Srobert auto hasOptionalType() { return hasType(optionalOrAliasType()); }
59*12c85518Srobert
isOptionalMemberCallWithName(llvm::StringRef MemberName,const std::optional<StatementMatcher> & Ignorable=std::nullopt)60*12c85518Srobert auto isOptionalMemberCallWithName(
61*12c85518Srobert llvm::StringRef MemberName,
62*12c85518Srobert const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
63*12c85518Srobert auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr()))
64*12c85518Srobert : cxxThisExpr());
65*12c85518Srobert return cxxMemberCallExpr(
66*12c85518Srobert on(expr(Exception)),
67*12c85518Srobert callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
68*12c85518Srobert }
69*12c85518Srobert
isOptionalOperatorCallWithName(llvm::StringRef operator_name,const std::optional<StatementMatcher> & Ignorable=std::nullopt)70*12c85518Srobert auto isOptionalOperatorCallWithName(
71*12c85518Srobert llvm::StringRef operator_name,
72*12c85518Srobert const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
73*12c85518Srobert return cxxOperatorCallExpr(
74*12c85518Srobert hasOverloadedOperatorName(operator_name),
75*12c85518Srobert callee(cxxMethodDecl(ofClass(optionalClass()))),
76*12c85518Srobert Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
77*12c85518Srobert }
78*12c85518Srobert
isMakeOptionalCall()79*12c85518Srobert auto isMakeOptionalCall() {
80*12c85518Srobert return callExpr(
81*12c85518Srobert callee(functionDecl(hasAnyName(
82*12c85518Srobert "std::make_optional", "base::make_optional", "absl::make_optional"))),
83*12c85518Srobert hasOptionalType());
84*12c85518Srobert }
85*12c85518Srobert
nulloptTypeDecl()86*12c85518Srobert auto nulloptTypeDecl() {
87*12c85518Srobert return namedDecl(
88*12c85518Srobert hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t"));
89*12c85518Srobert }
90*12c85518Srobert
hasNulloptType()91*12c85518Srobert auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
92*12c85518Srobert
93*12c85518Srobert // `optional` or `nullopt_t`
hasAnyOptionalType()94*12c85518Srobert auto hasAnyOptionalType() {
95*12c85518Srobert return hasType(hasUnqualifiedDesugaredType(
96*12c85518Srobert recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass())))));
97*12c85518Srobert }
98*12c85518Srobert
99*12c85518Srobert
inPlaceClass()100*12c85518Srobert auto inPlaceClass() {
101*12c85518Srobert return recordDecl(
102*12c85518Srobert hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
103*12c85518Srobert }
104*12c85518Srobert
isOptionalNulloptConstructor()105*12c85518Srobert auto isOptionalNulloptConstructor() {
106*12c85518Srobert return cxxConstructExpr(
107*12c85518Srobert hasOptionalType(),
108*12c85518Srobert hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
109*12c85518Srobert hasParameter(0, hasNulloptType()))));
110*12c85518Srobert }
111*12c85518Srobert
isOptionalInPlaceConstructor()112*12c85518Srobert auto isOptionalInPlaceConstructor() {
113*12c85518Srobert return cxxConstructExpr(hasOptionalType(),
114*12c85518Srobert hasArgument(0, hasType(inPlaceClass())));
115*12c85518Srobert }
116*12c85518Srobert
isOptionalValueOrConversionConstructor()117*12c85518Srobert auto isOptionalValueOrConversionConstructor() {
118*12c85518Srobert return cxxConstructExpr(
119*12c85518Srobert hasOptionalType(),
120*12c85518Srobert unless(hasDeclaration(
121*12c85518Srobert cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
122*12c85518Srobert argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
123*12c85518Srobert }
124*12c85518Srobert
isOptionalValueOrConversionAssignment()125*12c85518Srobert auto isOptionalValueOrConversionAssignment() {
126*12c85518Srobert return cxxOperatorCallExpr(
127*12c85518Srobert hasOverloadedOperatorName("="),
128*12c85518Srobert callee(cxxMethodDecl(ofClass(optionalClass()))),
129*12c85518Srobert unless(hasDeclaration(cxxMethodDecl(
130*12c85518Srobert anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
131*12c85518Srobert argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
132*12c85518Srobert }
133*12c85518Srobert
isNulloptConstructor()134*12c85518Srobert auto isNulloptConstructor() {
135*12c85518Srobert return cxxConstructExpr(hasNulloptType(), argumentCountIs(1),
136*12c85518Srobert hasArgument(0, hasNulloptType()));
137*12c85518Srobert }
138*12c85518Srobert
isOptionalNulloptAssignment()139*12c85518Srobert auto isOptionalNulloptAssignment() {
140*12c85518Srobert return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
141*12c85518Srobert callee(cxxMethodDecl(ofClass(optionalClass()))),
142*12c85518Srobert argumentCountIs(2),
143*12c85518Srobert hasArgument(1, hasNulloptType()));
144*12c85518Srobert }
145*12c85518Srobert
isStdSwapCall()146*12c85518Srobert auto isStdSwapCall() {
147*12c85518Srobert return callExpr(callee(functionDecl(hasName("std::swap"))),
148*12c85518Srobert argumentCountIs(2), hasArgument(0, hasOptionalType()),
149*12c85518Srobert hasArgument(1, hasOptionalType()));
150*12c85518Srobert }
151*12c85518Srobert
152*12c85518Srobert constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
153*12c85518Srobert
isValueOrStringEmptyCall()154*12c85518Srobert auto isValueOrStringEmptyCall() {
155*12c85518Srobert // `opt.value_or("").empty()`
156*12c85518Srobert return cxxMemberCallExpr(
157*12c85518Srobert callee(cxxMethodDecl(hasName("empty"))),
158*12c85518Srobert onImplicitObjectArgument(ignoringImplicit(
159*12c85518Srobert cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
160*12c85518Srobert callee(cxxMethodDecl(hasName("value_or"),
161*12c85518Srobert ofClass(optionalClass()))),
162*12c85518Srobert hasArgument(0, stringLiteral(hasSize(0))))
163*12c85518Srobert .bind(ValueOrCallID))));
164*12c85518Srobert }
165*12c85518Srobert
isValueOrNotEqX()166*12c85518Srobert auto isValueOrNotEqX() {
167*12c85518Srobert auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
168*12c85518Srobert return hasOperands(
169*12c85518Srobert ignoringImplicit(
170*12c85518Srobert cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
171*12c85518Srobert callee(cxxMethodDecl(hasName("value_or"),
172*12c85518Srobert ofClass(optionalClass()))),
173*12c85518Srobert hasArgument(0, Arg))
174*12c85518Srobert .bind(ValueOrCallID)),
175*12c85518Srobert ignoringImplicit(Arg));
176*12c85518Srobert };
177*12c85518Srobert
178*12c85518Srobert // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
179*12c85518Srobert // support this pattern for any expression, but the AST does not have a
180*12c85518Srobert // generic expression comparison facility, so we specialize to common cases
181*12c85518Srobert // seen in practice. FIXME: define a matcher that compares values across
182*12c85518Srobert // nodes, which would let us generalize this to any `X`.
183*12c85518Srobert return binaryOperation(hasOperatorName("!="),
184*12c85518Srobert anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
185*12c85518Srobert ComparesToSame(stringLiteral(hasSize(0))),
186*12c85518Srobert ComparesToSame(integerLiteral(equals(0)))));
187*12c85518Srobert }
188*12c85518Srobert
isCallReturningOptional()189*12c85518Srobert auto isCallReturningOptional() {
190*12c85518Srobert return callExpr(hasType(qualType(anyOf(
191*12c85518Srobert optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
192*12c85518Srobert }
193*12c85518Srobert
194*12c85518Srobert template <typename L, typename R>
isComparisonOperatorCall(L lhs_arg_matcher,R rhs_arg_matcher)195*12c85518Srobert auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
196*12c85518Srobert return cxxOperatorCallExpr(
197*12c85518Srobert anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
198*12c85518Srobert argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
199*12c85518Srobert hasArgument(1, rhs_arg_matcher));
200*12c85518Srobert }
201*12c85518Srobert
202*12c85518Srobert // Ensures that `Expr` is mapped to a `BoolValue` and returns it.
forceBoolValue(Environment & Env,const Expr & Expr)203*12c85518Srobert BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) {
204*12c85518Srobert auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None));
205*12c85518Srobert if (Value != nullptr)
206*12c85518Srobert return *Value;
207*12c85518Srobert
208*12c85518Srobert auto &Loc = Env.createStorageLocation(Expr);
209*12c85518Srobert Value = &Env.makeAtomicBoolValue();
210*12c85518Srobert Env.setValue(Loc, *Value);
211*12c85518Srobert Env.setStorageLocation(Expr, Loc);
212*12c85518Srobert return *Value;
213*12c85518Srobert }
214*12c85518Srobert
215*12c85518Srobert /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
216*12c85518Srobert /// property of the optional value `OptionalVal`.
setHasValue(Value & OptionalVal,BoolValue & HasValueVal)217*12c85518Srobert void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
218*12c85518Srobert OptionalVal.setProperty("has_value", HasValueVal);
219*12c85518Srobert }
220*12c85518Srobert
221*12c85518Srobert /// Creates a symbolic value for an `optional` value using `HasValueVal` as the
222*12c85518Srobert /// symbolic value of its "has_value" property.
createOptionalValue(Environment & Env,BoolValue & HasValueVal)223*12c85518Srobert StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
224*12c85518Srobert auto OptionalVal = std::make_unique<StructValue>();
225*12c85518Srobert setHasValue(*OptionalVal, HasValueVal);
226*12c85518Srobert return Env.takeOwnership(std::move(OptionalVal));
227*12c85518Srobert }
228*12c85518Srobert
229*12c85518Srobert /// Returns the symbolic value that represents the "has_value" property of the
230*12c85518Srobert /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
getHasValue(Environment & Env,Value * OptionalVal)231*12c85518Srobert BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
232*12c85518Srobert if (OptionalVal != nullptr) {
233*12c85518Srobert auto *HasValueVal =
234*12c85518Srobert cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
235*12c85518Srobert if (HasValueVal == nullptr) {
236*12c85518Srobert HasValueVal = &Env.makeAtomicBoolValue();
237*12c85518Srobert OptionalVal->setProperty("has_value", *HasValueVal);
238*12c85518Srobert }
239*12c85518Srobert return HasValueVal;
240*12c85518Srobert }
241*12c85518Srobert return nullptr;
242*12c85518Srobert }
243*12c85518Srobert
244*12c85518Srobert /// If `Type` is a reference type, returns the type of its pointee. Otherwise,
245*12c85518Srobert /// returns `Type` itself.
stripReference(QualType Type)246*12c85518Srobert QualType stripReference(QualType Type) {
247*12c85518Srobert return Type->isReferenceType() ? Type->getPointeeType() : Type;
248*12c85518Srobert }
249*12c85518Srobert
250*12c85518Srobert /// Returns true if and only if `Type` is an optional type.
isOptionalType(QualType Type)251*12c85518Srobert bool isOptionalType(QualType Type) {
252*12c85518Srobert if (!Type->isRecordType())
253*12c85518Srobert return false;
254*12c85518Srobert // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call.
255*12c85518Srobert auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString();
256*12c85518Srobert return TypeName == "std::optional" || TypeName == "absl::optional" ||
257*12c85518Srobert TypeName == "base::Optional";
258*12c85518Srobert }
259*12c85518Srobert
260*12c85518Srobert /// Returns the number of optional wrappers in `Type`.
261*12c85518Srobert ///
262*12c85518Srobert /// For example, if `Type` is `optional<optional<int>>`, the result of this
263*12c85518Srobert /// function will be 2.
countOptionalWrappers(const ASTContext & ASTCtx,QualType Type)264*12c85518Srobert int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
265*12c85518Srobert if (!isOptionalType(Type))
266*12c85518Srobert return 0;
267*12c85518Srobert return 1 + countOptionalWrappers(
268*12c85518Srobert ASTCtx,
269*12c85518Srobert cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
270*12c85518Srobert ->getTemplateArgs()
271*12c85518Srobert .get(0)
272*12c85518Srobert .getAsType()
273*12c85518Srobert .getDesugaredType(ASTCtx));
274*12c85518Srobert }
275*12c85518Srobert
276*12c85518Srobert /// Tries to initialize the `optional`'s value (that is, contents), and return
277*12c85518Srobert /// its location. Returns nullptr if the value can't be represented.
maybeInitializeOptionalValueMember(QualType Q,Value & OptionalVal,Environment & Env)278*12c85518Srobert StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
279*12c85518Srobert Value &OptionalVal,
280*12c85518Srobert Environment &Env) {
281*12c85518Srobert // The "value" property represents a synthetic field. As such, it needs
282*12c85518Srobert // `StorageLocation`, like normal fields (and other variables). So, we model
283*12c85518Srobert // it with a `ReferenceValue`, since that includes a storage location. Once
284*12c85518Srobert // the property is set, it will be shared by all environments that access the
285*12c85518Srobert // `Value` representing the optional (here, `OptionalVal`).
286*12c85518Srobert if (auto *ValueProp = OptionalVal.getProperty("value")) {
287*12c85518Srobert auto *ValueRef = clang::cast<ReferenceValue>(ValueProp);
288*12c85518Srobert auto &ValueLoc = ValueRef->getReferentLoc();
289*12c85518Srobert if (Env.getValue(ValueLoc) == nullptr) {
290*12c85518Srobert // The property was previously set, but the value has been lost. This can
291*12c85518Srobert // happen, for example, because of an environment merge (where the two
292*12c85518Srobert // environments mapped the property to different values, which resulted in
293*12c85518Srobert // them both being discarded), or when two blocks in the CFG, with neither
294*12c85518Srobert // a dominator of the other, visit the same optional value, or even when a
295*12c85518Srobert // block is revisited during testing to collect per-statement state.
296*12c85518Srobert // FIXME: This situation means that the optional contents are not shared
297*12c85518Srobert // between branches and the like. Practically, this lack of sharing
298*12c85518Srobert // reduces the precision of the model when the contents are relevant to
299*12c85518Srobert // the check, like another optional or a boolean that influences control
300*12c85518Srobert // flow.
301*12c85518Srobert auto *ValueVal = Env.createValue(ValueLoc.getType());
302*12c85518Srobert if (ValueVal == nullptr)
303*12c85518Srobert return nullptr;
304*12c85518Srobert Env.setValue(ValueLoc, *ValueVal);
305*12c85518Srobert }
306*12c85518Srobert return &ValueLoc;
307*12c85518Srobert }
308*12c85518Srobert
309*12c85518Srobert auto Ty = stripReference(Q);
310*12c85518Srobert auto *ValueVal = Env.createValue(Ty);
311*12c85518Srobert if (ValueVal == nullptr)
312*12c85518Srobert return nullptr;
313*12c85518Srobert auto &ValueLoc = Env.createStorageLocation(Ty);
314*12c85518Srobert Env.setValue(ValueLoc, *ValueVal);
315*12c85518Srobert auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc);
316*12c85518Srobert OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef)));
317*12c85518Srobert return &ValueLoc;
318*12c85518Srobert }
319*12c85518Srobert
initializeOptionalReference(const Expr * OptionalExpr,const MatchFinder::MatchResult &,LatticeTransferState & State)320*12c85518Srobert void initializeOptionalReference(const Expr *OptionalExpr,
321*12c85518Srobert const MatchFinder::MatchResult &,
322*12c85518Srobert LatticeTransferState &State) {
323*12c85518Srobert if (auto *OptionalVal =
324*12c85518Srobert State.Env.getValue(*OptionalExpr, SkipPast::Reference)) {
325*12c85518Srobert if (OptionalVal->getProperty("has_value") == nullptr) {
326*12c85518Srobert setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
327*12c85518Srobert }
328*12c85518Srobert }
329*12c85518Srobert }
330*12c85518Srobert
331*12c85518Srobert /// Returns true if and only if `OptionalVal` is initialized and known to be
332*12c85518Srobert /// empty in `Env.
isEmptyOptional(const Value & OptionalVal,const Environment & Env)333*12c85518Srobert bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
334*12c85518Srobert auto *HasValueVal =
335*12c85518Srobert cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
336*12c85518Srobert return HasValueVal != nullptr &&
337*12c85518Srobert Env.flowConditionImplies(Env.makeNot(*HasValueVal));
338*12c85518Srobert }
339*12c85518Srobert
340*12c85518Srobert /// Returns true if and only if `OptionalVal` is initialized and known to be
341*12c85518Srobert /// non-empty in `Env.
isNonEmptyOptional(const Value & OptionalVal,const Environment & Env)342*12c85518Srobert bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
343*12c85518Srobert auto *HasValueVal =
344*12c85518Srobert cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
345*12c85518Srobert return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal);
346*12c85518Srobert }
347*12c85518Srobert
transferUnwrapCall(const Expr * UnwrapExpr,const Expr * ObjectExpr,LatticeTransferState & State)348*12c85518Srobert void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
349*12c85518Srobert LatticeTransferState &State) {
350*12c85518Srobert if (auto *OptionalVal =
351*12c85518Srobert State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
352*12c85518Srobert if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
353*12c85518Srobert if (auto *Loc = maybeInitializeOptionalValueMember(
354*12c85518Srobert UnwrapExpr->getType(), *OptionalVal, State.Env))
355*12c85518Srobert State.Env.setStorageLocation(*UnwrapExpr, *Loc);
356*12c85518Srobert }
357*12c85518Srobert }
358*12c85518Srobert
transferMakeOptionalCall(const CallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)359*12c85518Srobert void transferMakeOptionalCall(const CallExpr *E,
360*12c85518Srobert const MatchFinder::MatchResult &,
361*12c85518Srobert LatticeTransferState &State) {
362*12c85518Srobert auto &Loc = State.Env.createStorageLocation(*E);
363*12c85518Srobert State.Env.setStorageLocation(*E, Loc);
364*12c85518Srobert State.Env.setValue(
365*12c85518Srobert Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
366*12c85518Srobert }
367*12c85518Srobert
transferOptionalHasValueCall(const CXXMemberCallExpr * CallExpr,const MatchFinder::MatchResult &,LatticeTransferState & State)368*12c85518Srobert void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
369*12c85518Srobert const MatchFinder::MatchResult &,
370*12c85518Srobert LatticeTransferState &State) {
371*12c85518Srobert if (auto *HasValueVal = getHasValue(
372*12c85518Srobert State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
373*12c85518Srobert SkipPast::ReferenceThenPointer))) {
374*12c85518Srobert auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
375*12c85518Srobert State.Env.setValue(CallExprLoc, *HasValueVal);
376*12c85518Srobert State.Env.setStorageLocation(*CallExpr, CallExprLoc);
377*12c85518Srobert }
378*12c85518Srobert }
379*12c85518Srobert
380*12c85518Srobert /// `ModelPred` builds a logical formula relating the predicate in
381*12c85518Srobert /// `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))382*12c85518Srobert void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
383*12c85518Srobert const MatchFinder::MatchResult &Result,
384*12c85518Srobert LatticeTransferState &State,
385*12c85518Srobert BoolValue &(*ModelPred)(Environment &Env,
386*12c85518Srobert BoolValue &ExprVal,
387*12c85518Srobert BoolValue &HasValueVal)) {
388*12c85518Srobert auto &Env = State.Env;
389*12c85518Srobert
390*12c85518Srobert const auto *ObjectArgumentExpr =
391*12c85518Srobert Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
392*12c85518Srobert ->getImplicitObjectArgument();
393*12c85518Srobert
394*12c85518Srobert auto *HasValueVal = getHasValue(
395*12c85518Srobert State.Env,
396*12c85518Srobert State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
397*12c85518Srobert if (HasValueVal == nullptr)
398*12c85518Srobert return;
399*12c85518Srobert
400*12c85518Srobert Env.addToFlowCondition(
401*12c85518Srobert ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal));
402*12c85518Srobert }
403*12c85518Srobert
transferValueOrStringEmptyCall(const clang::Expr * ComparisonExpr,const MatchFinder::MatchResult & Result,LatticeTransferState & State)404*12c85518Srobert void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
405*12c85518Srobert const MatchFinder::MatchResult &Result,
406*12c85518Srobert LatticeTransferState &State) {
407*12c85518Srobert return transferValueOrImpl(ComparisonExpr, Result, State,
408*12c85518Srobert [](Environment &Env, BoolValue &ExprVal,
409*12c85518Srobert BoolValue &HasValueVal) -> BoolValue & {
410*12c85518Srobert // If the result is *not* empty, then we know the
411*12c85518Srobert // optional must have been holding a value. If
412*12c85518Srobert // `ExprVal` is true, though, we don't learn
413*12c85518Srobert // anything definite about `has_value`, so we
414*12c85518Srobert // don't add any corresponding implications to
415*12c85518Srobert // the flow condition.
416*12c85518Srobert return Env.makeImplication(Env.makeNot(ExprVal),
417*12c85518Srobert HasValueVal);
418*12c85518Srobert });
419*12c85518Srobert }
420*12c85518Srobert
transferValueOrNotEqX(const Expr * ComparisonExpr,const MatchFinder::MatchResult & Result,LatticeTransferState & State)421*12c85518Srobert void transferValueOrNotEqX(const Expr *ComparisonExpr,
422*12c85518Srobert const MatchFinder::MatchResult &Result,
423*12c85518Srobert LatticeTransferState &State) {
424*12c85518Srobert transferValueOrImpl(ComparisonExpr, Result, State,
425*12c85518Srobert [](Environment &Env, BoolValue &ExprVal,
426*12c85518Srobert BoolValue &HasValueVal) -> BoolValue & {
427*12c85518Srobert // We know that if `(opt.value_or(X) != X)` then
428*12c85518Srobert // `opt.hasValue()`, even without knowing further
429*12c85518Srobert // details about the contents of `opt`.
430*12c85518Srobert return Env.makeImplication(ExprVal, HasValueVal);
431*12c85518Srobert });
432*12c85518Srobert }
433*12c85518Srobert
transferCallReturningOptional(const CallExpr * E,const MatchFinder::MatchResult & Result,LatticeTransferState & State)434*12c85518Srobert void transferCallReturningOptional(const CallExpr *E,
435*12c85518Srobert const MatchFinder::MatchResult &Result,
436*12c85518Srobert LatticeTransferState &State) {
437*12c85518Srobert if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
438*12c85518Srobert return;
439*12c85518Srobert
440*12c85518Srobert auto &Loc = State.Env.createStorageLocation(*E);
441*12c85518Srobert State.Env.setStorageLocation(*E, Loc);
442*12c85518Srobert State.Env.setValue(
443*12c85518Srobert Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()));
444*12c85518Srobert }
445*12c85518Srobert
assignOptionalValue(const Expr & E,Environment & Env,BoolValue & HasValueVal)446*12c85518Srobert void assignOptionalValue(const Expr &E, Environment &Env,
447*12c85518Srobert BoolValue &HasValueVal) {
448*12c85518Srobert if (auto *OptionalLoc =
449*12c85518Srobert Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
450*12c85518Srobert Env.setValue(*OptionalLoc, createOptionalValue(Env, HasValueVal));
451*12c85518Srobert }
452*12c85518Srobert }
453*12c85518Srobert
454*12c85518Srobert /// Returns a symbolic value for the "has_value" property of an `optional<T>`
455*12c85518Srobert /// value that is constructed/assigned from a value of type `U` or `optional<U>`
456*12c85518Srobert /// where `T` is constructible from `U`.
valueOrConversionHasValue(const FunctionDecl & F,const Expr & E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)457*12c85518Srobert BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
458*12c85518Srobert const MatchFinder::MatchResult &MatchRes,
459*12c85518Srobert LatticeTransferState &State) {
460*12c85518Srobert assert(F.getTemplateSpecializationArgs() != nullptr);
461*12c85518Srobert assert(F.getTemplateSpecializationArgs()->size() > 0);
462*12c85518Srobert
463*12c85518Srobert const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
464*12c85518Srobert *MatchRes.Context,
465*12c85518Srobert stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType()));
466*12c85518Srobert const int ArgTypeOptionalWrappersCount =
467*12c85518Srobert countOptionalWrappers(*MatchRes.Context, stripReference(E.getType()));
468*12c85518Srobert
469*12c85518Srobert // Check if this is a constructor/assignment call for `optional<T>` with
470*12c85518Srobert // argument of type `U` such that `T` is constructible from `U`.
471*12c85518Srobert if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
472*12c85518Srobert return State.Env.getBoolLiteralValue(true);
473*12c85518Srobert
474*12c85518Srobert // This is a constructor/assignment call for `optional<T>` with argument of
475*12c85518Srobert // type `optional<U>` such that `T` is constructible from `U`.
476*12c85518Srobert if (auto *HasValueVal =
477*12c85518Srobert getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference)))
478*12c85518Srobert return *HasValueVal;
479*12c85518Srobert return State.Env.makeAtomicBoolValue();
480*12c85518Srobert }
481*12c85518Srobert
transferValueOrConversionConstructor(const CXXConstructExpr * E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)482*12c85518Srobert void transferValueOrConversionConstructor(
483*12c85518Srobert const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
484*12c85518Srobert LatticeTransferState &State) {
485*12c85518Srobert assert(E->getNumArgs() > 0);
486*12c85518Srobert
487*12c85518Srobert assignOptionalValue(*E, State.Env,
488*12c85518Srobert valueOrConversionHasValue(*E->getConstructor(),
489*12c85518Srobert *E->getArg(0), MatchRes,
490*12c85518Srobert State));
491*12c85518Srobert }
492*12c85518Srobert
transferAssignment(const CXXOperatorCallExpr * E,BoolValue & HasValueVal,LatticeTransferState & State)493*12c85518Srobert void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
494*12c85518Srobert LatticeTransferState &State) {
495*12c85518Srobert assert(E->getNumArgs() > 0);
496*12c85518Srobert
497*12c85518Srobert auto *OptionalLoc =
498*12c85518Srobert State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
499*12c85518Srobert if (OptionalLoc == nullptr)
500*12c85518Srobert return;
501*12c85518Srobert
502*12c85518Srobert State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
503*12c85518Srobert
504*12c85518Srobert // Assign a storage location for the whole expression.
505*12c85518Srobert State.Env.setStorageLocation(*E, *OptionalLoc);
506*12c85518Srobert }
507*12c85518Srobert
transferValueOrConversionAssignment(const CXXOperatorCallExpr * E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)508*12c85518Srobert void transferValueOrConversionAssignment(
509*12c85518Srobert const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
510*12c85518Srobert LatticeTransferState &State) {
511*12c85518Srobert assert(E->getNumArgs() > 1);
512*12c85518Srobert transferAssignment(E,
513*12c85518Srobert valueOrConversionHasValue(*E->getDirectCallee(),
514*12c85518Srobert *E->getArg(1), MatchRes, State),
515*12c85518Srobert State);
516*12c85518Srobert }
517*12c85518Srobert
transferNulloptAssignment(const CXXOperatorCallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)518*12c85518Srobert void transferNulloptAssignment(const CXXOperatorCallExpr *E,
519*12c85518Srobert const MatchFinder::MatchResult &,
520*12c85518Srobert LatticeTransferState &State) {
521*12c85518Srobert transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
522*12c85518Srobert }
523*12c85518Srobert
transferSwap(const StorageLocation & OptionalLoc1,const StorageLocation & OptionalLoc2,LatticeTransferState & State)524*12c85518Srobert void transferSwap(const StorageLocation &OptionalLoc1,
525*12c85518Srobert const StorageLocation &OptionalLoc2,
526*12c85518Srobert LatticeTransferState &State) {
527*12c85518Srobert auto *OptionalVal1 = State.Env.getValue(OptionalLoc1);
528*12c85518Srobert assert(OptionalVal1 != nullptr);
529*12c85518Srobert
530*12c85518Srobert auto *OptionalVal2 = State.Env.getValue(OptionalLoc2);
531*12c85518Srobert assert(OptionalVal2 != nullptr);
532*12c85518Srobert
533*12c85518Srobert State.Env.setValue(OptionalLoc1, *OptionalVal2);
534*12c85518Srobert State.Env.setValue(OptionalLoc2, *OptionalVal1);
535*12c85518Srobert }
536*12c85518Srobert
transferSwapCall(const CXXMemberCallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)537*12c85518Srobert void transferSwapCall(const CXXMemberCallExpr *E,
538*12c85518Srobert const MatchFinder::MatchResult &,
539*12c85518Srobert LatticeTransferState &State) {
540*12c85518Srobert assert(E->getNumArgs() == 1);
541*12c85518Srobert
542*12c85518Srobert auto *OptionalLoc1 = State.Env.getStorageLocation(
543*12c85518Srobert *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer);
544*12c85518Srobert assert(OptionalLoc1 != nullptr);
545*12c85518Srobert
546*12c85518Srobert auto *OptionalLoc2 =
547*12c85518Srobert State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
548*12c85518Srobert assert(OptionalLoc2 != nullptr);
549*12c85518Srobert
550*12c85518Srobert transferSwap(*OptionalLoc1, *OptionalLoc2, State);
551*12c85518Srobert }
552*12c85518Srobert
transferStdSwapCall(const CallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)553*12c85518Srobert void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
554*12c85518Srobert LatticeTransferState &State) {
555*12c85518Srobert assert(E->getNumArgs() == 2);
556*12c85518Srobert
557*12c85518Srobert auto *OptionalLoc1 =
558*12c85518Srobert State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
559*12c85518Srobert assert(OptionalLoc1 != nullptr);
560*12c85518Srobert
561*12c85518Srobert auto *OptionalLoc2 =
562*12c85518Srobert State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference);
563*12c85518Srobert assert(OptionalLoc2 != nullptr);
564*12c85518Srobert
565*12c85518Srobert transferSwap(*OptionalLoc1, *OptionalLoc2, State);
566*12c85518Srobert }
567*12c85518Srobert
evaluateEquality(Environment & Env,BoolValue & EqVal,BoolValue & LHS,BoolValue & RHS)568*12c85518Srobert BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS,
569*12c85518Srobert BoolValue &RHS) {
570*12c85518Srobert // Logically, an optional<T> object is composed of two values - a `has_value`
571*12c85518Srobert // bit and a value of type T. Equality of optional objects compares both
572*12c85518Srobert // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
573*12c85518Srobert // when two optional objects are engaged, the equality of their respective
574*12c85518Srobert // values of type T matters. Since we only track the `has_value` bits, we
575*12c85518Srobert // can't make any conclusions about equality when we know that two optional
576*12c85518Srobert // objects are engaged.
577*12c85518Srobert //
578*12c85518Srobert // We express this as two facts about the equality:
579*12c85518Srobert // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
580*12c85518Srobert // If they are equal, then either both are set or both are unset.
581*12c85518Srobert // b) (!LHS & !RHS) => EqVal
582*12c85518Srobert // If neither is set, then they are equal.
583*12c85518Srobert // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
584*12c85518Srobert return Env.makeAnd(
585*12c85518Srobert Env.makeImplication(
586*12c85518Srobert EqVal, Env.makeOr(Env.makeAnd(LHS, RHS),
587*12c85518Srobert Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))),
588*12c85518Srobert Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS)));
589*12c85518Srobert }
590*12c85518Srobert
transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr * CmpExpr,const MatchFinder::MatchResult &,LatticeTransferState & State)591*12c85518Srobert void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
592*12c85518Srobert const MatchFinder::MatchResult &,
593*12c85518Srobert LatticeTransferState &State) {
594*12c85518Srobert Environment &Env = State.Env;
595*12c85518Srobert auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
596*12c85518Srobert if (auto *LHasVal = getHasValue(
597*12c85518Srobert Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference)))
598*12c85518Srobert if (auto *RHasVal = getHasValue(
599*12c85518Srobert Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) {
600*12c85518Srobert if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
601*12c85518Srobert CmpValue = &State.Env.makeNot(*CmpValue);
602*12c85518Srobert Env.addToFlowCondition(
603*12c85518Srobert evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal));
604*12c85518Srobert }
605*12c85518Srobert }
606*12c85518Srobert
transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr * CmpExpr,const clang::Expr * E,Environment & Env)607*12c85518Srobert void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
608*12c85518Srobert const clang::Expr *E, Environment &Env) {
609*12c85518Srobert auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
610*12c85518Srobert if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) {
611*12c85518Srobert if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
612*12c85518Srobert CmpValue = &Env.makeNot(*CmpValue);
613*12c85518Srobert Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal,
614*12c85518Srobert Env.getBoolLiteralValue(true)));
615*12c85518Srobert }
616*12c85518Srobert }
617*12c85518Srobert
618*12c85518Srobert std::optional<StatementMatcher>
ignorableOptional(const UncheckedOptionalAccessModelOptions & Options)619*12c85518Srobert ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
620*12c85518Srobert if (Options.IgnoreSmartPointerDereference) {
621*12c85518Srobert auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
622*12c85518Srobert anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
623*12c85518Srobert unless(hasArgument(0, expr(hasOptionalType()))))));
624*12c85518Srobert return expr(
625*12c85518Srobert anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
626*12c85518Srobert }
627*12c85518Srobert return std::nullopt;
628*12c85518Srobert }
629*12c85518Srobert
630*12c85518Srobert StatementMatcher
valueCall(const std::optional<StatementMatcher> & IgnorableOptional)631*12c85518Srobert valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
632*12c85518Srobert return isOptionalMemberCallWithName("value", IgnorableOptional);
633*12c85518Srobert }
634*12c85518Srobert
635*12c85518Srobert StatementMatcher
valueOperatorCall(const std::optional<StatementMatcher> & IgnorableOptional)636*12c85518Srobert valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
637*12c85518Srobert return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
638*12c85518Srobert isOptionalOperatorCallWithName("->", IgnorableOptional)));
639*12c85518Srobert }
640*12c85518Srobert
buildTransferMatchSwitch()641*12c85518Srobert auto buildTransferMatchSwitch() {
642*12c85518Srobert // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
643*12c85518Srobert // lot of duplicated work (e.g. string comparisons), consider providing APIs
644*12c85518Srobert // that avoid it through memoization.
645*12c85518Srobert return CFGMatchSwitchBuilder<LatticeTransferState>()
646*12c85518Srobert // Attach a symbolic "has_value" state to optional values that we see for
647*12c85518Srobert // the first time.
648*12c85518Srobert .CaseOfCFGStmt<Expr>(
649*12c85518Srobert expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
650*12c85518Srobert initializeOptionalReference)
651*12c85518Srobert
652*12c85518Srobert // make_optional
653*12c85518Srobert .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
654*12c85518Srobert
655*12c85518Srobert // optional::optional (in place)
656*12c85518Srobert .CaseOfCFGStmt<CXXConstructExpr>(
657*12c85518Srobert isOptionalInPlaceConstructor(),
658*12c85518Srobert [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
659*12c85518Srobert LatticeTransferState &State) {
660*12c85518Srobert assignOptionalValue(*E, State.Env,
661*12c85518Srobert State.Env.getBoolLiteralValue(true));
662*12c85518Srobert })
663*12c85518Srobert // nullopt_t::nullopt_t
664*12c85518Srobert .CaseOfCFGStmt<CXXConstructExpr>(
665*12c85518Srobert isNulloptConstructor(),
666*12c85518Srobert [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
667*12c85518Srobert LatticeTransferState &State) {
668*12c85518Srobert assignOptionalValue(*E, State.Env,
669*12c85518Srobert State.Env.getBoolLiteralValue(false));
670*12c85518Srobert })
671*12c85518Srobert // optional::optional(nullopt_t)
672*12c85518Srobert .CaseOfCFGStmt<CXXConstructExpr>(
673*12c85518Srobert isOptionalNulloptConstructor(),
674*12c85518Srobert [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
675*12c85518Srobert LatticeTransferState &State) {
676*12c85518Srobert assignOptionalValue(*E, State.Env,
677*12c85518Srobert State.Env.getBoolLiteralValue(false));
678*12c85518Srobert })
679*12c85518Srobert // optional::optional (value/conversion)
680*12c85518Srobert .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
681*12c85518Srobert transferValueOrConversionConstructor)
682*12c85518Srobert
683*12c85518Srobert
684*12c85518Srobert // optional::operator=
685*12c85518Srobert .CaseOfCFGStmt<CXXOperatorCallExpr>(
686*12c85518Srobert isOptionalValueOrConversionAssignment(),
687*12c85518Srobert transferValueOrConversionAssignment)
688*12c85518Srobert .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
689*12c85518Srobert transferNulloptAssignment)
690*12c85518Srobert
691*12c85518Srobert // optional::value
692*12c85518Srobert .CaseOfCFGStmt<CXXMemberCallExpr>(
693*12c85518Srobert valueCall(std::nullopt),
694*12c85518Srobert [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
695*12c85518Srobert LatticeTransferState &State) {
696*12c85518Srobert transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
697*12c85518Srobert })
698*12c85518Srobert
699*12c85518Srobert // optional::operator*, optional::operator->
700*12c85518Srobert .CaseOfCFGStmt<CallExpr>(valueOperatorCall(std::nullopt),
701*12c85518Srobert [](const CallExpr *E,
702*12c85518Srobert const MatchFinder::MatchResult &,
703*12c85518Srobert LatticeTransferState &State) {
704*12c85518Srobert transferUnwrapCall(E, E->getArg(0), State);
705*12c85518Srobert })
706*12c85518Srobert
707*12c85518Srobert // optional::has_value
708*12c85518Srobert .CaseOfCFGStmt<CXXMemberCallExpr>(
709*12c85518Srobert isOptionalMemberCallWithName("has_value"),
710*12c85518Srobert transferOptionalHasValueCall)
711*12c85518Srobert
712*12c85518Srobert // optional::operator bool
713*12c85518Srobert .CaseOfCFGStmt<CXXMemberCallExpr>(
714*12c85518Srobert isOptionalMemberCallWithName("operator bool"),
715*12c85518Srobert transferOptionalHasValueCall)
716*12c85518Srobert
717*12c85518Srobert // optional::emplace
718*12c85518Srobert .CaseOfCFGStmt<CXXMemberCallExpr>(
719*12c85518Srobert isOptionalMemberCallWithName("emplace"),
720*12c85518Srobert [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
721*12c85518Srobert LatticeTransferState &State) {
722*12c85518Srobert assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
723*12c85518Srobert State.Env.getBoolLiteralValue(true));
724*12c85518Srobert })
725*12c85518Srobert
726*12c85518Srobert // optional::reset
727*12c85518Srobert .CaseOfCFGStmt<CXXMemberCallExpr>(
728*12c85518Srobert isOptionalMemberCallWithName("reset"),
729*12c85518Srobert [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
730*12c85518Srobert LatticeTransferState &State) {
731*12c85518Srobert assignOptionalValue(*E->getImplicitObjectArgument(), State.Env,
732*12c85518Srobert State.Env.getBoolLiteralValue(false));
733*12c85518Srobert })
734*12c85518Srobert
735*12c85518Srobert // optional::swap
736*12c85518Srobert .CaseOfCFGStmt<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
737*12c85518Srobert transferSwapCall)
738*12c85518Srobert
739*12c85518Srobert // std::swap
740*12c85518Srobert .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
741*12c85518Srobert
742*12c85518Srobert // opt.value_or("").empty()
743*12c85518Srobert .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
744*12c85518Srobert transferValueOrStringEmptyCall)
745*12c85518Srobert
746*12c85518Srobert // opt.value_or(X) != X
747*12c85518Srobert .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
748*12c85518Srobert
749*12c85518Srobert // Comparisons (==, !=):
750*12c85518Srobert .CaseOfCFGStmt<CXXOperatorCallExpr>(
751*12c85518Srobert isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()),
752*12c85518Srobert transferOptionalAndOptionalCmp)
753*12c85518Srobert .CaseOfCFGStmt<CXXOperatorCallExpr>(
754*12c85518Srobert isComparisonOperatorCall(hasOptionalType(),
755*12c85518Srobert unless(hasAnyOptionalType())),
756*12c85518Srobert [](const clang::CXXOperatorCallExpr *Cmp,
757*12c85518Srobert const MatchFinder::MatchResult &, LatticeTransferState &State) {
758*12c85518Srobert transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
759*12c85518Srobert })
760*12c85518Srobert .CaseOfCFGStmt<CXXOperatorCallExpr>(
761*12c85518Srobert isComparisonOperatorCall(unless(hasAnyOptionalType()),
762*12c85518Srobert hasOptionalType()),
763*12c85518Srobert [](const clang::CXXOperatorCallExpr *Cmp,
764*12c85518Srobert const MatchFinder::MatchResult &, LatticeTransferState &State) {
765*12c85518Srobert transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
766*12c85518Srobert })
767*12c85518Srobert
768*12c85518Srobert // returns optional
769*12c85518Srobert .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
770*12c85518Srobert transferCallReturningOptional)
771*12c85518Srobert
772*12c85518Srobert .Build();
773*12c85518Srobert }
774*12c85518Srobert
diagnoseUnwrapCall(const Expr * UnwrapExpr,const Expr * ObjectExpr,const Environment & Env)775*12c85518Srobert std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr,
776*12c85518Srobert const Expr *ObjectExpr,
777*12c85518Srobert const Environment &Env) {
778*12c85518Srobert if (auto *OptionalVal =
779*12c85518Srobert Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
780*12c85518Srobert auto *Prop = OptionalVal->getProperty("has_value");
781*12c85518Srobert if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
782*12c85518Srobert if (Env.flowConditionImplies(*HasValueVal))
783*12c85518Srobert return {};
784*12c85518Srobert }
785*12c85518Srobert }
786*12c85518Srobert
787*12c85518Srobert // Record that this unwrap is *not* provably safe.
788*12c85518Srobert // FIXME: include either the name of the optional (if applicable) or a source
789*12c85518Srobert // range of the access for easier interpretation of the result.
790*12c85518Srobert return {ObjectExpr->getBeginLoc()};
791*12c85518Srobert }
792*12c85518Srobert
buildDiagnoseMatchSwitch(const UncheckedOptionalAccessModelOptions & Options)793*12c85518Srobert auto buildDiagnoseMatchSwitch(
794*12c85518Srobert const UncheckedOptionalAccessModelOptions &Options) {
795*12c85518Srobert // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
796*12c85518Srobert // lot of duplicated work (e.g. string comparisons), consider providing APIs
797*12c85518Srobert // that avoid it through memoization.
798*12c85518Srobert auto IgnorableOptional = ignorableOptional(Options);
799*12c85518Srobert return CFGMatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
800*12c85518Srobert // optional::value
801*12c85518Srobert .CaseOfCFGStmt<CXXMemberCallExpr>(
802*12c85518Srobert valueCall(IgnorableOptional),
803*12c85518Srobert [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
804*12c85518Srobert const Environment &Env) {
805*12c85518Srobert return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env);
806*12c85518Srobert })
807*12c85518Srobert
808*12c85518Srobert // optional::operator*, optional::operator->
809*12c85518Srobert .CaseOfCFGStmt<CallExpr>(
810*12c85518Srobert valueOperatorCall(IgnorableOptional),
811*12c85518Srobert [](const CallExpr *E, const MatchFinder::MatchResult &,
812*12c85518Srobert const Environment &Env) {
813*12c85518Srobert return diagnoseUnwrapCall(E, E->getArg(0), Env);
814*12c85518Srobert })
815*12c85518Srobert .Build();
816*12c85518Srobert }
817*12c85518Srobert
818*12c85518Srobert } // namespace
819*12c85518Srobert
820*12c85518Srobert ast_matchers::DeclarationMatcher
optionalClassDecl()821*12c85518Srobert UncheckedOptionalAccessModel::optionalClassDecl() {
822*12c85518Srobert return optionalClass();
823*12c85518Srobert }
824*12c85518Srobert
UncheckedOptionalAccessModel(ASTContext & Ctx)825*12c85518Srobert UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
826*12c85518Srobert : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
827*12c85518Srobert TransferMatchSwitch(buildTransferMatchSwitch()) {}
828*12c85518Srobert
transfer(const CFGElement * Elt,NoopLattice & L,Environment & Env)829*12c85518Srobert void UncheckedOptionalAccessModel::transfer(const CFGElement *Elt,
830*12c85518Srobert NoopLattice &L, Environment &Env) {
831*12c85518Srobert LatticeTransferState State(L, Env);
832*12c85518Srobert TransferMatchSwitch(*Elt, getASTContext(), State);
833*12c85518Srobert }
834*12c85518Srobert
compare(QualType Type,const Value & Val1,const Environment & Env1,const Value & Val2,const Environment & Env2)835*12c85518Srobert ComparisonResult UncheckedOptionalAccessModel::compare(
836*12c85518Srobert QualType Type, const Value &Val1, const Environment &Env1,
837*12c85518Srobert const Value &Val2, const Environment &Env2) {
838*12c85518Srobert if (!isOptionalType(Type))
839*12c85518Srobert return ComparisonResult::Unknown;
840*12c85518Srobert bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
841*12c85518Srobert bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
842*12c85518Srobert if (MustNonEmpty1 && MustNonEmpty2) return ComparisonResult::Same;
843*12c85518Srobert // If exactly one is true, then they're different, no reason to check whether
844*12c85518Srobert // they're definitely empty.
845*12c85518Srobert if (MustNonEmpty1 || MustNonEmpty2) return ComparisonResult::Different;
846*12c85518Srobert // Check if they're both definitely empty.
847*12c85518Srobert return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
848*12c85518Srobert ? ComparisonResult::Same
849*12c85518Srobert : ComparisonResult::Different;
850*12c85518Srobert }
851*12c85518Srobert
merge(QualType Type,const Value & Val1,const Environment & Env1,const Value & Val2,const Environment & Env2,Value & MergedVal,Environment & MergedEnv)852*12c85518Srobert bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
853*12c85518Srobert const Environment &Env1,
854*12c85518Srobert const Value &Val2,
855*12c85518Srobert const Environment &Env2,
856*12c85518Srobert Value &MergedVal,
857*12c85518Srobert Environment &MergedEnv) {
858*12c85518Srobert if (!isOptionalType(Type))
859*12c85518Srobert return true;
860*12c85518Srobert // FIXME: uses same approach as join for `BoolValues`. Requires non-const
861*12c85518Srobert // values, though, so will require updating the interface.
862*12c85518Srobert auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
863*12c85518Srobert bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
864*12c85518Srobert bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
865*12c85518Srobert if (MustNonEmpty1 && MustNonEmpty2)
866*12c85518Srobert MergedEnv.addToFlowCondition(HasValueVal);
867*12c85518Srobert else if (
868*12c85518Srobert // Only make the costly calls to `isEmptyOptional` if we got "unknown"
869*12c85518Srobert // (false) for both calls to `isNonEmptyOptional`.
870*12c85518Srobert !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) &&
871*12c85518Srobert isEmptyOptional(Val2, Env2))
872*12c85518Srobert MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal));
873*12c85518Srobert setHasValue(MergedVal, HasValueVal);
874*12c85518Srobert return true;
875*12c85518Srobert }
876*12c85518Srobert
widen(QualType Type,Value & Prev,const Environment & PrevEnv,Value & Current,Environment & CurrentEnv)877*12c85518Srobert Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev,
878*12c85518Srobert const Environment &PrevEnv,
879*12c85518Srobert Value &Current,
880*12c85518Srobert Environment &CurrentEnv) {
881*12c85518Srobert switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) {
882*12c85518Srobert case ComparisonResult::Same:
883*12c85518Srobert return &Prev;
884*12c85518Srobert case ComparisonResult::Different:
885*12c85518Srobert if (auto *PrevHasVal =
886*12c85518Srobert cast_or_null<BoolValue>(Prev.getProperty("has_value"))) {
887*12c85518Srobert if (isa<TopBoolValue>(PrevHasVal))
888*12c85518Srobert return &Prev;
889*12c85518Srobert }
890*12c85518Srobert if (auto *CurrentHasVal =
891*12c85518Srobert cast_or_null<BoolValue>(Current.getProperty("has_value"))) {
892*12c85518Srobert if (isa<TopBoolValue>(CurrentHasVal))
893*12c85518Srobert return &Current;
894*12c85518Srobert }
895*12c85518Srobert return &createOptionalValue(CurrentEnv, CurrentEnv.makeTopBoolValue());
896*12c85518Srobert case ComparisonResult::Unknown:
897*12c85518Srobert return nullptr;
898*12c85518Srobert }
899*12c85518Srobert llvm_unreachable("all cases covered in switch");
900*12c85518Srobert }
901*12c85518Srobert
UncheckedOptionalAccessDiagnoser(UncheckedOptionalAccessModelOptions Options)902*12c85518Srobert UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
903*12c85518Srobert UncheckedOptionalAccessModelOptions Options)
904*12c85518Srobert : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
905*12c85518Srobert
diagnose(ASTContext & Ctx,const CFGElement * Elt,const Environment & Env)906*12c85518Srobert std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose(
907*12c85518Srobert ASTContext &Ctx, const CFGElement *Elt, const Environment &Env) {
908*12c85518Srobert return DiagnoseMatchSwitch(*Elt, Ctx, Env);
909*12c85518Srobert }
910*12c85518Srobert
911*12c85518Srobert } // namespace dataflow
912*12c85518Srobert } // namespace clang
913