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