Coverage Report

Created: 2023-05-31 04:38

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