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