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;
146 assert(CurrentNode &&
"Already reached the root!");
154 return LCM.find(&PD->getActivePath())->getSecond();
157 const ExplodedNode *getCurrentNode()
const {
return CurrentNode; }
161 bool ascendToPrevNode() {
163 return static_cast<bool>(CurrentNode);
167 return getCurrLocationContext()->getParentMap();
172 const Stmt *getParent(
const Stmt *S)
const {
173 return getParentMap().getParent(S);
182 assert(LCM.count(Path) &&
183 "Failed to find the context associated with these pieces!");
184 return LCM.find(Path)->getSecond();
187 bool isInLocCtxMap(
const PathPieces *Path)
const {
return LCM.count(Path); }
189 PathPieces &getActivePath() {
return PD->getActivePath(); }
190 PathPieces &getMutablePieces() {
return PD->getMutablePieces(); }
193 bool shouldAddControlNotes()
const {
196 bool shouldGenerateDiagnostics()
const {
199 bool supportsLogicalOpControlFlow()
const {
210 std::unique_ptr<const ExplodedGraph> BugPath;
221 std::unique_ptr<const VisitorsDiagnosticsTy> VisitorsDiagnostics;
227 static std::optional<PathDiagnosticBuilder>
231 PathDiagnosticBuilder(
234 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics);
246 std::unique_ptr<PathDiagnostic>
251 const CallWithEntryStack &CallStack)
const;
252 void generatePathDiagnosticsForNode(PathDiagnosticConstruct &
C,
255 void generateMinimalDiagForBlockEdge(PathDiagnosticConstruct &
C,
259 generateDiagForGotoOP(
const PathDiagnosticConstruct &
C,
const Stmt *S,
263 generateDiagForSwitchOP(
const PathDiagnosticConstruct &
C,
const CFGBlock *Dst,
267 generateDiagForBinaryOP(
const PathDiagnosticConstruct &
C,
const Stmt *T,
271 ExecutionContinues(
const PathDiagnosticConstruct &
C)
const;
274 ExecutionContinues(llvm::raw_string_ostream &os,
275 const PathDiagnosticConstruct &
C)
const;
297 const auto *CE = dyn_cast_or_null<CallExpr>(CallSite);
302 for (
auto [Idx, ArgExpr] : llvm::enumerate(CE->arguments())) {
314 if (ArgExpr->getType()->isVoidPointerType())
339 return (llvm::Twine(Msg) +
" via " + std::to_string(ArgIndex) +
340 llvm::getOrdinalSuffix(ArgIndex) +
" parameter").str();
360 if (
X->getTag() == tagPreferred && Y->
getTag() == tagLesser)
363 if (Y->
getTag() == tagPreferred &&
X->getTag() == tagLesser)
375 unsigned N = path.size();
382 for (
unsigned i = 0; i < N; ++i) {
383 auto piece = std::move(path.front());
386 switch (piece->getKind()) {
397 if (
auto *nextEvent =
398 dyn_cast<PathDiagnosticEventPiece>(path.front().get())) {
399 auto *
event = cast<PathDiagnosticEventPiece>(piece.get());
403 if (
auto *pieceToKeep =
405 piece = std::move(pieceToKeep == event ? piece : path.front());
417 path.push_back(std::move(piece));
427 bool IsInteresting =
false) {
428 bool containsSomethingInteresting = IsInteresting;
429 const unsigned N = pieces.size();
431 for (
unsigned i = 0 ; i < N ; ++i) {
434 auto piece = std::move(pieces.front());
437 switch (piece->getKind()) {
439 auto &call = cast<PathDiagnosticCallPiece>(*piece);
446 containsSomethingInteresting =
true;
450 auto ¯o = cast<PathDiagnosticMacroPiece>(*piece);
453 containsSomethingInteresting =
true;
457 auto &
event = cast<PathDiagnosticEventPiece>(*piece);
461 containsSomethingInteresting |= !
event.isPrunable();
470 pieces.push_back(std::move(piece));
473 return containsSomethingInteresting;
478 for (
unsigned int i = 0; i < Path.size(); ++i) {
479 auto Piece = std::move(Path.front());
481 if (!isa<PathDiagnosticPopUpPiece>(*Piece))
482 Path.push_back(std::move(Piece));
498 for (
const auto &I : Pieces) {
499 auto *
Call = dyn_cast<PathDiagnosticCallPiece>(I.get());
504 if (LastCallLocation) {
506 if (CallerIsImplicit || !
Call->callEnter.asLocation().isValid())
507 Call->callEnter = *LastCallLocation;
508 if (CallerIsImplicit || !
Call->callReturn.asLocation().isValid())
509 Call->callReturn = *LastCallLocation;
515 if (
Call->callEnterWithin.asLocation().isValid() &&
517 ThisCallLocation = &
Call->callEnterWithin;
519 ThisCallLocation = &
Call->callEnter;
521 assert(ThisCallLocation &&
"Outermost call has an invalid location");
530 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
531 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
534 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
537 if (
auto *
CF = dyn_cast<PathDiagnosticControlFlowPiece>(I->get())) {
538 const Stmt *Start =
CF->getStartLocation().asStmt();
539 const Stmt *End =
CF->getEndLocation().asStmt();
540 if (isa_and_nonnull<CXXDefaultInitExpr>(Start)) {
543 }
else if (isa_and_nonnull<CXXDefaultInitExpr>(End)) {
544 PathPieces::iterator Next = std::next(I);
547 dyn_cast<PathDiagnosticControlFlowPiece>(Next->get())) {
548 NextCF->setStartLocation(
CF->getStartLocation());
564 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
565 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
568 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
571 if (!(*I)->getLocation().isValid() ||
572 !(*I)->getLocation().asLocation().isValid()) {
581 const PathDiagnosticConstruct &
C)
const {
582 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
584 C.getCurrLocationContext());
591 llvm::raw_string_ostream &os,
const PathDiagnosticConstruct &
C)
const {
593 if (os.str().empty())
599 os <<
"Execution continues on line "
600 << getSourceManager().getExpansionLineNumber(
Loc.asLocation())
603 os <<
"Execution jumps to the end of the ";
604 const Decl *D =
C.getCurrLocationContext()->getDecl();
605 if (isa<ObjCMethodDecl>(D))
607 else if (isa<FunctionDecl>(D))
610 assert(isa<BlockDecl>(D));
611 os <<
"anonymous block";
627 switch (
Parent->getStmtClass()) {
628 case Stmt::ForStmtClass:
629 case Stmt::DoStmtClass:
630 case Stmt::WhileStmtClass:
631 case Stmt::ObjCForCollectionStmtClass:
632 case Stmt::CXXForRangeStmtClass:
643 bool allowNestedContexts =
false) {
650 switch (
Parent->getStmtClass()) {
651 case Stmt::BinaryOperatorClass: {
652 const auto *B = cast<BinaryOperator>(
Parent);
653 if (B->isLogicalOp())
657 case Stmt::CompoundStmtClass:
658 case Stmt::StmtExprClass:
660 case Stmt::ChooseExprClass:
663 if (allowNestedContexts || cast<ChooseExpr>(
Parent)->getCond() == S)
667 case Stmt::BinaryConditionalOperatorClass:
668 case Stmt::ConditionalOperatorClass:
671 if (allowNestedContexts ||
672 cast<AbstractConditionalOperator>(
Parent)->getCond() == S)
676 case Stmt::CXXForRangeStmtClass:
677 if (cast<CXXForRangeStmt>(
Parent)->getBody() == S)
680 case Stmt::DoStmtClass:
682 case Stmt::ForStmtClass:
683 if (cast<ForStmt>(
Parent)->getBody() == S)
686 case Stmt::IfStmtClass:
687 if (cast<IfStmt>(
Parent)->getCond() != S)
690 case Stmt::ObjCForCollectionStmtClass:
691 if (cast<ObjCForCollectionStmt>(
Parent)->getBody() == S)
694 case Stmt::WhileStmtClass:
695 if (cast<WhileStmt>(
Parent)->getCond() != S)
705 assert(S &&
"Cannot have null Stmt for PathDiagnosticLocation");
724void PathDiagnosticBuilder::updateStackPiecesWithMessage(
726 if (R->hasCallStackHint(
P))
727 for (
const auto &I : CallStack) {
730 std::string stackMsg = R->getCallStackMessage(
P, N);
744 const PathDiagnosticConstruct &
C,
const CFGBlock *Dst,
750 llvm::raw_string_ostream os(sbuf);
756 switch (S->getStmtClass()) {
758 os <<
"No cases match in the switch statement. "
759 "Control jumps to line "
760 << End.asLocation().getExpansionLineNumber();
762 case Stmt::DefaultStmtClass:
763 os <<
"Control jumps to the 'default' case at line "
764 << End.asLocation().getExpansionLineNumber();
767 case Stmt::CaseStmtClass: {
768 os <<
"Control jumps to 'case ";
769 const auto *Case = cast<CaseStmt>(S);
773 bool GetRawInt =
true;
775 if (
const auto *DR = dyn_cast<DeclRefExpr>(LHS)) {
778 const auto *D = dyn_cast<EnumConstantDecl>(DR->getDecl());
789 os <<
":' at line " << End.asLocation().getExpansionLineNumber();
794 os <<
"'Default' branch taken. ";
795 End = ExecutionContinues(os,
C);
797 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
802 const PathDiagnosticConstruct &
C,
const Stmt *S,
805 llvm::raw_string_ostream os(sbuf);
808 os <<
"Control jumps to line " << End.asLocation().getExpansionLineNumber();
809 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str());
813 const PathDiagnosticConstruct &
C,
const Stmt *T,
const CFGBlock *Src,
818 const auto *B = cast<BinaryOperator>(T);
820 llvm::raw_string_ostream os(sbuf);
821 os <<
"Left side of '";
824 if (B->getOpcode() == BO_LAnd) {
837 End = ExecutionContinues(
C);
840 assert(B->getOpcode() == BO_LOr);
848 End = ExecutionContinues(
C);
856 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
860void PathDiagnosticBuilder::generateMinimalDiagForBlockEdge(
861 PathDiagnosticConstruct &
C,
BlockEdge BE)
const {
875 case Stmt::GotoStmtClass:
876 case Stmt::IndirectGotoStmtClass: {
877 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
878 C.getActivePath().push_front(generateDiagForGotoOP(
C, S, Start));
882 case Stmt::SwitchStmtClass: {
883 C.getActivePath().push_front(generateDiagForSwitchOP(
C, Dst, Start));
887 case Stmt::BreakStmtClass:
888 case Stmt::ContinueStmtClass: {
890 llvm::raw_string_ostream os(sbuf);
892 C.getActivePath().push_front(
893 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
898 case Stmt::BinaryConditionalOperatorClass:
899 case Stmt::ConditionalOperatorClass: {
901 llvm::raw_string_ostream os(sbuf);
902 os <<
"'?' condition is ";
911 if (
const Stmt *S = End.asStmt())
914 C.getActivePath().push_front(
915 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
920 case Stmt::BinaryOperatorClass: {
921 if (!
C.supportsLogicalOpControlFlow())
924 C.getActivePath().push_front(generateDiagForBinaryOP(
C, T, Src, Dst));
928 case Stmt::DoStmtClass:
931 llvm::raw_string_ostream os(sbuf);
933 os <<
"Loop condition is true. ";
936 if (
const Stmt *S = End.asStmt())
939 C.getActivePath().push_front(
940 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
945 if (
const Stmt *S = End.asStmt())
948 C.getActivePath().push_front(
949 std::make_shared<PathDiagnosticControlFlowPiece>(
950 Start, End,
"Loop condition is false. Exiting loop"));
954 case Stmt::WhileStmtClass:
955 case Stmt::ForStmtClass:
958 llvm::raw_string_ostream os(sbuf);
960 os <<
"Loop condition is false. ";
962 if (
const Stmt *S = End.asStmt())
965 C.getActivePath().push_front(
966 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
970 if (
const Stmt *S = End.asStmt())
973 C.getActivePath().push_front(
974 std::make_shared<PathDiagnosticControlFlowPiece>(
975 Start, End,
"Loop condition is true. Entering loop body"));
980 case Stmt::IfStmtClass: {
983 if (
const Stmt *S = End.asStmt())
987 C.getActivePath().push_front(
988 std::make_shared<PathDiagnosticControlFlowPiece>(
989 Start, End,
"Taking false branch"));
991 C.getActivePath().push_front(
992 std::make_shared<PathDiagnosticControlFlowPiece>(
993 Start, End,
"Taking true branch"));
1006 case Stmt::ForStmtClass:
1007 case Stmt::WhileStmtClass:
1008 case Stmt::ObjCForCollectionStmtClass:
1009 case Stmt::CXXForRangeStmtClass:
1038 const Stmt *S = SP->getStmt();
1048 const Stmt *LoopBody =
nullptr;
1050 case Stmt::CXXForRangeStmtClass: {
1051 const auto *FR = cast<CXXForRangeStmt>(Term);
1056 LoopBody = FR->getBody();
1059 case Stmt::ForStmtClass: {
1060 const auto *FS = cast<ForStmt>(Term);
1063 LoopBody = FS->getBody();
1066 case Stmt::ObjCForCollectionStmtClass: {
1067 const auto *FC = cast<ObjCForCollectionStmt>(Term);
1068 LoopBody = FC->getBody();
1071 case Stmt::WhileStmtClass:
1072 LoopBody = cast<WhileStmt>(Term)->getBody();
1102 std::make_shared<PathDiagnosticControlFlowPiece>(NewLoc, PrevLoc));
1110 if (
const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(S))
1111 return FS->getElement();
1118 "Loop body skipped when range is empty";
1120 "Loop body skipped when collection is empty";
1122static std::unique_ptr<FilesToLineNumsMap>
1125void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
1136 if (
C.shouldAddPathEdges()) {
1152 bool VisitedEntireCall =
C.PD->isWithinCall();
1153 C.PD->popActivePath();
1156 if (VisitedEntireCall) {
1157 Call = cast<PathDiagnosticCallPiece>(
C.getActivePath().front().get());
1161 const Decl *Caller = CE->getLocationContext()->getDecl();
1163 assert(
C.getActivePath().size() == 1 &&
1164 C.getActivePath().front().get() ==
Call);
1168 assert(
C.isInLocCtxMap(&
C.getActivePath()) &&
1169 "When we ascend to a previously unvisited call, the active path's "
1170 "address shouldn't change, but rather should be compacted into "
1171 "a single CallEvent!");
1172 C.updateLocCtxMap(&
C.getActivePath(),
C.getCurrLocationContext());
1175 assert(!
C.isInLocCtxMap(&
Call->path) &&
1176 "When we ascend to a previously unvisited call, this must be the "
1177 "first time we encounter the caller context!");
1178 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1180 Call->setCallee(*CE,
SM);
1183 PrevLoc =
Call->getLocation();
1185 if (!
C.CallStack.empty()) {
1186 assert(
C.CallStack.back().first ==
Call);
1187 C.CallStack.pop_back();
1192 assert(
C.getCurrLocationContext() ==
C.getLocationContextForActivePath() &&
1193 "The current position in the bug path is out of sync with the "
1194 "location context associated with the active path!");
1197 if (std::optional<CallExitEnd> CE =
P.getAs<
CallExitEnd>()) {
1203 assert(!
C.isInLocCtxMap(&
Call->path) &&
1204 "We just entered a call, this must've been the first time we "
1205 "encounter its context!");
1206 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1208 if (
C.shouldAddPathEdges()) {
1214 auto *
P =
Call.get();
1215 C.getActivePath().push_front(std::move(
Call));
1218 C.PD->pushActivePath(&
P->path);
1219 C.CallStack.push_back(CallWithEntry(
P,
C.getCurrentNode()));
1224 if (!
C.shouldAddPathEdges())
1230 if (!isa<ObjCForCollectionStmt>(PS->getStmt())) {
1238 if (
C.shouldAddControlNotes()) {
1239 generateMinimalDiagForBlockEdge(
C, *BE);
1242 if (!
C.shouldAddPathEdges()) {
1249 const Stmt *Body =
nullptr;
1251 if (
const auto *FS = dyn_cast<ForStmt>(
Loop))
1252 Body = FS->getBody();
1253 else if (
const auto *WS = dyn_cast<WhileStmt>(
Loop))
1254 Body = WS->getBody();
1255 else if (
const auto *OFS = dyn_cast<ObjCForCollectionStmt>(
Loop)) {
1256 Body = OFS->getBody();
1257 }
else if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(
Loop)) {
1258 Body = FRS->getBody();
1262 auto p = std::make_shared<PathDiagnosticEventPiece>(
1263 L,
"Looping back to the head of the loop");
1264 p->setPrunable(
true);
1268 if (!
C.shouldAddControlNotes()) {
1269 C.getActivePath().push_front(std::move(p));
1272 if (
const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
1292 if (!IsInLoopBody) {
1293 if (isa<ObjCForCollectionStmt>(Term)) {
1295 }
else if (isa<CXXForRangeStmt>(Term)) {
1307 C.getCurrLocationContext());
1308 auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
1309 PE->setPrunable(
true);
1313 if (!
C.shouldAddControlNotes()) {
1314 C.getActivePath().push_front(std::move(PE));
1317 }
else if (isa<BreakStmt, ContinueStmt, GotoStmt>(Term)) {
1325static std::unique_ptr<PathDiagnostic>
1328 return std::make_unique<PathDiagnostic>(
1332 std::make_unique<FilesToLineNumsMap>());
1335static std::unique_ptr<PathDiagnostic>
1339 return std::make_unique<PathDiagnostic>(
1356 if (isa<FullExpr, CXXBindTemporaryExpr, SubstNonTypeTemplateParmExpr>(S))
1366 switch (S->getStmtClass()) {
1367 case Stmt::BinaryOperatorClass: {
1368 const auto *BO = cast<BinaryOperator>(S);
1369 if (!BO->isLogicalOp())
1371 return BO->getLHS() == Cond || BO->getRHS() == Cond;
1373 case Stmt::IfStmtClass:
1374 return cast<IfStmt>(S)->getCond() == Cond;
1375 case Stmt::ForStmtClass:
1376 return cast<ForStmt>(S)->getCond() == Cond;
1377 case Stmt::WhileStmtClass:
1378 return cast<WhileStmt>(S)->getCond() == Cond;
1379 case Stmt::DoStmtClass:
1380 return cast<DoStmt>(S)->getCond() == Cond;
1381 case Stmt::ChooseExprClass:
1382 return cast<ChooseExpr>(S)->getCond() == Cond;
1383 case Stmt::IndirectGotoStmtClass:
1384 return cast<IndirectGotoStmt>(S)->getTarget() == Cond;
1385 case Stmt::SwitchStmtClass:
1386 return cast<SwitchStmt>(S)->getCond() == Cond;
1387 case Stmt::BinaryConditionalOperatorClass:
1388 return cast<BinaryConditionalOperator>(S)->getCond() == Cond;
1389 case Stmt::ConditionalOperatorClass: {
1390 const auto *CO = cast<ConditionalOperator>(S);
1391 return CO->getCond() == Cond ||
1392 CO->getLHS() == Cond ||
1393 CO->getRHS() == Cond;
1395 case Stmt::ObjCForCollectionStmtClass:
1396 return cast<ObjCForCollectionStmt>(S)->getElement() == Cond;
1397 case Stmt::CXXForRangeStmtClass: {
1398 const auto *FRS = cast<CXXForRangeStmt>(S);
1399 return FRS->getCond() == Cond || FRS->getRangeInit() == Cond;
1407 if (
const auto *FS = dyn_cast<ForStmt>(FL))
1408 return FS->getInc() == S || FS->getInit() == S;
1409 if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(FL))
1410 return FRS->getInc() == S || FRS->getRangeStmt() == S ||
1411 FRS->getLoopVarStmt() || FRS->getRangeInit() == S;
1424 PathPieces::iterator Prev = pieces.end();
1425 for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E;
1427 auto *Piece = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1436 const Stmt *InnerStmt =
nullptr;
1437 while (NextSrcContext.
isValid() && NextSrcContext.
asStmt() != InnerStmt) {
1438 SrcContexts.push_back(NextSrcContext);
1439 InnerStmt = NextSrcContext.
asStmt();
1448 const Stmt *Dst = Piece->getEndLocation().getStmtOrNull();
1458 if (llvm::is_contained(SrcContexts, DstContext))
1462 Piece->setStartLocation(DstContext);
1467 auto *PrevPiece = dyn_cast<PathDiagnosticControlFlowPiece>(Prev->get());
1470 if (
const Stmt *PrevSrc =
1471 PrevPiece->getStartLocation().getStmtOrNull()) {
1473 if (PrevSrcParent ==
1475 PrevPiece->setEndLocation(DstContext);
1486 std::make_shared<PathDiagnosticControlFlowPiece>(SrcLoc, DstContext);
1488 I = pieces.insert(I, std::move(
P));
1504 for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) {
1505 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1510 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1511 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1513 if (!s1Start || !s1End)
1516 PathPieces::iterator NextI = I; ++NextI;
1526 const auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1528 StringRef S = EV->getString();
1537 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1547 if (!s2Start || !s2End || s1End != s2Start)
1563 I = pieces.erase(I);
1576 SM.getExpansionRange(
Range.getEnd()).getEnd());
1579 if (FID !=
SM.getFileID(ExpansionRange.
getEnd()))
1580 return std::nullopt;
1582 std::optional<MemoryBufferRef> Buffer =
SM.getBufferOrNone(FID);
1584 return std::nullopt;
1586 unsigned BeginOffset =
SM.getFileOffset(ExpansionRange.
getBegin());
1587 unsigned EndOffset =
SM.getFileOffset(ExpansionRange.
getEnd());
1588 StringRef Snippet = Buffer->getBuffer().slice(BeginOffset, EndOffset);
1594 if (Snippet.find_first_of(
"\r\n") != StringRef::npos)
1595 return std::nullopt;
1598 return Snippet.size();
1624 for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) {
1626 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1633 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1634 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1636 PathPieces::iterator NextI = I; ++NextI;
1640 const auto *PieceNextI =
1641 dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1644 if (isa<PathDiagnosticEventPiece>(NextI->get())) {
1648 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1657 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1658 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1660 if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
1661 const size_t MAX_SHORT_LINE_LENGTH = 80;
1663 if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) {
1665 if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) {
1667 I = Path.erase(NextI);
1690 bool erased =
false;
1692 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E;
1696 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1701 const Stmt *start = PieceI->getStartLocation().getStmtOrNull();
1702 const Stmt *end = PieceI->getEndLocation().getStmtOrNull();
1717 if (!
SM.isWrittenInSameFile(FirstLoc, SecondLoc))
1719 if (
SM.isBeforeInTranslationUnit(SecondLoc, FirstLoc))
1720 std::swap(SecondLoc, FirstLoc);
1729 const size_t MAX_PUNY_EDGE_LENGTH = 2;
1730 if (*ByteWidth <= MAX_PUNY_EDGE_LENGTH) {
1742 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ++I) {
1743 const auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get());
1748 PathPieces::iterator NextI = I; ++NextI;
1752 const auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1758 if (PieceI->getString() == PieceNextI->getString()) {
1766 bool hasChanges =
false;
1772 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) {
1774 if (
auto *CallI = dyn_cast<PathDiagnosticCallPiece>(I->get())) {
1777 if (!OCS.count(CallI)) {
1787 auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1794 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1795 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1799 PathPieces::iterator NextI = I; ++NextI;
1803 const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1810 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1811 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1829 if (level1 && level1 == level2 && level1 == level3 && level1 == level4) {
1830 PieceI->setEndLocation(PieceNextI->getEndLocation());
1843 if (s1End && s1End == s2Start && level2) {
1844 bool removeEdge =
false;
1870 else if (s1Start && s2End &&
1883 else if (s1Start && s2End &&
1885 SourceRange EdgeRange(PieceI->getEndLocation().asLocation(),
1886 PieceI->getStartLocation().asLocation());
1893 PieceI->setEndLocation(PieceNextI->getEndLocation());
1907 if (s1End == s2Start) {
1908 const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(level3);
1909 if (FS && FS->getCollection()->IgnoreParens() == s2Start &&
1910 s2End == FS->getElement()) {
1911 PieceI->setEndLocation(PieceNextI->getEndLocation());
1948 const auto *FirstEdge =
1949 dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get());
1953 const Decl *D =
C.getLocationContextFor(&Path)->getDecl();
1956 if (FirstEdge->getStartLocation() != EntryLoc)
1968 for (
const auto &
P : path) {
1971 unsigned LineNo =
Loc.getLineNumber();
1973 ExecutedLines[FID].insert(LineNo);
1977PathDiagnosticConstruct::PathDiagnosticConstruct(
1980 : Consumer(PDC), CurrentNode(ErrorNode),
1981 SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()),
1986PathDiagnosticBuilder::PathDiagnosticBuilder(
1989 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics)
1991 ErrorNode(ErrorNode),
1992 VisitorsDiagnostics(
std::move(VisitorsDiagnostics)) {}
1994std::unique_ptr<PathDiagnostic>
1996 PathDiagnosticConstruct Construct(PDC, ErrorNode, R);
2005 auto EndNotes = VisitorsDiagnostics->find(ErrorNode);
2007 if (EndNotes != VisitorsDiagnostics->end()) {
2008 assert(!EndNotes->second.empty());
2009 LastPiece = EndNotes->second[0];
2014 Construct.PD->setEndOfPath(LastPiece);
2019 while (Construct.ascendToPrevNode()) {
2020 generatePathDiagnosticsForNode(Construct, PrevLoc);
2022 auto VisitorNotes = VisitorsDiagnostics->find(Construct.getCurrentNode());
2023 if (VisitorNotes == VisitorsDiagnostics->end())
2028 std::set<llvm::FoldingSetNodeID> DeduplicationSet;
2032 llvm::FoldingSetNodeID
ID;
2034 if (!DeduplicationSet.insert(ID).second)
2039 updateStackPiecesWithMessage(
Note, Construct.CallStack);
2040 Construct.getActivePath().push_front(
Note);
2048 Construct.getLocationContextForActivePath()->
getStackFrame();
2056 if (!Construct.PD->path.empty()) {
2058 bool stillHasNotes =
2060 assert(stillHasNotes);
2061 (void)stillHasNotes;
2065 if (!Opts.ShouldAddPopUpNotes)
2078 while (
optimizeEdges(Construct, Construct.getMutablePieces(), OCS)) {
2093 if (Opts.ShouldDisplayMacroExpansions)
2096 return std::move(Construct.PD);
2103void BugType::anchor() {}
2109LLVM_ATTRIBUTE_USED
static bool
2111 for (
const std::pair<StringRef, StringRef> &Pair : Registry.
Dependencies) {
2112 if (Pair.second == CheckerName)
2119 StringRef CheckerName) {
2121 if (
Checker.FullName == CheckerName)
2125 "Checker name not found in CheckerRegistry -- did you retrieve it "
2126 "correctly from CheckerManager::getCurrentCheckerName?");
2130 const BugType &bt, StringRef shortDesc, StringRef desc,
2132 const Decl *DeclToUnique)
2133 :
BugReport(
Kind::PathSensitive, bt, shortDesc, desc), ErrorNode(errorNode),
2134 ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() :
SourceRange()),
2135 UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) {
2137 ->getAnalysisManager()
2138 .getCheckerManager()
2139 ->getCheckerRegistryData(),
2141 "Some checkers depend on this one! We don't allow dependency "
2142 "checkers to emit warnings, because checkers should depend on "
2143 "*modeling*, not *diagnostics*.");
2147 ->getAnalysisManager()
2148 .getCheckerManager()
2149 ->getCheckerRegistryData(),
2151 "Hidden checkers musn't emit diagnostics as they are by definition "
2152 "non-user facing!");
2156 std::unique_ptr<BugReporterVisitor> visitor) {
2160 llvm::FoldingSetNodeID ID;
2161 visitor->Profile(ID);
2163 void *InsertPos =
nullptr;
2168 Callbacks.push_back(std::move(visitor));
2185 hash.AddInteger(
static_cast<int>(
getKind()));
2186 hash.AddPointer(&
BT);
2192 if (!range.isValid())
2194 hash.Add(range.getBegin());
2195 hash.Add(range.getEnd());
2200 hash.AddInteger(
static_cast<int>(
getKind()));
2201 hash.AddPointer(&
BT);
2215 if (!range.isValid())
2217 hash.Add(range.getBegin());
2218 hash.Add(range.getEnd());
2224 llvm::DenseMap<T, bugreporter::TrackingKind> &InterestingnessMap, T Val,
2226 auto Result = InterestingnessMap.insert({Val, TKind});
2246 "BugReport::markInteresting currently can only handle 2 different "
2247 "tracking kinds! Please define what tracking kind should this entitiy"
2248 "have, if it was already marked as interesting with a different kind!");
2260 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2272 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2284 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2295 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2311std::optional<bugreporter::TrackingKind>
2330 "BugReport::getInterestingnessKind currently can only handle 2 different "
2331 "tracking kinds! Please define what tracking kind should we return here "
2332 "when the kind of getAsRegion() and getAsSymbol() is different!");
2333 return std::nullopt;
2336std::optional<bugreporter::TrackingKind>
2339 return std::nullopt;
2344 return std::nullopt;
2345 return It->getSecond();
2348std::optional<bugreporter::TrackingKind>
2351 return std::nullopt;
2356 return It->getSecond();
2358 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2360 return std::nullopt;
2386 const Stmt *S =
nullptr;
2390 if (BE->getBlock() == &Exit)
2411 assert(
ErrorNode &&
"Cannot create a location with a null node.");
2433 if (
const auto *AS = dyn_cast<AttributedStmt>(S))
2434 S = AS->getSubStmt();
2437 if (
const auto *ME = dyn_cast<MemberExpr>(S))
2441 if (
const auto *B = dyn_cast<BinaryOperator>(S))
2447 if (S->getBeginLoc().isValid())
2471 : D(D), UserSuppressions(D.getASTContext()) {}
2475 assert(StrBugTypes.empty() &&
2476 "Destroying BugReporter before diagnostics are emitted!");
2479 for (
const auto I : EQClassesVector)
2486 for (
const auto EQ : EQClassesVector)
2493 StrBugTypes.clear();
2506 std::unique_ptr<ExplodedGraph> BugPath;
2513class BugPathGetter {
2514 std::unique_ptr<ExplodedGraph> TrimmedGraph;
2516 using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>;
2519 PriorityMapTy PriorityMap;
2523 using ReportNewNodePair =
2524 std::pair<PathSensitiveBugReport *, const ExplodedNode *>;
2527 BugPathInfo CurrentBugPath;
2530 template <
bool Descending>
2531 class PriorityCompare {
2532 const PriorityMapTy &PriorityMap;
2535 PriorityCompare(
const PriorityMapTy &M) : PriorityMap(M) {}
2538 PriorityMapTy::const_iterator LI = PriorityMap.find(LHS);
2539 PriorityMapTy::const_iterator RI = PriorityMap.find(RHS);
2540 PriorityMapTy::const_iterator E = PriorityMap.end();
2547 return Descending ? LI->second > RI->second
2548 : LI->second < RI->second;
2551 bool operator()(
const ReportNewNodePair &LHS,
2552 const ReportNewNodePair &RHS)
const {
2553 return (*
this)(LHS.second, RHS.second);
2561 BugPathInfo *getNextBugPath();
2566BugPathGetter::BugPathGetter(
const ExplodedGraph *OriginalGraph,
2569 for (
const auto I : bugReports) {
2570 assert(I->isValid() &&
2571 "We only allow BugReporterVisitors and BugReporter itself to "
2572 "invalidate reports!");
2573 Nodes.emplace_back(I->getErrorNode());
2579 TrimmedGraph = OriginalGraph->
trim(
Nodes, &ForwardMap);
2587 const ExplodedNode *NewNode = ForwardMap.lookup(Report->getErrorNode());
2589 "Failed to construct a trimmed graph that contains this error "
2591 ReportNodes.emplace_back(Report, NewNode);
2592 RemainingNodes.insert(NewNode);
2595 assert(!RemainingNodes.empty() &&
"No error node found in the trimmed graph");
2598 std::queue<const ExplodedNode *> WS;
2600 assert(TrimmedGraph->num_roots() == 1);
2601 WS.push(*TrimmedGraph->roots_begin());
2604 while (!WS.empty()) {
2608 PriorityMapTy::iterator PriorityEntry;
2610 std::tie(PriorityEntry, IsNew) = PriorityMap.insert({
Node,
Priority});
2614 assert(PriorityEntry->second <= Priority);
2618 if (RemainingNodes.erase(
Node))
2619 if (RemainingNodes.empty())
2627 llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap));
2630BugPathInfo *BugPathGetter::getNextBugPath() {
2631 if (ReportNodes.empty())
2635 std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val();
2636 assert(PriorityMap.contains(OrigN) &&
"error node not accessible from root");
2640 auto GNew = std::make_unique<ExplodedGraph>();
2656 CurrentBugPath.ErrorNode = NewN;
2662 GNew->addRoot(NewN);
2669 PriorityCompare<false>(PriorityMap));
2672 CurrentBugPath.BugPath = std::move(GNew);
2674 return &CurrentBugPath;
2681 using MacroStackTy = std::vector<
2682 std::pair<std::shared_ptr<PathDiagnosticMacroPiece>,
SourceLocation>>;
2684 using PiecesTy = std::vector<PathDiagnosticPieceRef>;
2686 MacroStackTy MacroStack;
2689 for (PathPieces::const_iterator I = path.begin(), E = path.end();
2691 const auto &piece = *I;
2694 if (
auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) {
2704 SM.getExpansionLoc(
Loc) :
2707 if (
Loc.isFileID()) {
2709 Pieces.push_back(piece);
2713 assert(
Loc.isMacroID());
2716 if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
2717 MacroStack.back().first->subPieces.push_back(piece);
2723 std::shared_ptr<PathDiagnosticMacroPiece> MacroGroup;
2726 SM.getExpansionLoc(
Loc) :
2730 while (!MacroStack.empty()) {
2731 if (InstantiationLoc == MacroStack.back().second) {
2732 MacroGroup = MacroStack.back().first;
2736 if (ParentInstantiationLoc == MacroStack.back().second) {
2737 MacroGroup = MacroStack.back().first;
2741 MacroStack.pop_back();
2744 if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
2746 auto NewGroup = std::make_shared<PathDiagnosticMacroPiece>(
2750 MacroGroup->subPieces.push_back(NewGroup);
2752 assert(InstantiationLoc.
isFileID());
2753 Pieces.push_back(NewGroup);
2756 MacroGroup = NewGroup;
2757 MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
2761 MacroGroup->subPieces.push_back(piece);
2767 path.insert(path.end(), Pieces.begin(), Pieces.end());
2773static std::unique_ptr<VisitorsDiagnosticsTy>
2777 std::unique_ptr<VisitorsDiagnosticsTy> Notes =
2778 std::make_unique<VisitorsDiagnosticsTy>();
2791 for (std::unique_ptr<BugReporterVisitor> &Visitor : R->
visitors())
2792 visitors.push_back(std::move(Visitor));
2799 for (
auto &
V : visitors) {
2800 V->finalizeVisitor(BRC, ErrorNode, *R);
2802 if (
auto Piece =
V->getEndPath(BRC, ErrorNode, *R)) {
2803 assert(!LastPiece &&
2804 "There can only be one final piece in a diagnostic.");
2806 "The final piece must contain a message!");
2807 LastPiece = std::move(Piece);
2808 (*Notes)[ErrorNode].push_back(LastPiece);
2814 for (
auto &
V : visitors) {
2815 auto P =
V->VisitNode(NextNode, BRC, *R);
2817 (*Notes)[NextNode].push_back(std::move(
P));
2829std::optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
2833 BugPathGetter BugGraph(&Reporter.
getGraph(), bugReports);
2835 while (BugPathInfo *BugPath = BugGraph.getNextBugPath()) {
2838 assert(R &&
"No original report found for sliced graph.");
2839 assert(R->
isValid() &&
"Report selected by trimmed graph marked invalid.");
2854 std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes =
2871 return PathDiagnosticBuilder(
2872 std::move(BRC), std::move(BugPath->BugPath), BugPath->Report,
2873 BugPath->ErrorNode, std::move(visitorNotes));
2880std::unique_ptr<DiagnosticForConsumerMapTy>
2884 assert(!bugReports.empty());
2886 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
2888 std::optional<PathDiagnosticBuilder> PDB =
2889 PathDiagnosticBuilder::findValidReport(bugReports, *
this);
2893 if (std::unique_ptr<PathDiagnostic> PD = PDB->generate(PC)) {
2894 (*Out)[PC] = std::move(PD);
2903 bool ValidSourceLoc = R->getLocation().isValid();
2904 assert(ValidSourceLoc);
2907 if (!ValidSourceLoc)
2915 llvm::FoldingSetNodeID ID;
2924 EQClasses.InsertNode(EQ, InsertPos);
2925 EQClassesVector.push_back(EQ);
2927 EQ->AddReport(std::move(R));
2931 if (
auto PR = dyn_cast<PathSensitiveBugReport>(R.get()))
2935 assert((E->isSink() || E->getLocation().getTag()) &&
2936 "Error node must either be a sink or have a tag");
2939 E->getLocationContext()->getAnalysisDeclContext();
2958struct FRIEC_WLItem {
2963 : N(n), I(N->succ_begin()), E(N->succ_end()) {}
2968BugReport *PathSensitiveBugReporter::findReportInEquivalenceClass(
2973 assert(
EQ.getReports().size() > 0);
2974 const BugType& BT =
EQ.getReports()[0]->getBugType();
2977 for (
auto &J :
EQ.getReports()) {
2978 if (
auto *PR = dyn_cast<PathSensitiveBugReport>(J.get())) {
2980 bugReports.push_back(PR);
2994 for (
const auto &I:
EQ.getReports()) {
2995 auto *R = dyn_cast<PathSensitiveBugReport>(I.get());
3000 if (errorNode->
isSink()) {
3002 "BugType::isSuppressSink() should not be 'true' for sink end nodes");
3006 bugReports.push_back(R);
3017 if (ErrorB->isInevitablySinking())
3022 using WLItem = FRIEC_WLItem;
3025 llvm::DenseMap<const ExplodedNode *, unsigned>
Visited;
3028 WL.push_back(errorNode);
3031 while (!WL.empty()) {
3032 WLItem &WI = WL.back();
3033 assert(!WI.N->succ_empty());
3035 for (; WI.I != WI.E; ++WI.I) {
3041 bugReports.push_back(R);
3052 unsigned &mark =
Visited[Succ];
3062 if (!WL.empty() && &WL.back() == &WI)
3069 return exampleReport;
3074 BugReport *report = findReportInEquivalenceClass(EQ, bugReports);
3079 for (
const std::string &CheckerOrPackage :
3086 std::unique_ptr<DiagnosticForConsumerMapTy> Diagnostics =
3089 for (
auto &
P : *Diagnostics) {
3091 std::unique_ptr<PathDiagnostic> &PD =
P.second;
3095 if (PD->path.empty()) {
3097 auto piece = std::make_unique<PathDiagnosticEventPiece>(
3100 piece->addRange(
Range);
3101 PD->setEndOfPath(std::move(piece));
3108 for (
const auto &I : llvm::reverse(report->
getNotes())) {
3110 auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
3113 ConvertedPiece->addRange(R);
3115 Pieces.push_front(std::move(ConvertedPiece));
3118 for (
const auto &I : llvm::reverse(report->
getNotes()))
3119 Pieces.push_front(I);
3122 for (
const auto &I : report->
getFixits())
3123 Pieces.back()->addFixit(I);
3137 if (
const auto FD = dyn_cast<FunctionDecl>(Signature)) {
3138 SignatureSourceRange = FD->getSourceRange();
3139 }
else if (
const auto OD = dyn_cast<ObjCMethodDecl>(Signature)) {
3140 SignatureSourceRange = OD->getSourceRange();
3146 : SignatureSourceRange.
getEnd();
3147 if (!Start.
isValid() || !End.isValid())
3149 unsigned StartLine =
SM.getExpansionLineNumber(Start);
3150 unsigned EndLine =
SM.getExpansionLineNumber(End);
3152 FileID FID =
SM.getFileID(
SM.getExpansionLoc(Start));
3153 for (
unsigned Line = StartLine;
Line <= EndLine;
Line++)
3154 ExecutedLines[FID].insert(
Line);
3164 FileID FID =
SM.getFileID(ExpansionLoc);
3165 unsigned LineNo =
SM.getExpansionLineNumber(ExpansionLoc);
3166 ExecutedLines[FID].insert(LineNo);
3171static std::unique_ptr<FilesToLineNumsMap>
3173 auto ExecutedLines = std::make_unique<FilesToLineNumsMap>();
3182 const Decl* D = CE->getCalleeContext()->getDecl();
3193 if (
const auto *RS = dyn_cast_or_null<ReturnStmt>(
P)) {
3198 if (isa_and_nonnull<SwitchCase, LabelStmt>(
P))
3204 return ExecutedLines;
3207std::unique_ptr<DiagnosticForConsumerMapTy>
3211 auto *basicReport = cast<BasicBugReport>(exampleReport);
3212 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
3213 for (
auto *Consumer : consumers)
3228 "The call piece should not be in a header file.");
3240 if (
auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get()))
3248 if (PD.
path.empty())
3257 if (
auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
3264 const auto *ND = dyn_cast<NamedDecl>(CP->
getCallee());
3267 llvm::raw_svector_ostream os(buf);
3268 os <<
" (within a call to '" << ND->getDeclName() <<
"')";
3283std::unique_ptr<DiagnosticForConsumerMapTy>
3284PathSensitiveBugReporter::generateDiagnosticForConsumerMap(
3287 std::vector<BasicBugReport *> BasicBugReports;
3288 std::vector<PathSensitiveBugReport *> PathSensitiveBugReports;
3289 if (isa<BasicBugReport>(exampleReport))
3291 consumers, bugReports);
3297 assert(!bugReports.empty());
3298 MaxBugClassSize.updateMax(bugReports.size());
3305 consumers, convertedArrayOfReports);
3310 MaxValidBugClassSize.updateMax(bugReports.size());
3315 for (
auto const &
P : *Out)
3316 if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.
AnalyzeAll)
3329 Loc, Ranges, Fixits);
3334 StringRef name, StringRef category,
3339 BugType *BT = getBugTypeForName(CheckName, name, category);
3340 auto R = std::make_unique<BasicBugReport>(*BT, str,
Loc);
3341 R->setDeclWithIssue(DeclWithIssue);
3342 for (
const auto &SR : Ranges)
3344 for (
const auto &FH : Fixits)
3350 StringRef name, StringRef category) {
3352 llvm::raw_svector_ostream(fullDesc) << CheckName.
getName() <<
":" << name
3354 std::unique_ptr<BugType> &BT = StrBugTypes[fullDesc];
3356 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 std::unique_ptr< PathDiagnostic > generateEmptyDiagnosticForReport(const PathSensitiveBugReport *R, const SourceManager &SM)
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 > generateDiagnosticForBasicReport(const BasicBugReport *R)
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 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.
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...
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)
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.
YAML serialization mapping.
llvm::SmallVector< std::pair< StringRef, StringRef >, 0 > Dependencies