clang 19.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"
22#include "clang/Analysis/CFG.h"
30#include "llvm/ADT/StringRef.h"
31#include "llvm/Support/Casting.h"
32#include "llvm/Support/ErrorHandling.h"
33#include <cassert>
34#include <memory>
35#include <optional>
36#include <utility>
37
38namespace clang {
39namespace dataflow {
40
42 llvm::StringRef Name) {
43 return NS.getDeclName().isIdentifier() && NS.getName() == Name &&
44 NS.getParent() != nullptr && NS.getParent()->isTranslationUnit();
45}
46
47static bool hasOptionalClassName(const CXXRecordDecl &RD) {
48 if (!RD.getDeclName().isIdentifier())
49 return false;
50
51 if (RD.getName() == "optional") {
52 if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
53 return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl");
54 return false;
55 }
56
57 if (RD.getName() == "Optional") {
58 // Check whether namespace is "::base" or "::folly".
59 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
60 return N != nullptr && (isTopLevelNamespaceWithName(*N, "base") ||
61 isTopLevelNamespaceWithName(*N, "folly"));
62 }
63
64 return false;
65}
66
68 if (RD == nullptr)
69 return nullptr;
70 if (hasOptionalClassName(*RD))
71 return RD;
72
73 if (!RD->hasDefinition())
74 return nullptr;
75
76 for (const CXXBaseSpecifier &Base : RD->bases())
77 if (const CXXRecordDecl *BaseClass =
78 getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl()))
79 return BaseClass;
80
81 return nullptr;
82}
83
84namespace {
85
86using namespace ::clang::ast_matchers;
87using LatticeTransferState = TransferState<NoopLattice>;
88
89AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); }
90
91AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
92 return getOptionalBaseClass(&Node) != nullptr;
93}
94
95auto desugarsToOptionalType() {
96 return hasUnqualifiedDesugaredType(
97 recordType(hasDeclaration(cxxRecordDecl(optionalClass()))));
98}
99
100auto desugarsToOptionalOrDerivedType() {
101 return hasUnqualifiedDesugaredType(
102 recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass()))));
103}
104
105auto hasOptionalType() { return hasType(desugarsToOptionalType()); }
106
107/// Matches any of the spellings of the optional types and sugar, aliases,
108/// derived classes, etc.
109auto hasOptionalOrDerivedType() {
110 return hasType(desugarsToOptionalOrDerivedType());
111}
112
113QualType getPublicType(const Expr *E) {
114 auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());
115 if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) {
116 QualType Ty = E->getType();
117 if (Ty->isPointerType())
118 return Ty->getPointeeType();
119 return Ty;
120 }
121
122 // Is the derived type that we're casting from the type of `*this`? In this
123 // special case, we can upcast to the base class even if the base is
124 // non-public.
125 bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr());
126
127 // Find the least-derived type in the path (i.e. the last entry in the list)
128 // that we can access.
129 const CXXBaseSpecifier *PublicBase = nullptr;
130 for (const CXXBaseSpecifier *Base : Cast->path()) {
131 if (Base->getAccessSpecifier() != AS_public && !CastingFromThis)
132 break;
133 PublicBase = Base;
134 CastingFromThis = false;
135 }
136
137 if (PublicBase != nullptr)
138 return PublicBase->getType();
139
140 // We didn't find any public type that we could cast to. There may be more
141 // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this
142 // will return the type of `getSubExpr()`.)
143 return getPublicType(Cast->getSubExpr());
144}
145
146// Returns the least-derived type for the receiver of `MCE` that
147// `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to.
148// Effectively, we upcast until we reach a non-public base class, unless that
149// base is a base of `*this`.
150//
151// This is needed to correctly match methods called on types derived from
152// `std::optional`.
153//
154// Say we have a `struct Derived : public std::optional<int> {} d;` For a call
155// `d.has_value()`, the `getImplicitObjectArgument()` looks like this:
156//
157// ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue
158// | <UncheckedDerivedToBase (optional -> __optional_storage_base)>
159// `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived'
160//
161// The type of the implicit object argument is `__optional_storage_base`
162// (since this is the internal type that `has_value()` is declared on). If we
163// call `IgnoreParenImpCasts()` on the implicit object argument, we get the
164// `DeclRefExpr`, which has type `Derived`. Neither of these types is
165// `optional`, and hence neither is sufficient for querying whether we are
166// calling a method on `optional`.
167//
168// Instead, starting with the most derived type, we need to follow the chain of
169// casts
170QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) {
171 return getPublicType(MCE.getImplicitObjectArgument());
172}
173
174AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType,
175 ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
176 return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder);
177}
178
179auto isOptionalMemberCallWithNameMatcher(
180 ast_matchers::internal::Matcher<NamedDecl> matcher,
181 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
182 return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable)))
183 : anything(),
184 publicReceiverType(desugarsToOptionalType()),
185 callee(cxxMethodDecl(matcher)));
186}
187
188auto isOptionalOperatorCallWithName(
189 llvm::StringRef operator_name,
190 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
191 return cxxOperatorCallExpr(
192 hasOverloadedOperatorName(operator_name),
193 callee(cxxMethodDecl(ofClass(optionalClass()))),
194 Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
195}
196
197auto isMakeOptionalCall() {
198 return callExpr(callee(functionDecl(hasAnyName(
199 "std::make_optional", "base::make_optional",
200 "absl::make_optional", "folly::make_optional"))),
201 hasOptionalType());
202}
203
204auto nulloptTypeDecl() {
205 return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",
206 "base::nullopt_t", "folly::None"));
207}
208
209auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
210
211auto inPlaceClass() {
212 return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
213 "base::in_place_t", "folly::in_place_t"));
214}
215
216auto isOptionalNulloptConstructor() {
217 return cxxConstructExpr(
218 hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
219 hasParameter(0, hasNulloptType()))),
220 hasOptionalOrDerivedType());
221}
222
223auto isOptionalInPlaceConstructor() {
224 return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())),
225 hasOptionalOrDerivedType());
226}
227
228auto isOptionalValueOrConversionConstructor() {
229 return cxxConstructExpr(
231 cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
232 argumentCountIs(1), hasArgument(0, unless(hasNulloptType())),
233 hasOptionalOrDerivedType());
234}
235
236auto isOptionalValueOrConversionAssignment() {
237 return cxxOperatorCallExpr(
239 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
241 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
242 argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
243}
244
245auto isOptionalNulloptAssignment() {
246 return cxxOperatorCallExpr(
248 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
249 argumentCountIs(2), hasArgument(1, hasNulloptType()));
250}
251
252auto isStdSwapCall() {
253 return callExpr(callee(functionDecl(hasName("std::swap"))),
254 argumentCountIs(2),
255 hasArgument(0, hasOptionalOrDerivedType()),
256 hasArgument(1, hasOptionalOrDerivedType()));
257}
258
259auto isStdForwardCall() {
260 return callExpr(callee(functionDecl(hasName("std::forward"))),
261 argumentCountIs(1),
262 hasArgument(0, hasOptionalOrDerivedType()));
263}
264
265constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
266
267auto isValueOrStringEmptyCall() {
268 // `opt.value_or("").empty()`
269 return cxxMemberCallExpr(
270 callee(cxxMethodDecl(hasName("empty"))),
271 onImplicitObjectArgument(ignoringImplicit(
273 callee(cxxMethodDecl(hasName("value_or"),
274 ofClass(optionalClass()))),
275 hasArgument(0, stringLiteral(hasSize(0))))
276 .bind(ValueOrCallID))));
277}
278
279auto isValueOrNotEqX() {
280 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
281 return hasOperands(
282 ignoringImplicit(
284 callee(cxxMethodDecl(hasName("value_or"),
285 ofClass(optionalClass()))),
286 hasArgument(0, Arg))
287 .bind(ValueOrCallID)),
288 ignoringImplicit(Arg));
289 };
290
291 // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
292 // support this pattern for any expression, but the AST does not have a
293 // generic expression comparison facility, so we specialize to common cases
294 // seen in practice. FIXME: define a matcher that compares values across
295 // nodes, which would let us generalize this to any `X`.
296 return binaryOperation(hasOperatorName("!="),
297 anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
298 ComparesToSame(stringLiteral(hasSize(0))),
299 ComparesToSame(integerLiteral(equals(0)))));
300}
301
302auto isCallReturningOptional() {
303 return callExpr(hasType(qualType(
304 anyOf(desugarsToOptionalOrDerivedType(),
305 referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
306}
307
308template <typename L, typename R>
309auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
310 return cxxOperatorCallExpr(
312 argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
313 hasArgument(1, rhs_arg_matcher));
314}
315
316/// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
317const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
318 auto *Value = Env.get<BoolValue>(Expr);
319 if (Value != nullptr)
320 return Value->formula();
321
323 Env.setValue(Expr, *Value);
324 return Value->formula();
325}
326
327StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) {
328 return OptionalLoc.getSyntheticField("has_value");
329}
330
331StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) {
332 return OptionalLoc.getSyntheticField("value");
333}
334
335/// Sets `HasValueVal` as the symbolic value that represents the "has_value"
336/// property of the optional at `OptionalLoc`.
337void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,
338 Environment &Env) {
339 Env.setValue(locForHasValue(OptionalLoc), HasValueVal);
340}
341
342/// Creates a symbolic value for an `optional` value at an existing storage
343/// location. Uses `HasValueVal` as the symbolic value of the "has_value"
344/// property.
345RecordValue &createOptionalValue(RecordStorageLocation &Loc,
346 BoolValue &HasValueVal, Environment &Env) {
347 auto &OptionalVal = Env.create<RecordValue>(Loc);
348 Env.setValue(Loc, OptionalVal);
349 setHasValue(Loc, HasValueVal, Env);
350 return OptionalVal;
351}
352
353/// Returns the symbolic value that represents the "has_value" property of the
354/// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null.
355BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {
356 if (OptionalLoc == nullptr)
357 return nullptr;
358 StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
359 auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);
360 if (HasValueVal == nullptr) {
361 HasValueVal = &Env.makeAtomicBoolValue();
362 Env.setValue(HasValueLoc, *HasValueVal);
363 }
364 return HasValueVal;
365}
366
367QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {
368 auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);
369 return CTSD.getTemplateArgs()[0].getAsType();
370}
371
372/// Returns the number of optional wrappers in `Type`.
373///
374/// For example, if `Type` is `optional<optional<int>>`, the result of this
375/// function will be 2.
376int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
377 const CXXRecordDecl *Optional =
378 getOptionalBaseClass(Type->getAsCXXRecordDecl());
379 if (Optional == nullptr)
380 return 0;
381 return 1 + countOptionalWrappers(
382 ASTCtx,
383 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
384}
385
386StorageLocation *getLocBehindPossiblePointer(const Expr &E,
387 const Environment &Env) {
388 if (E.isPRValue()) {
389 if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
390 return &PointerVal->getPointeeLoc();
391 return nullptr;
392 }
393 return Env.getStorageLocation(E);
394}
395
396void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
397 LatticeTransferState &State) {
398 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
399 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
400 if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)
401 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
402 }
403}
404
405void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
406 LatticeTransferState &State) {
407 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
408 getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
409 State.Env.setValue(
410 *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));
411}
412
413void transferMakeOptionalCall(const CallExpr *E,
414 const MatchFinder::MatchResult &,
415 LatticeTransferState &State) {
416 State.Env.setValue(
417 *E, createOptionalValue(State.Env.getResultObjectLocation(*E),
418 State.Env.getBoolLiteralValue(true), State.Env));
419}
420
421void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
422 const MatchFinder::MatchResult &,
423 LatticeTransferState &State) {
424 if (auto *HasValueVal = getHasValue(
425 State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
426 State.Env.setValue(*CallExpr, *HasValueVal);
427 }
428}
429
430/// `ModelPred` builds a logical formula relating the predicate in
431/// `ValueOrPredExpr` to the optional's `has_value` property.
432void transferValueOrImpl(
433 const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
434 LatticeTransferState &State,
435 const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
436 const Formula &HasValueVal)) {
437 auto &Env = State.Env;
438
439 const auto *MCE =
440 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);
441
442 auto *HasValueVal =
443 getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env));
444 if (HasValueVal == nullptr)
445 return;
446
447 Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
448 HasValueVal->formula()));
449}
450
451void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
452 const MatchFinder::MatchResult &Result,
453 LatticeTransferState &State) {
454 return transferValueOrImpl(ComparisonExpr, Result, State,
455 [](Environment &Env, const Formula &ExprVal,
456 const Formula &HasValueVal) -> const Formula & {
457 auto &A = Env.arena();
458 // If the result is *not* empty, then we know the
459 // optional must have been holding a value. If
460 // `ExprVal` is true, though, we don't learn
461 // anything definite about `has_value`, so we
462 // don't add any corresponding implications to
463 // the flow condition.
464 return A.makeImplies(A.makeNot(ExprVal),
465 HasValueVal);
466 });
467}
468
469void transferValueOrNotEqX(const Expr *ComparisonExpr,
470 const MatchFinder::MatchResult &Result,
471 LatticeTransferState &State) {
472 transferValueOrImpl(ComparisonExpr, Result, State,
473 [](Environment &Env, const Formula &ExprVal,
474 const Formula &HasValueVal) -> const Formula & {
475 auto &A = Env.arena();
476 // We know that if `(opt.value_or(X) != X)` then
477 // `opt.hasValue()`, even without knowing further
478 // details about the contents of `opt`.
479 return A.makeImplies(ExprVal, HasValueVal);
480 });
481}
482
483void transferCallReturningOptional(const CallExpr *E,
484 const MatchFinder::MatchResult &Result,
485 LatticeTransferState &State) {
486 if (State.Env.getValue(*E) != nullptr)
487 return;
488
489 RecordStorageLocation *Loc = nullptr;
490 if (E->isPRValue()) {
491 Loc = &State.Env.getResultObjectLocation(*E);
492 } else {
493 Loc = State.Env.get<RecordStorageLocation>(*E);
494 if (Loc == nullptr) {
495 Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));
496 State.Env.setStorageLocation(*E, *Loc);
497 }
498 }
499
500 RecordValue &Val =
501 createOptionalValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
502 if (E->isPRValue())
503 State.Env.setValue(*E, Val);
504}
505
506void constructOptionalValue(const Expr &E, Environment &Env,
507 BoolValue &HasValueVal) {
508 RecordStorageLocation &Loc = Env.getResultObjectLocation(E);
509 Env.setValue(E, createOptionalValue(Loc, HasValueVal, Env));
510}
511
512/// Returns a symbolic value for the "has_value" property of an `optional<T>`
513/// value that is constructed/assigned from a value of type `U` or `optional<U>`
514/// where `T` is constructible from `U`.
515BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
516 const MatchFinder::MatchResult &MatchRes,
517 LatticeTransferState &State) {
518 assert(F.getTemplateSpecializationArgs() != nullptr);
519 assert(F.getTemplateSpecializationArgs()->size() > 0);
520
521 const int TemplateParamOptionalWrappersCount =
522 countOptionalWrappers(*MatchRes.Context, F.getTemplateSpecializationArgs()
523 ->get(0)
524 .getAsType()
525 .getNonReferenceType());
526 const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
527 *MatchRes.Context, E.getType().getNonReferenceType());
528
529 // Check if this is a constructor/assignment call for `optional<T>` with
530 // argument of type `U` such that `T` is constructible from `U`.
531 if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
532 return State.Env.getBoolLiteralValue(true);
533
534 // This is a constructor/assignment call for `optional<T>` with argument of
535 // type `optional<U>` such that `T` is constructible from `U`.
536 auto *Loc = State.Env.get<RecordStorageLocation>(E);
537 if (auto *HasValueVal = getHasValue(State.Env, Loc))
538 return *HasValueVal;
539 return State.Env.makeAtomicBoolValue();
540}
541
542void transferValueOrConversionConstructor(
543 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
544 LatticeTransferState &State) {
545 assert(E->getNumArgs() > 0);
546
547 constructOptionalValue(*E, State.Env,
548 valueOrConversionHasValue(*E->getConstructor(),
549 *E->getArg(0), MatchRes,
550 State));
551}
552
553void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
554 LatticeTransferState &State) {
555 assert(E->getNumArgs() > 0);
556
557 if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) {
558 createOptionalValue(*Loc, HasValueVal, State.Env);
559
560 // Assign a storage location for the whole expression.
561 State.Env.setStorageLocation(*E, *Loc);
562 }
563}
564
565void transferValueOrConversionAssignment(
566 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
567 LatticeTransferState &State) {
568 assert(E->getNumArgs() > 1);
569 transferAssignment(E,
570 valueOrConversionHasValue(*E->getDirectCallee(),
571 *E->getArg(1), MatchRes, State),
572 State);
573}
574
575void transferNulloptAssignment(const CXXOperatorCallExpr *E,
576 const MatchFinder::MatchResult &,
577 LatticeTransferState &State) {
578 transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
579}
580
581void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
582 Environment &Env) {
583 // We account for cases where one or both of the optionals are not modeled,
584 // either lacking associated storage locations, or lacking values associated
585 // to such storage locations.
586
587 if (Loc1 == nullptr) {
588 if (Loc2 != nullptr)
589 createOptionalValue(*Loc2, Env.makeAtomicBoolValue(), Env);
590 return;
591 }
592 if (Loc2 == nullptr) {
593 createOptionalValue(*Loc1, Env.makeAtomicBoolValue(), Env);
594 return;
595 }
596
597 // Both expressions have locations, though they may not have corresponding
598 // values. In that case, we create a fresh value at this point. Note that if
599 // two branches both do this, they will not share the value, but it at least
600 // allows for local reasoning about the value. To avoid the above, we would
601 // need *lazy* value allocation.
602 // FIXME: allocate values lazily, instead of just creating a fresh value.
603 BoolValue *BoolVal1 = getHasValue(Env, Loc1);
604 if (BoolVal1 == nullptr)
605 BoolVal1 = &Env.makeAtomicBoolValue();
606
607 BoolValue *BoolVal2 = getHasValue(Env, Loc2);
608 if (BoolVal2 == nullptr)
609 BoolVal2 = &Env.makeAtomicBoolValue();
610
611 createOptionalValue(*Loc1, *BoolVal2, Env);
612 createOptionalValue(*Loc2, *BoolVal1, Env);
613}
614
615void transferSwapCall(const CXXMemberCallExpr *E,
616 const MatchFinder::MatchResult &,
617 LatticeTransferState &State) {
618 assert(E->getNumArgs() == 1);
619 auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
620 transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
621}
622
623void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
624 LatticeTransferState &State) {
625 assert(E->getNumArgs() == 2);
626 auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
627 auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1));
628 transferSwap(Arg0Loc, Arg1Loc, State.Env);
629}
630
631void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
632 LatticeTransferState &State) {
633 assert(E->getNumArgs() == 1);
634
635 if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
636 State.Env.setStorageLocation(*E, *Loc);
637}
638
639const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
640 const Formula &LHS, const Formula &RHS) {
641 // Logically, an optional<T> object is composed of two values - a `has_value`
642 // bit and a value of type T. Equality of optional objects compares both
643 // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
644 // when two optional objects are engaged, the equality of their respective
645 // values of type T matters. Since we only track the `has_value` bits, we
646 // can't make any conclusions about equality when we know that two optional
647 // objects are engaged.
648 //
649 // We express this as two facts about the equality:
650 // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
651 // If they are equal, then either both are set or both are unset.
652 // b) (!LHS & !RHS) => EqVal
653 // If neither is set, then they are equal.
654 // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
655 return A.makeAnd(
656 A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
657 A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
658 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
659}
660
661void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
662 const MatchFinder::MatchResult &,
663 LatticeTransferState &State) {
664 Environment &Env = State.Env;
665 auto &A = Env.arena();
666 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
667 auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0));
668 if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {
669 auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1));
670 if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {
671 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
672 CmpValue = &A.makeNot(*CmpValue);
673 Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
674 RHasVal->formula()));
675 }
676 }
677}
678
679void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
680 const clang::Expr *E, Environment &Env) {
681 auto &A = Env.arena();
682 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
683 auto *Loc = Env.get<RecordStorageLocation>(*E);
684 if (auto *HasVal = getHasValue(Env, Loc)) {
685 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
686 CmpValue = &A.makeNot(*CmpValue);
687 Env.assume(
688 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
689 }
690}
691
692void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,
693 const clang::Expr *E, Environment &Env) {
694 auto &A = Env.arena();
695 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
696 auto *Loc = Env.get<RecordStorageLocation>(*E);
697 if (auto *HasVal = getHasValue(Env, Loc)) {
698 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
699 CmpValue = &A.makeNot(*CmpValue);
700 Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
701 A.makeLiteral(false)));
702 }
703}
704
705std::optional<StatementMatcher>
706ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
707 if (Options.IgnoreSmartPointerDereference) {
708 auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
710 unless(hasArgument(0, expr(hasOptionalType()))))));
711 return expr(
712 anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
713 }
714 return std::nullopt;
715}
716
718valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
719 return isOptionalMemberCallWithNameMatcher(hasName("value"),
720 IgnorableOptional);
721}
722
724valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
725 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
726 isOptionalOperatorCallWithName("->", IgnorableOptional)));
727}
728
729auto buildTransferMatchSwitch() {
730 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
731 // lot of duplicated work (e.g. string comparisons), consider providing APIs
732 // that avoid it through memoization.
733 return CFGMatchSwitchBuilder<LatticeTransferState>()
734 // make_optional
735 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
736
737 // optional::optional (in place)
738 .CaseOfCFGStmt<CXXConstructExpr>(
739 isOptionalInPlaceConstructor(),
740 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
741 LatticeTransferState &State) {
742 constructOptionalValue(*E, State.Env,
743 State.Env.getBoolLiteralValue(true));
744 })
745 // optional::optional(nullopt_t)
746 .CaseOfCFGStmt<CXXConstructExpr>(
747 isOptionalNulloptConstructor(),
748 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
749 LatticeTransferState &State) {
750 constructOptionalValue(*E, State.Env,
751 State.Env.getBoolLiteralValue(false));
752 })
753 // optional::optional (value/conversion)
754 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
755 transferValueOrConversionConstructor)
756
757 // optional::operator=
758 .CaseOfCFGStmt<CXXOperatorCallExpr>(
759 isOptionalValueOrConversionAssignment(),
760 transferValueOrConversionAssignment)
761 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
762 transferNulloptAssignment)
763
764 // optional::value
765 .CaseOfCFGStmt<CXXMemberCallExpr>(
766 valueCall(std::nullopt),
767 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
768 LatticeTransferState &State) {
769 transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
770 })
771
772 // optional::operator*
773 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
774 [](const CallExpr *E,
775 const MatchFinder::MatchResult &,
776 LatticeTransferState &State) {
777 transferUnwrapCall(E, E->getArg(0), State);
778 })
779
780 // optional::operator->
781 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
782 [](const CallExpr *E,
783 const MatchFinder::MatchResult &,
784 LatticeTransferState &State) {
785 transferArrowOpCall(E, E->getArg(0), State);
786 })
787
788 // optional::has_value, optional::hasValue
789 // Of the supported optionals only folly::Optional uses hasValue, but this
790 // will also pass for other types
791 .CaseOfCFGStmt<CXXMemberCallExpr>(
792 isOptionalMemberCallWithNameMatcher(
793 hasAnyName("has_value", "hasValue")),
794 transferOptionalHasValueCall)
795
796 // optional::operator bool
797 .CaseOfCFGStmt<CXXMemberCallExpr>(
798 isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
799 transferOptionalHasValueCall)
800
801 // optional::emplace
802 .CaseOfCFGStmt<CXXMemberCallExpr>(
803 isOptionalMemberCallWithNameMatcher(hasName("emplace")),
804 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
805 LatticeTransferState &State) {
806 if (RecordStorageLocation *Loc =
807 getImplicitObjectLocation(*E, State.Env)) {
808 createOptionalValue(*Loc, State.Env.getBoolLiteralValue(true),
809 State.Env);
810 }
811 })
812
813 // optional::reset
814 .CaseOfCFGStmt<CXXMemberCallExpr>(
815 isOptionalMemberCallWithNameMatcher(hasName("reset")),
816 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
817 LatticeTransferState &State) {
818 if (RecordStorageLocation *Loc =
819 getImplicitObjectLocation(*E, State.Env)) {
820 createOptionalValue(*Loc, State.Env.getBoolLiteralValue(false),
821 State.Env);
822 }
823 })
824
825 // optional::swap
826 .CaseOfCFGStmt<CXXMemberCallExpr>(
827 isOptionalMemberCallWithNameMatcher(hasName("swap")),
828 transferSwapCall)
829
830 // std::swap
831 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
832
833 // std::forward
834 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
835
836 // opt.value_or("").empty()
837 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
838 transferValueOrStringEmptyCall)
839
840 // opt.value_or(X) != X
841 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
842
843 // Comparisons (==, !=):
844 .CaseOfCFGStmt<CXXOperatorCallExpr>(
845 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
846 transferOptionalAndOptionalCmp)
847 .CaseOfCFGStmt<CXXOperatorCallExpr>(
848 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
849 [](const clang::CXXOperatorCallExpr *Cmp,
850 const MatchFinder::MatchResult &, LatticeTransferState &State) {
851 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);
852 })
853 .CaseOfCFGStmt<CXXOperatorCallExpr>(
854 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
855 [](const clang::CXXOperatorCallExpr *Cmp,
856 const MatchFinder::MatchResult &, LatticeTransferState &State) {
857 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);
858 })
859 .CaseOfCFGStmt<CXXOperatorCallExpr>(
860 isComparisonOperatorCall(
861 hasOptionalType(),
862 unless(anyOf(hasOptionalType(), hasNulloptType()))),
863 [](const clang::CXXOperatorCallExpr *Cmp,
864 const MatchFinder::MatchResult &, LatticeTransferState &State) {
865 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
866 })
867 .CaseOfCFGStmt<CXXOperatorCallExpr>(
868 isComparisonOperatorCall(
869 unless(anyOf(hasOptionalType(), hasNulloptType())),
870 hasOptionalType()),
871 [](const clang::CXXOperatorCallExpr *Cmp,
872 const MatchFinder::MatchResult &, LatticeTransferState &State) {
873 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
874 })
875
876 // returns optional
877 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
878 transferCallReturningOptional)
879
880 .Build();
881}
882
883llvm::SmallVector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
884 const Environment &Env) {
885 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
886 getLocBehindPossiblePointer(*ObjectExpr, Env))) {
887 auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
888 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
889 if (Env.proves(HasValueVal->formula()))
890 return {};
891 }
892 }
893
894 // Record that this unwrap is *not* provably safe.
895 // FIXME: include either the name of the optional (if applicable) or a source
896 // range of the access for easier interpretation of the result.
897 return {ObjectExpr->getBeginLoc()};
898}
899
900auto buildDiagnoseMatchSwitch(
901 const UncheckedOptionalAccessModelOptions &Options) {
902 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
903 // lot of duplicated work (e.g. string comparisons), consider providing APIs
904 // that avoid it through memoization.
905 auto IgnorableOptional = ignorableOptional(Options);
906 return CFGMatchSwitchBuilder<const Environment,
908 // optional::value
909 .CaseOfCFGStmt<CXXMemberCallExpr>(
910 valueCall(IgnorableOptional),
911 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
912 const Environment &Env) {
913 return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
914 })
915
916 // optional::operator*, optional::operator->
917 .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
918 [](const CallExpr *E,
919 const MatchFinder::MatchResult &,
920 const Environment &Env) {
921 return diagnoseUnwrapCall(E->getArg(0), Env);
922 })
923 .Build();
924}
925
926} // namespace
927
930 return cxxRecordDecl(optionalClass());
931}
932
936 TransferMatchSwitch(buildTransferMatchSwitch()) {
937 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
938 [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
939 const CXXRecordDecl *Optional =
941 if (Optional == nullptr)
942 return {};
943 return {{"value", valueTypeFromOptionalDecl(*Optional)},
944 {"has_value", Ctx.BoolTy}};
945 });
946}
947
950 LatticeTransferState State(L, Env);
951 TransferMatchSwitch(Elt, getASTContext(), State);
952}
953
956 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
957
958} // namespace dataflow
959} // 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...
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:148
Defines the clang::SourceLocation class and associated facilities.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
CanQualType BoolTy
Definition: ASTContext.h:1089
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:618
bool hasDefinition() const
Definition: DeclCXX.h:571
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:3011
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition: DeclBase.h:2076
bool isTranslationUnit() const
Definition: DeclBase.h:2152
DeclContext * getDeclContext()
Definition: DeclBase.h:453
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:276
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:315
Represent a C++ namespace.
Definition: Decl.h:547
A (possibly-)qualified type.
Definition: Type.h:738
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition: Type.cpp:1862
const Formula & makeImplies(const Formula &LHS, const Formula &RHS)
Returns a formula for LHS => RHS.
Definition: Arena.cpp:78
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...
std::enable_if_t< std::is_base_of< Value, T >::value, T & > create(Args &&...args)
Creates a T (some subclass of Value), forwarding args to the constructor, and returns a reference to ...
Trivial lattice for dataflow analysis with exactly one element.
Definition: NoopLattice.h:25
UncheckedOptionalAccessDiagnoser(UncheckedOptionalAccessModelOptions Options={})
Dataflow analysis that models whether optionals hold values or not.
UncheckedOptionalAccessModel(ASTContext &Ctx, dataflow::Environment &Env)
void transfer(const CFGElement &Elt, NoopLattice &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:3078
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:5844
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:3141
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:3652
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 hasOptionalClassName(const CXXRecordDecl &RD)
static bool isTopLevelNamespaceWithName(const NamespaceDecl &NS, llvm::StringRef Name)
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:1675
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:121