48#include "llvm/ADT/ArrayRef.h"
49#include "llvm/ADT/DenseMap.h"
50#include "llvm/ADT/DenseSet.h"
51#include "llvm/ADT/FoldingSet.h"
52#include "llvm/ADT/STLExtras.h"
53#include "llvm/ADT/SmallPtrSet.h"
54#include "llvm/ADT/SmallString.h"
55#include "llvm/ADT/SmallVector.h"
56#include "llvm/ADT/Statistic.h"
57#include "llvm/ADT/StringExtras.h"
58#include "llvm/ADT/StringRef.h"
59#include "llvm/ADT/iterator_range.h"
60#include "llvm/Support/Casting.h"
61#include "llvm/Support/Compiler.h"
62#include "llvm/Support/ErrorHandling.h"
63#include "llvm/Support/MemoryBuffer.h"
64#include "llvm/Support/raw_ostream.h"
81#define DEBUG_TYPE "BugReporter"
84 "The maximum number of bug reports in the same equivalence class");
86 "The maximum number of bug reports in the same equivalence class "
87 "where at least one report is valid (not suppressed)");
91void BugReporterContext::anchor() {}
101 std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>;
105using VisitorsDiagnosticsTy =
106 llvm::DenseMap<const ExplodedNode *, std::vector<PathDiagnosticPieceRef>>;
110using LocationContextMap =
111 llvm::DenseMap<const PathPieces *, const LocationContext *>;
116class PathDiagnosticConstruct {
125 LocationContextMap LCM;
132 CallWithEntryStack CallStack;
136 std::unique_ptr<PathDiagnostic> PD;
142 const Decl *AnalysisEntryPoint);
147 assert(CurrentNode &&
"Already reached the root!");
155 return LCM.find(&PD->getActivePath())->getSecond();
158 const ExplodedNode *getCurrentNode()
const {
return CurrentNode; }
162 bool ascendToPrevNode() {
164 return static_cast<bool>(CurrentNode);
168 return getCurrLocationContext()->getParentMap();
173 const Stmt *getParent(
const Stmt *S)
const {
174 return getParentMap().getParent(S);
183 assert(LCM.count(Path) &&
184 "Failed to find the context associated with these pieces!");
185 return LCM.find(Path)->getSecond();
188 bool isInLocCtxMap(
const PathPieces *Path)
const {
return LCM.count(Path); }
190 PathPieces &getActivePath() {
return PD->getActivePath(); }
191 PathPieces &getMutablePieces() {
return PD->getMutablePieces(); }
194 bool shouldAddControlNotes()
const {
197 bool shouldGenerateDiagnostics()
const {
200 bool supportsLogicalOpControlFlow()
const {
211 std::unique_ptr<const ExplodedGraph> BugPath;
222 std::unique_ptr<const VisitorsDiagnosticsTy> VisitorsDiagnostics;
228 static std::optional<PathDiagnosticBuilder>
232 PathDiagnosticBuilder(
235 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics);
247 std::unique_ptr<PathDiagnostic>
252 const CallWithEntryStack &CallStack)
const;
253 void generatePathDiagnosticsForNode(PathDiagnosticConstruct &
C,
256 void generateMinimalDiagForBlockEdge(PathDiagnosticConstruct &
C,
260 generateDiagForGotoOP(
const PathDiagnosticConstruct &
C,
const Stmt *S,
264 generateDiagForSwitchOP(
const PathDiagnosticConstruct &
C,
const CFGBlock *Dst,
268 generateDiagForBinaryOP(
const PathDiagnosticConstruct &
C,
const Stmt *
T,
272 ExecutionContinues(
const PathDiagnosticConstruct &
C)
const;
275 ExecutionContinues(llvm::raw_string_ostream &os,
276 const PathDiagnosticConstruct &
C)
const;
298 const auto *CE = dyn_cast_or_null<CallExpr>(CallSite);
303 for (
auto [Idx, ArgExpr] : llvm::enumerate(CE->arguments())) {
315 if (ArgExpr->getType()->isVoidPointerType())
340 return (llvm::Twine(Msg) +
" via " + std::to_string(ArgIndex) +
341 llvm::getOrdinalSuffix(ArgIndex) +
" parameter").str();
361 if (
X->getTag() == tagPreferred && Y->
getTag() == tagLesser)
364 if (Y->
getTag() == tagPreferred &&
X->getTag() == tagLesser)
376 unsigned N = path.size();
383 for (
unsigned i = 0; i < N; ++i) {
384 auto piece = std::move(path.front());
387 switch (piece->getKind()) {
398 if (
auto *nextEvent =
399 dyn_cast<PathDiagnosticEventPiece>(path.front().get())) {
400 auto *
event = cast<PathDiagnosticEventPiece>(piece.get());
404 if (
auto *pieceToKeep =
406 piece = std::move(pieceToKeep == event ? piece : path.front());
418 path.push_back(std::move(piece));
428 bool IsInteresting =
false) {
429 bool containsSomethingInteresting = IsInteresting;
430 const unsigned N = pieces.size();
432 for (
unsigned i = 0 ; i < N ; ++i) {
435 auto piece = std::move(pieces.front());
438 switch (piece->getKind()) {
440 auto &call = cast<PathDiagnosticCallPiece>(*piece);
447 containsSomethingInteresting =
true;
451 auto ¯o = cast<PathDiagnosticMacroPiece>(*piece);
454 containsSomethingInteresting =
true;
458 auto &
event = cast<PathDiagnosticEventPiece>(*piece);
462 containsSomethingInteresting |= !
event.isPrunable();
471 pieces.push_back(std::move(piece));
474 return containsSomethingInteresting;
479 for (
unsigned int i = 0; i < Path.size(); ++i) {
480 auto Piece = std::move(Path.front());
482 if (!isa<PathDiagnosticPopUpPiece>(*Piece))
483 Path.push_back(std::move(Piece));
499 for (
const auto &I : Pieces) {
500 auto *
Call = dyn_cast<PathDiagnosticCallPiece>(I.get());
505 if (LastCallLocation) {
507 if (CallerIsImplicit || !
Call->callEnter.asLocation().isValid())
508 Call->callEnter = *LastCallLocation;
509 if (CallerIsImplicit || !
Call->callReturn.asLocation().isValid())
510 Call->callReturn = *LastCallLocation;
516 if (
Call->callEnterWithin.asLocation().isValid() &&
518 ThisCallLocation = &
Call->callEnterWithin;
520 ThisCallLocation = &
Call->callEnter;
522 assert(ThisCallLocation &&
"Outermost call has an invalid location");
531 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
532 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
535 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
538 if (
auto *
CF = dyn_cast<PathDiagnosticControlFlowPiece>(I->get())) {
539 const Stmt *Start =
CF->getStartLocation().asStmt();
540 const Stmt *End =
CF->getEndLocation().asStmt();
541 if (isa_and_nonnull<CXXDefaultInitExpr>(Start)) {
544 }
else if (isa_and_nonnull<CXXDefaultInitExpr>(End)) {
545 PathPieces::iterator Next = std::next(I);
548 dyn_cast<PathDiagnosticControlFlowPiece>(Next->get())) {
549 NextCF->setStartLocation(
CF->getStartLocation());
565 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
566 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
569 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
572 if (!(*I)->getLocation().isValid() ||
573 !(*I)->getLocation().asLocation().isValid()) {
582 const PathDiagnosticConstruct &
C)
const {
583 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
585 C.getCurrLocationContext());
592 llvm::raw_string_ostream &os,
const PathDiagnosticConstruct &
C)
const {
594 if (os.str().empty())
600 os <<
"Execution continues on line "
601 << getSourceManager().getExpansionLineNumber(
Loc.asLocation())
604 os <<
"Execution jumps to the end of the ";
605 const Decl *D =
C.getCurrLocationContext()->getDecl();
606 if (isa<ObjCMethodDecl>(D))
608 else if (isa<FunctionDecl>(D))
611 assert(isa<BlockDecl>(D));
612 os <<
"anonymous block";
628 switch (
Parent->getStmtClass()) {
629 case Stmt::ForStmtClass:
630 case Stmt::DoStmtClass:
631 case Stmt::WhileStmtClass:
632 case Stmt::ObjCForCollectionStmtClass:
633 case Stmt::CXXForRangeStmtClass:
644 bool allowNestedContexts =
false) {
651 switch (
Parent->getStmtClass()) {
652 case Stmt::BinaryOperatorClass: {
653 const auto *B = cast<BinaryOperator>(
Parent);
654 if (B->isLogicalOp())
658 case Stmt::CompoundStmtClass:
659 case Stmt::StmtExprClass:
661 case Stmt::ChooseExprClass:
664 if (allowNestedContexts || cast<ChooseExpr>(
Parent)->getCond() == S)
668 case Stmt::BinaryConditionalOperatorClass:
669 case Stmt::ConditionalOperatorClass:
672 if (allowNestedContexts ||
673 cast<AbstractConditionalOperator>(
Parent)->getCond() == S)
677 case Stmt::CXXForRangeStmtClass:
678 if (cast<CXXForRangeStmt>(
Parent)->getBody() == S)
681 case Stmt::DoStmtClass:
683 case Stmt::ForStmtClass:
684 if (cast<ForStmt>(
Parent)->getBody() == S)
687 case Stmt::IfStmtClass:
688 if (cast<IfStmt>(
Parent)->getCond() != S)
691 case Stmt::ObjCForCollectionStmtClass:
692 if (cast<ObjCForCollectionStmt>(
Parent)->getBody() == S)
695 case Stmt::WhileStmtClass:
696 if (cast<WhileStmt>(
Parent)->getCond() != S)
706 assert(S &&
"Cannot have null Stmt for PathDiagnosticLocation");
725void PathDiagnosticBuilder::updateStackPiecesWithMessage(
727 if (R->hasCallStackHint(
P))
728 for (
const auto &I : CallStack) {
731 std::string stackMsg = R->getCallStackMessage(
P, N);
745 const PathDiagnosticConstruct &
C,
const CFGBlock *Dst,
751 llvm::raw_string_ostream os(sbuf);
757 switch (S->getStmtClass()) {
759 os <<
"No cases match in the switch statement. "
760 "Control jumps to line "
761 << End.asLocation().getExpansionLineNumber();
763 case Stmt::DefaultStmtClass:
764 os <<
"Control jumps to the 'default' case at line "
765 << End.asLocation().getExpansionLineNumber();
768 case Stmt::CaseStmtClass: {
769 os <<
"Control jumps to 'case ";
770 const auto *Case = cast<CaseStmt>(S);
774 bool GetRawInt =
true;
776 if (
const auto *DR = dyn_cast<DeclRefExpr>(LHS)) {
779 const auto *D = dyn_cast<EnumConstantDecl>(DR->getDecl());
790 os <<
":' at line " << End.asLocation().getExpansionLineNumber();
795 os <<
"'Default' branch taken. ";
796 End = ExecutionContinues(os,
C);
798 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
803 const PathDiagnosticConstruct &
C,
const Stmt *S,
806 llvm::raw_string_ostream os(sbuf);
809 os <<
"Control jumps to line " << End.asLocation().getExpansionLineNumber();
810 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str());
814 const PathDiagnosticConstruct &
C,
const Stmt *
T,
const CFGBlock *Src,
819 const auto *B = cast<BinaryOperator>(
T);
821 llvm::raw_string_ostream os(sbuf);
822 os <<
"Left side of '";
825 if (B->getOpcode() == BO_LAnd) {
838 End = ExecutionContinues(
C);
841 assert(B->getOpcode() == BO_LOr);
849 End = ExecutionContinues(
C);
857 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
861void PathDiagnosticBuilder::generateMinimalDiagForBlockEdge(
862 PathDiagnosticConstruct &
C,
BlockEdge BE)
const {
872 switch (
T->getStmtClass()) {
876 case Stmt::GotoStmtClass:
877 case Stmt::IndirectGotoStmtClass: {
878 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
879 C.getActivePath().push_front(generateDiagForGotoOP(
C, S, Start));
883 case Stmt::SwitchStmtClass: {
884 C.getActivePath().push_front(generateDiagForSwitchOP(
C, Dst, Start));
888 case Stmt::BreakStmtClass:
889 case Stmt::ContinueStmtClass: {
891 llvm::raw_string_ostream os(sbuf);
893 C.getActivePath().push_front(
894 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
899 case Stmt::BinaryConditionalOperatorClass:
900 case Stmt::ConditionalOperatorClass: {
902 llvm::raw_string_ostream os(sbuf);
903 os <<
"'?' condition is ";
912 if (
const Stmt *S = End.asStmt())
915 C.getActivePath().push_front(
916 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
921 case Stmt::BinaryOperatorClass: {
922 if (!
C.supportsLogicalOpControlFlow())
925 C.getActivePath().push_front(generateDiagForBinaryOP(
C,
T, Src, Dst));
929 case Stmt::DoStmtClass:
932 llvm::raw_string_ostream os(sbuf);
934 os <<
"Loop condition is true. ";
937 if (
const Stmt *S = End.asStmt())
940 C.getActivePath().push_front(
941 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
946 if (
const Stmt *S = End.asStmt())
949 C.getActivePath().push_front(
950 std::make_shared<PathDiagnosticControlFlowPiece>(
951 Start, End,
"Loop condition is false. Exiting loop"));
955 case Stmt::WhileStmtClass:
956 case Stmt::ForStmtClass:
959 llvm::raw_string_ostream os(sbuf);
961 os <<
"Loop condition is false. ";
963 if (
const Stmt *S = End.asStmt())
966 C.getActivePath().push_front(
967 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
971 if (
const Stmt *S = End.asStmt())
974 C.getActivePath().push_front(
975 std::make_shared<PathDiagnosticControlFlowPiece>(
976 Start, End,
"Loop condition is true. Entering loop body"));
981 case Stmt::IfStmtClass: {
984 if (
const Stmt *S = End.asStmt())
988 C.getActivePath().push_front(
989 std::make_shared<PathDiagnosticControlFlowPiece>(
990 Start, End,
"Taking false branch"));
992 C.getActivePath().push_front(
993 std::make_shared<PathDiagnosticControlFlowPiece>(
994 Start, End,
"Taking true branch"));
1007 case Stmt::ForStmtClass:
1008 case Stmt::WhileStmtClass:
1009 case Stmt::ObjCForCollectionStmtClass:
1010 case Stmt::CXXForRangeStmtClass:
1039 const Stmt *S = SP->getStmt();
1049 const Stmt *LoopBody =
nullptr;
1051 case Stmt::CXXForRangeStmtClass: {
1052 const auto *FR = cast<CXXForRangeStmt>(Term);
1057 LoopBody = FR->getBody();
1060 case Stmt::ForStmtClass: {
1061 const auto *FS = cast<ForStmt>(Term);
1064 LoopBody = FS->getBody();
1067 case Stmt::ObjCForCollectionStmtClass: {
1068 const auto *FC = cast<ObjCForCollectionStmt>(Term);
1069 LoopBody = FC->getBody();
1072 case Stmt::WhileStmtClass:
1073 LoopBody = cast<WhileStmt>(Term)->getBody();
1103 std::make_shared<PathDiagnosticControlFlowPiece>(NewLoc, PrevLoc));
1111 if (
const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(S))
1112 return FS->getElement();
1119 "Loop body skipped when range is empty";
1121 "Loop body skipped when collection is empty";
1123static std::unique_ptr<FilesToLineNumsMap>
1126void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
1137 if (
C.shouldAddPathEdges()) {
1153 bool VisitedEntireCall =
C.PD->isWithinCall();
1154 C.PD->popActivePath();
1157 if (VisitedEntireCall) {
1158 Call = cast<PathDiagnosticCallPiece>(
C.getActivePath().front().get());
1162 const Decl *Caller = CE->getLocationContext()->getDecl();
1164 assert(
C.getActivePath().size() == 1 &&
1165 C.getActivePath().front().get() ==
Call);
1169 assert(
C.isInLocCtxMap(&
C.getActivePath()) &&
1170 "When we ascend to a previously unvisited call, the active path's "
1171 "address shouldn't change, but rather should be compacted into "
1172 "a single CallEvent!");
1173 C.updateLocCtxMap(&
C.getActivePath(),
C.getCurrLocationContext());
1176 assert(!
C.isInLocCtxMap(&
Call->path) &&
1177 "When we ascend to a previously unvisited call, this must be the "
1178 "first time we encounter the caller context!");
1179 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1181 Call->setCallee(*CE,
SM);
1184 PrevLoc =
Call->getLocation();
1186 if (!
C.CallStack.empty()) {
1187 assert(
C.CallStack.back().first ==
Call);
1188 C.CallStack.pop_back();
1193 assert(
C.getCurrLocationContext() ==
C.getLocationContextForActivePath() &&
1194 "The current position in the bug path is out of sync with the "
1195 "location context associated with the active path!");
1198 if (std::optional<CallExitEnd> CE =
P.getAs<
CallExitEnd>()) {
1204 assert(!
C.isInLocCtxMap(&
Call->path) &&
1205 "We just entered a call, this must've been the first time we "
1206 "encounter its context!");
1207 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1209 if (
C.shouldAddPathEdges()) {
1215 auto *
P =
Call.get();
1216 C.getActivePath().push_front(std::move(
Call));
1219 C.PD->pushActivePath(&
P->path);
1220 C.CallStack.push_back(CallWithEntry(
P,
C.getCurrentNode()));
1225 if (!
C.shouldAddPathEdges())
1231 if (!isa<ObjCForCollectionStmt>(PS->getStmt())) {
1239 if (
C.shouldAddControlNotes()) {
1240 generateMinimalDiagForBlockEdge(
C, *BE);
1243 if (!
C.shouldAddPathEdges()) {
1250 const Stmt *Body =
nullptr;
1252 if (
const auto *FS = dyn_cast<ForStmt>(
Loop))
1253 Body = FS->getBody();
1254 else if (
const auto *WS = dyn_cast<WhileStmt>(
Loop))
1255 Body = WS->getBody();
1256 else if (
const auto *OFS = dyn_cast<ObjCForCollectionStmt>(
Loop)) {
1257 Body = OFS->getBody();
1258 }
else if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(
Loop)) {
1259 Body = FRS->getBody();
1263 auto p = std::make_shared<PathDiagnosticEventPiece>(
1264 L,
"Looping back to the head of the loop");
1265 p->setPrunable(
true);
1269 if (!
C.shouldAddControlNotes()) {
1270 C.getActivePath().push_front(std::move(p));
1273 if (
const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
1293 if (!IsInLoopBody) {
1294 if (isa<ObjCForCollectionStmt>(Term)) {
1296 }
else if (isa<CXXForRangeStmt>(Term)) {
1308 C.getCurrLocationContext());
1309 auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
1310 PE->setPrunable(
true);
1314 if (!
C.shouldAddControlNotes()) {
1315 C.getActivePath().push_front(std::move(PE));
1318 }
else if (isa<BreakStmt, ContinueStmt, GotoStmt>(Term)) {
1326static std::unique_ptr<PathDiagnostic>
1328 const Decl *AnalysisEntryPoint) {
1330 return std::make_unique<PathDiagnostic>(
1334 AnalysisEntryPoint, std::make_unique<FilesToLineNumsMap>());
1337static std::unique_ptr<PathDiagnostic>
1340 const Decl *AnalysisEntryPoint) {
1342 return std::make_unique<PathDiagnostic>(
1359 if (isa<FullExpr, CXXBindTemporaryExpr, SubstNonTypeTemplateParmExpr>(S))
1369 switch (S->getStmtClass()) {
1370 case Stmt::BinaryOperatorClass: {
1371 const auto *BO = cast<BinaryOperator>(S);
1372 if (!BO->isLogicalOp())
1374 return BO->getLHS() == Cond || BO->getRHS() == Cond;
1376 case Stmt::IfStmtClass:
1377 return cast<IfStmt>(S)->getCond() == Cond;
1378 case Stmt::ForStmtClass:
1379 return cast<ForStmt>(S)->getCond() == Cond;
1380 case Stmt::WhileStmtClass:
1381 return cast<WhileStmt>(S)->getCond() == Cond;
1382 case Stmt::DoStmtClass:
1383 return cast<DoStmt>(S)->getCond() == Cond;
1384 case Stmt::ChooseExprClass:
1385 return cast<ChooseExpr>(S)->getCond() == Cond;
1386 case Stmt::IndirectGotoStmtClass:
1387 return cast<IndirectGotoStmt>(S)->getTarget() == Cond;
1388 case Stmt::SwitchStmtClass:
1389 return cast<SwitchStmt>(S)->getCond() == Cond;
1390 case Stmt::BinaryConditionalOperatorClass:
1391 return cast<BinaryConditionalOperator>(S)->getCond() == Cond;
1392 case Stmt::ConditionalOperatorClass: {
1393 const auto *CO = cast<ConditionalOperator>(S);
1394 return CO->getCond() == Cond ||
1395 CO->getLHS() == Cond ||
1396 CO->getRHS() == Cond;
1398 case Stmt::ObjCForCollectionStmtClass:
1399 return cast<ObjCForCollectionStmt>(S)->getElement() == Cond;
1400 case Stmt::CXXForRangeStmtClass: {
1401 const auto *FRS = cast<CXXForRangeStmt>(S);
1402 return FRS->getCond() == Cond || FRS->getRangeInit() == Cond;
1410 if (
const auto *FS = dyn_cast<ForStmt>(FL))
1411 return FS->getInc() == S || FS->getInit() == S;
1412 if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(FL))
1413 return FRS->getInc() == S || FRS->getRangeStmt() == S ||
1414 FRS->getLoopVarStmt() || FRS->getRangeInit() == S;
1427 PathPieces::iterator Prev = pieces.end();
1428 for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E;
1430 auto *Piece = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1439 const Stmt *InnerStmt =
nullptr;
1440 while (NextSrcContext.
isValid() && NextSrcContext.
asStmt() != InnerStmt) {
1441 SrcContexts.push_back(NextSrcContext);
1442 InnerStmt = NextSrcContext.
asStmt();
1451 const Stmt *Dst = Piece->getEndLocation().getStmtOrNull();
1461 if (llvm::is_contained(SrcContexts, DstContext))
1465 Piece->setStartLocation(DstContext);
1470 auto *PrevPiece = dyn_cast<PathDiagnosticControlFlowPiece>(Prev->get());
1473 if (
const Stmt *PrevSrc =
1474 PrevPiece->getStartLocation().getStmtOrNull()) {
1476 if (PrevSrcParent ==
1478 PrevPiece->setEndLocation(DstContext);
1489 std::make_shared<PathDiagnosticControlFlowPiece>(SrcLoc, DstContext);
1491 I = pieces.insert(I, std::move(
P));
1507 for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) {
1508 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1513 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1514 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1516 if (!s1Start || !s1End)
1519 PathPieces::iterator NextI = I; ++NextI;
1529 const auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1531 StringRef S = EV->getString();
1540 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1550 if (!s2Start || !s2End || s1End != s2Start)
1566 I = pieces.erase(I);
1579 SM.getExpansionRange(
Range.getEnd()).getEnd());
1582 if (FID !=
SM.getFileID(ExpansionRange.
getEnd()))
1583 return std::nullopt;
1585 std::optional<MemoryBufferRef> Buffer =
SM.getBufferOrNone(FID);
1587 return std::nullopt;
1589 unsigned BeginOffset =
SM.getFileOffset(ExpansionRange.
getBegin());
1590 unsigned EndOffset =
SM.getFileOffset(ExpansionRange.
getEnd());
1591 StringRef Snippet = Buffer->getBuffer().slice(BeginOffset, EndOffset);
1597 if (Snippet.find_first_of(
"\r\n") != StringRef::npos)
1598 return std::nullopt;
1601 return Snippet.size();
1627 for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) {
1629 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1636 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1637 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1639 PathPieces::iterator NextI = I; ++NextI;
1643 const auto *PieceNextI =
1644 dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1647 if (isa<PathDiagnosticEventPiece>(NextI->get())) {
1651 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1660 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1661 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1663 if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
1664 const size_t MAX_SHORT_LINE_LENGTH = 80;
1666 if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) {
1668 if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) {
1670 I = Path.erase(NextI);
1693 bool erased =
false;
1695 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E;
1699 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1704 const Stmt *start = PieceI->getStartLocation().getStmtOrNull();
1705 const Stmt *end = PieceI->getEndLocation().getStmtOrNull();
1720 if (!
SM.isWrittenInSameFile(FirstLoc, SecondLoc))
1722 if (
SM.isBeforeInTranslationUnit(SecondLoc, FirstLoc))
1723 std::swap(SecondLoc, FirstLoc);
1732 const size_t MAX_PUNY_EDGE_LENGTH = 2;
1733 if (*ByteWidth <= MAX_PUNY_EDGE_LENGTH) {
1745 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ++I) {
1746 const auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get());
1751 PathPieces::iterator NextI = I; ++NextI;
1755 const auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1761 if (PieceI->getString() == PieceNextI->getString()) {
1769 bool hasChanges =
false;
1775 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) {
1777 if (
auto *CallI = dyn_cast<PathDiagnosticCallPiece>(I->get())) {
1780 if (!OCS.count(CallI)) {
1790 auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1797 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1798 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1802 PathPieces::iterator NextI = I; ++NextI;
1806 const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1813 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1814 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1832 if (level1 && level1 == level2 && level1 == level3 && level1 == level4) {
1833 PieceI->setEndLocation(PieceNextI->getEndLocation());
1846 if (s1End && s1End == s2Start && level2) {
1847 bool removeEdge =
false;
1873 else if (s1Start && s2End &&
1886 else if (s1Start && s2End &&
1888 SourceRange EdgeRange(PieceI->getEndLocation().asLocation(),
1889 PieceI->getStartLocation().asLocation());
1896 PieceI->setEndLocation(PieceNextI->getEndLocation());
1910 if (s1End == s2Start) {
1911 const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(level3);
1912 if (FS && FS->getCollection()->IgnoreParens() == s2Start &&
1913 s2End == FS->getElement()) {
1914 PieceI->setEndLocation(PieceNextI->getEndLocation());
1951 const auto *FirstEdge =
1952 dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get());
1956 const Decl *D =
C.getLocationContextFor(&Path)->getDecl();
1959 if (FirstEdge->getStartLocation() != EntryLoc)
1971 for (
const auto &
P : path) {
1974 unsigned LineNo =
Loc.getLineNumber();
1976 ExecutedLines[FID].insert(LineNo);
1980PathDiagnosticConstruct::PathDiagnosticConstruct(
1983 : Consumer(PDC), CurrentNode(ErrorNode),
1984 SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()),
1986 AnalysisEntryPoint)) {
1990PathDiagnosticBuilder::PathDiagnosticBuilder(
1993 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics)
1995 ErrorNode(ErrorNode),
1996 VisitorsDiagnostics(
std::move(VisitorsDiagnostics)) {}
1998std::unique_ptr<PathDiagnostic>
2000 const Decl *EntryPoint = getBugReporter().getAnalysisEntryPoint();
2001 PathDiagnosticConstruct Construct(PDC, ErrorNode, R, EntryPoint);
2010 auto EndNotes = VisitorsDiagnostics->find(ErrorNode);
2012 if (EndNotes != VisitorsDiagnostics->end()) {
2013 assert(!EndNotes->second.empty());
2014 LastPiece = EndNotes->second[0];
2019 Construct.PD->setEndOfPath(LastPiece);
2024 while (Construct.ascendToPrevNode()) {
2025 generatePathDiagnosticsForNode(Construct, PrevLoc);
2027 auto VisitorNotes = VisitorsDiagnostics->find(Construct.getCurrentNode());
2028 if (VisitorNotes == VisitorsDiagnostics->end())
2033 std::set<llvm::FoldingSetNodeID> DeduplicationSet;
2037 llvm::FoldingSetNodeID
ID;
2039 if (!DeduplicationSet.insert(ID).second)
2044 updateStackPiecesWithMessage(
Note, Construct.CallStack);
2045 Construct.getActivePath().push_front(
Note);
2053 Construct.getLocationContextForActivePath()->
getStackFrame();
2061 if (!Construct.PD->path.empty()) {
2063 bool stillHasNotes =
2065 assert(stillHasNotes);
2066 (void)stillHasNotes;
2070 if (!Opts.ShouldAddPopUpNotes)
2083 while (
optimizeEdges(Construct, Construct.getMutablePieces(), OCS)) {
2098 if (Opts.ShouldDisplayMacroExpansions)
2101 return std::move(Construct.PD);
2108void BugType::anchor() {}
2114LLVM_ATTRIBUTE_USED
static bool
2116 for (
const std::pair<StringRef, StringRef> &Pair : Registry.
Dependencies) {
2117 if (Pair.second == CheckerName)
2124 StringRef CheckerName) {
2126 if (
Checker.FullName == CheckerName)
2130 "Checker name not found in CheckerRegistry -- did you retrieve it "
2131 "correctly from CheckerManager::getCurrentCheckerName?");
2135 const BugType &bt, StringRef shortDesc, StringRef desc,
2137 const Decl *DeclToUnique)
2138 :
BugReport(
Kind::PathSensitive, bt, shortDesc, desc), ErrorNode(errorNode),
2139 ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() :
SourceRange()),
2140 UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) {
2142 ->getAnalysisManager()
2143 .getCheckerManager()
2144 ->getCheckerRegistryData(),
2146 "Some checkers depend on this one! We don't allow dependency "
2147 "checkers to emit warnings, because checkers should depend on "
2148 "*modeling*, not *diagnostics*.");
2152 ->getAnalysisManager()
2153 .getCheckerManager()
2154 ->getCheckerRegistryData(),
2156 "Hidden checkers musn't emit diagnostics as they are by definition "
2157 "non-user facing!");
2161 std::unique_ptr<BugReporterVisitor> visitor) {
2165 llvm::FoldingSetNodeID ID;
2166 visitor->Profile(ID);
2168 void *InsertPos =
nullptr;
2173 Callbacks.push_back(std::move(visitor));
2190 hash.AddInteger(
static_cast<int>(
getKind()));
2191 hash.AddPointer(&
BT);
2197 if (!range.isValid())
2199 hash.Add(range.getBegin());
2200 hash.Add(range.getEnd());
2205 hash.AddInteger(
static_cast<int>(
getKind()));
2206 hash.AddPointer(&
BT);
2220 if (!range.isValid())
2222 hash.Add(range.getBegin());
2223 hash.Add(range.getEnd());
2229 llvm::DenseMap<T, bugreporter::TrackingKind> &InterestingnessMap,
T Val,
2231 auto Result = InterestingnessMap.insert({Val, TKind});
2251 "BugReport::markInteresting currently can only handle 2 different "
2252 "tracking kinds! Please define what tracking kind should this entitiy"
2253 "have, if it was already marked as interesting with a different kind!");
2265 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2277 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2289 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2300 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2316std::optional<bugreporter::TrackingKind>
2335 "BugReport::getInterestingnessKind currently can only handle 2 different "
2336 "tracking kinds! Please define what tracking kind should we return here "
2337 "when the kind of getAsRegion() and getAsSymbol() is different!");
2338 return std::nullopt;
2341std::optional<bugreporter::TrackingKind>
2344 return std::nullopt;
2349 return std::nullopt;
2350 return It->getSecond();
2353std::optional<bugreporter::TrackingKind>
2356 return std::nullopt;
2361 return It->getSecond();
2363 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2365 return std::nullopt;
2391 const Stmt *S =
nullptr;
2395 if (BE->getBlock() == &Exit)
2416 assert(
ErrorNode &&
"Cannot create a location with a null node.");
2438 if (
const auto *AS = dyn_cast<AttributedStmt>(S))
2439 S = AS->getSubStmt();
2442 if (
const auto *ME = dyn_cast<MemberExpr>(S))
2446 if (
const auto *B = dyn_cast<BinaryOperator>(S))
2452 if (S->getBeginLoc().isValid())
2476 : D(D), UserSuppressions(D.getASTContext()) {}
2480 assert(StrBugTypes.empty() &&
2481 "Destroying BugReporter before diagnostics are emitted!");
2484 for (
const auto I : EQClassesVector)
2491 for (
const auto EQ : EQClassesVector)
2498 StrBugTypes.clear();
2511 std::unique_ptr<ExplodedGraph> BugPath;
2518class BugPathGetter {
2519 std::unique_ptr<ExplodedGraph> TrimmedGraph;
2521 using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>;
2524 PriorityMapTy PriorityMap;
2528 using ReportNewNodePair =
2529 std::pair<PathSensitiveBugReport *, const ExplodedNode *>;
2532 BugPathInfo CurrentBugPath;
2535 template <
bool Descending>
2536 class PriorityCompare {
2537 const PriorityMapTy &PriorityMap;
2540 PriorityCompare(
const PriorityMapTy &M) : PriorityMap(M) {}
2543 PriorityMapTy::const_iterator LI = PriorityMap.find(LHS);
2544 PriorityMapTy::const_iterator RI = PriorityMap.find(RHS);
2545 PriorityMapTy::const_iterator E = PriorityMap.end();
2552 return Descending ? LI->second > RI->second
2553 : LI->second < RI->second;
2556 bool operator()(
const ReportNewNodePair &LHS,
2557 const ReportNewNodePair &RHS)
const {
2558 return (*
this)(LHS.second, RHS.second);
2566 BugPathInfo *getNextBugPath();
2571BugPathGetter::BugPathGetter(
const ExplodedGraph *OriginalGraph,
2574 for (
const auto I : bugReports) {
2575 assert(I->isValid() &&
2576 "We only allow BugReporterVisitors and BugReporter itself to "
2577 "invalidate reports!");
2578 Nodes.emplace_back(I->getErrorNode());
2584 TrimmedGraph = OriginalGraph->
trim(
Nodes, &ForwardMap);
2592 const ExplodedNode *NewNode = ForwardMap.lookup(Report->getErrorNode());
2594 "Failed to construct a trimmed graph that contains this error "
2596 ReportNodes.emplace_back(Report, NewNode);
2597 RemainingNodes.insert(NewNode);
2600 assert(!RemainingNodes.empty() &&
"No error node found in the trimmed graph");
2603 std::queue<const ExplodedNode *> WS;
2605 assert(TrimmedGraph->num_roots() == 1);
2606 WS.push(*TrimmedGraph->roots_begin());
2609 while (!WS.empty()) {
2613 PriorityMapTy::iterator PriorityEntry;
2615 std::tie(PriorityEntry, IsNew) = PriorityMap.insert({
Node,
Priority});
2619 assert(PriorityEntry->second <= Priority);
2623 if (RemainingNodes.erase(
Node))
2624 if (RemainingNodes.empty())
2632 llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap));
2635BugPathInfo *BugPathGetter::getNextBugPath() {
2636 if (ReportNodes.empty())
2640 std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val();
2641 assert(PriorityMap.contains(OrigN) &&
"error node not accessible from root");
2645 auto GNew = std::make_unique<ExplodedGraph>();
2661 CurrentBugPath.ErrorNode = NewN;
2667 GNew->addRoot(NewN);
2674 PriorityCompare<false>(PriorityMap));
2677 CurrentBugPath.BugPath = std::move(GNew);
2679 return &CurrentBugPath;
2686 using MacroStackTy = std::vector<
2687 std::pair<std::shared_ptr<PathDiagnosticMacroPiece>,
SourceLocation>>;
2689 using PiecesTy = std::vector<PathDiagnosticPieceRef>;
2691 MacroStackTy MacroStack;
2694 for (PathPieces::const_iterator I = path.begin(), E = path.end();
2696 const auto &piece = *I;
2699 if (
auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) {
2709 SM.getExpansionLoc(
Loc) :
2712 if (
Loc.isFileID()) {
2714 Pieces.push_back(piece);
2718 assert(
Loc.isMacroID());
2721 if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
2722 MacroStack.back().first->subPieces.push_back(piece);
2728 std::shared_ptr<PathDiagnosticMacroPiece> MacroGroup;
2731 SM.getExpansionLoc(
Loc) :
2735 while (!MacroStack.empty()) {
2736 if (InstantiationLoc == MacroStack.back().second) {
2737 MacroGroup = MacroStack.back().first;
2741 if (ParentInstantiationLoc == MacroStack.back().second) {
2742 MacroGroup = MacroStack.back().first;
2746 MacroStack.pop_back();
2749 if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
2751 auto NewGroup = std::make_shared<PathDiagnosticMacroPiece>(
2755 MacroGroup->subPieces.push_back(NewGroup);
2757 assert(InstantiationLoc.
isFileID());
2758 Pieces.push_back(NewGroup);
2761 MacroGroup = NewGroup;
2762 MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
2766 MacroGroup->subPieces.push_back(piece);
2772 path.insert(path.end(), Pieces.begin(), Pieces.end());
2778static std::unique_ptr<VisitorsDiagnosticsTy>
2782 std::unique_ptr<VisitorsDiagnosticsTy> Notes =
2783 std::make_unique<VisitorsDiagnosticsTy>();
2796 for (std::unique_ptr<BugReporterVisitor> &Visitor : R->
visitors())
2797 visitors.push_back(std::move(Visitor));
2804 for (
auto &
V : visitors) {
2805 V->finalizeVisitor(BRC, ErrorNode, *R);
2807 if (
auto Piece =
V->getEndPath(BRC, ErrorNode, *R)) {
2808 assert(!LastPiece &&
2809 "There can only be one final piece in a diagnostic.");
2811 "The final piece must contain a message!");
2812 LastPiece = std::move(Piece);
2813 (*Notes)[ErrorNode].push_back(LastPiece);
2819 for (
auto &
V : visitors) {
2820 auto P =
V->VisitNode(NextNode, BRC, *R);
2822 (*Notes)[NextNode].push_back(std::move(
P));
2834std::optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
2838 BugPathGetter BugGraph(&Reporter.
getGraph(), bugReports);
2840 while (BugPathInfo *BugPath = BugGraph.getNextBugPath()) {
2843 assert(R &&
"No original report found for sliced graph.");
2844 assert(R->
isValid() &&
"Report selected by trimmed graph marked invalid.");
2859 std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes =
2876 return PathDiagnosticBuilder(
2877 std::move(BRC), std::move(BugPath->BugPath), BugPath->Report,
2878 BugPath->ErrorNode, std::move(visitorNotes));
2885std::unique_ptr<DiagnosticForConsumerMapTy>
2889 assert(!bugReports.empty());
2891 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
2893 std::optional<PathDiagnosticBuilder> PDB =
2894 PathDiagnosticBuilder::findValidReport(bugReports, *
this);
2898 if (std::unique_ptr<PathDiagnostic> PD = PDB->generate(PC)) {
2899 (*Out)[PC] = std::move(PD);
2908 bool ValidSourceLoc = R->getLocation().isValid();
2909 assert(ValidSourceLoc);
2912 if (!ValidSourceLoc)
2920 llvm::FoldingSetNodeID ID;
2929 EQClasses.InsertNode(EQ, InsertPos);
2930 EQClassesVector.push_back(EQ);
2932 EQ->AddReport(std::move(R));
2936 if (
auto PR = dyn_cast<PathSensitiveBugReport>(R.get()))
2940 assert((E->isSink() || E->getLocation().getTag()) &&
2941 "Error node must either be a sink or have a tag");
2944 E->getLocationContext()->getAnalysisDeclContext();
2963struct FRIEC_WLItem {
2968 : N(n), I(N->succ_begin()), E(N->succ_end()) {}
2973BugReport *PathSensitiveBugReporter::findReportInEquivalenceClass(
2978 assert(
EQ.getReports().size() > 0);
2979 const BugType& BT =
EQ.getReports()[0]->getBugType();
2982 for (
auto &J :
EQ.getReports()) {
2983 if (
auto *PR = dyn_cast<PathSensitiveBugReport>(J.get())) {
2985 bugReports.push_back(PR);
2999 for (
const auto &I:
EQ.getReports()) {
3000 auto *R = dyn_cast<PathSensitiveBugReport>(I.get());
3005 if (errorNode->
isSink()) {
3007 "BugType::isSuppressSink() should not be 'true' for sink end nodes");
3011 bugReports.push_back(R);
3022 if (ErrorB->isInevitablySinking())
3027 using WLItem = FRIEC_WLItem;
3030 llvm::DenseMap<const ExplodedNode *, unsigned>
Visited;
3033 WL.push_back(errorNode);
3036 while (!WL.empty()) {
3037 WLItem &WI = WL.back();
3038 assert(!WI.N->succ_empty());
3040 for (; WI.I != WI.E; ++WI.I) {
3046 bugReports.push_back(R);
3057 unsigned &mark =
Visited[Succ];
3067 if (!WL.empty() && &WL.back() == &WI)
3074 return exampleReport;
3079 BugReport *report = findReportInEquivalenceClass(EQ, bugReports);
3084 for (
const std::string &CheckerOrPackage :
3091 std::unique_ptr<DiagnosticForConsumerMapTy> Diagnostics =
3094 for (
auto &
P : *Diagnostics) {
3096 std::unique_ptr<PathDiagnostic> &PD =
P.second;
3100 if (PD->path.empty()) {
3102 auto piece = std::make_unique<PathDiagnosticEventPiece>(
3105 piece->addRange(
Range);
3106 PD->setEndOfPath(std::move(piece));
3113 for (
const auto &I : llvm::reverse(report->
getNotes())) {
3115 auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
3118 ConvertedPiece->addRange(R);
3120 Pieces.push_front(std::move(ConvertedPiece));
3123 for (
const auto &I : llvm::reverse(report->
getNotes()))
3124 Pieces.push_front(I);
3127 for (
const auto &I : report->
getFixits())
3128 Pieces.back()->addFixit(I);
3136 Pieces.push_front(std::make_shared<PathDiagnosticEventPiece>(
3138 "[debug] analyzing from " +
3152 if (
const auto FD = dyn_cast<FunctionDecl>(Signature)) {
3153 SignatureSourceRange = FD->getSourceRange();
3154 }
else if (
const auto OD = dyn_cast<ObjCMethodDecl>(Signature)) {
3155 SignatureSourceRange = OD->getSourceRange();
3161 : SignatureSourceRange.
getEnd();
3162 if (!Start.
isValid() || !End.isValid())
3164 unsigned StartLine =
SM.getExpansionLineNumber(Start);
3165 unsigned EndLine =
SM.getExpansionLineNumber(End);
3167 FileID FID =
SM.getFileID(
SM.getExpansionLoc(Start));
3168 for (
unsigned Line = StartLine;
Line <= EndLine;
Line++)
3169 ExecutedLines[FID].insert(
Line);
3179 FileID FID =
SM.getFileID(ExpansionLoc);
3180 unsigned LineNo =
SM.getExpansionLineNumber(ExpansionLoc);
3181 ExecutedLines[FID].insert(LineNo);
3186static std::unique_ptr<FilesToLineNumsMap>
3188 auto ExecutedLines = std::make_unique<FilesToLineNumsMap>();
3197 const Decl* D = CE->getCalleeContext()->getDecl();
3208 if (
const auto *RS = dyn_cast_or_null<ReturnStmt>(
P)) {
3213 if (isa_and_nonnull<SwitchCase, LabelStmt>(
P))
3219 return ExecutedLines;
3222std::unique_ptr<DiagnosticForConsumerMapTy>
3226 auto *basicReport = cast<BasicBugReport>(exampleReport);
3227 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
3228 for (
auto *Consumer : consumers)
3244 "The call piece should not be in a header file.");
3256 if (
auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get()))
3264 if (PD.
path.empty())
3273 if (
auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
3280 const auto *ND = dyn_cast<NamedDecl>(CP->
getCallee());
3283 llvm::raw_svector_ostream os(buf);
3284 os <<
" (within a call to '" << ND->getDeclName() <<
"')";
3299std::unique_ptr<DiagnosticForConsumerMapTy>
3300PathSensitiveBugReporter::generateDiagnosticForConsumerMap(
3303 std::vector<BasicBugReport *> BasicBugReports;
3304 std::vector<PathSensitiveBugReport *> PathSensitiveBugReports;
3305 if (isa<BasicBugReport>(exampleReport))
3307 consumers, bugReports);
3313 assert(!bugReports.empty());
3314 MaxBugClassSize.updateMax(bugReports.size());
3321 consumers, convertedArrayOfReports);
3326 MaxValidBugClassSize.updateMax(bugReports.size());
3331 for (
auto const &
P : *Out)
3332 if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.
AnalyzeAll)
3345 Loc, Ranges, Fixits);
3350 StringRef name, StringRef category,
3355 BugType *BT = getBugTypeForName(CheckName, name, category);
3356 auto R = std::make_unique<BasicBugReport>(*BT, str,
Loc);
3357 R->setDeclWithIssue(DeclWithIssue);
3358 for (
const auto &SR : Ranges)
3360 for (
const auto &FH : Fixits)
3366 StringRef name, StringRef category) {
3368 llvm::raw_svector_ostream(fullDesc) << CheckName.
getName() <<
":" << name
3370 std::unique_ptr<BugType> &BT = StrBugTypes[fullDesc];
3372 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 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 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)
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.
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=std::nullopt, ArrayRef< FixItHint > Fixits=std::nullopt)
const AnalyzerOptions & getAnalyzerOptions()
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()
The bug visitor will walk all the nodes in a path and collect all the constraints.
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 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.
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