Coverage Report

Created: 2022-07-16 07:03

/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/Analysis/FlowSensitive/DataflowEnvironment.h"
22
#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
23
#include "clang/Analysis/FlowSensitive/NoopLattice.h"
24
#include "clang/Analysis/FlowSensitive/Value.h"
25
#include "clang/Basic/SourceLocation.h"
26
#include "llvm/ADT/StringRef.h"
27
#include "llvm/Support/Casting.h"
28
#include <cassert>
29
#include <memory>
30
#include <utility>
31
#include <vector>
32
33
namespace clang {
34
namespace dataflow {
35
namespace {
36
37
using namespace ::clang::ast_matchers;
38
using LatticeTransferState = TransferState<NoopLattice>;
39
40
6.80k
DeclarationMatcher optionalClass() {
41
6.80k
  return classTemplateSpecializationDecl(
42
6.80k
      anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
43
6.80k
            hasName("__optional_destruct_base"), hasName("absl::optional"),
44
6.80k
            hasName("base::Optional")),
45
6.80k
      hasTemplateArgument(0, refersToType(type().bind("T"))));
46
6.80k
}
47
48
2.67k
auto optionalOrAliasType() {
49
2.67k
  return hasUnqualifiedDesugaredType(
50
2.67k
      recordType(hasDeclaration(optionalClass())));
51
2.67k
}
52
53
/// Matches any of the spellings of the optional types and sugar, aliases, etc.
54
2.18k
auto hasOptionalType() { return hasType(optionalOrAliasType()); }
55
56
auto isOptionalMemberCallWithName(
57
    llvm::StringRef MemberName,
58
1.70k
    llvm::Optional<StatementMatcher> Ignorable = llvm::None) {
59
1.70k
  auto Exception = unless(Ignorable ? 
expr(anyOf(*Ignorable, cxxThisExpr()))486
60
1.70k
                                    : 
cxxThisExpr()1.21k
);
61
1.70k
  return cxxMemberCallExpr(
62
1.70k
      on(expr(Exception)),
63
1.70k
      callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
64
1.70k
}
65
66
auto isOptionalOperatorCallWithName(
67
    llvm::StringRef operator_name,
68
972
    llvm::Optional<StatementMatcher> Ignorable = llvm::None) {
69
972
  return cxxOperatorCallExpr(
70
972
      hasOverloadedOperatorName(operator_name),
71
972
      callee(cxxMethodDecl(ofClass(optionalClass()))),
72
972
      Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : 
callExpr()0
);
73
972
}
74
75
243
auto isMakeOptionalCall() {
76
243
  return callExpr(
77
243
      callee(functionDecl(hasAnyName(
78
243
          "std::make_optional", "base::make_optional", "absl::make_optional"))),
79
243
      hasOptionalType());
80
243
}
81
82
972
auto hasNulloptType() {
83
972
  return hasType(namedDecl(
84
972
      hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")));
85
972
}
86
87
243
auto inPlaceClass() {
88
243
  return recordDecl(
89
243
      hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
90
243
}
91
92
243
auto isOptionalNulloptConstructor() {
93
243
  return cxxConstructExpr(hasOptionalType(), argumentCountIs(1),
94
243
                          hasArgument(0, hasNulloptType()));
95
243
}
96
97
243
auto isOptionalInPlaceConstructor() {
98
243
  return cxxConstructExpr(hasOptionalType(),
99
243
                          hasArgument(0, hasType(inPlaceClass())));
100
243
}
101
102
243
auto isOptionalValueOrConversionConstructor() {
103
243
  return cxxConstructExpr(
104
243
      hasOptionalType(),
105
243
      unless(hasDeclaration(
106
243
          cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
107
243
      argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
108
243
}
109
110
243
auto isOptionalValueOrConversionAssignment() {
111
243
  return cxxOperatorCallExpr(
112
243
      hasOverloadedOperatorName("="),
113
243
      callee(cxxMethodDecl(ofClass(optionalClass()))),
114
243
      unless(hasDeclaration(cxxMethodDecl(
115
243
          anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
116
243
      argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
117
243
}
118
119
243
auto isOptionalNulloptAssignment() {
120
243
  return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
121
243
                             callee(cxxMethodDecl(ofClass(optionalClass()))),
122
243
                             argumentCountIs(2),
123
243
                             hasArgument(1, hasNulloptType()));
124
243
}
125
126
243
auto isStdSwapCall() {
127
243
  return callExpr(callee(functionDecl(hasName("std::swap"))),
128
243
                  argumentCountIs(2), hasArgument(0, hasOptionalType()),
129
243
                  hasArgument(1, hasOptionalType()));
130
243
}
131
132
constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
133
134
243
auto isValueOrStringEmptyCall() {
135
  // `opt.value_or("").empty()`
136
243
  return cxxMemberCallExpr(
137
243
      callee(cxxMethodDecl(hasName("empty"))),
138
243
      onImplicitObjectArgument(ignoringImplicit(
139
243
          cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
140
243
                            callee(cxxMethodDecl(hasName("value_or"),
141
243
                                                 ofClass(optionalClass()))),
142
243
                            hasArgument(0, stringLiteral(hasSize(0))))
143
243
              .bind(ValueOrCallID))));
144
243
}
145
146
243
auto isValueOrNotEqX() {
147
729
  auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
148
729
    return hasOperands(
149
729
        ignoringImplicit(
150
729
            cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
151
729
                              callee(cxxMethodDecl(hasName("value_or"),
152
729
                                                   ofClass(optionalClass()))),
153
729
                              hasArgument(0, Arg))
154
729
                .bind(ValueOrCallID)),
155
729
        ignoringImplicit(Arg));
156
729
  };
157
158
  // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
159
  // support this pattern for any expression, but the AST does not have a
160
  // generic expression comparison facility, so we specialize to common cases
161
  // seen in practice.  FIXME: define a matcher that compares values across
162
  // nodes, which would let us generalize this to any `X`.
163
243
  return binaryOperation(hasOperatorName("!="),
164
243
                         anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
165
243
                               ComparesToSame(stringLiteral(hasSize(0))),
166
243
                               ComparesToSame(integerLiteral(equals(0)))));
167
243
}
168
169
243
auto isCallReturningOptional() {
170
243
  return callExpr(hasType(qualType(anyOf(
171
243
      optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
172
243
}
173
174
/// Sets `HasValueVal` as the symbolic value that represents the "has_value"
175
/// property of the optional value `OptionalVal`.
176
771
void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
177
771
  OptionalVal.setProperty("has_value", HasValueVal);
178
771
}
179
180
/// Creates a symbolic value for an `optional` value using `HasValueVal` as the
181
/// symbolic value of its "has_value" property.
182
510
StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
183
510
  auto OptionalVal = std::make_unique<StructValue>();
184
510
  setHasValue(*OptionalVal, HasValueVal);
185
510
  return Env.takeOwnership(std::move(OptionalVal));
186
510
}
187
188
/// Returns the symbolic value that represents the "has_value" property of the
189
/// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
190
237
BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
191
237
  if (OptionalVal != nullptr) {
192
237
    auto *HasValueVal =
193
237
        cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
194
237
    if (HasValueVal == nullptr) {
195
24
      HasValueVal = &Env.makeAtomicBoolValue();
196
24
      OptionalVal->setProperty("has_value", *HasValueVal);
197
24
    }
198
237
    return HasValueVal;
199
237
  }
200
0
  return nullptr;
201
237
}
202
203
/// If `Type` is a reference type, returns the type of its pointee. Otherwise,
204
/// returns `Type` itself.
205
897
QualType stripReference(QualType Type) {
206
897
  return Type->isReferenceType() ? 
Type->getPointeeType()18
:
Type879
;
207
897
}
208
209
/// Returns true if and only if `Type` is an optional type.
210
670
bool IsOptionalType(QualType Type) {
211
670
  if (!Type->isRecordType())
212
322
    return false;
213
  // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call.
214
348
  auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString();
215
348
  return TypeName == "std::optional" || 
TypeName == "absl::optional"296
||
216
348
         
TypeName == "base::Optional"244
;
217
670
}
218
219
/// Returns the number of optional wrappers in `Type`.
220
///
221
/// For example, if `Type` is `optional<optional<int>>`, the result of this
222
/// function will be 2.
223
528
int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
224
528
  if (!IsOptionalType(Type))
225
468
    return 0;
226
60
  return 1 + countOptionalWrappers(
227
60
                 ASTCtx,
228
60
                 cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
229
60
                     ->getTemplateArgs()
230
60
                     .get(0)
231
60
                     .getAsType()
232
60
                     .getDesugaredType(ASTCtx));
233
528
}
234
235
/// Tries to initialize the `optional`'s value (that is, contents), and return
236
/// its location. Returns nullptr if the value can't be represented.
237
StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
238
                                                    Value &OptionalVal,
239
618
                                                    Environment &Env) {
240
  // The "value" property represents a synthetic field. As such, it needs
241
  // `StorageLocation`, like normal fields (and other variables). So, we model
242
  // it with a `ReferenceValue`, since that includes a storage location.  Once
243
  // the property is set, it will be shared by all environments that access the
244
  // `Value` representing the optional (here, `OptionalVal`).
245
618
  if (auto *ValueProp = OptionalVal.getProperty("value")) {
246
189
    auto *ValueRef = clang::cast<ReferenceValue>(ValueProp);
247
189
    auto &ValueLoc = ValueRef->getReferentLoc();
248
189
    if (Env.getValue(ValueLoc) == nullptr) {
249
      // The property was previously set, but the value has been lost. This can
250
      // happen, for example, because of an environment merge (where the two
251
      // environments mapped the property to different values, which resulted in
252
      // them both being discarded), or when two blocks in the CFG, with neither
253
      // a dominator of the other, visit the same optional value, or even when a
254
      // block is revisited during testing to collect per-statement state.
255
      // FIXME: This situation means that the optional contents are not shared
256
      // between branches and the like. Practically, this lack of sharing
257
      // reduces the precision of the model when the contents are relevant to
258
      // the check, like another optional or a boolean that influences control
259
      // flow.
260
147
      auto *ValueVal = Env.createValue(ValueLoc.getType());
261
147
      if (ValueVal == nullptr)
262
0
        return nullptr;
263
147
      Env.setValue(ValueLoc, *ValueVal);
264
147
    }
265
189
    return &ValueLoc;
266
189
  }
267
268
429
  auto Ty = stripReference(Q);
269
429
  auto *ValueVal = Env.createValue(Ty);
270
429
  if (ValueVal == nullptr)
271
6
    return nullptr;
272
423
  auto &ValueLoc = Env.createStorageLocation(Ty);
273
423
  Env.setValue(ValueLoc, *ValueVal);
274
423
  auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc);
275
423
  OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef)));
276
423
  return &ValueLoc;
277
429
}
278
279
void initializeOptionalReference(const Expr *OptionalExpr,
280
                                 const MatchFinder::MatchResult &,
281
993
                                 LatticeTransferState &State) {
282
993
  if (auto *OptionalVal =
283
993
          State.Env.getValue(*OptionalExpr, SkipPast::Reference)) {
284
981
    if (OptionalVal->getProperty("has_value") == nullptr) {
285
165
      setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
286
165
    }
287
981
  }
288
993
}
289
290
/// Returns true if and only if `OptionalVal` is initialized and known to be
291
/// empty in `Env.
292
106
bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
293
106
  auto *HasValueVal =
294
106
      cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
295
106
  return HasValueVal != nullptr &&
296
106
         Env.flowConditionImplies(Env.makeNot(*HasValueVal));
297
106
}
298
299
/// Returns true if and only if `OptionalVal` is initialized and known to be
300
/// non-empty in `Env.
301
179
bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
302
179
  auto *HasValueVal =
303
179
      cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
304
179
  return HasValueVal != nullptr && 
Env.flowConditionImplies(*HasValueVal)167
;
305
179
}
306
307
void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
308
618
                        LatticeTransferState &State) {
309
618
  if (auto *OptionalVal =
310
618
          State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
311
618
    if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
312
618
      if (auto *Loc = maybeInitializeOptionalValueMember(
313
618
              UnwrapExpr->getType(), *OptionalVal, State.Env))
314
612
        State.Env.setStorageLocation(*UnwrapExpr, *Loc);
315
618
  }
316
618
}
317
318
void transferMakeOptionalCall(const CallExpr *E,
319
                              const MatchFinder::MatchResult &,
320
24
                              LatticeTransferState &State) {
321
24
  auto &Loc = State.Env.createStorageLocation(*E);
322
24
  State.Env.setStorageLocation(*E, Loc);
323
24
  State.Env.setValue(
324
24
      Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
325
24
}
326
327
void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
328
                                  const MatchFinder::MatchResult &,
329
159
                                  LatticeTransferState &State) {
330
159
  if (auto *HasValueVal = getHasValue(
331
159
          State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
332
159
                                        SkipPast::ReferenceThenPointer))) {
333
159
    auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
334
159
    State.Env.setValue(CallExprLoc, *HasValueVal);
335
159
    State.Env.setStorageLocation(*CallExpr, CallExprLoc);
336
159
  }
337
159
}
338
339
/// `ModelPred` builds a logical formula relating the predicate in
340
/// `ValueOrPredExpr` to the optional's `has_value` property.
341
void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
342
                         const MatchFinder::MatchResult &Result,
343
                         LatticeTransferState &State,
344
                         BoolValue &(*ModelPred)(Environment &Env,
345
                                                 BoolValue &ExprVal,
346
30
                                                 BoolValue &HasValueVal)) {
347
30
  auto &Env = State.Env;
348
349
30
  const auto *ObjectArgumentExpr =
350
30
      Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
351
30
          ->getImplicitObjectArgument();
352
353
30
  auto *HasValueVal = getHasValue(
354
30
      State.Env,
355
30
      State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
356
30
  if (HasValueVal == nullptr)
357
0
    return;
358
359
30
  auto *ExprValue = cast_or_null<BoolValue>(
360
30
      State.Env.getValue(*ValueOrPredExpr, SkipPast::None));
361
30
  if (ExprValue == nullptr) {
362
12
    auto &ExprLoc = State.Env.createStorageLocation(*ValueOrPredExpr);
363
12
    ExprValue = &State.Env.makeAtomicBoolValue();
364
12
    State.Env.setValue(ExprLoc, *ExprValue);
365
12
    State.Env.setStorageLocation(*ValueOrPredExpr, ExprLoc);
366
12
  }
367
368
30
  Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal));
369
30
}
370
371
void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
372
                                    const MatchFinder::MatchResult &Result,
373
6
                                    LatticeTransferState &State) {
374
6
  return transferValueOrImpl(ComparisonExpr, Result, State,
375
6
                             [](Environment &Env, BoolValue &ExprVal,
376
6
                                BoolValue &HasValueVal) -> BoolValue & {
377
                               // If the result is *not* empty, then we know the
378
                               // optional must have been holding a value. If
379
                               // `ExprVal` is true, though, we don't learn
380
                               // anything definite about `has_value`, so we
381
                               // don't add any corresponding implications to
382
                               // the flow condition.
383
6
                               return Env.makeImplication(Env.makeNot(ExprVal),
384
6
                                                          HasValueVal);
385
6
                             });
386
6
}
387
388
void transferValueOrNotEqX(const Expr *ComparisonExpr,
389
                           const MatchFinder::MatchResult &Result,
390
24
                           LatticeTransferState &State) {
391
24
  transferValueOrImpl(ComparisonExpr, Result, State,
392
24
                      [](Environment &Env, BoolValue &ExprVal,
393
24
                         BoolValue &HasValueVal) -> BoolValue & {
394
                        // We know that if `(opt.value_or(X) != X)` then
395
                        // `opt.hasValue()`, even without knowing further
396
                        // details about the contents of `opt`.
397
24
                        return Env.makeImplication(ExprVal, HasValueVal);
398
24
                      });
399
24
}
400
401
void transferCallReturningOptional(const CallExpr *E,
402
                                   const MatchFinder::MatchResult &Result,
403
264
                                   LatticeTransferState &State) {
404
264
  if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
405
138
    return;
406
407
126
  auto &Loc = State.Env.createStorageLocation(*E);
408
126
  State.Env.setStorageLocation(*E, Loc);
409
126
  State.Env.setValue(
410
126
      Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()));
411
126
}
412
413
void assignOptionalValue(const Expr &E, LatticeTransferState &State,
414
294
                         BoolValue &HasValueVal) {
415
294
  if (auto *OptionalLoc =
416
294
          State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
417
294
    State.Env.setValue(*OptionalLoc,
418
294
                       createOptionalValue(State.Env, HasValueVal));
419
294
  }
420
294
}
421
422
/// Returns a symbolic value for the "has_value" property of an `optional<T>`
423
/// value that is constructed/assigned from a value of type `U` or `optional<U>`
424
/// where `T` is constructible from `U`.
425
BoolValue &value_orConversionHasValue(const FunctionDecl &F, const Expr &E,
426
                                      const MatchFinder::MatchResult &MatchRes,
427
234
                                      LatticeTransferState &State) {
428
234
  assert(F.getTemplateSpecializationArgs()->size() > 0);
429
430
0
  const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
431
234
      *MatchRes.Context,
432
234
      stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType()));
433
234
  const int ArgTypeOptionalWrappersCount =
434
234
      countOptionalWrappers(*MatchRes.Context, stripReference(E.getType()));
435
436
  // Check if this is a constructor/assignment call for `optional<T>` with
437
  // argument of type `U` such that `T` is constructible from `U`.
438
234
  if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
439
186
    return State.Env.getBoolLiteralValue(true);
440
441
  // This is a constructor/assignment call for `optional<T>` with argument of
442
  // type `optional<U>` such that `T` is constructible from `U`.
443
48
  if (auto *HasValueVal =
444
48
          getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference)))
445
48
    return *HasValueVal;
446
0
  return State.Env.makeAtomicBoolValue();
447
48
}
448
449
void transferValueOrConversionConstructor(
450
    const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
451
198
    LatticeTransferState &State) {
452
198
  assert(E->getNumArgs() > 0);
453
454
0
  assignOptionalValue(*E, State,
455
198
                      value_orConversionHasValue(*E->getConstructor(),
456
198
                                                 *E->getArg(0), MatchRes,
457
198
                                                 State));
458
198
}
459
460
void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
461
66
                        LatticeTransferState &State) {
462
66
  assert(E->getNumArgs() > 0);
463
464
0
  auto *OptionalLoc =
465
66
      State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
466
66
  if (OptionalLoc == nullptr)
467
0
    return;
468
469
66
  State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
470
471
  // Assign a storage location for the whole expression.
472
66
  State.Env.setStorageLocation(*E, *OptionalLoc);
473
66
}
474
475
void transferValueOrConversionAssignment(
476
    const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
477
36
    LatticeTransferState &State) {
478
36
  assert(E->getNumArgs() > 1);
479
0
  transferAssignment(E,
480
36
                     value_orConversionHasValue(*E->getDirectCallee(),
481
36
                                                *E->getArg(1), MatchRes, State),
482
36
                     State);
483
36
}
484
485
void transferNulloptAssignment(const CXXOperatorCallExpr *E,
486
                               const MatchFinder::MatchResult &,
487
30
                               LatticeTransferState &State) {
488
30
  transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
489
30
}
490
491
void transferSwap(const StorageLocation &OptionalLoc1,
492
                  const StorageLocation &OptionalLoc2,
493
24
                  LatticeTransferState &State) {
494
24
  auto *OptionalVal1 = State.Env.getValue(OptionalLoc1);
495
24
  assert(OptionalVal1 != nullptr);
496
497
0
  auto *OptionalVal2 = State.Env.getValue(OptionalLoc2);
498
24
  assert(OptionalVal2 != nullptr);
499
500
0
  State.Env.setValue(OptionalLoc1, *OptionalVal2);
501
24
  State.Env.setValue(OptionalLoc2, *OptionalVal1);
502
24
}
503
504
void transferSwapCall(const CXXMemberCallExpr *E,
505
                      const MatchFinder::MatchResult &,
506
12
                      LatticeTransferState &State) {
507
12
  assert(E->getNumArgs() == 1);
508
509
0
  auto *OptionalLoc1 = State.Env.getStorageLocation(
510
12
      *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer);
511
12
  assert(OptionalLoc1 != nullptr);
512
513
0
  auto *OptionalLoc2 =
514
12
      State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
515
12
  assert(OptionalLoc2 != nullptr);
516
517
0
  transferSwap(*OptionalLoc1, *OptionalLoc2, State);
518
12
}
519
520
void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
521
12
                         LatticeTransferState &State) {
522
12
  assert(E->getNumArgs() == 2);
523
524
0
  auto *OptionalLoc1 =
525
12
      State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
526
12
  assert(OptionalLoc1 != nullptr);
527
528
0
  auto *OptionalLoc2 =
529
12
      State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference);
530
12
  assert(OptionalLoc2 != nullptr);
531
532
0
  transferSwap(*OptionalLoc1, *OptionalLoc2, State);
533
12
}
534
535
llvm::Optional<StatementMatcher>
536
486
ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
537
486
  if (Options.IgnoreSmartPointerDereference)
538
486
    return memberExpr(hasObjectExpression(ignoringParenImpCasts(
539
486
        cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("->"),
540
486
                                  hasOverloadedOperatorName("*")),
541
486
                            unless(hasArgument(0, expr(hasOptionalType())))))));
542
0
  return llvm::None;
543
486
}
544
545
StatementMatcher
546
486
valueCall(llvm::Optional<StatementMatcher> &IgnorableOptional) {
547
486
  return isOptionalMemberCallWithName("value", IgnorableOptional);
548
486
}
549
550
StatementMatcher
551
486
valueOperatorCall(llvm::Optional<StatementMatcher> &IgnorableOptional) {
552
486
  return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
553
486
                    isOptionalOperatorCallWithName("->", IgnorableOptional)));
554
486
}
555
556
auto buildTransferMatchSwitch(
557
243
    const UncheckedOptionalAccessModelOptions &Options) {
558
  // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
559
  // lot of duplicated work (e.g. string comparisons), consider providing APIs
560
  // that avoid it through memoization.
561
243
  auto IgnorableOptional = ignorableOptional(Options);
562
243
  return MatchSwitchBuilder<LatticeTransferState>()
563
      // Attach a symbolic "has_value" state to optional values that we see for
564
      // the first time.
565
243
      .CaseOf<Expr>(
566
243
          expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
567
243
          initializeOptionalReference)
568
569
      // make_optional
570
243
      .CaseOf<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
571
572
      // optional::optional
573
243
      .CaseOf<CXXConstructExpr>(
574
243
          isOptionalInPlaceConstructor(),
575
243
          [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
576
243
             LatticeTransferState &State) {
577
24
            assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true));
578
24
          })
579
243
      .CaseOf<CXXConstructExpr>(
580
243
          isOptionalNulloptConstructor(),
581
243
          [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
582
243
             LatticeTransferState &State) {
583
42
            assignOptionalValue(*E, State,
584
42
                                State.Env.getBoolLiteralValue(false));
585
42
          })
586
243
      .CaseOf<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
587
243
                                transferValueOrConversionConstructor)
588
589
      // optional::operator=
590
243
      .CaseOf<CXXOperatorCallExpr>(isOptionalValueOrConversionAssignment(),
591
243
                                   transferValueOrConversionAssignment)
592
243
      .CaseOf<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
593
243
                                   transferNulloptAssignment)
594
595
      // optional::value
596
243
      .CaseOf<CXXMemberCallExpr>(
597
243
          valueCall(IgnorableOptional),
598
243
          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
599
522
             LatticeTransferState &State) {
600
522
            transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
601
522
          })
602
603
      // optional::operator*, optional::operator->
604
243
      .CaseOf<CallExpr>(valueOperatorCall(IgnorableOptional),
605
243
                        [](const CallExpr *E, const MatchFinder::MatchResult &,
606
243
                           LatticeTransferState &State) {
607
96
                          transferUnwrapCall(E, E->getArg(0), State);
608
96
                        })
609
610
      // optional::has_value
611
243
      .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("has_value"),
612
243
                                 transferOptionalHasValueCall)
613
614
      // optional::operator bool
615
243
      .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("operator bool"),
616
243
                                 transferOptionalHasValueCall)
617
618
      // optional::emplace
619
243
      .CaseOf<CXXMemberCallExpr>(
620
243
          isOptionalMemberCallWithName("emplace"),
621
243
          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
622
243
             LatticeTransferState &State) {
623
12
            assignOptionalValue(*E->getImplicitObjectArgument(), State,
624
12
                                State.Env.getBoolLiteralValue(true));
625
12
          })
626
627
      // optional::reset
628
243
      .CaseOf<CXXMemberCallExpr>(
629
243
          isOptionalMemberCallWithName("reset"),
630
243
          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
631
243
             LatticeTransferState &State) {
632
18
            assignOptionalValue(*E->getImplicitObjectArgument(), State,
633
18
                                State.Env.getBoolLiteralValue(false));
634
18
          })
635
636
      // optional::swap
637
243
      .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
638
243
                                 transferSwapCall)
639
640
      // std::swap
641
243
      .CaseOf<CallExpr>(isStdSwapCall(), transferStdSwapCall)
642
643
      // opt.value_or("").empty()
644
243
      .CaseOf<Expr>(isValueOrStringEmptyCall(), transferValueOrStringEmptyCall)
645
646
      // opt.value_or(X) != X
647
243
      .CaseOf<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
648
649
      // returns optional
650
243
      .CaseOf<CallExpr>(isCallReturningOptional(),
651
243
                        transferCallReturningOptional)
652
653
243
      .Build();
654
243
}
655
656
std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr,
657
                                               const Expr *ObjectExpr,
658
306
                                               const Environment &Env) {
659
306
  if (auto *OptionalVal =
660
306
          Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
661
306
    auto *Prop = OptionalVal->getProperty("has_value");
662
306
    if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
663
303
      if (Env.flowConditionImplies(*HasValueVal))
664
183
        return {};
665
303
    }
666
306
  }
667
668
  // Record that this unwrap is *not* provably safe.
669
  // FIXME: include either the name of the optional (if applicable) or a source
670
  // range of the access for easier interpretation of the result.
671
123
  return {ObjectExpr->getBeginLoc()};
672
306
}
673
674
auto buildDiagnoseMatchSwitch(
675
243
    const UncheckedOptionalAccessModelOptions &Options) {
676
  // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
677
  // lot of duplicated work (e.g. string comparisons), consider providing APIs
678
  // that avoid it through memoization.
679
243
  auto IgnorableOptional = ignorableOptional(Options);
680
243
  return MatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
681
      // optional::value
682
243
      .CaseOf<CXXMemberCallExpr>(
683
243
          valueCall(IgnorableOptional),
684
243
          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
685
258
             const Environment &Env) {
686
258
            return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env);
687
258
          })
688
689
      // optional::operator*, optional::operator->
690
243
      .CaseOf<CallExpr>(
691
243
          valueOperatorCall(IgnorableOptional),
692
243
          [](const CallExpr *E, const MatchFinder::MatchResult &,
693
243
             const Environment &Env) {
694
48
            return diagnoseUnwrapCall(E, E->getArg(0), Env);
695
48
          })
696
243
      .Build();
697
243
}
698
699
} // namespace
700
701
ast_matchers::DeclarationMatcher
702
0
UncheckedOptionalAccessModel::optionalClassDecl() {
703
0
  return optionalClass();
704
0
}
705
706
UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(
707
    ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options)
708
    : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
709
243
      TransferMatchSwitch(buildTransferMatchSwitch(Options)) {}
710
711
void UncheckedOptionalAccessModel::transfer(const Stmt *S, NoopLattice &L,
712
6.91k
                                            Environment &Env) {
713
6.91k
  LatticeTransferState State(L, Env);
714
6.91k
  TransferMatchSwitch(*S, getASTContext(), State);
715
6.91k
}
716
717
bool UncheckedOptionalAccessModel::compareEquivalent(QualType Type,
718
                                                     const Value &Val1,
719
                                                     const Environment &Env1,
720
                                                     const Value &Val2,
721
27
                                                     const Environment &Env2) {
722
27
  return isNonEmptyOptional(Val1, Env1) == isNonEmptyOptional(Val2, Env2);
723
27
}
724
725
bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
726
                                         const Environment &Env1,
727
                                         const Value &Val2,
728
                                         const Environment &Env2,
729
                                         Value &MergedVal,
730
142
                                         Environment &MergedEnv) {
731
142
  if (!IsOptionalType(Type))
732
46
    return true;
733
734
96
  auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
735
96
  if (isNonEmptyOptional(Val1, Env1) && 
isNonEmptyOptional(Val2, Env2)29
)
736
18
    MergedEnv.addToFlowCondition(HasValueVal);
737
78
  else if (isEmptyOptional(Val1, Env1) && 
isEmptyOptional(Val2, Env2)28
)
738
14
    MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal));
739
96
  setHasValue(MergedVal, HasValueVal);
740
96
  return true;
741
142
}
742
743
UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
744
    UncheckedOptionalAccessModelOptions Options)
745
243
    : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
746
747
std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose(
748
3.39k
    ASTContext &Context, const Stmt *Stmt, const Environment &Env) {
749
3.39k
  return DiagnoseMatchSwitch(*Stmt, Context, Env);
750
3.39k
}
751
752
} // namespace dataflow
753
} // namespace clang