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