clang 22.0.0git
UncheckedStatusOrAccessModel.cpp
Go to the documentation of this file.
1//===- UncheckedStatusOrAccessModel.cpp -----------------------------------===//
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
10
11#include <cassert>
12#include <utility>
13
14#include "clang/AST/DeclCXX.h"
16#include "clang/AST/Expr.h"
17#include "clang/AST/ExprCXX.h"
18#include "clang/AST/TypeBase.h"
22#include "clang/Analysis/CFG.h"
31#include "clang/Basic/LLVM.h"
33#include "llvm/ADT/StringMap.h"
34
36namespace {
37
38using ::clang::ast_matchers::MatchFinder;
40
41} // namespace
42
43static bool namespaceEquals(const NamespaceDecl *NS,
44 clang::ArrayRef<clang::StringRef> NamespaceNames) {
45 while (!NamespaceNames.empty() && NS) {
46 if (NS->getName() != NamespaceNames.consume_back())
47 return false;
48 NS = dyn_cast_or_null<NamespaceDecl>(NS->getParent());
49 }
50 return NamespaceNames.empty() && !NS;
51}
52
53// TODO: move this to a proper place to share with the rest of clang
55 StringRef Name) {
56 if (Type.isNull())
57 return false;
58 if (auto *RD = Type->getAsRecordDecl())
59 if (RD->getName() == Name)
60 if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD->getDeclContext()))
61 return namespaceEquals(N, NS);
62 return false;
63}
64
66 return isTypeNamed(Type, {"absl", "internal_statusor"}, "OperatorBase");
67}
68
69static bool isSafeUnwrap(RecordStorageLocation *StatusOrLoc,
70 const Environment &Env) {
71 if (!StatusOrLoc)
72 return false;
73 auto &StatusLoc = locForStatus(*StatusOrLoc);
74 auto *OkVal = Env.get<BoolValue>(locForOk(StatusLoc));
75 return OkVal != nullptr && Env.proves(OkVal->formula());
76}
77
80 auto *RD = Ty->getAsCXXRecordDecl();
81 if (RD == nullptr)
82 return nullptr;
83 if (isStatusOrType(Ty) ||
84 // In case we are analyzing code under OperatorBase itself that uses
85 // operator* (e.g. to implement operator->).
88 if (!RD->hasDefinition())
89 return nullptr;
90 for (const auto &Base : RD->bases())
91 if (auto *QT = getStatusOrBaseClass(Base.getType()))
92 return QT;
93 return nullptr;
94}
95
99
100static auto ofClassStatus() {
101 using namespace ::clang::ast_matchers; // NOLINT: Too many names
102 return ofClass(hasName("::absl::Status"));
103}
104
105static auto isStatusMemberCallWithName(llvm::StringRef member_name) {
106 using namespace ::clang::ast_matchers; // NOLINT: Too many names
107 return cxxMemberCallExpr(
108 on(expr(unless(cxxThisExpr()))),
109 callee(cxxMethodDecl(hasName(member_name), ofClassStatus())));
110}
111
112static auto isStatusOrMemberCallWithName(llvm::StringRef member_name) {
113 using namespace ::clang::ast_matchers; // NOLINT: Too many names
114 return cxxMemberCallExpr(
115 on(expr(unless(cxxThisExpr()))),
116 callee(cxxMethodDecl(
117 hasName(member_name),
119}
120
121static auto isStatusOrOperatorCallWithName(llvm::StringRef operator_name) {
122 using namespace ::clang::ast_matchers; // NOLINT: Too many names
123 return cxxOperatorCallExpr(
124 hasOverloadedOperatorName(operator_name),
125 callee(cxxMethodDecl(
127}
128
129static auto valueCall() {
130 using namespace ::clang::ast_matchers; // NOLINT: Too many names
131 return anyOf(isStatusOrMemberCallWithName("value"),
132 isStatusOrMemberCallWithName("ValueOrDie"));
133}
134
135static auto valueOperatorCall() {
136 using namespace ::clang::ast_matchers; // NOLINT: Too many names
139}
140
142 using namespace ::clang::ast_matchers; // NOLINT: Too many names
143 return hasCanonicalType(qualType(hasDeclaration(statusClass())));
144}
145
146static auto isComparisonOperatorCall(llvm::StringRef operator_name) {
147 using namespace ::clang::ast_matchers; // NOLINT: Too many names
148 return cxxOperatorCallExpr(
149 hasOverloadedOperatorName(operator_name), argumentCountIs(2),
150 hasArgument(0, anyOf(hasType(statusType()), hasType(statusOrType()))),
151 hasArgument(1, anyOf(hasType(statusType()), hasType(statusOrType()))));
152}
153
154static auto isOkStatusCall() {
155 using namespace ::clang::ast_matchers; // NOLINT: Too many names
156 return callExpr(callee(functionDecl(hasName("::absl::OkStatus"))));
157}
158
159static auto isNotOkStatusCall() {
160 using namespace ::clang::ast_matchers; // NOLINT: Too many names
161 return callExpr(callee(functionDecl(hasAnyName(
162 "::absl::AbortedError", "::absl::AlreadyExistsError",
163 "::absl::CancelledError", "::absl::DataLossError",
164 "::absl::DeadlineExceededError", "::absl::FailedPreconditionError",
165 "::absl::InternalError", "::absl::InvalidArgumentError",
166 "::absl::NotFoundError", "::absl::OutOfRangeError",
167 "::absl::PermissionDeniedError", "::absl::ResourceExhaustedError",
168 "::absl::UnauthenticatedError", "::absl::UnavailableError",
169 "::absl::UnimplementedError", "::absl::UnknownError"))));
170}
171
172static auto isPointerComparisonOperatorCall(std::string operator_name) {
173 using namespace ::clang::ast_matchers; // NOLINT: Too many names
174 return binaryOperator(hasOperatorName(operator_name),
175 hasLHS(hasType(hasCanonicalType(pointerType(
176 pointee(anyOf(statusOrType(), statusType())))))),
177 hasRHS(hasType(hasCanonicalType(pointerType(
178 pointee(anyOf(statusOrType(), statusType())))))));
179}
180
181// The nullPointerConstant in the two matchers below is to support
182// absl::StatusOr<void*> X = nullptr.
183// nullptr does not match the bound type.
184// TODO: be less restrictive around convertible types in general.
186 using namespace ::clang::ast_matchers; // NOLINT: Too many names
187 return cxxOperatorCallExpr(
189 callee(cxxMethodDecl(ofClass(statusOrClass()))),
190 hasArgument(1, anyOf(hasType(hasUnqualifiedDesugaredType(
191 type(equalsBoundNode("T")))),
192 nullPointerConstant())));
193}
194
196 using namespace ::clang::ast_matchers; // NOLINT: Too many names
197 return cxxConstructExpr(
198 hasType(statusOrType()),
199 hasArgument(0,
200 anyOf(hasType(hasCanonicalType(type(equalsBoundNode("T")))),
201 nullPointerConstant(),
202 hasType(namedDecl(hasAnyName("absl::in_place_t",
203 "std::in_place_t"))))));
204}
205
207 using namespace ::clang::ast_matchers; // NOLINT: Too many names
208 return cxxConstructExpr(hasType(statusOrType()));
209}
210
211static auto isStatusConstructor() {
212 using namespace ::clang::ast_matchers; // NOLINT: Too many names
213 return cxxConstructExpr(hasType(statusType()));
214}
216 using namespace ::clang::ast_matchers; // NOLINT: Too many names
217 return callExpr(callee(
218 functionDecl(hasName("::absl::log_internal::GetReferenceableValue"))));
219}
220
221static auto isLoggingCheckEqImpl() {
222 using namespace ::clang::ast_matchers; // NOLINT: Too many names
223 return callExpr(
224 callee(functionDecl(hasName("::absl::log_internal::Check_EQImpl"))));
225}
226
228 using namespace ::clang::ast_matchers; // NOLINT: Too many names
229 return callExpr(
230 callee(functionDecl(hasName("::absl::log_internal::AsStatus"))),
231 hasArgument(0, hasType(statusClass())));
232}
233
235 using namespace ::clang::ast_matchers; // NOLINT: Too many names
236 return callExpr(
237 callee(functionDecl(hasName("::absl::log_internal::AsStatus"))),
238 hasArgument(0, hasType(statusOrType())));
239}
240
242 using namespace ::clang::ast_matchers; // NOLINT: Too many names
243 return anyOf(statusOrType(), referenceType(pointee(statusOrType())));
244}
245
247 using namespace ::clang::ast_matchers; // NOLINT: Too many names
248 return cxxMemberCallExpr(callee(
249 cxxMethodDecl(parameterCountIs(0), isConst(),
251}
252
254 using namespace ::clang::ast_matchers; // NOLINT: Too many names
255 return cxxOperatorCallExpr(
256 callee(cxxMethodDecl(parameterCountIs(0), isConst(),
257 returns(possiblyReferencedStatusOrType()))));
258}
259
261 using namespace ::clang::ast_matchers; // NOLINT: Too many names
262 return cxxMemberCallExpr(callee(cxxMethodDecl(
263 parameterCountIs(0), isConst(),
264 returns(pointerType(pointee(possiblyReferencedStatusOrType()))))));
265}
266
268 using namespace ::clang::ast_matchers; // NOLINT: Too many names
270 parameterCountIs(0), isConst(),
271 returns(pointerType(pointee(possiblyReferencedStatusOrType()))))));
272}
273
274static auto isNonConstMemberCall() {
275 using namespace ::clang::ast_matchers; // NOLINT: Too many names
276 return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst()))));
277}
278
280 using namespace ::clang::ast_matchers; // NOLINT: Too many names
281 return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));
282}
283
284static auto
288 // StatusOr::value, StatusOr::ValueOrDie
289 .CaseOfCFGStmt<CXXMemberCallExpr>(
290 valueCall(),
291 [](const CXXMemberCallExpr *E,
293 const Environment &Env) {
294 if (!isSafeUnwrap(getImplicitObjectLocation(*E, Env), Env))
297 })
298
299 // StatusOr::operator*, StatusOr::operator->
300 .CaseOfCFGStmt<CXXOperatorCallExpr>(
302 [](const CXXOperatorCallExpr *E,
304 const Environment &Env) {
305 RecordStorageLocation *StatusOrLoc =
306 Env.get<RecordStorageLocation>(*E->getArg(0));
307 if (!isSafeUnwrap(StatusOrLoc, Env))
308 return llvm::SmallVector<SourceLocation>({E->getOperatorLoc()});
310 })
311 .Build();
312}
313
317
324
326 Environment &Env) {
327 auto &OkVal = Env.makeAtomicBoolValue();
328 Env.setValue(locForOk(StatusLoc), OkVal);
329 return OkVal;
330}
331
333 Environment &Env) {
334 return initializeStatus(locForStatus(StatusOrLoc), Env);
335}
336
338 using namespace ::clang::ast_matchers; // NOLINT: Too many names
340 hasName("absl::StatusOr"),
341 hasTemplateArgument(0, refersToType(type().bind("T"))));
342}
343
345 using namespace ::clang::ast_matchers; // NOLINT: Too many names
346 return cxxRecordDecl(hasName("absl::Status"));
347}
348
350 using namespace ::clang::ast_matchers; // NOLINT: Too many names
352 hasName("absl::internal_statusor::OperatorBase"));
353}
354
356 using namespace ::clang::ast_matchers; // NOLINT: Too many names
357 return hasCanonicalType(qualType(hasDeclaration(statusOrClass())));
358}
359
361 return isTypeNamed(Type, {"absl"}, "StatusOr");
362}
363
365 return isTypeNamed(Type, {"absl"}, "Status");
366}
367
368llvm::StringMap<QualType> getSyntheticFields(QualType Ty, QualType StatusType,
369 const CXXRecordDecl &RD) {
370 if (auto *TRD = getStatusOrBaseClass(Ty))
371 return {{"status", StatusType}, {"value", getStatusOrValueType(TRD)}};
372 if (isStatusType(Ty) || (RD.hasDefinition() &&
373 RD.isDerivedFrom(StatusType->getAsCXXRecordDecl())))
374 return {{"ok", RD.getASTContext().BoolTy}};
375 return {};
376}
377
381
383 return StatusLoc.getSyntheticField("ok");
384}
385
387 if (auto *Val = Env.get<BoolValue>(locForOk(StatusLoc)))
388 return *Val;
389 return initializeStatus(StatusLoc, Env);
390}
391
394 LatticeTransferState &State) {
395 RecordStorageLocation *StatusOrLoc =
396 getImplicitObjectLocation(*Expr, State.Env);
397 if (StatusOrLoc == nullptr)
398 return;
399
400 auto &OkVal = valForOk(locForStatus(*StatusOrLoc), State.Env);
401 State.Env.setValue(*Expr, OkVal);
402}
403
406 LatticeTransferState &State) {
407 RecordStorageLocation *StatusOrLoc =
408 getImplicitObjectLocation(*Expr, State.Env);
409 if (StatusOrLoc == nullptr)
410 return;
411
412 RecordStorageLocation &StatusLoc = locForStatus(*StatusOrLoc);
413
414 if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
415 initializeStatusOr(*StatusOrLoc, State.Env);
416
417 if (Expr->isPRValue())
418 copyRecord(StatusLoc, State.Env.getResultObjectLocation(*Expr), State.Env);
419 else
420 State.Env.setStorageLocation(*Expr, StatusLoc);
421}
422
425 LatticeTransferState &State) {
426 RecordStorageLocation *StatusLoc =
427 getImplicitObjectLocation(*Expr, State.Env);
428 if (StatusLoc == nullptr)
429 return;
430
431 if (Value *Val = State.Env.getValue(locForOk(*StatusLoc)))
432 State.Env.setValue(*Expr, *Val);
433}
434
437 LatticeTransferState &State) {
438 // S.Update(OtherS) sets S to the error code of OtherS if it is OK,
439 // otherwise does nothing.
440 assert(Expr->getNumArgs() == 1);
441 auto *Arg = Expr->getArg(0);
442 RecordStorageLocation *ArgRecord =
443 Arg->isPRValue() ? &State.Env.getResultObjectLocation(*Arg)
444 : State.Env.get<RecordStorageLocation>(*Arg);
446 if (ThisLoc == nullptr || ArgRecord == nullptr)
447 return;
448
449 auto &ThisOkVal = valForOk(*ThisLoc, State.Env);
450 auto &ArgOkVal = valForOk(*ArgRecord, State.Env);
451 auto &A = State.Env.arena();
452 auto &NewVal = State.Env.makeAtomicBoolValue();
453 State.Env.assume(A.makeImplies(A.makeNot(ThisOkVal.formula()),
454 A.makeNot(NewVal.formula())));
455 State.Env.assume(A.makeImplies(NewVal.formula(), ArgOkVal.formula()));
456 State.Env.setValue(locForOk(*ThisLoc), NewVal);
457}
458
460 RecordStorageLocation &RhsStatusLoc,
461 Environment &Env) {
462 auto &A = Env.arena();
463 // Logically, a Status object is composed of an error code that could take one
464 // of multiple possible values, including the "ok" value. We track whether a
465 // Status object has an "ok" value and represent this as an `ok` bit. Equality
466 // of Status objects compares their error codes. Therefore, merely comparing
467 // the `ok` bits isn't sufficient: when two Status objects are assigned non-ok
468 // error codes the equality of their respective error codes matters. Since we
469 // only track the `ok` bits, we can't make any conclusions about equality when
470 // we know that two Status objects have non-ok values.
471
472 auto &LhsOkVal = valForOk(LhsStatusLoc, Env);
473 auto &RhsOkVal = valForOk(RhsStatusLoc, Env);
474
475 auto &Res = Env.makeAtomicBoolValue();
476
477 // lhs && rhs => res (a.k.a. !res => !lhs || !rhs)
478 Env.assume(A.makeImplies(A.makeAnd(LhsOkVal.formula(), RhsOkVal.formula()),
479 Res.formula()));
480 // res => (lhs == rhs)
481 Env.assume(A.makeImplies(
482 Res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));
483
484 return &Res;
485}
486
487static BoolValue *
489 RecordStorageLocation &RhsStatusOrLoc,
490 Environment &Env) {
491 auto &A = Env.arena();
492 // Logically, a StatusOr<T> object is composed of two values - a Status and a
493 // value of type T. Equality of StatusOr objects compares both values.
494 // Therefore, merely comparing the `ok` bits of the Status values isn't
495 // sufficient. When two StatusOr objects are engaged, the equality of their
496 // respective values of type T matters. Similarly, when two StatusOr objects
497 // have Status values that have non-ok error codes, the equality of the error
498 // codes matters. Since we only track the `ok` bits of the Status values, we
499 // can't make any conclusions about equality when we know that two StatusOr
500 // objects are engaged or when their Status values contain non-ok error codes.
501 auto &LhsOkVal = valForOk(locForStatus(LhsStatusOrLoc), Env);
502 auto &RhsOkVal = valForOk(locForStatus(RhsStatusOrLoc), Env);
503 auto &res = Env.makeAtomicBoolValue();
504
505 // res => (lhs == rhs)
506 Env.assume(A.makeImplies(
507 res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));
508 return &res;
509}
510
511static BoolValue *evaluateEquality(const Expr *LhsExpr, const Expr *RhsExpr,
512 Environment &Env) {
513 // Check the type of both sides in case an operator== is added that admits
514 // different types.
515 if (isStatusOrType(LhsExpr->getType()) &&
516 isStatusOrType(RhsExpr->getType())) {
517 auto *LhsStatusOrLoc = Env.get<RecordStorageLocation>(*LhsExpr);
518 if (LhsStatusOrLoc == nullptr)
519 return nullptr;
520 auto *RhsStatusOrLoc = Env.get<RecordStorageLocation>(*RhsExpr);
521 if (RhsStatusOrLoc == nullptr)
522 return nullptr;
523
524 return evaluateStatusOrEquality(*LhsStatusOrLoc, *RhsStatusOrLoc, Env);
525 }
526 if (isStatusType(LhsExpr->getType()) && isStatusType(RhsExpr->getType())) {
527 auto *LhsStatusLoc = Env.get<RecordStorageLocation>(*LhsExpr);
528 if (LhsStatusLoc == nullptr)
529 return nullptr;
530
531 auto *RhsStatusLoc = Env.get<RecordStorageLocation>(*RhsExpr);
532 if (RhsStatusLoc == nullptr)
533 return nullptr;
534
535 return evaluateStatusEquality(*LhsStatusLoc, *RhsStatusLoc, Env);
536 }
537 return nullptr;
538}
539
542 bool IsNegative) {
543 auto *LhsAndRhsVal =
544 evaluateEquality(Expr->getArg(0), Expr->getArg(1), State.Env);
545 if (LhsAndRhsVal == nullptr)
546 return;
547
548 if (IsNegative)
549 State.Env.setValue(*Expr, State.Env.makeNot(*LhsAndRhsVal));
550 else
551 State.Env.setValue(*Expr, *LhsAndRhsVal);
552}
553
555 Environment &Env) {
556 if (auto *PointerVal = Env.get<PointerValue>(Expr))
557 return dyn_cast<RecordStorageLocation>(&PointerVal->getPointeeLoc());
558 return nullptr;
559}
560
562 const Expr *RhsExpr,
563 Environment &Env) {
564 assert(LhsExpr->getType()->isPointerType());
565 assert(RhsExpr->getType()->isPointerType());
566 RecordStorageLocation *LhsStatusLoc = nullptr;
567 RecordStorageLocation *RhsStatusLoc = nullptr;
568 if (isStatusOrType(LhsExpr->getType()->getPointeeType()) &&
569 isStatusOrType(RhsExpr->getType()->getPointeeType())) {
570 auto *LhsStatusOrLoc = getPointeeLocation(*LhsExpr, Env);
571 auto *RhsStatusOrLoc = getPointeeLocation(*RhsExpr, Env);
572 if (LhsStatusOrLoc == nullptr || RhsStatusOrLoc == nullptr)
573 return nullptr;
574 LhsStatusLoc = &locForStatus(*LhsStatusOrLoc);
575 RhsStatusLoc = &locForStatus(*RhsStatusOrLoc);
576 } else if (isStatusType(LhsExpr->getType()->getPointeeType()) &&
577 isStatusType(RhsExpr->getType()->getPointeeType())) {
578 LhsStatusLoc = getPointeeLocation(*LhsExpr, Env);
579 RhsStatusLoc = getPointeeLocation(*RhsExpr, Env);
580 }
581 if (LhsStatusLoc == nullptr || RhsStatusLoc == nullptr)
582 return nullptr;
583 auto &LhsOkVal = valForOk(*LhsStatusLoc, Env);
584 auto &RhsOkVal = valForOk(*RhsStatusLoc, Env);
585 auto &Res = Env.makeAtomicBoolValue();
586 auto &A = Env.arena();
587 Env.assume(A.makeImplies(
588 Res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));
589 return &Res;
590}
591
594 bool IsNegative) {
595 auto *LhsAndRhsVal =
596 evaluatePointerEquality(Expr->getLHS(), Expr->getRHS(), State.Env);
597 if (LhsAndRhsVal == nullptr)
598 return;
599
600 if (IsNegative)
601 State.Env.setValue(*Expr, State.Env.makeNot(*LhsAndRhsVal));
602 else
603 State.Env.setValue(*Expr, *LhsAndRhsVal);
604}
605
608 LatticeTransferState &State) {
609 auto &OkVal =
610 initializeStatus(State.Env.getResultObjectLocation(*Expr), State.Env);
611 State.Env.assume(OkVal.formula());
612}
613
616 LatticeTransferState &State) {
617 auto &OkVal =
618 initializeStatus(State.Env.getResultObjectLocation(*Expr), State.Env);
619 auto &A = State.Env.arena();
620 State.Env.assume(A.makeNot(OkVal.formula()));
621}
622
625 LatticeTransferState &State) {
626 RecordStorageLocation *StatusOrLoc =
627 getImplicitObjectLocation(*Expr, State.Env);
628 if (StatusOrLoc == nullptr)
629 return;
630
631 auto &OkVal = valForOk(locForStatus(*StatusOrLoc), State.Env);
632 State.Env.assume(OkVal.formula());
633}
634
637 LatticeTransferState &State) {
638 assert(Expr->getNumArgs() > 1);
639
640 auto *StatusOrLoc = State.Env.get<RecordStorageLocation>(*Expr->getArg(0));
641 if (StatusOrLoc == nullptr)
642 return;
643
644 auto &OkVal = initializeStatusOr(*StatusOrLoc, State.Env);
645 State.Env.assume(OkVal.formula());
646}
647
650 LatticeTransferState &State) {
651 auto &OkVal =
652 initializeStatusOr(State.Env.getResultObjectLocation(*Expr), State.Env);
653 State.Env.assume(OkVal.formula());
654}
655
658 LatticeTransferState &State) {
659 RecordStorageLocation &StatusOrLoc = State.Env.getResultObjectLocation(*Expr);
660 RecordStorageLocation &StatusLoc = locForStatus(StatusOrLoc);
661
662 if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
663 initializeStatusOr(StatusOrLoc, State.Env);
664}
665
668 LatticeTransferState &State) {
669 RecordStorageLocation &StatusLoc = State.Env.getResultObjectLocation(*Expr);
670
671 if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
672 initializeStatus(StatusLoc, State.Env);
673}
674static void
677 LatticeTransferState &State) {
678 assert(Expr->getNumArgs() == 1);
679 if (Expr->getArg(0)->isPRValue())
680 return;
681 auto *ArgLoc = State.Env.getStorageLocation(*Expr->getArg(0));
682 if (ArgLoc == nullptr)
683 return;
684
685 State.Env.setStorageLocation(*Expr, *ArgLoc);
686}
687
690 LatticeTransferState &State) {
691 assert(Expr->getNumArgs() > 2);
692
693 auto *EqVal = evaluateEquality(Expr->getArg(0), Expr->getArg(1), State.Env);
694 if (EqVal == nullptr)
695 return;
696
697 // Consider modelling this more accurately instead of assigning BoolValue
698 // as the value of an expression of pointer type.
699 // For now, this is being handled in transferPointerToBoolean.
700 State.Env.setValue(*Expr, State.Env.makeNot(*EqVal));
701}
702
705 LatticeTransferState &State) {
706 assert(Expr->getNumArgs() == 1);
707
708 auto *ArgLoc = State.Env.get<RecordStorageLocation>(*Expr->getArg(0));
709 if (ArgLoc == nullptr)
710 return;
711
712 if (State.Env.getValue(locForOk(*ArgLoc)) == nullptr)
713 initializeStatus(*ArgLoc, State.Env);
714
715 auto &ExprVal = State.Env.create<PointerValue>(*ArgLoc);
716 State.Env.setValue(*Expr, ExprVal);
717}
718
721 LatticeTransferState &State) {
722 assert(Expr->getNumArgs() == 1);
723
724 auto *ArgLoc = State.Env.get<RecordStorageLocation>(*Expr->getArg(0));
725 if (ArgLoc == nullptr)
726 return;
727
728 RecordStorageLocation &StatusLoc = locForStatus(*ArgLoc);
729
730 if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
731 initializeStatusOr(*ArgLoc, State.Env);
732
733 auto &ExprVal = State.Env.create<PointerValue>(StatusLoc);
734 State.Env.setValue(*Expr, ExprVal);
735}
736
739 LatticeTransferState &State) {
740 if (auto *SubExprVal =
741 dyn_cast_or_null<BoolValue>(State.Env.getValue(*Expr->getSubExpr())))
742 State.Env.setValue(*Expr, *SubExprVal);
743}
744
746 LatticeTransferState &State) {
747 RecordStorageLocation *StatusOrLoc =
748 Expr->isPRValue() ? &State.Env.getResultObjectLocation(*Expr)
749 : State.Env.get<RecordStorageLocation>(*Expr);
750 if (StatusOrLoc != nullptr &&
751 State.Env.getValue(locForOk(locForStatus(*StatusOrLoc))) == nullptr)
752 initializeStatusOr(*StatusOrLoc, State.Env);
753}
754
758 assert(isStatusOrType(Expr->getType()));
759 if (RecordLoc == nullptr)
760 return false;
761 const FunctionDecl *DirectCallee = Expr->getDirectCallee();
762 if (DirectCallee == nullptr)
763 return false;
764 StorageLocation &Loc =
765 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
766 *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
767 initializeStatusOr(cast<RecordStorageLocation>(Loc), State.Env);
768 });
769 if (Expr->isPRValue()) {
770 auto &ResultLoc = State.Env.getResultObjectLocation(*Expr);
771 copyRecord(cast<RecordStorageLocation>(Loc), ResultLoc, State.Env);
772 } else {
773 State.Env.setStorageLocation(*Expr, Loc);
774 }
775 return true;
776}
777
787 if (RecordLoc == nullptr)
788 return;
789 auto *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, Expr,
790 State.Env);
791 State.Env.setValue(*Expr, *Val);
792}
793
794static void
801
804 LatticeTransferState &State) {
805 auto *RecordLoc = cast_or_null<RecordStorageLocation>(
806 State.Env.getStorageLocation(*Expr->getArg(0)));
808}
809
816
819 LatticeTransferState &State) {
820 auto *RecordLoc = cast_or_null<RecordStorageLocation>(
821 State.Env.getStorageLocation(*Expr->getArg(0)));
823}
824
828 LatticeTransferState &State) {
829 if (RecordLoc) {
830 State.Lattice.clearConstMethodReturnValues(*RecordLoc);
831 State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc);
832 }
835}
836
843
844static void
847 LatticeTransferState &State) {
848 auto *RecordLoc = cast_or_null<RecordStorageLocation>(
849 State.Env.getStorageLocation(*Expr->getArg(0)));
851}
852
855 if (!E.isPRValue())
856 return dyn_cast_or_null<RecordStorageLocation>(Env.getStorageLocation(E));
857 if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
858 return dyn_cast_or_null<RecordStorageLocation>(
859 &PointerVal->getPointeeLoc());
860 return nullptr;
861}
862
866 using namespace ::clang::ast_matchers; // NOLINT: Too many names
867 return std::move(Builder)
870 .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("status"),
872 .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusMemberCallWithName("ok"),
874 .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusMemberCallWithName("Update"),
876 .CaseOfCFGStmt<CXXOperatorCallExpr>(
879 LatticeTransferState &State) {
881 /*IsNegative=*/false);
882 })
883 .CaseOfCFGStmt<CXXOperatorCallExpr>(
886 LatticeTransferState &State) {
888 /*IsNegative=*/true);
889 })
890 .CaseOfCFGStmt<BinaryOperator>(
893 LatticeTransferState &State) {
895 /*IsNegative=*/false);
896 })
897 .CaseOfCFGStmt<BinaryOperator>(
900 LatticeTransferState &State) {
902 /*IsNegative=*/true);
903 })
904 .CaseOfCFGStmt<CallExpr>(isOkStatusCall(), transferOkStatusCall)
906 .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("emplace"),
910 .CaseOfCFGStmt<CXXConstructExpr>(isStatusOrValueConstructor(),
912 .CaseOfCFGStmt<CallExpr>(isAsStatusCallWithStatus(),
914 .CaseOfCFGStmt<CallExpr>(isAsStatusCallWithStatusOr(),
918 .CaseOfCFGStmt<CallExpr>(isLoggingCheckEqImpl(),
920 // This needs to go before the const accessor call matcher, because these
921 // look like them, but we model `operator`* and `get` to return the same
922 // object. Also, we model them for non-const cases.
923 .CaseOfCFGStmt<CXXOperatorCallExpr>(
925 [](const CXXOperatorCallExpr *E,
927 LatticeTransferState &State) {
929 E, getSmartPtrLikeStorageLocation(*E->getArg(0), State.Env),
930 State, [](StorageLocation &Loc) {});
931 })
932 .CaseOfCFGStmt<CXXOperatorCallExpr>(
934 [](const CXXOperatorCallExpr *E,
936 LatticeTransferState &State) {
938 E, getSmartPtrLikeStorageLocation(*E->getArg(0), State.Env),
939 State, [](StorageLocation &Loc) {});
940 })
941 .CaseOfCFGStmt<CXXMemberCallExpr>(
944 LatticeTransferState &State) {
946 E, getImplicitObjectLocation(*E, State.Env), State,
947 [](StorageLocation &Loc) {});
948 })
949 .CaseOfCFGStmt<CXXMemberCallExpr>(
952 LatticeTransferState &State) {
954 E, getImplicitObjectLocation(*E, State.Env), State,
955 [](StorageLocation &Loc) {});
956 })
957 // const accessor calls
958 .CaseOfCFGStmt<CXXMemberCallExpr>(isConstStatusOrAccessorMemberCall(),
960 .CaseOfCFGStmt<CXXOperatorCallExpr>(
963 .CaseOfCFGStmt<CXXMemberCallExpr>(
966 .CaseOfCFGStmt<CXXOperatorCallExpr>(
969 // non-const member calls that may modify the state of an object.
970 .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
974 // N.B. These need to come after all other CXXConstructExpr.
975 // These are there to make sure that every Status and StatusOr object
976 // have their ok boolean initialized when constructed. If we were to
977 // lazily initialize them when we first access them, we can produce
978 // false positives if that first access is in a control flow statement.
979 // You can comment out these two constructors and see tests fail.
980 .CaseOfCFGStmt<CXXConstructExpr>(isStatusOrConstructor(),
982 .CaseOfCFGStmt<CXXConstructExpr>(isStatusConstructor(),
984 .CaseOfCFGStmt<ImplicitCastExpr>(
985 implicitCastExpr(hasCastKind(CK_PointerToBoolean)),
987 .Build();
988}
989
991 for (Type *Ty : Ctx.getTypes())
992 if (isStatusType(QualType(Ty, 0)))
993 return QualType(Ty, 0);
994
995 return QualType();
996}
997
999 Environment &Env)
1002 TransferMatchSwitch(buildTransferMatchSwitch(Ctx, {})) {
1003 QualType StatusType = findStatusType(Ctx);
1004 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
1005 [StatusType](QualType Ty) -> llvm::StringMap<QualType> {
1007 if (RD == nullptr)
1008 return {};
1009
1010 if (auto Fields = getSyntheticFields(Ty, StatusType, *RD);
1011 !Fields.empty())
1012 return Fields;
1013 return {};
1014 });
1015}
1016
1018 Environment &Env) {
1019 LatticeTransferState State(L, Env);
1020 TransferMatchSwitch(Elt, getASTContext(), State);
1021}
1022
1023} // namespace clang::dataflow::statusor_model
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the C++ template declaration subclasses.
Defines the clang::Expr interface and subclasses for C++ expressions.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
llvm::MachO::RecordLoc RecordLoc
Definition MachO.h:41
Defines the clang::SourceLocation class and associated facilities.
C Language Family Type Representation.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:220
const SmallVectorImpl< Type * > & getTypes() const
CanQualType BoolTy
A builtin binary operation expression such as "x + y" or "x <= y".
Definition Expr.h:3972
Represents a top-level expression in a basic block.
Definition CFG.h:55
Represents a call to a C++ constructor.
Definition ExprCXX.h:1548
Represents a call to a member function that may be written either with member call syntax (e....
Definition ExprCXX.h:179
SourceLocation getExprLoc() const LLVM_READONLY
Definition ExprCXX.h:220
A call to an overloaded operator written using operator syntax.
Definition ExprCXX.h:84
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
bool hasDefinition() const
Definition DeclCXX.h:561
bool isDerivedFrom(const CXXRecordDecl *Base) const
Determine whether this class is derived from the class Base.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:2877
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3081
Represents a class template specialization, which refers to a class template with a given set of temp...
const TemplateArgumentList & getTemplateArgs() const
Retrieve the template arguments of the class template specialization.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2109
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:546
This represents one expression.
Definition Expr.h:112
bool isPRValue() const
Definition Expr.h:285
QualType getType() const
Definition Expr.h:144
Represents a function declaration or definition.
Definition Decl.h:2000
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition Expr.h:3787
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:301
Represent a C++ namespace.
Definition Decl.h:592
A (possibly-)qualified type.
Definition TypeBase.h:937
const TemplateArgument & get(unsigned Idx) const
Retrieve the template argument at a given index.
QualType getAsType() const
Retrieve the type for a type template argument.
The base class of the type hierarchy.
Definition TypeBase.h:1833
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
Definition Type.h:41
bool isPointerType() const
Definition TypeBase.h:8515
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:752
Models a boolean.
Definition Value.h:94
Collects cases of a "match switch": a collection of matchers paired with callbacks,...
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.
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...
Models a symbolic pointer. Specifically, any value of type T*.
Definition Value.h:170
A storage location for a record (struct, class, or union).
StorageLocation & getSyntheticField(llvm::StringRef Name) const
Returns the storage location for the synthetic field Name.
Base class for elements of the local variable store and of the heap.
Base class for all values computed by abstract interpretation.
Definition Value.h:33
UncheckedStatusOrAccessDiagnoser(UncheckedStatusOrAccessModelOptions Options={})
llvm::SmallVector< SourceLocation > operator()(const CFGElement &Elt, ASTContext &Ctx, const TransferStateForDiagnostics< UncheckedStatusOrAccessModel::Lattice > &State)
void transfer(const CFGElement &Elt, Lattice &L, Environment &Env)
internal::Matcher< QualType > TypeMatcher
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
internal::Matcher< Decl > DeclarationMatcher
Types of matchers for the top-level classes in the AST class hierarchy.
const internal::VariadicDynCastAllOfMatcher< Stmt, ImplicitCastExpr > implicitCastExpr
Matches the implicit cast nodes of Clang's AST.
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.
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
const internal::VariadicFunction< internal::Matcher< NamedDecl >, StringRef, internal::hasAnyNameFunc > hasAnyName
Matches NamedDecl nodes that have any of the specified names.
internal::Matcher< Stmt > StatementMatcher
const internal::VariadicDynCastAllOfMatcher< Stmt, BinaryOperator > binaryOperator
Matches binary operator expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXConstructExpr > cxxConstructExpr
Matches constructor call expressions (including implicit ones).
const internal::VariadicDynCastAllOfMatcher< Decl, ClassTemplateSpecializationDecl > classTemplateSpecializationDecl
Matches C++ class template specializations.
const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl
Matches function declarations.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl
Matches C++ class declarations.
internal::PolymorphicMatcher< internal::HasDeclarationMatcher, void(internal::HasDeclarationSupportedTypes), internal::Matcher< Decl > > hasDeclaration(const internal::Matcher< Decl > &InnerMatcher)
Matches a node if the declaration associated with that node matches the given matcher.
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf
Matches if any of the given matchers matches.
const internal::VariadicAllOfMatcher< QualType > qualType
Matches QualTypes in the clang AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXThisExpr > cxxThisExpr
Matches implicit and explicit this expressions.
static void transferLoggingCheckEqImpl(const CallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static void transferStatusOrReturningCall(const CallExpr *Expr, LatticeTransferState &State)
static void transferValueAssignmentCall(const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
TransferState< UncheckedStatusOrAccessModel::Lattice > LatticeTransferState
static ClassTemplateSpecializationDecl * getStatusOrBaseClass(const QualType &Ty)
static bool isStatusOrOperatorBaseType(QualType Type)
static void transferConstStatusOrAccessorMemberCall(const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &Result, LatticeTransferState &State)
static auto isStatusMemberCallWithName(llvm::StringRef member_name)
static auto buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options)
static auto isComparisonOperatorCall(llvm::StringRef operator_name)
RecordStorageLocation & locForStatus(RecordStorageLocation &StatusOrLoc)
static void transferNotOkStatusCall(const CallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static BoolValue * evaluateStatusEquality(RecordStorageLocation &LhsStatusLoc, RecordStorageLocation &RhsStatusLoc, Environment &Env)
static void transferStatusConstructor(const CXXConstructExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static BoolValue * evaluateStatusOrEquality(RecordStorageLocation &LhsStatusOrLoc, RecordStorageLocation &RhsStatusOrLoc, Environment &Env)
static auto isStatusOrOperatorCallWithName(llvm::StringRef operator_name)
static void transferNonConstMemberOperatorCall(const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &Result, LatticeTransferState &State)
static void transferValueConstructor(const CXXConstructExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static void transferComparisonOperator(const CXXOperatorCallExpr *Expr, LatticeTransferState &State, bool IsNegative)
static void transferConstStatusOrPointerAccessorMemberCall(const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &Result, LatticeTransferState &State)
clang::ast_matchers::DeclarationMatcher statusOrClass()
static void transferOkStatusCall(const CallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static auto isPointerComparisonOperatorCall(std::string operator_name)
clang::ast_matchers::TypeMatcher statusOrType()
static bool isSafeUnwrap(RecordStorageLocation *StatusOrLoc, const Environment &Env)
static void transferPointerComparisonOperator(const BinaryOperator *Expr, LatticeTransferState &State, bool IsNegative)
static void transferNonConstMemberCall(const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &Result, LatticeTransferState &State)
static void transferStatusOrOkCall(const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static bool namespaceEquals(const NamespaceDecl *NS, clang::ArrayRef< clang::StringRef > NamespaceNames)
static bool isTypeNamed(QualType Type, clang::ArrayRef< clang::StringRef > NS, StringRef Name)
static void transferStatusOrConstructor(const CXXConstructExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static void transferLoggingGetReferenceableValueCall(const CallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
clang::ast_matchers::DeclarationMatcher statusClass()
static RecordStorageLocation * getSmartPtrLikeStorageLocation(const Expr &E, const Environment &Env)
static void handleNonConstMemberCall(const CallExpr *Expr, RecordStorageLocation *RecordLoc, const MatchFinder::MatchResult &Result, LatticeTransferState &State)
BoolValue & initializeStatusOr(RecordStorageLocation &StatusOrLoc, Environment &Env)
static void transferAsStatusCallWithStatusOr(const CallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
QualType findStatusType(const ASTContext &Ctx)
BoolValue & initializeStatus(RecordStorageLocation &StatusLoc, Environment &Env)
static void transferPointerToBoolean(const ImplicitCastExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
llvm::StringMap< QualType > getSyntheticFields(QualType Ty, QualType StatusType, const CXXRecordDecl &RD)
StorageLocation & locForOk(RecordStorageLocation &StatusLoc)
static void transferConstStatusOrPointerAccessorMemberOperatorCall(const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &Result, LatticeTransferState &State)
static void handleConstStatusOrPointerAccessorMemberCall(const CallExpr *Expr, RecordStorageLocation *RecordLoc, const MatchFinder::MatchResult &Result, LatticeTransferState &State)
static auto isStatusOrMemberCallWithName(llvm::StringRef member_name)
static BoolValue * evaluateEquality(const Expr *LhsExpr, const Expr *RhsExpr, Environment &Env)
static void transferStatusCall(const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static clang::ast_matchers::TypeMatcher statusType()
static void transferStatusOkCall(const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static void transferEmplaceCall(const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static void transferAsStatusCallWithStatus(const CallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static BoolValue * evaluatePointerEquality(const Expr *LhsExpr, const Expr *RhsExpr, Environment &Env)
static QualType getStatusOrValueType(ClassTemplateSpecializationDecl *TRD)
static RecordStorageLocation * getPointeeLocation(const Expr &Expr, Environment &Env)
BoolValue & valForOk(RecordStorageLocation &StatusLoc, Environment &Env)
static void transferConstStatusOrAccessorMemberOperatorCall(const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &Result, LatticeTransferState &State)
static void transferStatusUpdateCall(const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static bool doHandleConstStatusOrAccessorMemberCall(const CallExpr *Expr, RecordStorageLocation *RecordLoc, const MatchFinder::MatchResult &Result, LatticeTransferState &State)
CFGMatchSwitch< LatticeTransferState > buildTransferMatchSwitch(ASTContext &Ctx, CFGMatchSwitchBuilder< LatticeTransferState > Builder)
static void handleConstStatusOrAccessorMemberCall(const CallExpr *Expr, RecordStorageLocation *RecordLoc, const MatchFinder::MatchResult &Result, LatticeTransferState &State)
clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass()
ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall(clang::StringRef MethodName="value")
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.
ast_matchers::StatementMatcher isPointerLikeOperatorStar()
Matchers: For now, these match on any class with an operator* or operator-> where the return types ha...
void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst, Environment &Env, QualType TypeToCopy=QualType())
Copies a record (struct, class, or union) from Src to Dst.
Definition RecordOps.cpp:57
void transferSmartPointerLikeCachedGet(const CallExpr *GetExpr, RecordStorageLocation *SmartPointerLoc, TransferState< LatticeT > &State, llvm::function_ref< void(StorageLocation &)> InitializeLoc)
A transfer function for operator-> (and get) calls that can be cached.
internal::Matcher< NamedDecl > hasName(StringRef Name)
Matches NamedDecl nodes that have the specified name.
std::function< Result(const CFGElement &, ASTContext &, State &)> CFGMatchSwitch
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXMemberCallExpr > cxxMemberCallExpr
Matches member call expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXOperatorCallExpr > cxxOperatorCallExpr
Matches overloaded operator calls.
const AstTypeMatcher< PointerType > pointerType
internal::PolymorphicMatcher< internal::HasOverloadedOperatorNameMatcher, AST_POLYMORPHIC_SUPPORTED_TYPES(CXXOperatorCallExpr, FunctionDecl), std::vector< std::string > > hasOverloadedOperatorName(StringRef Name)
Matches overloaded operator names.
RecordStorageLocation * getImplicitObjectLocation(const CXXMemberCallExpr &MCE, const Environment &Env)
Returns the storage location for the implicit object of a CXXMemberCallExpr, or null if none is defin...
const internal::VariadicDynCastAllOfMatcher< Decl, CXXMethodDecl > cxxMethodDecl
Matches method declarations.
ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall(clang::StringRef MethodName="get")
ast_matchers::StatementMatcher isPointerLikeOperatorArrow()
const AstTypeMatcher< ReferenceType > referenceType
@ Result
The result type of a method or function.
Definition TypeBase.h:905
U cast(CodeGen::Address addr)
Definition Address.h:327
Contains all information for a given match.
A read-only version of TransferState.
Definition MatchSwitch.h:55
Contains all information for a given match.