49#include "llvm/ADT/ArrayRef.h"
50#include "llvm/ADT/DenseMap.h"
51#include "llvm/ADT/DenseSet.h"
52#include "llvm/ADT/FoldingSet.h"
53#include "llvm/ADT/STLExtras.h"
54#include "llvm/ADT/SmallPtrSet.h"
55#include "llvm/ADT/SmallString.h"
56#include "llvm/ADT/SmallVector.h"
57#include "llvm/ADT/Statistic.h"
58#include "llvm/ADT/StringExtras.h"
59#include "llvm/ADT/StringRef.h"
60#include "llvm/ADT/iterator_range.h"
61#include "llvm/Support/Casting.h"
62#include "llvm/Support/Compiler.h"
63#include "llvm/Support/ErrorHandling.h"
64#include "llvm/Support/MemoryBuffer.h"
65#include "llvm/Support/raw_ostream.h"
82#define DEBUG_TYPE "BugReporter"
85 "The maximum number of bug reports in the same equivalence class");
87 "The maximum number of bug reports in the same equivalence class "
88 "where at least one report is valid (not suppressed)");
90STATISTIC(NumTimesReportPassesZ3,
"Number of reports passed Z3");
91STATISTIC(NumTimesReportRefuted,
"Number of reports refuted by Z3");
93 "Number of times a report equivalence class was aborted by the Z3 "
96 "Number of times all reports of an equivalence class was refuted");
100void BugReporterContext::anchor() {}
110 std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>;
114using VisitorsDiagnosticsTy =
115 llvm::DenseMap<const ExplodedNode *, std::vector<PathDiagnosticPieceRef>>;
119using LocationContextMap =
120 llvm::DenseMap<const PathPieces *, const LocationContext *>;
125class PathDiagnosticConstruct {
134 LocationContextMap LCM;
141 CallWithEntryStack CallStack;
145 std::unique_ptr<PathDiagnostic> PD;
151 const Decl *AnalysisEntryPoint);
156 assert(CurrentNode &&
"Already reached the root!");
164 return LCM.find(&PD->getActivePath())->getSecond();
167 const ExplodedNode *getCurrentNode()
const {
return CurrentNode; }
171 bool ascendToPrevNode() {
173 return static_cast<bool>(CurrentNode);
177 return getCurrLocationContext()->getParentMap();
182 const Stmt *getParent(
const Stmt *S)
const {
183 return getParentMap().getParent(S);
192 assert(LCM.count(
Path) &&
193 "Failed to find the context associated with these pieces!");
194 return LCM.find(
Path)->getSecond();
199 PathPieces &getActivePath() {
return PD->getActivePath(); }
200 PathPieces &getMutablePieces() {
return PD->getMutablePieces(); }
203 bool shouldAddControlNotes()
const {
206 bool shouldGenerateDiagnostics()
const {
209 bool supportsLogicalOpControlFlow()
const {
220 std::unique_ptr<const ExplodedGraph> BugPath;
231 std::unique_ptr<const VisitorsDiagnosticsTy> VisitorsDiagnostics;
237 static std::optional<PathDiagnosticBuilder>
241 PathDiagnosticBuilder(
244 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics);
256 std::unique_ptr<PathDiagnostic>
261 const CallWithEntryStack &CallStack)
const;
262 void generatePathDiagnosticsForNode(PathDiagnosticConstruct &
C,
265 void generateMinimalDiagForBlockEdge(PathDiagnosticConstruct &
C,
269 generateDiagForGotoOP(
const PathDiagnosticConstruct &
C,
const Stmt *S,
273 generateDiagForSwitchOP(
const PathDiagnosticConstruct &
C,
const CFGBlock *Dst,
277 generateDiagForBinaryOP(
const PathDiagnosticConstruct &
C,
const Stmt *
T,
281 ExecutionContinues(
const PathDiagnosticConstruct &
C)
const;
284 ExecutionContinues(llvm::raw_string_ostream &os,
285 const PathDiagnosticConstruct &
C)
const;
307 const auto *CE = dyn_cast_or_null<CallExpr>(CallSite);
312 for (
auto [Idx, ArgExpr] : llvm::enumerate(CE->arguments())) {
324 if (ArgExpr->getType()->isVoidPointerType())
349 return (llvm::Twine(Msg) +
" via " + std::to_string(ArgIndex) +
350 llvm::getOrdinalSuffix(ArgIndex) +
" parameter").str();
370 if (
X->getTag() == tagPreferred && Y->
getTag() == tagLesser)
373 if (Y->
getTag() == tagPreferred &&
X->getTag() == tagLesser)
385 unsigned N = path.size();
392 for (
unsigned i = 0; i < N; ++i) {
393 auto piece = std::move(path.front());
396 switch (piece->getKind()) {
407 if (
auto *nextEvent =
408 dyn_cast<PathDiagnosticEventPiece>(path.front().get())) {
409 auto *
event = cast<PathDiagnosticEventPiece>(piece.get());
413 if (
auto *pieceToKeep =
415 piece = std::move(pieceToKeep == event ? piece : path.front());
427 path.push_back(std::move(piece));
437 bool IsInteresting =
false) {
438 bool containsSomethingInteresting = IsInteresting;
439 const unsigned N = pieces.size();
441 for (
unsigned i = 0 ; i < N ; ++i) {
444 auto piece = std::move(pieces.front());
447 switch (piece->getKind()) {
449 auto &call = cast<PathDiagnosticCallPiece>(*piece);
456 containsSomethingInteresting =
true;
460 auto ¯o = cast<PathDiagnosticMacroPiece>(*piece);
463 containsSomethingInteresting =
true;
467 auto &
event = cast<PathDiagnosticEventPiece>(*piece);
471 containsSomethingInteresting |= !
event.isPrunable();
480 pieces.push_back(std::move(piece));
483 return containsSomethingInteresting;
488 for (
unsigned int i = 0; i <
Path.size(); ++i) {
489 auto Piece = std::move(
Path.front());
491 if (!isa<PathDiagnosticPopUpPiece>(*Piece))
492 Path.push_back(std::move(Piece));
508 for (
const auto &I : Pieces) {
509 auto *
Call = dyn_cast<PathDiagnosticCallPiece>(I.get());
514 if (LastCallLocation) {
516 if (CallerIsImplicit || !
Call->callEnter.asLocation().isValid())
517 Call->callEnter = *LastCallLocation;
518 if (CallerIsImplicit || !
Call->callReturn.asLocation().isValid())
519 Call->callReturn = *LastCallLocation;
525 if (
Call->callEnterWithin.asLocation().isValid() &&
527 ThisCallLocation = &
Call->callEnterWithin;
529 ThisCallLocation = &
Call->callEnter;
531 assert(ThisCallLocation &&
"Outermost call has an invalid location");
540 for (PathPieces::iterator I = Pieces.begin(),
E = Pieces.end(); I !=
E;) {
541 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
544 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
547 if (
auto *
CF = dyn_cast<PathDiagnosticControlFlowPiece>(I->get())) {
548 const Stmt *Start =
CF->getStartLocation().asStmt();
549 const Stmt *End =
CF->getEndLocation().asStmt();
550 if (isa_and_nonnull<CXXDefaultInitExpr>(Start)) {
553 }
else if (isa_and_nonnull<CXXDefaultInitExpr>(End)) {
554 PathPieces::iterator Next = std::next(I);
557 dyn_cast<PathDiagnosticControlFlowPiece>(Next->get())) {
558 NextCF->setStartLocation(
CF->getStartLocation());
574 for (PathPieces::iterator I = Pieces.begin(),
E = Pieces.end(); I !=
E;) {
575 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
578 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
581 if (!(*I)->getLocation().isValid() ||
582 !(*I)->getLocation().asLocation().isValid()) {
591 const PathDiagnosticConstruct &
C)
const {
592 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
594 C.getCurrLocationContext());
601 llvm::raw_string_ostream &os,
const PathDiagnosticConstruct &
C)
const {
603 if (os.str().empty())
609 os <<
"Execution continues on line "
610 << getSourceManager().getExpansionLineNumber(
Loc.asLocation())
613 os <<
"Execution jumps to the end of the ";
614 const Decl *
D =
C.getCurrLocationContext()->getDecl();
615 if (isa<ObjCMethodDecl>(
D))
617 else if (isa<FunctionDecl>(
D))
620 assert(isa<BlockDecl>(
D));
621 os <<
"anonymous block";
637 switch (
Parent->getStmtClass()) {
638 case Stmt::ForStmtClass:
639 case Stmt::DoStmtClass:
640 case Stmt::WhileStmtClass:
641 case Stmt::ObjCForCollectionStmtClass:
642 case Stmt::CXXForRangeStmtClass:
653 bool allowNestedContexts =
false) {
660 switch (
Parent->getStmtClass()) {
661 case Stmt::BinaryOperatorClass: {
662 const auto *B = cast<BinaryOperator>(
Parent);
663 if (B->isLogicalOp())
667 case Stmt::CompoundStmtClass:
668 case Stmt::StmtExprClass:
670 case Stmt::ChooseExprClass:
673 if (allowNestedContexts || cast<ChooseExpr>(
Parent)->getCond() == S)
677 case Stmt::BinaryConditionalOperatorClass:
678 case Stmt::ConditionalOperatorClass:
681 if (allowNestedContexts ||
682 cast<AbstractConditionalOperator>(
Parent)->getCond() == S)
686 case Stmt::CXXForRangeStmtClass:
687 if (cast<CXXForRangeStmt>(
Parent)->getBody() == S)
690 case Stmt::DoStmtClass:
692 case Stmt::ForStmtClass:
693 if (cast<ForStmt>(
Parent)->getBody() == S)
696 case Stmt::IfStmtClass:
697 if (cast<IfStmt>(
Parent)->getCond() != S)
700 case Stmt::ObjCForCollectionStmtClass:
701 if (cast<ObjCForCollectionStmt>(
Parent)->getBody() == S)
704 case Stmt::WhileStmtClass:
705 if (cast<WhileStmt>(
Parent)->getCond() != S)
715 assert(S &&
"Cannot have null Stmt for PathDiagnosticLocation");
734void PathDiagnosticBuilder::updateStackPiecesWithMessage(
736 if (R->hasCallStackHint(
P))
737 for (
const auto &I : CallStack) {
740 std::string stackMsg = R->getCallStackMessage(
P, N);
754 const PathDiagnosticConstruct &
C,
const CFGBlock *Dst,
760 llvm::raw_string_ostream os(sbuf);
766 switch (S->getStmtClass()) {
768 os <<
"No cases match in the switch statement. "
769 "Control jumps to line "
770 << End.asLocation().getExpansionLineNumber();
772 case Stmt::DefaultStmtClass:
773 os <<
"Control jumps to the 'default' case at line "
774 << End.asLocation().getExpansionLineNumber();
777 case Stmt::CaseStmtClass: {
778 os <<
"Control jumps to 'case ";
779 const auto *Case = cast<CaseStmt>(S);
783 bool GetRawInt =
true;
785 if (
const auto *DR = dyn_cast<DeclRefExpr>(LHS)) {
788 const auto *
D = dyn_cast<EnumConstantDecl>(DR->getDecl());
799 os <<
":' at line " << End.asLocation().getExpansionLineNumber();
804 os <<
"'Default' branch taken. ";
805 End = ExecutionContinues(os,
C);
807 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf);
811 const PathDiagnosticConstruct &
C,
const Stmt *S,
814 llvm::raw_string_ostream os(sbuf);
817 os <<
"Control jumps to line " << End.asLocation().getExpansionLineNumber();
818 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf);
822 const PathDiagnosticConstruct &
C,
const Stmt *
T,
const CFGBlock *Src,
827 const auto *B = cast<BinaryOperator>(
T);
829 llvm::raw_string_ostream os(sbuf);
830 os <<
"Left side of '";
833 if (B->getOpcode() == BO_LAnd) {
846 End = ExecutionContinues(
C);
849 assert(B->getOpcode() == BO_LOr);
857 End = ExecutionContinues(
C);
865 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf);
868void PathDiagnosticBuilder::generateMinimalDiagForBlockEdge(
869 PathDiagnosticConstruct &
C,
BlockEdge BE)
const {
879 switch (
T->getStmtClass()) {
883 case Stmt::GotoStmtClass:
884 case Stmt::IndirectGotoStmtClass: {
885 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
886 C.getActivePath().push_front(generateDiagForGotoOP(
C, S, Start));
890 case Stmt::SwitchStmtClass: {
891 C.getActivePath().push_front(generateDiagForSwitchOP(
C, Dst, Start));
895 case Stmt::BreakStmtClass:
896 case Stmt::ContinueStmtClass: {
898 llvm::raw_string_ostream os(sbuf);
900 C.getActivePath().push_front(
901 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf));
906 case Stmt::BinaryConditionalOperatorClass:
907 case Stmt::ConditionalOperatorClass: {
909 llvm::raw_string_ostream os(sbuf);
910 os <<
"'?' condition is ";
919 if (
const Stmt *S = End.asStmt())
922 C.getActivePath().push_front(
923 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf));
928 case Stmt::BinaryOperatorClass: {
929 if (!
C.supportsLogicalOpControlFlow())
932 C.getActivePath().push_front(generateDiagForBinaryOP(
C,
T, Src, Dst));
936 case Stmt::DoStmtClass:
939 llvm::raw_string_ostream os(sbuf);
941 os <<
"Loop condition is true. ";
944 if (
const Stmt *S = End.asStmt())
947 C.getActivePath().push_front(
948 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf));
952 if (
const Stmt *S = End.asStmt())
955 C.getActivePath().push_front(
956 std::make_shared<PathDiagnosticControlFlowPiece>(
957 Start, End,
"Loop condition is false. Exiting loop"));
961 case Stmt::WhileStmtClass:
962 case Stmt::ForStmtClass:
965 llvm::raw_string_ostream os(sbuf);
967 os <<
"Loop condition is false. ";
969 if (
const Stmt *S = End.asStmt())
972 C.getActivePath().push_front(
973 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf));
976 if (
const Stmt *S = End.asStmt())
979 C.getActivePath().push_front(
980 std::make_shared<PathDiagnosticControlFlowPiece>(
981 Start, End,
"Loop condition is true. Entering loop body"));
986 case Stmt::IfStmtClass: {
989 if (
const Stmt *S = End.asStmt())
993 C.getActivePath().push_front(
994 std::make_shared<PathDiagnosticControlFlowPiece>(
995 Start, End,
"Taking false branch"));
997 C.getActivePath().push_front(
998 std::make_shared<PathDiagnosticControlFlowPiece>(
999 Start, End,
"Taking true branch"));
1012 case Stmt::ForStmtClass:
1013 case Stmt::WhileStmtClass:
1014 case Stmt::ObjCForCollectionStmtClass:
1015 case Stmt::CXXForRangeStmtClass:
1044 const Stmt *S = SP->getStmt();
1054 const Stmt *LoopBody =
nullptr;
1056 case Stmt::CXXForRangeStmtClass: {
1057 const auto *FR = cast<CXXForRangeStmt>(Term);
1062 LoopBody = FR->getBody();
1065 case Stmt::ForStmtClass: {
1066 const auto *FS = cast<ForStmt>(Term);
1069 LoopBody = FS->getBody();
1072 case Stmt::ObjCForCollectionStmtClass: {
1073 const auto *FC = cast<ObjCForCollectionStmt>(Term);
1074 LoopBody = FC->getBody();
1077 case Stmt::WhileStmtClass:
1078 LoopBody = cast<WhileStmt>(Term)->getBody();
1108 std::make_shared<PathDiagnosticControlFlowPiece>(NewLoc, PrevLoc));
1116 if (
const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(S))
1117 return FS->getElement();
1124 "Loop body skipped when range is empty";
1126 "Loop body skipped when collection is empty";
1128static std::unique_ptr<FilesToLineNumsMap>
1131void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
1142 if (
C.shouldAddPathEdges()) {
1158 bool VisitedEntireCall =
C.PD->isWithinCall();
1159 C.PD->popActivePath();
1162 if (VisitedEntireCall) {
1163 Call = cast<PathDiagnosticCallPiece>(
C.getActivePath().front().get());
1167 const Decl *Caller = CE->getLocationContext()->getDecl();
1169 assert(
C.getActivePath().size() == 1 &&
1170 C.getActivePath().front().get() ==
Call);
1174 assert(
C.isInLocCtxMap(&
C.getActivePath()) &&
1175 "When we ascend to a previously unvisited call, the active path's "
1176 "address shouldn't change, but rather should be compacted into "
1177 "a single CallEvent!");
1178 C.updateLocCtxMap(&
C.getActivePath(),
C.getCurrLocationContext());
1181 assert(!
C.isInLocCtxMap(&
Call->path) &&
1182 "When we ascend to a previously unvisited call, this must be the "
1183 "first time we encounter the caller context!");
1184 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1186 Call->setCallee(*CE,
SM);
1189 PrevLoc =
Call->getLocation();
1191 if (!
C.CallStack.empty()) {
1192 assert(
C.CallStack.back().first ==
Call);
1193 C.CallStack.pop_back();
1198 assert(
C.getCurrLocationContext() ==
C.getLocationContextForActivePath() &&
1199 "The current position in the bug path is out of sync with the "
1200 "location context associated with the active path!");
1203 if (std::optional<CallExitEnd> CE =
P.getAs<
CallExitEnd>()) {
1209 assert(!
C.isInLocCtxMap(&
Call->path) &&
1210 "We just entered a call, this must've been the first time we "
1211 "encounter its context!");
1212 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1214 if (
C.shouldAddPathEdges()) {
1220 auto *
P =
Call.get();
1221 C.getActivePath().push_front(std::move(
Call));
1224 C.PD->pushActivePath(&
P->path);
1225 C.CallStack.push_back(CallWithEntry(
P,
C.getCurrentNode()));
1230 if (!
C.shouldAddPathEdges())
1236 if (!isa<ObjCForCollectionStmt>(PS->getStmt())) {
1244 if (
C.shouldAddControlNotes()) {
1245 generateMinimalDiagForBlockEdge(
C, *BE);
1248 if (!
C.shouldAddPathEdges()) {
1255 const Stmt *Body =
nullptr;
1257 if (
const auto *FS = dyn_cast<ForStmt>(
Loop))
1258 Body = FS->getBody();
1259 else if (
const auto *WS = dyn_cast<WhileStmt>(
Loop))
1260 Body = WS->getBody();
1261 else if (
const auto *OFS = dyn_cast<ObjCForCollectionStmt>(
Loop)) {
1262 Body = OFS->getBody();
1263 }
else if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(
Loop)) {
1264 Body = FRS->getBody();
1268 auto p = std::make_shared<PathDiagnosticEventPiece>(
1269 L,
"Looping back to the head of the loop");
1270 p->setPrunable(
true);
1274 if (!
C.shouldAddControlNotes()) {
1275 C.getActivePath().push_front(std::move(p));
1278 if (
const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
1298 if (!IsInLoopBody) {
1299 if (isa<ObjCForCollectionStmt>(Term)) {
1301 }
else if (isa<CXXForRangeStmt>(Term)) {
1313 C.getCurrLocationContext());
1314 auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
1315 PE->setPrunable(
true);
1319 if (!
C.shouldAddControlNotes()) {
1320 C.getActivePath().push_front(std::move(PE));
1323 }
else if (isa<BreakStmt, ContinueStmt, GotoStmt>(Term)) {
1331static std::unique_ptr<PathDiagnostic>
1333 const Decl *AnalysisEntryPoint) {
1335 return std::make_unique<PathDiagnostic>(
1339 AnalysisEntryPoint, std::make_unique<FilesToLineNumsMap>());
1342static std::unique_ptr<PathDiagnostic>
1345 const Decl *AnalysisEntryPoint) {
1347 return std::make_unique<PathDiagnostic>(
1364 if (isa<FullExpr, CXXBindTemporaryExpr, SubstNonTypeTemplateParmExpr>(S))
1374 switch (S->getStmtClass()) {
1375 case Stmt::BinaryOperatorClass: {
1376 const auto *BO = cast<BinaryOperator>(S);
1377 if (!BO->isLogicalOp())
1379 return BO->getLHS() == Cond || BO->getRHS() == Cond;
1381 case Stmt::IfStmtClass:
1382 return cast<IfStmt>(S)->getCond() == Cond;
1383 case Stmt::ForStmtClass:
1384 return cast<ForStmt>(S)->getCond() == Cond;
1385 case Stmt::WhileStmtClass:
1386 return cast<WhileStmt>(S)->getCond() == Cond;
1387 case Stmt::DoStmtClass:
1388 return cast<DoStmt>(S)->getCond() == Cond;
1389 case Stmt::ChooseExprClass:
1390 return cast<ChooseExpr>(S)->getCond() == Cond;
1391 case Stmt::IndirectGotoStmtClass:
1392 return cast<IndirectGotoStmt>(S)->getTarget() == Cond;
1393 case Stmt::SwitchStmtClass:
1394 return cast<SwitchStmt>(S)->getCond() == Cond;
1395 case Stmt::BinaryConditionalOperatorClass:
1396 return cast<BinaryConditionalOperator>(S)->getCond() == Cond;
1397 case Stmt::ConditionalOperatorClass: {
1398 const auto *CO = cast<ConditionalOperator>(S);
1399 return CO->getCond() == Cond ||
1400 CO->getLHS() == Cond ||
1401 CO->getRHS() == Cond;
1403 case Stmt::ObjCForCollectionStmtClass:
1404 return cast<ObjCForCollectionStmt>(S)->getElement() == Cond;
1405 case Stmt::CXXForRangeStmtClass: {
1406 const auto *FRS = cast<CXXForRangeStmt>(S);
1407 return FRS->getCond() == Cond || FRS->getRangeInit() == Cond;
1415 if (
const auto *FS = dyn_cast<ForStmt>(FL))
1416 return FS->getInc() == S || FS->getInit() == S;
1417 if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(FL))
1418 return FRS->getInc() == S || FRS->getRangeStmt() == S ||
1419 FRS->getLoopVarStmt() || FRS->getRangeInit() == S;
1432 PathPieces::iterator Prev = pieces.end();
1433 for (PathPieces::iterator I = pieces.begin(),
E = Prev; I !=
E;
1435 auto *Piece = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1444 const Stmt *InnerStmt =
nullptr;
1445 while (NextSrcContext.
isValid() && NextSrcContext.
asStmt() != InnerStmt) {
1446 SrcContexts.push_back(NextSrcContext);
1447 InnerStmt = NextSrcContext.
asStmt();
1456 const Stmt *Dst = Piece->getEndLocation().getStmtOrNull();
1466 if (llvm::is_contained(SrcContexts, DstContext))
1470 Piece->setStartLocation(DstContext);
1475 auto *PrevPiece = dyn_cast<PathDiagnosticControlFlowPiece>(Prev->get());
1478 if (
const Stmt *PrevSrc =
1479 PrevPiece->getStartLocation().getStmtOrNull()) {
1481 if (PrevSrcParent ==
1483 PrevPiece->setEndLocation(DstContext);
1494 std::make_shared<PathDiagnosticControlFlowPiece>(SrcLoc, DstContext);
1496 I = pieces.insert(I, std::move(
P));
1512 for (PathPieces::iterator I = pieces.begin(),
E = pieces.end(); I !=
E; ++I) {
1513 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1518 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1519 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1521 if (!s1Start || !s1End)
1524 PathPieces::iterator NextI = I; ++NextI;
1534 const auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1536 StringRef S = EV->getString();
1545 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1555 if (!s2Start || !s2End || s1End != s2Start)
1571 I = pieces.erase(I);
1584 SM.getExpansionRange(
Range.getEnd()).getEnd());
1587 if (FID !=
SM.getFileID(ExpansionRange.
getEnd()))
1588 return std::nullopt;
1590 std::optional<MemoryBufferRef> Buffer =
SM.getBufferOrNone(FID);
1592 return std::nullopt;
1594 unsigned BeginOffset =
SM.getFileOffset(ExpansionRange.
getBegin());
1595 unsigned EndOffset =
SM.getFileOffset(ExpansionRange.
getEnd());
1596 StringRef Snippet = Buffer->getBuffer().slice(BeginOffset, EndOffset);
1602 if (Snippet.find_first_of(
"\r\n") != StringRef::npos)
1603 return std::nullopt;
1606 return Snippet.size();
1632 for (PathPieces::iterator I =
Path.begin(),
E =
Path.end(); I !=
E; ) {
1634 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1641 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1642 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1644 PathPieces::iterator NextI = I; ++NextI;
1648 const auto *PieceNextI =
1649 dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1652 if (isa<PathDiagnosticEventPiece>(NextI->get())) {
1656 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1665 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1666 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1668 if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
1669 const size_t MAX_SHORT_LINE_LENGTH = 80;
1671 if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) {
1673 if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) {
1675 I =
Path.erase(NextI);
1698 bool erased =
false;
1700 for (PathPieces::iterator I = path.begin(),
E = path.end(); I !=
E;
1704 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1709 const Stmt *start = PieceI->getStartLocation().getStmtOrNull();
1710 const Stmt *end = PieceI->getEndLocation().getStmtOrNull();
1725 if (!
SM.isWrittenInSameFile(FirstLoc, SecondLoc))
1727 if (
SM.isBeforeInTranslationUnit(SecondLoc, FirstLoc))
1728 std::swap(SecondLoc, FirstLoc);
1737 const size_t MAX_PUNY_EDGE_LENGTH = 2;
1738 if (*ByteWidth <= MAX_PUNY_EDGE_LENGTH) {
1750 for (PathPieces::iterator I = path.begin(),
E = path.end(); I !=
E; ++I) {
1751 const auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get());
1756 PathPieces::iterator NextI = I; ++NextI;
1760 const auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1766 if (PieceI->getString() == PieceNextI->getString()) {
1774 bool hasChanges =
false;
1780 for (PathPieces::iterator I = path.begin(),
E = path.end(); I !=
E; ) {
1782 if (
auto *CallI = dyn_cast<PathDiagnosticCallPiece>(I->get())) {
1785 if (!OCS.count(CallI)) {
1795 auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1802 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1803 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1807 PathPieces::iterator NextI = I; ++NextI;
1811 const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1818 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1819 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1837 if (level1 && level1 == level2 && level1 == level3 && level1 == level4) {
1838 PieceI->setEndLocation(PieceNextI->getEndLocation());
1851 if (s1End && s1End == s2Start && level2) {
1852 bool removeEdge =
false;
1878 else if (s1Start && s2End &&
1891 else if (s1Start && s2End &&
1893 SourceRange EdgeRange(PieceI->getEndLocation().asLocation(),
1894 PieceI->getStartLocation().asLocation());
1901 PieceI->setEndLocation(PieceNextI->getEndLocation());
1915 if (s1End == s2Start) {
1916 const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(level3);
1917 if (FS && FS->getCollection()->IgnoreParens() == s2Start &&
1918 s2End == FS->getElement()) {
1919 PieceI->setEndLocation(PieceNextI->getEndLocation());
1956 const auto *FirstEdge =
1957 dyn_cast<PathDiagnosticControlFlowPiece>(
Path.front().get());
1961 const Decl *
D =
C.getLocationContextFor(&
Path)->getDecl();
1964 if (FirstEdge->getStartLocation() != EntryLoc)
1976 for (
const auto &
P : path) {
1979 unsigned LineNo =
Loc.getLineNumber();
1981 ExecutedLines[FID].insert(LineNo);
1985PathDiagnosticConstruct::PathDiagnosticConstruct(
1988 : Consumer(PDC), CurrentNode(ErrorNode),
1989 SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()),
1991 AnalysisEntryPoint)) {
1995PathDiagnosticBuilder::PathDiagnosticBuilder(
1998 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics)
2000 ErrorNode(ErrorNode),
2001 VisitorsDiagnostics(
std::move(VisitorsDiagnostics)) {}
2003std::unique_ptr<PathDiagnostic>
2005 const Decl *EntryPoint = getBugReporter().getAnalysisEntryPoint();
2006 PathDiagnosticConstruct Construct(PDC, ErrorNode, R, EntryPoint);
2015 auto EndNotes = VisitorsDiagnostics->find(ErrorNode);
2017 if (EndNotes != VisitorsDiagnostics->end()) {
2018 assert(!EndNotes->second.empty());
2019 LastPiece = EndNotes->second[0];
2024 Construct.PD->setEndOfPath(LastPiece);
2029 while (Construct.ascendToPrevNode()) {
2030 generatePathDiagnosticsForNode(Construct, PrevLoc);
2032 auto VisitorNotes = VisitorsDiagnostics->find(Construct.getCurrentNode());
2033 if (VisitorNotes == VisitorsDiagnostics->end())
2038 std::set<llvm::FoldingSetNodeID> DeduplicationSet;
2042 llvm::FoldingSetNodeID
ID;
2044 if (!DeduplicationSet.insert(ID).second)
2049 updateStackPiecesWithMessage(
Note, Construct.CallStack);
2050 Construct.getActivePath().push_front(
Note);
2058 Construct.getLocationContextForActivePath()->
getStackFrame();
2066 if (!Construct.PD->path.empty()) {
2068 bool stillHasNotes =
2070 assert(stillHasNotes);
2071 (void)stillHasNotes;
2075 if (!Opts.ShouldAddPopUpNotes)
2088 while (
optimizeEdges(Construct, Construct.getMutablePieces(), OCS)) {
2103 if (Opts.ShouldDisplayMacroExpansions)
2106 return std::move(Construct.PD);
2113void BugType::anchor() {}
2119LLVM_ATTRIBUTE_USED
static bool
2121 for (
const std::pair<StringRef, StringRef> &Pair : Registry.
Dependencies) {
2122 if (Pair.second == CheckerName)
2129 StringRef CheckerName) {
2131 if (
Checker.FullName == CheckerName)
2135 "Checker name not found in CheckerRegistry -- did you retrieve it "
2136 "correctly from CheckerManager::getCurrentCheckerName?");
2140 const BugType &bt, StringRef shortDesc, StringRef desc,
2142 const Decl *DeclToUnique)
2143 :
BugReport(
Kind::PathSensitive, bt, shortDesc, desc), ErrorNode(errorNode),
2144 ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() :
SourceRange()),
2145 UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) {
2147 ->getAnalysisManager()
2148 .getCheckerManager()
2149 ->getCheckerRegistryData(),
2151 "Some checkers depend on this one! We don't allow dependency "
2152 "checkers to emit warnings, because checkers should depend on "
2153 "*modeling*, not *diagnostics*.");
2157 ->getAnalysisManager()
2158 .getCheckerManager()
2159 ->getCheckerRegistryData(),
2161 "Hidden checkers musn't emit diagnostics as they are by definition "
2162 "non-user facing!");
2166 std::unique_ptr<BugReporterVisitor> visitor) {
2170 llvm::FoldingSetNodeID ID;
2171 visitor->Profile(ID);
2173 void *InsertPos =
nullptr;
2178 Callbacks.push_back(std::move(visitor));
2195 hash.AddInteger(
static_cast<int>(
getKind()));
2196 hash.AddPointer(&
BT);
2202 if (!range.isValid())
2204 hash.Add(range.getBegin());
2205 hash.Add(range.getEnd());
2210 hash.AddInteger(
static_cast<int>(
getKind()));
2211 hash.AddPointer(&
BT);
2225 if (!range.isValid())
2227 hash.Add(range.getBegin());
2228 hash.Add(range.getEnd());
2234 llvm::DenseMap<T, bugreporter::TrackingKind> &InterestingnessMap,
T Val,
2236 auto Result = InterestingnessMap.insert({Val, TKind});
2256 "BugReport::markInteresting currently can only handle 2 different "
2257 "tracking kinds! Please define what tracking kind should this entitiy"
2258 "have, if it was already marked as interesting with a different kind!");
2270 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2282 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2294 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2305 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2321std::optional<bugreporter::TrackingKind>
2340 "BugReport::getInterestingnessKind currently can only handle 2 different "
2341 "tracking kinds! Please define what tracking kind should we return here "
2342 "when the kind of getAsRegion() and getAsSymbol() is different!");
2343 return std::nullopt;
2346std::optional<bugreporter::TrackingKind>
2349 return std::nullopt;
2354 return std::nullopt;
2355 return It->getSecond();
2358std::optional<bugreporter::TrackingKind>
2361 return std::nullopt;
2366 return It->getSecond();
2368 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2370 return std::nullopt;
2396 const Stmt *S =
nullptr;
2400 if (BE->getBlock() == &Exit)
2443 assert(
ErrorNode &&
"Cannot create a location with a null node.");
2468 if (
const auto *AS = dyn_cast<AttributedStmt>(S))
2469 S = AS->getSubStmt();
2472 if (
const auto *ME = dyn_cast<MemberExpr>(S))
2476 if (
const auto *B = dyn_cast<BinaryOperator>(S))
2482 if (S->getBeginLoc().isValid())
2506 :
D(
D), UserSuppressions(
D.getASTContext()) {}
2510 assert(StrBugTypes.empty() &&
2511 "Destroying BugReporter before diagnostics are emitted!");
2514 for (
const auto I : EQClassesVector)
2521 for (
const auto EQ : EQClassesVector)
2528 StrBugTypes.clear();
2541 std::unique_ptr<ExplodedGraph> BugPath;
2548class BugPathGetter {
2549 std::unique_ptr<ExplodedGraph> TrimmedGraph;
2551 using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>;
2554 PriorityMapTy PriorityMap;
2558 using ReportNewNodePair =
2559 std::pair<PathSensitiveBugReport *, const ExplodedNode *>;
2562 BugPathInfo CurrentBugPath;
2565 template <
bool Descending>
2566 class PriorityCompare {
2567 const PriorityMapTy &PriorityMap;
2570 PriorityCompare(
const PriorityMapTy &M) : PriorityMap(M) {}
2573 PriorityMapTy::const_iterator LI = PriorityMap.find(LHS);
2574 PriorityMapTy::const_iterator RI = PriorityMap.find(RHS);
2575 PriorityMapTy::const_iterator
E = PriorityMap.end();
2582 return Descending ? LI->second > RI->second
2583 : LI->second < RI->second;
2586 bool operator()(
const ReportNewNodePair &LHS,
2587 const ReportNewNodePair &RHS)
const {
2588 return (*
this)(LHS.second, RHS.second);
2596 BugPathInfo *getNextBugPath();
2601BugPathGetter::BugPathGetter(
const ExplodedGraph *OriginalGraph,
2604 for (
const auto I : bugReports) {
2605 assert(I->isValid() &&
2606 "We only allow BugReporterVisitors and BugReporter itself to "
2607 "invalidate reports!");
2608 Nodes.emplace_back(I->getErrorNode());
2614 TrimmedGraph = OriginalGraph->
trim(
Nodes, &ForwardMap);
2624 "Failed to construct a trimmed graph that contains this error "
2626 ReportNodes.emplace_back(
Report, NewNode);
2627 RemainingNodes.insert(NewNode);
2630 assert(!RemainingNodes.empty() &&
"No error node found in the trimmed graph");
2633 std::queue<const ExplodedNode *> WS;
2635 assert(TrimmedGraph->num_roots() == 1);
2636 WS.push(*TrimmedGraph->roots_begin());
2639 while (!WS.empty()) {
2643 PriorityMapTy::iterator PriorityEntry;
2645 std::tie(PriorityEntry, IsNew) = PriorityMap.insert({
Node,
Priority});
2649 assert(PriorityEntry->second <= Priority);
2653 if (RemainingNodes.erase(
Node))
2654 if (RemainingNodes.empty())
2662 llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap));
2665BugPathInfo *BugPathGetter::getNextBugPath() {
2666 if (ReportNodes.empty())
2670 std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val();
2671 assert(PriorityMap.contains(OrigN) &&
"error node not accessible from root");
2675 auto GNew = std::make_unique<ExplodedGraph>();
2691 CurrentBugPath.ErrorNode = NewN;
2697 GNew->addRoot(NewN);
2704 PriorityCompare<false>(PriorityMap));
2707 CurrentBugPath.BugPath = std::move(GNew);
2709 return &CurrentBugPath;
2716 using MacroStackTy = std::vector<
2717 std::pair<std::shared_ptr<PathDiagnosticMacroPiece>,
SourceLocation>>;
2719 using PiecesTy = std::vector<PathDiagnosticPieceRef>;
2721 MacroStackTy MacroStack;
2724 for (PathPieces::const_iterator I = path.begin(),
E = path.end();
2726 const auto &piece = *I;
2729 if (
auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) {
2739 SM.getExpansionLoc(
Loc) :
2742 if (
Loc.isFileID()) {
2744 Pieces.push_back(piece);
2748 assert(
Loc.isMacroID());
2751 if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
2752 MacroStack.back().first->subPieces.push_back(piece);
2758 std::shared_ptr<PathDiagnosticMacroPiece> MacroGroup;
2761 SM.getExpansionLoc(
Loc) :
2765 while (!MacroStack.empty()) {
2766 if (InstantiationLoc == MacroStack.back().second) {
2767 MacroGroup = MacroStack.back().first;
2771 if (ParentInstantiationLoc == MacroStack.back().second) {
2772 MacroGroup = MacroStack.back().first;
2776 MacroStack.pop_back();
2779 if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
2781 auto NewGroup = std::make_shared<PathDiagnosticMacroPiece>(
2785 MacroGroup->subPieces.push_back(NewGroup);
2787 assert(InstantiationLoc.
isFileID());
2788 Pieces.push_back(NewGroup);
2791 MacroGroup = NewGroup;
2792 MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
2796 MacroGroup->subPieces.push_back(piece);
2802 path.insert(path.end(), Pieces.begin(), Pieces.end());
2808static std::unique_ptr<VisitorsDiagnosticsTy>
2812 std::unique_ptr<VisitorsDiagnosticsTy> Notes =
2813 std::make_unique<VisitorsDiagnosticsTy>();
2826 for (std::unique_ptr<BugReporterVisitor> &Visitor : R->
visitors())
2827 visitors.push_back(std::move(Visitor));
2834 for (
auto &
V : visitors) {
2835 V->finalizeVisitor(BRC, ErrorNode, *R);
2837 if (
auto Piece =
V->getEndPath(BRC, ErrorNode, *R)) {
2838 assert(!LastPiece &&
2839 "There can only be one final piece in a diagnostic.");
2841 "The final piece must contain a message!");
2842 LastPiece = std::move(Piece);
2843 (*Notes)[ErrorNode].push_back(LastPiece);
2849 for (
auto &
V : visitors) {
2850 auto P =
V->VisitNode(NextNode, BRC, *R);
2852 (*Notes)[NextNode].push_back(std::move(
P));
2864std::optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
2869 BugPathGetter BugGraph(&Reporter.
getGraph(), bugReports);
2871 while (BugPathInfo *BugPath = BugGraph.getNextBugPath()) {
2874 assert(R &&
"No original report found for sliced graph.");
2875 assert(R->
isValid() &&
"Report selected by trimmed graph marked invalid.");
2890 std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes =
2905 switch (Z3Oracle.interpretQueryResult(CrosscheckResult)) {
2907 ++NumTimesReportRefuted;
2908 R->
markInvalid(
"Infeasible constraints",
nullptr);
2911 ++NumTimesReportEQClassAborted;
2914 ++NumTimesReportPassesZ3;
2920 return PathDiagnosticBuilder(std::move(BRC), std::move(BugPath->BugPath),
2921 BugPath->Report, BugPath->ErrorNode,
2922 std::move(visitorNotes));
2926 ++NumTimesReportEQClassWasExhausted;
2930std::unique_ptr<DiagnosticForConsumerMapTy>
2934 assert(!bugReports.empty());
2936 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
2938 std::optional<PathDiagnosticBuilder> PDB =
2939 PathDiagnosticBuilder::findValidReport(bugReports, *
this);
2943 if (std::unique_ptr<PathDiagnostic> PD = PDB->generate(PC)) {
2944 (*Out)[PC] = std::move(PD);
2953 bool ValidSourceLoc = R->getLocation().isValid();
2954 assert(ValidSourceLoc);
2957 if (!ValidSourceLoc)
2965 llvm::FoldingSetNodeID ID;
2974 EQClasses.InsertNode(EQ, InsertPos);
2975 EQClassesVector.push_back(EQ);
2977 EQ->AddReport(std::move(R));
2981 if (
auto PR = dyn_cast<PathSensitiveBugReport>(R.get()))
2985 assert((
E->isSink() ||
E->getLocation().getTag()) &&
2986 "Error node must either be a sink or have a tag");
2989 E->getLocationContext()->getAnalysisDeclContext();
3008struct FRIEC_WLItem {
3013 : N(n), I(N->succ_begin()),
E(N->succ_end()) {}
3018BugReport *PathSensitiveBugReporter::findReportInEquivalenceClass(
3023 assert(
EQ.getReports().size() > 0);
3024 const BugType& BT =
EQ.getReports()[0]->getBugType();
3027 for (
auto &J :
EQ.getReports()) {
3028 if (
auto *PR = dyn_cast<PathSensitiveBugReport>(J.get())) {
3030 bugReports.push_back(PR);
3044 for (
const auto &I:
EQ.getReports()) {
3045 auto *R = dyn_cast<PathSensitiveBugReport>(I.get());
3050 if (errorNode->
isSink()) {
3052 "BugType::isSuppressSink() should not be 'true' for sink end nodes");
3056 bugReports.push_back(R);
3067 if (ErrorB->isInevitablySinking())
3072 using WLItem = FRIEC_WLItem;
3075 llvm::DenseMap<const ExplodedNode *, unsigned>
Visited;
3078 WL.push_back(errorNode);
3081 while (!WL.empty()) {
3082 WLItem &WI = WL.back();
3083 assert(!WI.N->succ_empty());
3085 for (; WI.I != WI.E; ++WI.I) {
3091 bugReports.push_back(R);
3102 unsigned &mark =
Visited[Succ];
3112 if (!WL.empty() && &WL.back() == &WI)
3119 return exampleReport;
3124 BugReport *report = findReportInEquivalenceClass(EQ, bugReports);
3129 for (
const std::string &CheckerOrPackage :
3136 std::unique_ptr<DiagnosticForConsumerMapTy> Diagnostics =
3139 for (
auto &
P : *Diagnostics) {
3141 std::unique_ptr<PathDiagnostic> &PD =
P.second;
3145 if (PD->path.empty()) {
3147 auto piece = std::make_unique<PathDiagnosticEventPiece>(
3150 piece->addRange(
Range);
3151 PD->setEndOfPath(std::move(piece));
3158 for (
const auto &I : llvm::reverse(report->
getNotes())) {
3160 auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
3163 ConvertedPiece->addRange(R);
3165 Pieces.push_front(std::move(ConvertedPiece));
3168 for (
const auto &I : llvm::reverse(report->
getNotes()))
3169 Pieces.push_front(I);
3172 for (
const auto &I : report->
getFixits())
3173 Pieces.back()->addFixit(I);
3181 Pieces.push_front(std::make_shared<PathDiagnosticEventPiece>(
3183 "[debug] analyzing from " +
3197 if (
const auto FD = dyn_cast<FunctionDecl>(Signature)) {
3198 SignatureSourceRange = FD->getSourceRange();
3199 }
else if (
const auto OD = dyn_cast<ObjCMethodDecl>(Signature)) {
3200 SignatureSourceRange = OD->getSourceRange();
3206 : SignatureSourceRange.
getEnd();
3207 if (!Start.
isValid() || !End.isValid())
3209 unsigned StartLine =
SM.getExpansionLineNumber(Start);
3210 unsigned EndLine =
SM.getExpansionLineNumber(End);
3212 FileID FID =
SM.getFileID(
SM.getExpansionLoc(Start));
3213 for (
unsigned Line = StartLine;
Line <= EndLine;
Line++)
3214 ExecutedLines[FID].insert(
Line);
3224 FileID FID =
SM.getFileID(ExpansionLoc);
3225 unsigned LineNo =
SM.getExpansionLineNumber(ExpansionLoc);
3226 ExecutedLines[FID].insert(LineNo);
3231static std::unique_ptr<FilesToLineNumsMap>
3233 auto ExecutedLines = std::make_unique<FilesToLineNumsMap>();
3242 const Decl*
D = CE->getCalleeContext()->getDecl();
3253 if (
const auto *RS = dyn_cast_or_null<ReturnStmt>(
P)) {
3258 if (isa_and_nonnull<SwitchCase, LabelStmt>(
P))
3264 return ExecutedLines;
3267std::unique_ptr<DiagnosticForConsumerMapTy>
3271 auto *basicReport = cast<BasicBugReport>(exampleReport);
3272 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
3273 for (
auto *Consumer : consumers)
3289 "The call piece should not be in a header file.");
3301 if (
auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(
Path.back().get()))
3309 if (PD.
path.empty())
3318 if (
auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
3325 const auto *ND = dyn_cast<NamedDecl>(CP->
getCallee());
3328 llvm::raw_svector_ostream os(buf);
3329 os <<
" (within a call to '" << ND->getDeclName() <<
"')";
3344std::unique_ptr<DiagnosticForConsumerMapTy>
3345PathSensitiveBugReporter::generateDiagnosticForConsumerMap(
3348 std::vector<BasicBugReport *> BasicBugReports;
3349 std::vector<PathSensitiveBugReport *> PathSensitiveBugReports;
3350 if (isa<BasicBugReport>(exampleReport))
3352 consumers, bugReports);
3358 assert(!bugReports.empty());
3359 MaxBugClassSize.updateMax(bugReports.size());
3366 consumers, convertedArrayOfReports);
3371 MaxValidBugClassSize.updateMax(bugReports.size());
3376 for (
auto const &
P : *Out)
3377 if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.
AnalyzeAll)
3390 Loc, Ranges, Fixits);
3395 StringRef name, StringRef category,
3400 BugType *BT = getBugTypeForName(CheckName, name, category);
3401 auto R = std::make_unique<BasicBugReport>(*BT, str,
Loc);
3402 R->setDeclWithIssue(DeclWithIssue);
3403 for (
const auto &SR : Ranges)
3405 for (
const auto &FH : Fixits)
3411 StringRef name, StringRef category) {
3413 llvm::raw_svector_ostream(fullDesc) << CheckName.
getName() <<
":" << name
3415 std::unique_ptr<BugType> &BT = StrBugTypes[fullDesc];
3417 BT = std::make_unique<BugType>(CheckName, name, category);
BoundNodesTreeBuilder Nodes
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
static void dropFunctionEntryEdge(const PathDiagnosticConstruct &C, PathPieces &Path)
Drop the very first edge in a path, which should be a function entry edge.
constexpr llvm::StringLiteral StrLoopRangeEmpty
static PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S, const LocationContext *LC, bool allowNestedContexts=false)
static std::unique_ptr< FilesToLineNumsMap > findExecutedLines(const SourceManager &SM, const ExplodedNode *N)
static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond)
static void updateExecutedLinesWithDiagnosticPieces(PathDiagnostic &PD)
Populate executes lines with lines containing at least one diagnostics.
static void removeRedundantMsgs(PathPieces &path)
An optimization pass over PathPieces that removes redundant diagnostics generated by both ConditionBR...
constexpr llvm::StringLiteral StrLoopCollectionEmpty
static void adjustCallLocations(PathPieces &Pieces, PathDiagnosticLocation *LastCallLocation=nullptr)
Recursively scan through a path and make sure that all call pieces have valid locations.
static void removeIdenticalEvents(PathPieces &path)
static const Stmt * getTerminatorCondition(const CFGBlock *B)
A customized wrapper for CFGBlock::getTerminatorCondition() which returns the element for ObjCForColl...
static std::unique_ptr< VisitorsDiagnosticsTy > generateVisitorsDiagnostics(PathSensitiveBugReport *R, const ExplodedNode *ErrorNode, BugReporterContext &BRC)
Generate notes from all visitors.
static bool removeUnneededCalls(const PathDiagnosticConstruct &C, PathPieces &pieces, const PathSensitiveBugReport *R, bool IsInteresting=false)
Recursively scan through a path and prune out calls and macros pieces that aren't needed.
static const Stmt * findReasonableStmtCloseToFunctionExit(const ExplodedNode *N)
static void populateExecutedLinesWithStmt(const Stmt *S, const SourceManager &SM, FilesToLineNumsMap &ExecutedLines)
static bool isJumpToFalseBranch(const BlockEdge *BE)
static std::optional< size_t > getLengthOnSingleLine(const SourceManager &SM, SourceRange Range)
Returns the number of bytes in the given (character-based) SourceRange.
static bool isLoop(const Stmt *Term)
static bool isContainedByStmt(const ParentMap &PM, const Stmt *S, const Stmt *SubS)
constexpr llvm::StringLiteral StrEnteringLoop
static void addEdgeToPath(PathPieces &path, PathDiagnosticLocation &PrevLoc, PathDiagnosticLocation NewLoc)
Adds a sanitized control-flow diagnostic edge to a path.
static bool isIncrementOrInitInForLoop(const Stmt *S, const Stmt *FL)
static std::unique_ptr< PathDiagnostic > generateEmptyDiagnosticForReport(const PathSensitiveBugReport *R, const SourceManager &SM, const Decl *AnalysisEntryPoint)
static void removeContextCycles(PathPieces &Path, const SourceManager &SM)
Eliminate two-edge cycles created by addContextEdges().
static bool lexicalContains(const ParentMap &PM, const Stmt *X, const Stmt *Y)
Return true if X is contained by Y.
static std::unique_ptr< PathDiagnostic > generateDiagnosticForBasicReport(const BasicBugReport *R, const Decl *AnalysisEntryPoint)
static void removePopUpNotes(PathPieces &Path)
Same logic as above to remove extra pieces.
STATISTIC(MaxBugClassSize, "The maximum number of bug reports in the same equivalence class")
static void insertToInterestingnessMap(llvm::DenseMap< T, bugreporter::TrackingKind > &InterestingnessMap, T Val, bugreporter::TrackingKind TKind)
constexpr llvm::StringLiteral StrLoopBodyZero
static const Stmt * getEnclosingParent(const Stmt *S, const ParentMap &PM)
static void removePunyEdges(PathPieces &path, const SourceManager &SM, const ParentMap &PM)
static const Stmt * getStmtParent(const Stmt *S, const ParentMap &PM)
static bool exitingDestructor(const ExplodedNode *N)
static void CompactMacroExpandedPieces(PathPieces &path, const SourceManager &SM)
CompactMacroExpandedPieces - This function postprocesses a PathDiagnostic object and collapses PathDi...
static void simplifySimpleBranches(PathPieces &pieces)
Move edges from a branch condition to a branch target when the condition is simple.
static void populateExecutedLinesWithFunctionSignature(const Decl *Signature, const SourceManager &SM, FilesToLineNumsMap &ExecutedLines)
Insert all lines participating in the function signature Signature into ExecutedLines.
static void resetDiagnosticLocationToMainFile(PathDiagnostic &PD)
static bool optimizeEdges(const PathDiagnosticConstruct &C, PathPieces &path, OptimizedCallsSet &OCS)
static bool hasImplicitBody(const Decl *D)
Returns true if the given decl has been implicitly given a body, either by the analyzer or by the com...
static PathDiagnosticCallPiece * getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP, const SourceManager &SMgr)
llvm::DenseSet< const PathDiagnosticCallPiece * > OptimizedCallsSet
static void addContextEdges(PathPieces &pieces, const LocationContext *LC)
Adds synthetic edges from top-level statements to their subexpressions.
static LLVM_ATTRIBUTE_USED bool isDependency(const CheckerRegistryData &Registry, StringRef CheckerName)
static PathDiagnosticEventPiece * eventsDescribeSameCondition(PathDiagnosticEventPiece *X, PathDiagnosticEventPiece *Y)
static bool isInLoopBody(const ParentMap &PM, const Stmt *S, const Stmt *Term)
static void removeEdgesToDefaultInitializers(PathPieces &Pieces)
Remove edges in and out of C++ default initializer expressions.
static const Stmt * getStmtBeforeCond(const ParentMap &PM, const Stmt *Term, const ExplodedNode *N)
static void removePiecesWithInvalidLocations(PathPieces &Pieces)
Remove all pieces with invalid locations as these cannot be serialized.
static LLVM_ATTRIBUTE_USED bool isHidden(const CheckerRegistryData &Registry, StringRef CheckerName)
Defines the clang::Expr interface and subclasses for C++ expressions.
llvm::DenseSet< const void * > Visited
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Defines the Objective-C statement AST node classes.
SourceManager & getSourceManager()
AnalysisDeclContext contains the context data for the function, method or block under analysis.
static std::string getFunctionName(const Decl *D)
bool isBodyAutosynthesized() const
bool isBodyAutosynthesizedFromModelFile() const
Stores options for the analyzer from the command line.
const CFGBlock * getSrc() const
const CFGBlock * getDst() const
Represents a single basic block in a source-level CFG.
succ_iterator succ_begin()
Stmt * getTerminatorStmt()
const Stmt * getLoopTarget() const
Stmt * getTerminatorCondition(bool StripParens=true)
unsigned succ_size() const
CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for statement, represented as 'for (ra...
Represents a point when we begin processing an inlined call.
Represents a point when we finish the call exit sequence (for inlined call).
const StackFrameContext * getCalleeContext() const
Decl - This represents one declaration (or definition), e.g.
ASTContext & getASTContext() const LLVM_READONLY
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
virtual bool hasBody() const
Returns true if this Decl represents a declaration for a body of code, such as a function or method d...
SourceLocation getLocation() const
This represents one expression.
llvm::APSInt EvaluateKnownConstInt(const ASTContext &Ctx, SmallVectorImpl< PartialDiagnosticAt > *Diag=nullptr) const
EvaluateKnownConstInt - Call EvaluateAsRValue and return the folded integer.
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
ForStmt - This represents a 'for (init;cond;inc)' stmt.
A SourceLocation and its associated SourceManager.
IfStmt - This represents an if/then/else.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const Decl * getDecl() const
const ParentMap & getParentMap() const
const StackFrameContext * getStackFrame() const
Represents Objective-C's collection statement.
bool isConsumedExpr(Expr *E) const
Stmt * getParent(Stmt *) const
Stmt * getParentIgnoreParens(Stmt *) const
Represents a point after we ran remove dead bindings AFTER processing the given statement.
Represents a program point just before an implicit call event.
std::optional< T > getAs() const
Convert to the specified ProgramPoint type, returning std::nullopt if this ProgramPoint is not of the...
const LocationContext * getLocationContext() const
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
SourceLocation getBegin() const
It represents a stack frame of the call stack (based on CallEvent).
const Stmt * getCallSite() const
Stmt - This represents one statement.
StmtClass getStmtClass() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
SourceLocation getBeginLoc() const LLVM_READONLY
WhileStmt - This represents a 'while' stmt.
static bool isInCodeFile(SourceLocation SL, const SourceManager &SM)
const Decl * getDeclWithIssue() const override
The smallest declaration that contains the bug location.
PathDiagnosticLocation getUniqueingLocation() const override
Get the location on which the report should be uniqued.
void Profile(llvm::FoldingSetNodeID &hash) const override
Reports are uniqued to ensure that we do not emit multiple diagnostics for each bug.
const Decl * getUniqueingDecl() const override
Get the declaration that corresponds to (usually contains) the uniqueing location.
This class provides an interface through which checkers can create individual bug reports.
llvm::ArrayRef< FixItHint > getFixits() const
void addRange(SourceRange R)
Add a range to a bug report.
SmallVector< SourceRange, 4 > Ranges
virtual PathDiagnosticLocation getLocation() const =0
The primary location of the bug report that points at the undesirable behavior in the code.
ArrayRef< std::shared_ptr< PathDiagnosticNotePiece > > getNotes()
void addFixItHint(const FixItHint &F)
Add a fix-it hint to the bug report.
StringRef getDescription() const
A verbose warning message that is appropriate for displaying next to the source code that introduces ...
const BugType & getBugType() const
StringRef getShortDescription(bool UseFallback=true) const
A short general warning message that is appropriate for displaying in the list of all reported bugs.
virtual ArrayRef< SourceRange > getRanges() const
Get the SourceRanges associated with the report.
static PathDiagnosticPieceRef getDefaultEndPath(const BugReporterContext &BRC, const ExplodedNode *N, const PathSensitiveBugReport &BR)
Generates the default final diagnostic piece.
virtual ~BugReporterVisitor()
virtual std::unique_ptr< DiagnosticForConsumerMapTy > generateDiagnosticForConsumerMap(BugReport *exampleReport, ArrayRef< PathDiagnosticConsumer * > consumers, ArrayRef< BugReport * > bugReports)
Generate the diagnostics for the given bug report.
void FlushReports()
Generate and flush diagnostics for all bug reports.
BugReporter(BugReporterData &d)
const SourceManager & getSourceManager()
const Decl * getAnalysisEntryPoint() const
Get the top-level entry point for the issue to be reported.
const AnalyzerOptions & getAnalyzerOptions()
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges={}, ArrayRef< FixItHint > Fixits={})
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
ArrayRef< PathDiagnosticConsumer * > getPathDiagnosticConsumers()
bool isSuppressed(const BugReport &)
Return true if the given bug report was explicitly suppressed by the user.
bool isSuppressOnSink() const
isSuppressOnSink - Returns true if bug reports associated with this bug type should be suppressed if ...
StringRef getCategory() const
StringRef getDescription() const
StringRef getCheckerName() const
CheckerNameRef getCheckerName() const
This wrapper is used to ensure that only StringRefs originating from the CheckerRegistry are used as ...
StringRef getName() const
Visitor that tries to report interesting diagnostics from conditions.
static bool isPieceMessageGeneric(const PathDiagnosticPiece *Piece)
static const char * getTag()
Return the tag associated with this visitor.
bool isValid() const =delete
std::unique_ptr< ExplodedGraph > trim(ArrayRef< const NodeTy * > Nodes, InterExplodedGraphMap *ForwardMap=nullptr, InterExplodedGraphMap *InverseMap=nullptr) const
Creates a trimmed version of the graph that only contains paths leading to the given nodes.
const CFGBlock * getCFGBlock() const
const ProgramStateRef & getState() const
pred_iterator pred_begin()
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
const Stmt * getPreviousStmtForDiagnostics() const
Find the statement that was executed immediately before this node.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
void addPredecessor(ExplodedNode *V, ExplodedGraph &G)
addPredeccessor - Adds a predecessor to the current node, and in tandem add this node as a successor ...
const Stmt * getNextStmtForDiagnostics() const
Find the next statement that was executed on this node's execution path.
const ParentMap & getParentMap() const
SVal getSVal(const Stmt *S) const
Get the value of an arbitrary expression at this node.
const LocationContext * getLocationContext() const
std::optional< T > getLocationAs() const &
const Stmt * getCurrentOrPreviousStmtForDiagnostics() const
Find the statement that was executed at or immediately before this node.
ExplodedNode * getFirstPred()
const ExplodedNode *const * const_succ_iterator
ProgramStateManager & getStateManager()
ExplodedGraph & getGraph()
Suppress reports that might lead to known false positives.
MemRegion - The root abstract class for all memory regions.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
Prints path notes when a message is sent to a nil receiver.
PathDiagnosticLocation getLocation() const override
PathDiagnosticLocation callEnter
void setCallStackMessage(StringRef st)
bool hasCallStackMessage()
const Decl * getCallee() const
static std::shared_ptr< PathDiagnosticCallPiece > construct(const CallExitEnd &CE, const SourceManager &SM)
const Decl * getCaller() const
PathDiagnosticLocation callEnterWithin
virtual bool supportsLogicalOpControlFlow() const
bool shouldAddPathEdges() const
void HandlePathDiagnostic(std::unique_ptr< PathDiagnostic > D)
bool shouldAddControlNotes() const
bool shouldGenerateDiagnostics() const
PathDiagnosticLocation getStartLocation() const
void setStartLocation(const PathDiagnosticLocation &L)
PathDiagnosticLocation getEndLocation() const
static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME, const SourceManager &SM)
For member expressions, return the location of the '.
const Stmt * asStmt() const
void Profile(llvm::FoldingSetNodeID &ID) const
const SourceManager & getManager() const
static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO, const SourceManager &SM)
Create the location for the operator of the binary expression.
static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS, const SourceManager &SM)
Create a location for the end of the compound statement.
static SourceLocation getValidSourceLocation(const Stmt *S, LocationOrAnalysisDeclContext LAC, bool UseEndOfStatement=false)
Construct a source location that corresponds to either the beginning or the end of the given statemen...
static PathDiagnosticLocation createEnd(const Stmt *S, const SourceManager &SM, const LocationOrAnalysisDeclContext LAC)
Create a location for the end of the statement.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
FullSourceLoc asLocation() const
static PathDiagnosticLocation createDeclEnd(const LocationContext *LC, const SourceManager &SM)
Constructs a location for the end of the enclosing declaration body.
const Stmt * getStmtOrNull() const
static PathDiagnosticLocation createSingleLocation(const PathDiagnosticLocation &PDL)
Convert the given location into a single kind location.
ArrayRef< SourceRange > getRanges() const
Return the SourceRanges associated with this PathDiagnosticPiece.
virtual PathDiagnosticLocation getLocation() const =0
void setAsLastInMainSourceFile()
const void * getTag() const
Return the opaque tag (if any) on the PathDiagnosticPiece.
StringRef getString() const
PathDiagnosticLocation getLocation() const override
PathDiagnostic - PathDiagnostic objects represent a single path-sensitive diagnostic.
void setDeclWithIssue(const Decl *D)
void appendToDesc(StringRef S)
void setLocation(PathDiagnosticLocation NewLoc)
const FilesToLineNumsMap & getExecutedLines() const
PathPieces flatten(bool ShouldFlattenMacros) const
llvm::SmallSet< const LocationContext *, 2 > InterestingLocationContexts
A set of location contexts that correspoind to call sites which should be considered "interesting".
void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind=bugreporter::TrackingKind::Thorough)
Marks a symbol as interesting.
PathDiagnosticLocation getUniqueingLocation() const override
Get the location on which the report should be uniqued.
VisitorList Callbacks
A set of custom visitors which generate "event" diagnostics at interesting points in the path.
const Stmt * getStmt() const
PathDiagnosticLocation getLocation() const override
The primary location of the bug report that points at the undesirable behavior in the code.
const Decl * getDeclWithIssue() const override
The smallest declaration that contains the bug location.
bool shouldPrunePath() const
Indicates whether or not any path pruning should take place when generating a PathDiagnostic from thi...
ArrayRef< SourceRange > getRanges() const override
Get the SourceRanges associated with the report.
llvm::DenseMap< SymbolRef, bugreporter::TrackingKind > InterestingSymbols
Profile to identify equivalent bug reports for error report coalescing.
const Decl * getUniqueingDecl() const override
Get the declaration containing the uniqueing location.
const ExplodedNode * getErrorNode() const
PathSensitiveBugReport(const BugType &bt, StringRef desc, const ExplodedNode *errorNode)
const ExplodedNode * ErrorNode
The ExplodedGraph node against which the report was thrown.
void markInvalid(const void *Tag, const void *Data)
Marks the current report as invalid, meaning that it is probably a false positive and should not be r...
void Profile(llvm::FoldingSetNodeID &hash) const override
Profile to identify equivalent bug reports for error report coalescing.
void clearVisitors()
Remove all visitors attached to this bug report.
void addVisitor(std::unique_ptr< BugReporterVisitor > visitor)
Add custom or predefined bug report visitors to this report.
bool isValid() const
Returns whether or not this report should be considered valid.
std::optional< bugreporter::TrackingKind > getInterestingnessKind(SymbolRef sym) const
void markNotInteresting(SymbolRef sym)
llvm::DenseMap< const MemRegion *, bugreporter::TrackingKind > InterestingRegions
A (stack of) set of regions that are registered with this report as being "interesting",...
bool isInteresting(SymbolRef sym) const
const SourceRange ErrorNodeRange
The range that corresponds to ErrorNode's program point.
llvm::FoldingSet< BugReporterVisitor > CallbacksSet
Used for ensuring the visitors are only added once.
GRBugReporter is used for generating path-sensitive reports.
const ExplodedGraph & getGraph() const
getGraph - Get the exploded graph created by the analysis engine for the analyzed method or function.
std::unique_ptr< DiagnosticForConsumerMapTy > generatePathDiagnostics(ArrayRef< PathDiagnosticConsumer * > consumers, ArrayRef< PathSensitiveBugReport * > &bugReports)
bugReports A set of bug reports within a single equivalence class
void emitReport(std::unique_ptr< BugReport > R) override
Add the given report to the set of reports tracked by BugReporter.
ProgramStateManager & getStateManager() const
getStateManager - Return the state manager used by the analysis engine.
A Range represents the closed range [from, to].
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
SymbolRef getAsLocSymbol(bool IncludeBaseRegions=false) const
If this SVal is a location and wraps a symbol, return that SymbolRef.
std::string getMessage(const ExplodedNode *N) override
Search the call expression for the symbol Sym and dispatch the 'getMessageForX()' methods to construc...
virtual std::string getMessageForSymbolNotFound()
virtual std::string getMessageForReturn(const CallExpr *CallExpr)
virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex)
Produces the message of the following form: 'Msg via Nth parameter'.
virtual ~StackHintGenerator()=0
The visitor detects NoteTags and displays the event notes they contain.
static const char * getTag()
Return the tag associated with this visitor.
The oracle will decide if a report should be accepted or rejected based on the results of the Z3 solv...
The bug visitor will walk all the nodes in a path and collect all the constraints.
TrackingKind
Specifies the type of tracking for an expression.
@ Thorough
Default tracking kind – specifies that as much information should be gathered about the tracked expre...
@ Condition
Specifies that a more moderate tracking should be used for the expression value.
llvm::DenseMap< const ExplodedNode *, const ExplodedNode * > InterExplodedGraphMap
std::map< FileID, std::set< unsigned > > FilesToLineNumsMap
File IDs mapped to sets of line numbers.
@ CF
Indicates that the tracked object is a CF object.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
bool EQ(InterpState &S, CodePtr OpPC)
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
@ Result
The result type of a method or function.
const FunctionProtoType * T
Diagnostic wrappers for TextAPI types for error reporting.
llvm::SmallVector< std::pair< StringRef, StringRef >, 0 > Dependencies