clang 22.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
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/AST/Type.h"
23#include "clang/Analysis/CFG.h"
33#include "llvm/ADT/StringRef.h"
34#include "llvm/Support/ErrorHandling.h"
35#include <cassert>
36#include <optional>
37
38namespace clang {
39namespace dataflow {
40
41// Note: the Names appear in reverse order. E.g., to check
42// if NS is foo::bar::, call isFullyQualifiedNamespaceEqualTo(NS, "bar", "foo")
43template <class... NameTypes>
45 llvm::StringRef Name,
46 NameTypes... Names) {
47 if (!(NS.getDeclName().isIdentifier() && NS.getName() == Name &&
48 NS.getParent() != nullptr))
49 return false;
50
51 if constexpr (sizeof...(NameTypes) > 0) {
52 if (NS.getParent()->isTranslationUnit())
53 return false;
54 if (const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.getParent()))
55 return isFullyQualifiedNamespaceEqualTo(*NextNS, Names...);
56 return false;
57 } else {
58 return NS.getParent()->isTranslationUnit();
59 }
60}
61
62static bool hasOptionalClassName(const CXXRecordDecl &RD) {
63 if (!RD.getDeclName().isIdentifier())
64 return false;
65
66 if (RD.getName() == "optional") {
67 if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
68 return N->isStdNamespace() ||
70 return false;
71 }
72
73 if (RD.getName() == "Optional") {
74 // Check whether namespace is "::base" or "::folly".
75 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
76 return N != nullptr && (isFullyQualifiedNamespaceEqualTo(*N, "base") ||
78 }
79
80 if (RD.getName() == "Optional_Base") {
81 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
82 return N != nullptr &&
83 isFullyQualifiedNamespaceEqualTo(*N, "bslstl", "BloombergLP");
84 }
85
86 if (RD.getName() == "NullableValue") {
87 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
88 return N != nullptr &&
89 isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP");
90 }
91
92 return false;
93}
94
96 if (RD == nullptr)
97 return nullptr;
98 if (hasOptionalClassName(*RD))
99 return RD;
100
101 if (!RD->hasDefinition())
102 return nullptr;
103
104 for (const CXXBaseSpecifier &Base : RD->bases())
105 if (const CXXRecordDecl *BaseClass =
106 getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl()))
107 return BaseClass;
108
109 return nullptr;
110}
111
113 const CXXRecordDecl *Optional =
115 return Optional != nullptr;
116}
117
118namespace {
119
120using namespace ::clang::ast_matchers;
121
122using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>;
123
124AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); }
125
126AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
127 return getOptionalBaseClass(&Node) != nullptr;
128}
129
130auto desugarsToOptionalType() {
131 return hasUnqualifiedDesugaredType(
132 recordType(hasDeclaration(cxxRecordDecl(optionalClass()))));
133}
134
135auto desugarsToOptionalOrDerivedType() {
136 return hasUnqualifiedDesugaredType(
137 recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass()))));
138}
139
140auto hasOptionalType() { return hasType(desugarsToOptionalType()); }
141
142/// Matches any of the spellings of the optional types and sugar, aliases,
143/// derived classes, etc.
144auto hasOptionalOrDerivedType() {
145 return hasType(desugarsToOptionalOrDerivedType());
146}
147
148QualType getPublicType(const Expr *E) {
149 auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());
150 if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) {
151 QualType Ty = E->getType();
152 if (Ty->isPointerType())
153 return Ty->getPointeeType();
154 return Ty;
155 }
156
157 // Is the derived type that we're casting from the type of `*this`? In this
158 // special case, we can upcast to the base class even if the base is
159 // non-public.
160 bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr());
161
162 // Find the least-derived type in the path (i.e. the last entry in the list)
163 // that we can access.
164 const CXXBaseSpecifier *PublicBase = nullptr;
165 for (const CXXBaseSpecifier *Base : Cast->path()) {
166 if (Base->getAccessSpecifier() != AS_public && !CastingFromThis)
167 break;
168 PublicBase = Base;
169 CastingFromThis = false;
170 }
171
172 if (PublicBase != nullptr)
173 return PublicBase->getType();
174
175 // We didn't find any public type that we could cast to. There may be more
176 // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this
177 // will return the type of `getSubExpr()`.)
178 return getPublicType(Cast->getSubExpr());
179}
180
181// Returns the least-derived type for the receiver of `MCE` that
182// `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to.
183// Effectively, we upcast until we reach a non-public base class, unless that
184// base is a base of `*this`.
185//
186// This is needed to correctly match methods called on types derived from
187// `std::optional`.
188//
189// Say we have a `struct Derived : public std::optional<int> {} d;` For a call
190// `d.has_value()`, the `getImplicitObjectArgument()` looks like this:
191//
192// ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue
193// | <UncheckedDerivedToBase (optional -> __optional_storage_base)>
194// `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived'
195//
196// The type of the implicit object argument is `__optional_storage_base`
197// (since this is the internal type that `has_value()` is declared on). If we
198// call `IgnoreParenImpCasts()` on the implicit object argument, we get the
199// `DeclRefExpr`, which has type `Derived`. Neither of these types is
200// `optional`, and hence neither is sufficient for querying whether we are
201// calling a method on `optional`.
202//
203// Instead, starting with the most derived type, we need to follow the chain of
204// casts
205QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) {
206 return getPublicType(MCE.getImplicitObjectArgument());
207}
208
209AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType,
210 ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
211 return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder);
212}
213
214auto isOptionalMemberCallWithNameMatcher(
215 ast_matchers::internal::Matcher<NamedDecl> matcher,
216 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
217 return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable)))
218 : anything(),
219 publicReceiverType(desugarsToOptionalType()),
220 callee(cxxMethodDecl(matcher)));
221}
222
223auto isOptionalOperatorCallWithName(
224 llvm::StringRef operator_name,
225 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
226 return cxxOperatorCallExpr(
227 hasOverloadedOperatorName(operator_name),
228 callee(cxxMethodDecl(ofClass(optionalClass()))),
229 Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
230}
231
232auto isMakeOptionalCall() {
233 return callExpr(
235 "std::make_optional", "base::make_optional", "absl::make_optional",
236 "folly::make_optional", "bsl::make_optional"))),
237 hasOptionalType());
238}
239
240auto nulloptTypeDecl() {
241 return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",
242 "base::nullopt_t", "folly::None",
243 "bsl::nullopt_t"));
244}
245
246auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
247
248auto inPlaceClass() {
249 return namedDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
250 "base::in_place_t", "folly::in_place_t",
251 "bsl::in_place_t"));
252}
253
254auto isOptionalNulloptConstructor() {
255 return cxxConstructExpr(
256 hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
257 hasParameter(0, hasNulloptType()))),
258 hasOptionalOrDerivedType());
259}
260
261auto isOptionalInPlaceConstructor() {
262 return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())),
263 hasOptionalOrDerivedType());
264}
265
266auto isOptionalValueOrConversionConstructor() {
267 return cxxConstructExpr(
269 cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
270 argumentCountIs(1), hasArgument(0, unless(hasNulloptType())),
271 hasOptionalOrDerivedType());
272}
273
274auto isOptionalValueOrConversionAssignment() {
275 return cxxOperatorCallExpr(
277 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
279 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
280 argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
281}
282
283auto isOptionalNulloptAssignment() {
284 return cxxOperatorCallExpr(
286 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
287 argumentCountIs(2), hasArgument(1, hasNulloptType()));
288}
289
290auto isStdSwapCall() {
291 return callExpr(callee(functionDecl(hasName("std::swap"))),
292 argumentCountIs(2),
293 hasArgument(0, hasOptionalOrDerivedType()),
294 hasArgument(1, hasOptionalOrDerivedType()));
295}
296
297auto isStdForwardCall() {
298 return callExpr(callee(functionDecl(hasName("std::forward"))),
299 argumentCountIs(1),
300 hasArgument(0, hasOptionalOrDerivedType()));
301}
302
303constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
304
305auto isValueOrStringEmptyCall() {
306 // `opt.value_or("").empty()`
307 return cxxMemberCallExpr(
308 callee(cxxMethodDecl(hasName("empty"))),
309 onImplicitObjectArgument(ignoringImplicit(
311 callee(cxxMethodDecl(hasName("value_or"),
312 ofClass(optionalClass()))),
313 hasArgument(0, stringLiteral(hasSize(0))))
314 .bind(ValueOrCallID))));
315}
316
317auto isValueOrNotEqX() {
318 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
319 return hasOperands(
320 ignoringImplicit(
322 callee(cxxMethodDecl(hasName("value_or"),
323 ofClass(optionalClass()))),
324 hasArgument(0, Arg))
325 .bind(ValueOrCallID)),
326 ignoringImplicit(Arg));
327 };
328
329 // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
330 // support this pattern for any expression, but the AST does not have a
331 // generic expression comparison facility, so we specialize to common cases
332 // seen in practice. FIXME: define a matcher that compares values across
333 // nodes, which would let us generalize this to any `X`.
334 return binaryOperation(hasOperatorName("!="),
335 anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
336 ComparesToSame(stringLiteral(hasSize(0))),
337 ComparesToSame(integerLiteral(equals(0)))));
338}
339
340auto isZeroParamConstMemberCall() {
341 return cxxMemberCallExpr(
342 callee(cxxMethodDecl(parameterCountIs(0), isConst())));
343}
344
345auto isZeroParamConstMemberOperatorCall() {
346 return cxxOperatorCallExpr(
347 callee(cxxMethodDecl(parameterCountIs(0), isConst())));
348}
349
350auto isNonConstMemberCall() {
351 return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst()))));
352}
353
354auto isNonConstMemberOperatorCall() {
355 return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));
356}
357
358auto isCallReturningOptional() {
359 return callExpr(hasType(qualType(
360 anyOf(desugarsToOptionalOrDerivedType(),
361 referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
362}
363
364template <typename L, typename R>
365auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
366 return cxxOperatorCallExpr(
368 argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
369 hasArgument(1, rhs_arg_matcher));
370}
371
372/// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
373const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
374 auto *Value = Env.get<BoolValue>(Expr);
375 if (Value != nullptr)
376 return Value->formula();
377
378 Value = &Env.makeAtomicBoolValue();
379 Env.setValue(Expr, *Value);
380 return Value->formula();
381}
382
383StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) {
384 return OptionalLoc.getSyntheticField("has_value");
385}
386
387StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) {
388 return OptionalLoc.getSyntheticField("value");
389}
390
391/// Sets `HasValueVal` as the symbolic value that represents the "has_value"
392/// property of the optional at `OptionalLoc`.
393void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,
394 Environment &Env) {
395 Env.setValue(locForHasValue(OptionalLoc), HasValueVal);
396}
397
398/// Returns the symbolic value that represents the "has_value" property of the
399/// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null.
400BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {
401 if (OptionalLoc == nullptr)
402 return nullptr;
403 StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
404 auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);
405 if (HasValueVal == nullptr) {
406 HasValueVal = &Env.makeAtomicBoolValue();
407 Env.setValue(HasValueLoc, *HasValueVal);
408 }
409 return HasValueVal;
410}
411
412QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {
414 return CTSD.getTemplateArgs()[0].getAsType();
415}
416
417/// Returns the number of optional wrappers in `Type`.
418///
419/// For example, if `Type` is `optional<optional<int>>`, the result of this
420/// function will be 2.
421int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
422 const CXXRecordDecl *Optional =
423 getOptionalBaseClass(Type->getAsCXXRecordDecl());
424 if (Optional == nullptr)
425 return 0;
426 return 1 + countOptionalWrappers(
427 ASTCtx,
428 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
429}
430
431StorageLocation *getLocBehindPossiblePointer(const Expr &E,
432 const Environment &Env) {
433 if (E.isPRValue()) {
434 if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
435 return &PointerVal->getPointeeLoc();
436 return nullptr;
437 }
438 return Env.getStorageLocation(E);
439}
440
441void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
442 LatticeTransferState &State) {
443 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
444 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
445 if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)
446 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
447 }
448}
449
450void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
451 LatticeTransferState &State) {
452 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
453 getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
454 State.Env.setValue(
455 *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));
456}
457
458void transferMakeOptionalCall(const CallExpr *E,
459 const MatchFinder::MatchResult &,
460 LatticeTransferState &State) {
461 setHasValue(State.Env.getResultObjectLocation(*E),
462 State.Env.getBoolLiteralValue(true), State.Env);
463}
464
465void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
466 const MatchFinder::MatchResult &,
467 LatticeTransferState &State) {
468 if (auto *HasValueVal = getHasValue(
469 State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
470 State.Env.setValue(*CallExpr, *HasValueVal);
471 }
472}
473
474void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr,
475 const MatchFinder::MatchResult &,
476 LatticeTransferState &State) {
477 if (auto *HasValueVal = getHasValue(
478 State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
479 State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal));
480 }
481}
482
483/// `ModelPred` builds a logical formula relating the predicate in
484/// `ValueOrPredExpr` to the optional's `has_value` property.
485void transferValueOrImpl(
486 const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
487 LatticeTransferState &State,
488 const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
489 const Formula &HasValueVal)) {
490 auto &Env = State.Env;
491
492 const auto *MCE =
493 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);
494
495 auto *HasValueVal =
496 getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env));
497 if (HasValueVal == nullptr)
498 return;
499
500 Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
501 HasValueVal->formula()));
502}
503
504void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
505 const MatchFinder::MatchResult &Result,
506 LatticeTransferState &State) {
507 return transferValueOrImpl(ComparisonExpr, Result, State,
508 [](Environment &Env, const Formula &ExprVal,
509 const Formula &HasValueVal) -> const Formula & {
510 auto &A = Env.arena();
511 // If the result is *not* empty, then we know the
512 // optional must have been holding a value. If
513 // `ExprVal` is true, though, we don't learn
514 // anything definite about `has_value`, so we
515 // don't add any corresponding implications to
516 // the flow condition.
517 return A.makeImplies(A.makeNot(ExprVal),
518 HasValueVal);
519 });
520}
521
522void transferValueOrNotEqX(const Expr *ComparisonExpr,
523 const MatchFinder::MatchResult &Result,
524 LatticeTransferState &State) {
525 transferValueOrImpl(ComparisonExpr, Result, State,
526 [](Environment &Env, const Formula &ExprVal,
527 const Formula &HasValueVal) -> const Formula & {
528 auto &A = Env.arena();
529 // We know that if `(opt.value_or(X) != X)` then
530 // `opt.hasValue()`, even without knowing further
531 // details about the contents of `opt`.
532 return A.makeImplies(ExprVal, HasValueVal);
533 });
534}
535
536void transferCallReturningOptional(const CallExpr *E,
537 const MatchFinder::MatchResult &Result,
538 LatticeTransferState &State) {
539 RecordStorageLocation *Loc = nullptr;
540 if (E->isPRValue()) {
541 Loc = &State.Env.getResultObjectLocation(*E);
542 } else {
543 Loc = State.Env.get<RecordStorageLocation>(*E);
544 if (Loc == nullptr) {
545 Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));
546 State.Env.setStorageLocation(*E, *Loc);
547 }
548 }
549
550 if (State.Env.getValue(locForHasValue(*Loc)) != nullptr)
551 return;
552
553 setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
554}
555
556// Returns true if the const accessor is handled by caching.
557// Returns false if we could not cache. We should perform default handling
558// in that case.
559bool handleConstMemberCall(const CallExpr *CE,
560 dataflow::RecordStorageLocation *RecordLoc,
561 const MatchFinder::MatchResult &Result,
562 LatticeTransferState &State) {
563 if (RecordLoc == nullptr)
564 return false;
565
566 // Cache if the const method returns a reference.
567 if (CE->isGLValue()) {
568 const FunctionDecl *DirectCallee = CE->getDirectCallee();
569 if (DirectCallee == nullptr)
570 return false;
571
572 // Initialize the optional's "has_value" property to true if the type is
573 // optional, otherwise no-op. If we want to support const ref to pointers or
574 // bools we should initialize their values here too.
575 auto Init = [&](StorageLocation &Loc) {
576 if (isSupportedOptionalType(CE->getType()))
577 setHasValue(cast<RecordStorageLocation>(Loc),
578 State.Env.makeAtomicBoolValue(), State.Env);
579 };
580 StorageLocation &Loc =
581 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
582 *RecordLoc, DirectCallee, State.Env, Init);
583
584 State.Env.setStorageLocation(*CE, Loc);
585 return true;
586 }
587 // PRValue cases:
588 if (CE->getType()->isBooleanType() || CE->getType()->isPointerType()) {
589 // If the const method returns a boolean or pointer type.
590 Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE,
591 State.Env);
592 if (Val == nullptr)
593 return false;
594 State.Env.setValue(*CE, *Val);
595 return true;
596 }
597 if (isSupportedOptionalType(CE->getType())) {
598 // If the const method returns an optional by value.
599 const FunctionDecl *DirectCallee = CE->getDirectCallee();
600 if (DirectCallee == nullptr)
601 return false;
602 StorageLocation &Loc =
603 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
604 *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
605 setHasValue(cast<RecordStorageLocation>(Loc),
606 State.Env.makeAtomicBoolValue(), State.Env);
607 });
608 // Use copyRecord to link the optional to the result object of the call
609 // expression.
610 auto &ResultLoc = State.Env.getResultObjectLocation(*CE);
611 copyRecord(cast<RecordStorageLocation>(Loc), ResultLoc, State.Env);
612 return true;
613 }
614
615 return false;
616}
617
618void handleConstMemberCallWithFallbacks(
619 const CallExpr *CE, dataflow::RecordStorageLocation *RecordLoc,
620 const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
621 if (handleConstMemberCall(CE, RecordLoc, Result, State))
622 return;
623 // Perform default handling if the call returns an optional, but wasn't
624 // handled by caching.
625 if (isSupportedOptionalType(CE->getType()))
626 transferCallReturningOptional(CE, Result, State);
627}
628
629void transferConstMemberCall(const CXXMemberCallExpr *MCE,
630 const MatchFinder::MatchResult &Result,
631 LatticeTransferState &State) {
632 handleConstMemberCallWithFallbacks(
633 MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
634}
635
636void transferConstMemberOperatorCall(const CXXOperatorCallExpr *OCE,
637 const MatchFinder::MatchResult &Result,
638 LatticeTransferState &State) {
639 auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
640 State.Env.getStorageLocation(*OCE->getArg(0)));
641 handleConstMemberCallWithFallbacks(OCE, RecordLoc, Result, State);
642}
643
644void handleNonConstMemberCall(const CallExpr *CE,
645 dataflow::RecordStorageLocation *RecordLoc,
646 const MatchFinder::MatchResult &Result,
647 LatticeTransferState &State) {
648 if (RecordLoc != nullptr) {
649 // When a non-const member function is called, clear all (non-const)
650 // optional fields of the receiver. Const-qualified fields can't be
651 // changed (at least, not without UB).
652 for (const auto &[Field, FieldLoc] : RecordLoc->children()) {
653 QualType FieldType = Field->getType();
654 if (!FieldType.isConstQualified() &&
655 isSupportedOptionalType(Field->getType())) {
656 auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
657 if (FieldRecordLoc) {
658 setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),
659 State.Env);
660 }
661 }
662 }
663 State.Lattice.clearConstMethodReturnValues(*RecordLoc);
664 State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc);
665 }
666
667 // Perform default handling if the call returns an optional.
668 if (isSupportedOptionalType(CE->getType())) {
669 transferCallReturningOptional(CE, Result, State);
670 }
671}
672
673void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE,
674 const MatchFinder::MatchResult &Result,
675 LatticeTransferState &State) {
676 handleNonConstMemberCall(
677 MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
678}
679
680void transferValue_NonConstMemberOperatorCall(
681 const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result,
682 LatticeTransferState &State) {
683 auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
684 State.Env.getStorageLocation(*OCE->getArg(0)));
685 handleNonConstMemberCall(OCE, RecordLoc, Result, State);
686}
687
688void constructOptionalValue(const Expr &E, Environment &Env,
689 BoolValue &HasValueVal) {
690 RecordStorageLocation &Loc = Env.getResultObjectLocation(E);
691 setHasValue(Loc, HasValueVal, Env);
692}
693
694/// Returns a symbolic value for the "has_value" property of an `optional<T>`
695/// value that is constructed/assigned from a value of type `U` or `optional<U>`
696/// where `T` is constructible from `U`.
697BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E,
698 const MatchFinder::MatchResult &MatchRes,
699 LatticeTransferState &State) {
700 const int DestTypeOptionalWrappersCount =
701 countOptionalWrappers(*MatchRes.Context, DestType);
702 const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
703 *MatchRes.Context, E.getType().getNonReferenceType());
704
705 // Is this an constructor of the form `template<class U> optional(U &&)` /
706 // assignment of the form `template<class U> optional& operator=(U &&)`
707 // (where `T` is assignable / constructible from `U`)?
708 // We recognize this because the number of optionals in the optional being
709 // assigned to is different from the function argument type.
710 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)
711 return State.Env.getBoolLiteralValue(true);
712
713 // Otherwise, this must be a constructor of the form
714 // `template <class U> optional<optional<U> &&)` / assignment of the form
715 // `template <class U> optional& operator=(optional<U> &&)
716 // (where, again, `T` is assignable / constructible from `U`).
717 auto *Loc = State.Env.get<RecordStorageLocation>(E);
718 if (auto *HasValueVal = getHasValue(State.Env, Loc))
719 return *HasValueVal;
720 return State.Env.makeAtomicBoolValue();
721}
722
723void transferValueOrConversionConstructor(
724 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
725 LatticeTransferState &State) {
726 assert(E->getNumArgs() > 0);
727
728 constructOptionalValue(
729 *E, State.Env,
730 valueOrConversionHasValue(
731 E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0),
732 MatchRes, State));
733}
734
735void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
736 LatticeTransferState &State) {
737 assert(E->getNumArgs() > 0);
738
739 if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) {
740 setHasValue(*Loc, HasValueVal, State.Env);
741
742 // Assign a storage location for the whole expression.
743 State.Env.setStorageLocation(*E, *Loc);
744 }
745}
746
747void transferValueOrConversionAssignment(
748 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
749 LatticeTransferState &State) {
750 assert(E->getNumArgs() > 1);
751 transferAssignment(
752 E,
753 valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(),
754 *E->getArg(1), MatchRes, State),
755 State);
756}
757
758void transferNulloptAssignment(const CXXOperatorCallExpr *E,
759 const MatchFinder::MatchResult &,
760 LatticeTransferState &State) {
761 transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
762}
763
764void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
765 Environment &Env) {
766 // We account for cases where one or both of the optionals are not modeled,
767 // either lacking associated storage locations, or lacking values associated
768 // to such storage locations.
769
770 if (Loc1 == nullptr) {
771 if (Loc2 != nullptr)
772 setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env);
773 return;
774 }
775 if (Loc2 == nullptr) {
776 setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env);
777 return;
778 }
779
780 // Both expressions have locations, though they may not have corresponding
781 // values. In that case, we create a fresh value at this point. Note that if
782 // two branches both do this, they will not share the value, but it at least
783 // allows for local reasoning about the value. To avoid the above, we would
784 // need *lazy* value allocation.
785 // FIXME: allocate values lazily, instead of just creating a fresh value.
786 BoolValue *BoolVal1 = getHasValue(Env, Loc1);
787 if (BoolVal1 == nullptr)
788 BoolVal1 = &Env.makeAtomicBoolValue();
789
790 BoolValue *BoolVal2 = getHasValue(Env, Loc2);
791 if (BoolVal2 == nullptr)
792 BoolVal2 = &Env.makeAtomicBoolValue();
793
794 setHasValue(*Loc1, *BoolVal2, Env);
795 setHasValue(*Loc2, *BoolVal1, Env);
796}
797
798void transferSwapCall(const CXXMemberCallExpr *E,
799 const MatchFinder::MatchResult &,
800 LatticeTransferState &State) {
801 assert(E->getNumArgs() == 1);
802 auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
803 transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
804}
805
806void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
807 LatticeTransferState &State) {
808 assert(E->getNumArgs() == 2);
809 auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
810 auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1));
811 transferSwap(Arg0Loc, Arg1Loc, State.Env);
812}
813
814void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
815 LatticeTransferState &State) {
816 assert(E->getNumArgs() == 1);
817
818 if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
819 State.Env.setStorageLocation(*E, *Loc);
820}
821
822const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
823 const Formula &LHS, const Formula &RHS) {
824 // Logically, an optional<T> object is composed of two values - a `has_value`
825 // bit and a value of type T. Equality of optional objects compares both
826 // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
827 // when two optional objects are engaged, the equality of their respective
828 // values of type T matters. Since we only track the `has_value` bits, we
829 // can't make any conclusions about equality when we know that two optional
830 // objects are engaged.
831 //
832 // We express this as two facts about the equality:
833 // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
834 // If they are equal, then either both are set or both are unset.
835 // b) (!LHS & !RHS) => EqVal
836 // If neither is set, then they are equal.
837 // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
838 return A.makeAnd(
839 A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
840 A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
841 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
842}
843
844void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
845 const MatchFinder::MatchResult &,
846 LatticeTransferState &State) {
847 Environment &Env = State.Env;
848 auto &A = Env.arena();
849 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
850 auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0));
851 if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {
852 auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1));
853 if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {
854 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
855 CmpValue = &A.makeNot(*CmpValue);
856 Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
857 RHasVal->formula()));
858 }
859 }
860}
861
862void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
863 const clang::Expr *E, Environment &Env) {
864 auto &A = Env.arena();
865 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
866 auto *Loc = Env.get<RecordStorageLocation>(*E);
867 if (auto *HasVal = getHasValue(Env, Loc)) {
868 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
869 CmpValue = &A.makeNot(*CmpValue);
870 Env.assume(
871 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
872 }
873}
874
875void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,
876 const clang::Expr *E, Environment &Env) {
877 auto &A = Env.arena();
878 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
879 auto *Loc = Env.get<RecordStorageLocation>(*E);
880 if (auto *HasVal = getHasValue(Env, Loc)) {
881 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
882 CmpValue = &A.makeNot(*CmpValue);
883 Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
884 A.makeLiteral(false)));
885 }
886}
887
888std::optional<StatementMatcher>
889ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
890 if (Options.IgnoreSmartPointerDereference) {
891 auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
893 unless(hasArgument(0, expr(hasOptionalType()))))));
894 return expr(
895 anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
896 }
897 return std::nullopt;
898}
899
901valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
902 return isOptionalMemberCallWithNameMatcher(hasName("value"),
903 IgnorableOptional);
904}
905
907valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
908 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
909 isOptionalOperatorCallWithName("->", IgnorableOptional)));
910}
911
913 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
914 // lot of duplicated work (e.g. string comparisons), consider providing APIs
915 // that avoid it through memoization.
917 // make_optional
918 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
919
920 // optional::optional (in place)
921 .CaseOfCFGStmt<CXXConstructExpr>(
922 isOptionalInPlaceConstructor(),
923 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
924 LatticeTransferState &State) {
925 constructOptionalValue(*E, State.Env,
926 State.Env.getBoolLiteralValue(true));
927 })
928 // optional::optional(nullopt_t)
929 .CaseOfCFGStmt<CXXConstructExpr>(
930 isOptionalNulloptConstructor(),
931 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
932 LatticeTransferState &State) {
933 constructOptionalValue(*E, State.Env,
934 State.Env.getBoolLiteralValue(false));
935 })
936 // optional::optional (value/conversion)
937 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
938 transferValueOrConversionConstructor)
939
940 // optional::operator=
941 .CaseOfCFGStmt<CXXOperatorCallExpr>(
942 isOptionalValueOrConversionAssignment(),
943 transferValueOrConversionAssignment)
944 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
945 transferNulloptAssignment)
946
947 // optional::value
948 .CaseOfCFGStmt<CXXMemberCallExpr>(
949 valueCall(std::nullopt),
950 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
951 LatticeTransferState &State) {
952 transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
953 })
954
955 // optional::operator*
956 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
957 [](const CallExpr *E,
958 const MatchFinder::MatchResult &,
959 LatticeTransferState &State) {
960 transferUnwrapCall(E, E->getArg(0), State);
961 })
962
963 // optional::operator->
964 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
965 [](const CallExpr *E,
966 const MatchFinder::MatchResult &,
967 LatticeTransferState &State) {
968 transferArrowOpCall(E, E->getArg(0), State);
969 })
970
971 // optional::has_value, optional::hasValue
972 // Of the supported optionals only folly::Optional uses hasValue, but this
973 // will also pass for other types
974 .CaseOfCFGStmt<CXXMemberCallExpr>(
975 isOptionalMemberCallWithNameMatcher(
976 hasAnyName("has_value", "hasValue")),
977 transferOptionalHasValueCall)
978
979 // optional::operator bool
980 .CaseOfCFGStmt<CXXMemberCallExpr>(
981 isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
982 transferOptionalHasValueCall)
983
984 // NullableValue::isNull
985 // Only NullableValue has isNull
986 .CaseOfCFGStmt<CXXMemberCallExpr>(
987 isOptionalMemberCallWithNameMatcher(hasName("isNull")),
988 transferOptionalIsNullCall)
989
990 // NullableValue::makeValue, NullableValue::makeValueInplace
991 // Only NullableValue has these methods, but this
992 // will also pass for other types
993 .CaseOfCFGStmt<CXXMemberCallExpr>(
994 isOptionalMemberCallWithNameMatcher(
995 hasAnyName("makeValue", "makeValueInplace")),
996 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
997 LatticeTransferState &State) {
998 if (RecordStorageLocation *Loc =
999 getImplicitObjectLocation(*E, State.Env)) {
1000 setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);
1001 }
1002 })
1003
1004 // optional::emplace
1005 .CaseOfCFGStmt<CXXMemberCallExpr>(
1006 isOptionalMemberCallWithNameMatcher(hasName("emplace")),
1007 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
1008 LatticeTransferState &State) {
1009 if (RecordStorageLocation *Loc =
1010 getImplicitObjectLocation(*E, State.Env)) {
1011 setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);
1012 }
1013 })
1014
1015 // optional::reset
1016 .CaseOfCFGStmt<CXXMemberCallExpr>(
1017 isOptionalMemberCallWithNameMatcher(hasName("reset")),
1018 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
1019 LatticeTransferState &State) {
1020 if (RecordStorageLocation *Loc =
1021 getImplicitObjectLocation(*E, State.Env)) {
1022 setHasValue(*Loc, State.Env.getBoolLiteralValue(false),
1023 State.Env);
1024 }
1025 })
1026
1027 // optional::swap
1028 .CaseOfCFGStmt<CXXMemberCallExpr>(
1029 isOptionalMemberCallWithNameMatcher(hasName("swap")),
1030 transferSwapCall)
1031
1032 // std::swap
1033 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
1034
1035 // std::forward
1036 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
1037
1038 // opt.value_or("").empty()
1039 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
1040 transferValueOrStringEmptyCall)
1041
1042 // opt.value_or(X) != X
1043 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
1044
1045 // Comparisons (==, !=):
1046 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1047 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
1048 transferOptionalAndOptionalCmp)
1049 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1050 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
1051 [](const clang::CXXOperatorCallExpr *Cmp,
1052 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1053 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);
1054 })
1055 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1056 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
1057 [](const clang::CXXOperatorCallExpr *Cmp,
1058 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1059 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);
1060 })
1061 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1062 isComparisonOperatorCall(
1063 hasOptionalType(),
1064 unless(anyOf(hasOptionalType(), hasNulloptType()))),
1065 [](const clang::CXXOperatorCallExpr *Cmp,
1066 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1067 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
1068 })
1069 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1070 isComparisonOperatorCall(
1071 unless(anyOf(hasOptionalType(), hasNulloptType())),
1072 hasOptionalType()),
1073 [](const clang::CXXOperatorCallExpr *Cmp,
1074 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1075 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
1076 })
1077
1078 // Smart-pointer-like operator* and operator-> calls that may look like
1079 // const accessors (below) but need special handling to allow mixing
1080 // the accessor calls.
1081 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1083 [](const CXXOperatorCallExpr *E,
1084 const MatchFinder::MatchResult &Result,
1085 LatticeTransferState &State) {
1087 E,
1088 dyn_cast_or_null<RecordStorageLocation>(
1089 getLocBehindPossiblePointer(*E->getArg(0), State.Env)),
1090 State, [](StorageLocation &Loc) {});
1091 })
1092 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1094 [](const CXXOperatorCallExpr *E,
1095 const MatchFinder::MatchResult &Result,
1096 LatticeTransferState &State) {
1098 E,
1099 dyn_cast_or_null<RecordStorageLocation>(
1100 getLocBehindPossiblePointer(*E->getArg(0), State.Env)),
1101 State, [](StorageLocation &Loc) {});
1102 })
1103 .CaseOfCFGStmt<CXXMemberCallExpr>(
1105 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
1106 LatticeTransferState &State) {
1108 E, getImplicitObjectLocation(*E, State.Env), State,
1109 [](StorageLocation &Loc) {});
1110 })
1111 .CaseOfCFGStmt<CXXMemberCallExpr>(
1113 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
1114 LatticeTransferState &State) {
1116 E, getImplicitObjectLocation(*E, State.Env), State,
1117 [](StorageLocation &Loc) {});
1118 })
1119
1120 // const accessor calls
1121 .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),
1122 transferConstMemberCall)
1123 .CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(),
1124 transferConstMemberOperatorCall)
1125 // non-const member calls that may modify the state of an object.
1126 .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
1127 transferValue_NonConstMemberCall)
1128 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1129 isNonConstMemberOperatorCall(),
1130 transferValue_NonConstMemberOperatorCall)
1131
1132 // other cases of returning optional
1133 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
1134 transferCallReturningOptional)
1135
1136 .Build();
1137}
1138
1139llvm::SmallVector<UncheckedOptionalAccessDiagnostic>
1140diagnoseUnwrapCall(const Expr *ObjectExpr, const Environment &Env) {
1141 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
1142 getLocBehindPossiblePointer(*ObjectExpr, Env))) {
1143 auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
1144 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
1145 if (Env.proves(HasValueVal->formula()))
1146 return {};
1147 }
1148 }
1149
1150 // Record that this unwrap is *not* provably safe.
1151 // FIXME: include the name of the optional (if applicable).
1152 auto Range = CharSourceRange::getTokenRange(ObjectExpr->getSourceRange());
1154}
1155
1156auto buildDiagnoseMatchSwitch(
1157 const UncheckedOptionalAccessModelOptions &Options) {
1158 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
1159 // lot of duplicated work (e.g. string comparisons), consider providing APIs
1160 // that avoid it through memoization.
1161 const auto IgnorableOptional = ignorableOptional(Options);
1162
1163 auto DiagBuilder =
1165 const Environment,
1166 llvm::SmallVector<UncheckedOptionalAccessDiagnostic>>()
1167 // optional::operator*, optional::operator->
1168 .CaseOfCFGStmt<CallExpr>(
1169 valueOperatorCall(IgnorableOptional),
1170 [](const CallExpr *E, const MatchFinder::MatchResult &,
1171 const Environment &Env) {
1172 return diagnoseUnwrapCall(E->getArg(0), Env);
1173 });
1174
1175 auto Builder = Options.IgnoreValueCalls
1176 ? std::move(DiagBuilder)
1177 : std::move(DiagBuilder)
1178 // optional::value
1179 .CaseOfCFGStmt<CXXMemberCallExpr>(
1180 valueCall(IgnorableOptional),
1181 [](const CXXMemberCallExpr *E,
1182 const MatchFinder::MatchResult &,
1183 const Environment &Env) {
1184 return diagnoseUnwrapCall(
1185 E->getImplicitObjectArgument(), Env);
1186 });
1187
1188 return std::move(Builder).Build();
1189}
1190
1191} // namespace
1192
1197
1199 Environment &Env)
1202 TransferMatchSwitch(buildTransferMatchSwitch()) {
1203 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
1204 [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
1205 const CXXRecordDecl *Optional =
1207 if (Optional == nullptr)
1208 return {};
1209 return {{"value", valueTypeFromOptionalDecl(*Optional)},
1210 {"has_value", Ctx.BoolTy}};
1211 });
1212}
1213
1216 Environment &Env) {
1217 LatticeTransferState State(L, Env);
1218 TransferMatchSwitch(Elt, getASTContext(), State);
1219}
1220
1223 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
1224
1225} // namespace dataflow
1226} // namespace clang
Defines the clang::ASTContext interface.
#define AST_MATCHER(Type, DefineMatcher)
AST_MATCHER(Type, DefineMatcher) { ... } defines a zero parameter function named DefineMatcher() that...
#define AST_MATCHER_P(Type, DefineMatcher, ParamType, Param)
AST_MATCHER_P(Type, DefineMatcher, ParamType, Param) { ... } defines a single-parameter function name...
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::Expr interface and subclasses for C++ expressions.
llvm::MachO::RecordLoc RecordLoc
Definition MachO.h:41
Defines an enumeration for C++ overloaded operators.
MatchFinder::MatchResult MatchResult
Defines the clang::SourceLocation class and associated facilities.
C Language Family Type Representation.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:220
CanQualType BoolTy
Represents a top-level expression in a basic block.
Definition CFG.h:55
Represents a base class of a C++ class.
Definition DeclCXX.h:146
OverloadedOperatorKind getOperator() const
Returns the kind of overloaded operator that this expression refers to.
Definition ExprCXX.h:114
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
base_class_range bases()
Definition DeclCXX.h:608
bool hasDefinition() const
Definition DeclCXX.h:561
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3081
static CharSourceRange getTokenRange(SourceRange R)
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2109
bool isTranslationUnit() const
Definition DeclBase.h:2185
DeclContext * getDeclContext()
Definition DeclBase.h:448
bool isIdentifier() const
Predicate functions for querying what type of name this is.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:301
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition Decl.h:340
Represent a C++ namespace.
Definition Decl.h:592
A (possibly-)qualified type.
Definition TypeBase.h:937
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
The Arena owns the objects that model data within an analysis.
Definition Arena.h:21
Models a boolean.
Definition Value.h:94
Collects cases of a "match switch": a collection of matchers paired with callbacks,...
CFGMatchSwitchBuilder && CaseOfCFGStmt(MatchSwitchMatcher< Stmt > M, MatchSwitchAction< NodeT, State, Result > A) &&
Registers an action A for CFGStmts that will be triggered by the match of the pattern M against the S...
CFGMatchSwitch< State, Result > Build() &&
Holds the state of the program (store and heap) at a given program point.
DataflowAnalysisContext & getDataflowAnalysisContext() const
Returns the DataflowAnalysisContext used by the environment.
Models a symbolic pointer. Specifically, any value of type T*.
Definition Value.h:170
A storage location for a record (struct, class, or union).
Base class for elements of the local variable store and of the heap.
UncheckedOptionalAccessDiagnoser(UncheckedOptionalAccessModelOptions Options={})
UncheckedOptionalAccessModel(ASTContext &Ctx, dataflow::Environment &Env)
void transfer(const CFGElement &Elt, UncheckedOptionalAccessLattice &L, Environment &Env)
static ast_matchers::DeclarationMatcher optionalClassDecl()
Returns a matcher for the optional classes covered by this model.
Base class for all values computed by abstract interpretation.
Definition Value.h:33
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
internal::Matcher< Decl > DeclarationMatcher
Types of matchers for the top-level classes in the AST class hierarchy.
const internal::VariadicDynCastAllOfMatcher< Stmt, StringLiteral > stringLiteral
Matches string literals (also matches wide string literals).
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, NamedDecl > namedDecl
Matches a declaration of anything that could have a name.
internal::TrueMatcher anything()
Matches any node.
const internal::VariadicFunction< internal::Matcher< NamedDecl >, StringRef, internal::hasAnyNameFunc > hasAnyName
Matches NamedDecl nodes that have any of the specified names.
const internal::MapAnyOfMatcher< BinaryOperator, CXXOperatorCallExpr, CXXRewrittenBinaryOperator > binaryOperation
Matches nodes which can be used with binary operators.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXConstructorDecl > cxxConstructorDecl
Matches C++ constructor declarations.
internal::Matcher< Stmt > StatementMatcher
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXConstructExpr > cxxConstructExpr
Matches constructor call expressions (including implicit ones).
const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl
Matches function declarations.
const AstTypeMatcher< RecordType > recordType
const internal::VariadicDynCastAllOfMatcher< Stmt, MemberExpr > memberExpr
Matches member expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl
Matches C++ class declarations.
const internal::VariadicDynCastAllOfMatcher< Stmt, IntegerLiteral > integerLiteral
Matches integer literals of all sizes / encodings, e.g.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXNullPtrLiteralExpr > cxxNullPtrLiteralExpr
Matches nullptr literal.
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.
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf
Matches if any of the given matchers matches.
const internal::VariadicAllOfMatcher< QualType > qualType
Matches QualTypes in the clang AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXThisExpr > cxxThisExpr
Matches implicit and explicit this expressions.
TransferState< UncheckedStatusOrAccessModel::Lattice > LatticeTransferState
CFGMatchSwitch< LatticeTransferState > buildTransferMatchSwitch(ASTContext &Ctx, CFGMatchSwitchBuilder< LatticeTransferState > Builder)
Dataflow Directional Tag Classes.
Definition AdornedCFG.h:29
ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall(clang::StringRef MethodName="value")
static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS, llvm::StringRef Name, NameTypes... Names)
void transferSmartPointerLikeCachedDeref(const CallExpr *DerefExpr, RecordStorageLocation *SmartPointerLoc, TransferState< LatticeT > &State, llvm::function_ref< void(StorageLocation &)> InitializeLoc)
A transfer function for operator* (and value) calls that can be cached.
static bool hasOptionalClassName(const CXXRecordDecl &RD)
void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst, Environment &Env, QualType TypeToCopy=QualType())
Copies a record (struct, class, or union) from Src to Dst.
Definition RecordOps.cpp:57
void transferSmartPointerLikeCachedGet(const CallExpr *GetExpr, RecordStorageLocation *SmartPointerLoc, TransferState< LatticeT > &State, llvm::function_ref< void(StorageLocation &)> InitializeLoc)
A transfer function for operator-> (and get) calls that can be cached.
internal::Matcher< NamedDecl > hasName(StringRef Name)
Matches NamedDecl nodes that have the specified name.
CachedConstAccessorsLattice< NoopLattice > UncheckedOptionalAccessLattice
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXMemberCallExpr > cxxMemberCallExpr
Matches member call expressions.
static bool isSupportedOptionalType(QualType Ty)
ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow()
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXOperatorCallExpr > cxxOperatorCallExpr
Matches overloaded operator calls.
internal::PolymorphicMatcher< internal::HasOverloadedOperatorNameMatcher, AST_POLYMORPHIC_SUPPORTED_TYPES(CXXOperatorCallExpr, FunctionDecl), std::vector< std::string > > hasOverloadedOperatorName(StringRef Name)
Matches overloaded operator names.
RecordStorageLocation * getImplicitObjectLocation(const CXXMemberCallExpr &MCE, const Environment &Env)
Returns the storage location for the implicit object of a CXXMemberCallExpr, or null if none is defin...
static const CXXRecordDecl * getOptionalBaseClass(const CXXRecordDecl *RD)
ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar()
const internal::VariadicDynCastAllOfMatcher< Decl, CXXMethodDecl > cxxMethodDecl
Matches method declarations.
ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall(clang::StringRef MethodName="get")
const AstTypeMatcher< ReferenceType > referenceType
bool Cast(InterpState &S, CodePtr OpPC)
Definition Interp.h:2492
bool equals(const til::SExpr *E1, const til::SExpr *E2)
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
@ AS_public
Definition Specifiers.h:124
@ Result
The result type of a method or function.
Definition TypeBase.h:905
@ Type
The name was classified as a type.
Definition Sema.h:563
U cast(CodeGen::Address addr)
Definition Address.h:327
Diagnostic information for an unchecked optional access.