45#include "llvm/ADT/ArrayRef.h"
46#include "llvm/ADT/DenseMap.h"
47#include "llvm/ADT/DenseSet.h"
48#include "llvm/ADT/FoldingSet.h"
49#include "llvm/ADT/STLExtras.h"
50#include "llvm/ADT/SmallPtrSet.h"
51#include "llvm/ADT/SmallString.h"
52#include "llvm/ADT/SmallVector.h"
53#include "llvm/ADT/Statistic.h"
54#include "llvm/ADT/StringExtras.h"
55#include "llvm/ADT/StringRef.h"
56#include "llvm/ADT/iterator_range.h"
57#include "llvm/Support/Casting.h"
58#include "llvm/Support/Compiler.h"
59#include "llvm/Support/ErrorHandling.h"
60#include "llvm/Support/MemoryBuffer.h"
61#include "llvm/Support/raw_ostream.h"
78#define DEBUG_TYPE "BugReporter"
81 "The maximum number of bug reports in the same equivalence class");
83 "The maximum number of bug reports in the same equivalence class "
84 "where at least one report is valid (not suppressed)");
88void BugReporterContext::anchor() {}
98 std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>;
102using VisitorsDiagnosticsTy =
103 llvm::DenseMap<const ExplodedNode *, std::vector<PathDiagnosticPieceRef>>;
107using LocationContextMap =
108 llvm::DenseMap<const PathPieces *, const LocationContext *>;
113class PathDiagnosticConstruct {
122 LocationContextMap LCM;
129 CallWithEntryStack CallStack;
133 std::unique_ptr<PathDiagnostic> PD;
143 assert(CurrentNode &&
"Already reached the root!");
151 return LCM.find(&PD->getActivePath())->getSecond();
154 const ExplodedNode *getCurrentNode()
const {
return CurrentNode; }
158 bool ascendToPrevNode() {
160 return static_cast<bool>(CurrentNode);
164 return getCurrLocationContext()->getParentMap();
169 const Stmt *getParent(
const Stmt *S)
const {
170 return getParentMap().getParent(S);
179 assert(LCM.count(Path) &&
180 "Failed to find the context associated with these pieces!");
181 return LCM.find(Path)->getSecond();
184 bool isInLocCtxMap(
const PathPieces *Path)
const {
return LCM.count(Path); }
186 PathPieces &getActivePath() {
return PD->getActivePath(); }
187 PathPieces &getMutablePieces() {
return PD->getMutablePieces(); }
190 bool shouldAddControlNotes()
const {
193 bool shouldGenerateDiagnostics()
const {
196 bool supportsLogicalOpControlFlow()
const {
207 std::unique_ptr<const ExplodedGraph> BugPath;
218 std::unique_ptr<const VisitorsDiagnosticsTy> VisitorsDiagnostics;
224 static std::optional<PathDiagnosticBuilder>
228 PathDiagnosticBuilder(
231 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics);
243 std::unique_ptr<PathDiagnostic>
248 const CallWithEntryStack &CallStack)
const;
249 void generatePathDiagnosticsForNode(PathDiagnosticConstruct &
C,
252 void generateMinimalDiagForBlockEdge(PathDiagnosticConstruct &
C,
256 generateDiagForGotoOP(
const PathDiagnosticConstruct &
C,
const Stmt *S,
260 generateDiagForSwitchOP(
const PathDiagnosticConstruct &
C,
const CFGBlock *Dst,
264 generateDiagForBinaryOP(
const PathDiagnosticConstruct &
C,
const Stmt *T,
268 ExecutionContinues(
const PathDiagnosticConstruct &
C)
const;
271 ExecutionContinues(llvm::raw_string_ostream &os,
272 const PathDiagnosticConstruct &
C)
const;
294 const auto *CE = dyn_cast_or_null<CallExpr>(CallSite);
299 for (
auto [Idx, ArgExpr] : llvm::enumerate(CE->arguments())) {
311 if (ArgExpr->getType()->isVoidPointerType())
336 return (llvm::Twine(Msg) +
" via " + std::to_string(ArgIndex) +
337 llvm::getOrdinalSuffix(ArgIndex) +
" parameter").str();
357 if (
X->getTag() == tagPreferred && Y->
getTag() == tagLesser)
360 if (Y->
getTag() == tagPreferred &&
X->getTag() == tagLesser)
372 unsigned N = path.size();
379 for (
unsigned i = 0; i < N; ++i) {
380 auto piece = std::move(path.front());
383 switch (piece->getKind()) {
394 if (
auto *nextEvent =
395 dyn_cast<PathDiagnosticEventPiece>(path.front().get())) {
396 auto *
event = cast<PathDiagnosticEventPiece>(piece.get());
400 if (
auto *pieceToKeep =
402 piece = std::move(pieceToKeep == event ? piece : path.front());
414 path.push_back(std::move(piece));
424 bool IsInteresting =
false) {
425 bool containsSomethingInteresting = IsInteresting;
426 const unsigned N = pieces.size();
428 for (
unsigned i = 0 ; i < N ; ++i) {
431 auto piece = std::move(pieces.front());
434 switch (piece->getKind()) {
436 auto &call = cast<PathDiagnosticCallPiece>(*piece);
443 containsSomethingInteresting =
true;
447 auto ¯o = cast<PathDiagnosticMacroPiece>(*piece);
450 containsSomethingInteresting =
true;
454 auto &
event = cast<PathDiagnosticEventPiece>(*piece);
458 containsSomethingInteresting |= !
event.isPrunable();
467 pieces.push_back(std::move(piece));
470 return containsSomethingInteresting;
475 for (
unsigned int i = 0; i < Path.size(); ++i) {
476 auto Piece = std::move(Path.front());
478 if (!isa<PathDiagnosticPopUpPiece>(*Piece))
479 Path.push_back(std::move(Piece));
495 for (
const auto &I : Pieces) {
496 auto *
Call = dyn_cast<PathDiagnosticCallPiece>(I.get());
501 if (LastCallLocation) {
503 if (CallerIsImplicit || !
Call->callEnter.asLocation().isValid())
504 Call->callEnter = *LastCallLocation;
505 if (CallerIsImplicit || !
Call->callReturn.asLocation().isValid())
506 Call->callReturn = *LastCallLocation;
512 if (
Call->callEnterWithin.asLocation().isValid() &&
514 ThisCallLocation = &
Call->callEnterWithin;
516 ThisCallLocation = &
Call->callEnter;
518 assert(ThisCallLocation &&
"Outermost call has an invalid location");
527 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
528 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
531 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
534 if (
auto *
CF = dyn_cast<PathDiagnosticControlFlowPiece>(I->get())) {
535 const Stmt *Start =
CF->getStartLocation().asStmt();
536 const Stmt *End =
CF->getEndLocation().asStmt();
537 if (isa_and_nonnull<CXXDefaultInitExpr>(Start)) {
540 }
else if (isa_and_nonnull<CXXDefaultInitExpr>(End)) {
541 PathPieces::iterator Next = std::next(I);
544 dyn_cast<PathDiagnosticControlFlowPiece>(Next->get())) {
545 NextCF->setStartLocation(
CF->getStartLocation());
561 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
562 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
565 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
568 if (!(*I)->getLocation().isValid() ||
569 !(*I)->getLocation().asLocation().isValid()) {
578 const PathDiagnosticConstruct &
C)
const {
579 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
581 C.getCurrLocationContext());
588 llvm::raw_string_ostream &os,
const PathDiagnosticConstruct &
C)
const {
590 if (os.str().empty())
596 os <<
"Execution continues on line "
597 << getSourceManager().getExpansionLineNumber(
Loc.asLocation())
600 os <<
"Execution jumps to the end of the ";
601 const Decl *D =
C.getCurrLocationContext()->getDecl();
602 if (isa<ObjCMethodDecl>(D))
604 else if (isa<FunctionDecl>(D))
607 assert(isa<BlockDecl>(D));
608 os <<
"anonymous block";
624 switch (
Parent->getStmtClass()) {
625 case Stmt::ForStmtClass:
626 case Stmt::DoStmtClass:
627 case Stmt::WhileStmtClass:
628 case Stmt::ObjCForCollectionStmtClass:
629 case Stmt::CXXForRangeStmtClass:
640 bool allowNestedContexts =
false) {
647 switch (
Parent->getStmtClass()) {
648 case Stmt::BinaryOperatorClass: {
649 const auto *B = cast<BinaryOperator>(
Parent);
650 if (B->isLogicalOp())
654 case Stmt::CompoundStmtClass:
655 case Stmt::StmtExprClass:
657 case Stmt::ChooseExprClass:
660 if (allowNestedContexts || cast<ChooseExpr>(
Parent)->getCond() == S)
664 case Stmt::BinaryConditionalOperatorClass:
665 case Stmt::ConditionalOperatorClass:
668 if (allowNestedContexts ||
669 cast<AbstractConditionalOperator>(
Parent)->getCond() == S)
673 case Stmt::CXXForRangeStmtClass:
674 if (cast<CXXForRangeStmt>(
Parent)->getBody() == S)
677 case Stmt::DoStmtClass:
679 case Stmt::ForStmtClass:
680 if (cast<ForStmt>(
Parent)->getBody() == S)
683 case Stmt::IfStmtClass:
684 if (cast<IfStmt>(
Parent)->getCond() != S)
687 case Stmt::ObjCForCollectionStmtClass:
688 if (cast<ObjCForCollectionStmt>(
Parent)->getBody() == S)
691 case Stmt::WhileStmtClass:
692 if (cast<WhileStmt>(
Parent)->getCond() != S)
702 assert(S &&
"Cannot have null Stmt for PathDiagnosticLocation");
721void PathDiagnosticBuilder::updateStackPiecesWithMessage(
723 if (R->hasCallStackHint(
P))
724 for (
const auto &I : CallStack) {
727 std::string stackMsg = R->getCallStackMessage(
P, N);
741 const PathDiagnosticConstruct &
C,
const CFGBlock *Dst,
747 llvm::raw_string_ostream os(sbuf);
753 switch (S->getStmtClass()) {
755 os <<
"No cases match in the switch statement. "
756 "Control jumps to line "
757 << End.asLocation().getExpansionLineNumber();
759 case Stmt::DefaultStmtClass:
760 os <<
"Control jumps to the 'default' case at line "
761 << End.asLocation().getExpansionLineNumber();
764 case Stmt::CaseStmtClass: {
765 os <<
"Control jumps to 'case ";
766 const auto *Case = cast<CaseStmt>(S);
770 bool GetRawInt =
true;
772 if (
const auto *DR = dyn_cast<DeclRefExpr>(LHS)) {
775 const auto *D = dyn_cast<EnumConstantDecl>(DR->getDecl());
786 os <<
":' at line " << End.asLocation().getExpansionLineNumber();
791 os <<
"'Default' branch taken. ";
792 End = ExecutionContinues(os,
C);
794 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
799 const PathDiagnosticConstruct &
C,
const Stmt *S,
802 llvm::raw_string_ostream os(sbuf);
805 os <<
"Control jumps to line " << End.asLocation().getExpansionLineNumber();
806 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str());
810 const PathDiagnosticConstruct &
C,
const Stmt *T,
const CFGBlock *Src,
815 const auto *B = cast<BinaryOperator>(T);
817 llvm::raw_string_ostream os(sbuf);
818 os <<
"Left side of '";
821 if (B->getOpcode() == BO_LAnd) {
834 End = ExecutionContinues(
C);
837 assert(B->getOpcode() == BO_LOr);
845 End = ExecutionContinues(
C);
853 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
857void PathDiagnosticBuilder::generateMinimalDiagForBlockEdge(
858 PathDiagnosticConstruct &
C,
BlockEdge BE)
const {
872 case Stmt::GotoStmtClass:
873 case Stmt::IndirectGotoStmtClass: {
874 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
875 C.getActivePath().push_front(generateDiagForGotoOP(
C, S, Start));
879 case Stmt::SwitchStmtClass: {
880 C.getActivePath().push_front(generateDiagForSwitchOP(
C, Dst, Start));
884 case Stmt::BreakStmtClass:
885 case Stmt::ContinueStmtClass: {
887 llvm::raw_string_ostream os(sbuf);
889 C.getActivePath().push_front(
890 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
895 case Stmt::BinaryConditionalOperatorClass:
896 case Stmt::ConditionalOperatorClass: {
898 llvm::raw_string_ostream os(sbuf);
899 os <<
"'?' condition is ";
908 if (
const Stmt *S = End.asStmt())
911 C.getActivePath().push_front(
912 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
917 case Stmt::BinaryOperatorClass: {
918 if (!
C.supportsLogicalOpControlFlow())
921 C.getActivePath().push_front(generateDiagForBinaryOP(
C, T, Src, Dst));
925 case Stmt::DoStmtClass:
928 llvm::raw_string_ostream os(sbuf);
930 os <<
"Loop condition is true. ";
933 if (
const Stmt *S = End.asStmt())
936 C.getActivePath().push_front(
937 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
942 if (
const Stmt *S = End.asStmt())
945 C.getActivePath().push_front(
946 std::make_shared<PathDiagnosticControlFlowPiece>(
947 Start, End,
"Loop condition is false. Exiting loop"));
951 case Stmt::WhileStmtClass:
952 case Stmt::ForStmtClass:
955 llvm::raw_string_ostream os(sbuf);
957 os <<
"Loop condition is false. ";
959 if (
const Stmt *S = End.asStmt())
962 C.getActivePath().push_front(
963 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
967 if (
const Stmt *S = End.asStmt())
970 C.getActivePath().push_front(
971 std::make_shared<PathDiagnosticControlFlowPiece>(
972 Start, End,
"Loop condition is true. Entering loop body"));
977 case Stmt::IfStmtClass: {
980 if (
const Stmt *S = End.asStmt())
984 C.getActivePath().push_front(
985 std::make_shared<PathDiagnosticControlFlowPiece>(
986 Start, End,
"Taking false branch"));
988 C.getActivePath().push_front(
989 std::make_shared<PathDiagnosticControlFlowPiece>(
990 Start, End,
"Taking true branch"));
1003 case Stmt::ForStmtClass:
1004 case Stmt::WhileStmtClass:
1005 case Stmt::ObjCForCollectionStmtClass:
1006 case Stmt::CXXForRangeStmtClass:
1035 const Stmt *S = SP->getStmt();
1045 const Stmt *LoopBody =
nullptr;
1047 case Stmt::CXXForRangeStmtClass: {
1048 const auto *FR = cast<CXXForRangeStmt>(Term);
1053 LoopBody = FR->getBody();
1056 case Stmt::ForStmtClass: {
1057 const auto *FS = cast<ForStmt>(Term);
1060 LoopBody = FS->getBody();
1063 case Stmt::ObjCForCollectionStmtClass: {
1064 const auto *FC = cast<ObjCForCollectionStmt>(Term);
1065 LoopBody = FC->getBody();
1068 case Stmt::WhileStmtClass:
1069 LoopBody = cast<WhileStmt>(Term)->getBody();
1099 std::make_shared<PathDiagnosticControlFlowPiece>(NewLoc, PrevLoc));
1107 if (
const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(S))
1108 return FS->getElement();
1115 "Loop body skipped when range is empty";
1117 "Loop body skipped when collection is empty";
1119static std::unique_ptr<FilesToLineNumsMap>
1122void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
1133 if (
C.shouldAddPathEdges()) {
1149 bool VisitedEntireCall =
C.PD->isWithinCall();
1150 C.PD->popActivePath();
1153 if (VisitedEntireCall) {
1154 Call = cast<PathDiagnosticCallPiece>(
C.getActivePath().front().get());
1158 const Decl *Caller = CE->getLocationContext()->getDecl();
1160 assert(
C.getActivePath().size() == 1 &&
1161 C.getActivePath().front().get() ==
Call);
1165 assert(
C.isInLocCtxMap(&
C.getActivePath()) &&
1166 "When we ascend to a previously unvisited call, the active path's "
1167 "address shouldn't change, but rather should be compacted into "
1168 "a single CallEvent!");
1169 C.updateLocCtxMap(&
C.getActivePath(),
C.getCurrLocationContext());
1172 assert(!
C.isInLocCtxMap(&
Call->path) &&
1173 "When we ascend to a previously unvisited call, this must be the "
1174 "first time we encounter the caller context!");
1175 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1177 Call->setCallee(*CE,
SM);
1180 PrevLoc =
Call->getLocation();
1182 if (!
C.CallStack.empty()) {
1183 assert(
C.CallStack.back().first ==
Call);
1184 C.CallStack.pop_back();
1189 assert(
C.getCurrLocationContext() ==
C.getLocationContextForActivePath() &&
1190 "The current position in the bug path is out of sync with the "
1191 "location context associated with the active path!");
1194 if (std::optional<CallExitEnd> CE =
P.getAs<
CallExitEnd>()) {
1200 assert(!
C.isInLocCtxMap(&
Call->path) &&
1201 "We just entered a call, this must've been the first time we "
1202 "encounter its context!");
1203 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1205 if (
C.shouldAddPathEdges()) {
1211 auto *
P =
Call.get();
1212 C.getActivePath().push_front(std::move(
Call));
1215 C.PD->pushActivePath(&
P->path);
1216 C.CallStack.push_back(CallWithEntry(
P,
C.getCurrentNode()));
1221 if (!
C.shouldAddPathEdges())
1227 if (!isa<ObjCForCollectionStmt>(PS->getStmt())) {
1235 if (
C.shouldAddControlNotes()) {
1236 generateMinimalDiagForBlockEdge(
C, *BE);
1239 if (!
C.shouldAddPathEdges()) {
1246 const Stmt *Body =
nullptr;
1248 if (
const auto *FS = dyn_cast<ForStmt>(
Loop))
1249 Body = FS->getBody();
1250 else if (
const auto *WS = dyn_cast<WhileStmt>(
Loop))
1251 Body = WS->getBody();
1252 else if (
const auto *OFS = dyn_cast<ObjCForCollectionStmt>(
Loop)) {
1253 Body = OFS->getBody();
1254 }
else if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(
Loop)) {
1255 Body = FRS->getBody();
1259 auto p = std::make_shared<PathDiagnosticEventPiece>(
1260 L,
"Looping back to the head of the loop");
1261 p->setPrunable(
true);
1265 if (!
C.shouldAddControlNotes()) {
1266 C.getActivePath().push_front(std::move(p));
1269 if (
const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
1289 if (!IsInLoopBody) {
1290 if (isa<ObjCForCollectionStmt>(Term)) {
1292 }
else if (isa<CXXForRangeStmt>(Term)) {
1304 C.getCurrLocationContext());
1305 auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
1306 PE->setPrunable(
true);
1310 if (!
C.shouldAddControlNotes()) {
1311 C.getActivePath().push_front(std::move(PE));
1314 }
else if (isa<BreakStmt, ContinueStmt, GotoStmt>(Term)) {
1322static std::unique_ptr<PathDiagnostic>
1325 return std::make_unique<PathDiagnostic>(
1329 std::make_unique<FilesToLineNumsMap>());
1332static std::unique_ptr<PathDiagnostic>
1336 return std::make_unique<PathDiagnostic>(
1353 if (isa<FullExpr, CXXBindTemporaryExpr, SubstNonTypeTemplateParmExpr>(S))
1363 switch (S->getStmtClass()) {
1364 case Stmt::BinaryOperatorClass: {
1365 const auto *BO = cast<BinaryOperator>(S);
1366 if (!BO->isLogicalOp())
1368 return BO->getLHS() == Cond || BO->getRHS() == Cond;
1370 case Stmt::IfStmtClass:
1371 return cast<IfStmt>(S)->getCond() == Cond;
1372 case Stmt::ForStmtClass:
1373 return cast<ForStmt>(S)->getCond() == Cond;
1374 case Stmt::WhileStmtClass:
1375 return cast<WhileStmt>(S)->getCond() == Cond;
1376 case Stmt::DoStmtClass:
1377 return cast<DoStmt>(S)->getCond() == Cond;
1378 case Stmt::ChooseExprClass:
1379 return cast<ChooseExpr>(S)->getCond() == Cond;
1380 case Stmt::IndirectGotoStmtClass:
1381 return cast<IndirectGotoStmt>(S)->getTarget() == Cond;
1382 case Stmt::SwitchStmtClass:
1383 return cast<SwitchStmt>(S)->getCond() == Cond;
1384 case Stmt::BinaryConditionalOperatorClass:
1385 return cast<BinaryConditionalOperator>(S)->getCond() == Cond;
1386 case Stmt::ConditionalOperatorClass: {
1387 const auto *CO = cast<ConditionalOperator>(S);
1388 return CO->getCond() == Cond ||
1389 CO->getLHS() == Cond ||
1390 CO->getRHS() == Cond;
1392 case Stmt::ObjCForCollectionStmtClass:
1393 return cast<ObjCForCollectionStmt>(S)->getElement() == Cond;
1394 case Stmt::CXXForRangeStmtClass: {
1395 const auto *FRS = cast<CXXForRangeStmt>(S);
1396 return FRS->getCond() == Cond || FRS->getRangeInit() == Cond;
1404 if (
const auto *FS = dyn_cast<ForStmt>(FL))
1405 return FS->getInc() == S || FS->getInit() == S;
1406 if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(FL))
1407 return FRS->getInc() == S || FRS->getRangeStmt() == S ||
1408 FRS->getLoopVarStmt() || FRS->getRangeInit() == S;
1421 PathPieces::iterator Prev = pieces.end();
1422 for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E;
1424 auto *Piece = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1433 const Stmt *InnerStmt =
nullptr;
1434 while (NextSrcContext.
isValid() && NextSrcContext.
asStmt() != InnerStmt) {
1435 SrcContexts.push_back(NextSrcContext);
1436 InnerStmt = NextSrcContext.
asStmt();
1445 const Stmt *Dst = Piece->getEndLocation().getStmtOrNull();
1455 if (llvm::is_contained(SrcContexts, DstContext))
1459 Piece->setStartLocation(DstContext);
1464 auto *PrevPiece = dyn_cast<PathDiagnosticControlFlowPiece>(Prev->get());
1467 if (
const Stmt *PrevSrc =
1468 PrevPiece->getStartLocation().getStmtOrNull()) {
1470 if (PrevSrcParent ==
1472 PrevPiece->setEndLocation(DstContext);
1483 std::make_shared<PathDiagnosticControlFlowPiece>(SrcLoc, DstContext);
1485 I = pieces.insert(I, std::move(
P));
1501 for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) {
1502 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1507 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1508 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1510 if (!s1Start || !s1End)
1513 PathPieces::iterator NextI = I; ++NextI;
1523 const auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1525 StringRef S = EV->getString();
1534 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1544 if (!s2Start || !s2End || s1End != s2Start)
1560 I = pieces.erase(I);
1573 SM.getExpansionRange(
Range.getEnd()).getEnd());
1576 if (FID !=
SM.getFileID(ExpansionRange.
getEnd()))
1577 return std::nullopt;
1579 std::optional<MemoryBufferRef> Buffer =
SM.getBufferOrNone(FID);
1581 return std::nullopt;
1583 unsigned BeginOffset =
SM.getFileOffset(ExpansionRange.
getBegin());
1584 unsigned EndOffset =
SM.getFileOffset(ExpansionRange.
getEnd());
1585 StringRef Snippet = Buffer->getBuffer().slice(BeginOffset, EndOffset);
1591 if (Snippet.find_first_of(
"\r\n") != StringRef::npos)
1592 return std::nullopt;
1595 return Snippet.size();
1621 for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) {
1623 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1630 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1631 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1633 PathPieces::iterator NextI = I; ++NextI;
1637 const auto *PieceNextI =
1638 dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1641 if (isa<PathDiagnosticEventPiece>(NextI->get())) {
1645 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1654 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1655 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1657 if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
1658 const size_t MAX_SHORT_LINE_LENGTH = 80;
1660 if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) {
1662 if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) {
1664 I = Path.erase(NextI);
1687 bool erased =
false;
1689 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E;
1693 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1698 const Stmt *start = PieceI->getStartLocation().getStmtOrNull();
1699 const Stmt *end = PieceI->getEndLocation().getStmtOrNull();
1714 if (!
SM.isWrittenInSameFile(FirstLoc, SecondLoc))
1716 if (
SM.isBeforeInTranslationUnit(SecondLoc, FirstLoc))
1717 std::swap(SecondLoc, FirstLoc);
1726 const size_t MAX_PUNY_EDGE_LENGTH = 2;
1727 if (*ByteWidth <= MAX_PUNY_EDGE_LENGTH) {
1739 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ++I) {
1740 const auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get());
1745 PathPieces::iterator NextI = I; ++NextI;
1749 const auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1755 if (PieceI->getString() == PieceNextI->getString()) {
1763 bool hasChanges =
false;
1769 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) {
1771 if (
auto *CallI = dyn_cast<PathDiagnosticCallPiece>(I->get())) {
1774 if (!OCS.count(CallI)) {
1784 auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1791 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1792 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1796 PathPieces::iterator NextI = I; ++NextI;
1800 const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1807 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1808 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1826 if (level1 && level1 == level2 && level1 == level3 && level1 == level4) {
1827 PieceI->setEndLocation(PieceNextI->getEndLocation());
1840 if (s1End && s1End == s2Start && level2) {
1841 bool removeEdge =
false;
1867 else if (s1Start && s2End &&
1880 else if (s1Start && s2End &&
1882 SourceRange EdgeRange(PieceI->getEndLocation().asLocation(),
1883 PieceI->getStartLocation().asLocation());
1890 PieceI->setEndLocation(PieceNextI->getEndLocation());
1904 if (s1End == s2Start) {
1905 const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(level3);
1906 if (FS && FS->getCollection()->IgnoreParens() == s2Start &&
1907 s2End == FS->getElement()) {
1908 PieceI->setEndLocation(PieceNextI->getEndLocation());
1945 const auto *FirstEdge =
1946 dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get());
1950 const Decl *D =
C.getLocationContextFor(&Path)->getDecl();
1953 if (FirstEdge->getStartLocation() != EntryLoc)
1965 for (
const auto &
P : path) {
1968 unsigned LineNo =
Loc.getLineNumber();
1970 ExecutedLines[FID].insert(LineNo);
1974PathDiagnosticConstruct::PathDiagnosticConstruct(
1977 : Consumer(PDC), CurrentNode(ErrorNode),
1978 SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()),
1983PathDiagnosticBuilder::PathDiagnosticBuilder(
1986 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics)
1988 ErrorNode(ErrorNode),
1989 VisitorsDiagnostics(
std::move(VisitorsDiagnostics)) {}
1991std::unique_ptr<PathDiagnostic>
1993 PathDiagnosticConstruct Construct(PDC, ErrorNode, R);
2002 auto EndNotes = VisitorsDiagnostics->find(ErrorNode);
2004 if (EndNotes != VisitorsDiagnostics->end()) {
2005 assert(!EndNotes->second.empty());
2006 LastPiece = EndNotes->second[0];
2011 Construct.PD->setEndOfPath(LastPiece);
2016 while (Construct.ascendToPrevNode()) {
2017 generatePathDiagnosticsForNode(Construct, PrevLoc);
2019 auto VisitorNotes = VisitorsDiagnostics->find(Construct.getCurrentNode());
2020 if (VisitorNotes == VisitorsDiagnostics->end())
2025 std::set<llvm::FoldingSetNodeID> DeduplicationSet;
2029 llvm::FoldingSetNodeID
ID;
2031 if (!DeduplicationSet.insert(ID).second)
2036 updateStackPiecesWithMessage(
Note, Construct.CallStack);
2037 Construct.getActivePath().push_front(
Note);
2045 Construct.getLocationContextForActivePath()->
getStackFrame();
2053 if (!Construct.PD->path.empty()) {
2055 bool stillHasNotes =
2057 assert(stillHasNotes);
2058 (void)stillHasNotes;
2062 if (!Opts.ShouldAddPopUpNotes)
2075 while (
optimizeEdges(Construct, Construct.getMutablePieces(), OCS)) {
2090 if (Opts.ShouldDisplayMacroExpansions)
2093 return std::move(Construct.PD);
2100void BugType::anchor() {}
2106LLVM_ATTRIBUTE_USED
static bool
2108 for (
const std::pair<StringRef, StringRef> &Pair : Registry.
Dependencies) {
2109 if (Pair.second == CheckerName)
2116 StringRef CheckerName) {
2118 if (
Checker.FullName == CheckerName)
2122 "Checker name not found in CheckerRegistry -- did you retrieve it "
2123 "correctly from CheckerManager::getCurrentCheckerName?");
2127 const BugType &bt, StringRef shortDesc, StringRef desc,
2129 const Decl *DeclToUnique)
2130 :
BugReport(
Kind::PathSensitive, bt, shortDesc, desc), ErrorNode(errorNode),
2131 ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() :
SourceRange()),
2132 UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) {
2134 ->getAnalysisManager()
2135 .getCheckerManager()
2136 ->getCheckerRegistryData(),
2138 "Some checkers depend on this one! We don't allow dependency "
2139 "checkers to emit warnings, because checkers should depend on "
2140 "*modeling*, not *diagnostics*.");
2145 ->getAnalysisManager()
2146 .getCheckerManager()
2147 ->getCheckerRegistryData(),
2149 "Hidden checkers musn't emit diagnostics as they are by definition "
2150 "non-user facing!");
2154 std::unique_ptr<BugReporterVisitor> visitor) {
2158 llvm::FoldingSetNodeID ID;
2159 visitor->Profile(ID);
2161 void *InsertPos =
nullptr;
2166 Callbacks.push_back(std::move(visitor));
2183 hash.AddInteger(
static_cast<int>(
getKind()));
2184 hash.AddPointer(&
BT);
2190 if (!range.isValid())
2192 hash.Add(range.getBegin());
2193 hash.Add(range.getEnd());
2198 hash.AddInteger(
static_cast<int>(
getKind()));
2199 hash.AddPointer(&
BT);
2213 if (!range.isValid())
2215 hash.Add(range.getBegin());
2216 hash.Add(range.getEnd());
2222 llvm::DenseMap<T, bugreporter::TrackingKind> &InterestingnessMap, T Val,
2224 auto Result = InterestingnessMap.insert({Val, TKind});
2244 "BugReport::markInteresting currently can only handle 2 different "
2245 "tracking kinds! Please define what tracking kind should this entitiy"
2246 "have, if it was already marked as interesting with a different kind!");
2258 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2270 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2282 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2293 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2309std::optional<bugreporter::TrackingKind>
2328 "BugReport::getInterestingnessKind currently can only handle 2 different "
2329 "tracking kinds! Please define what tracking kind should we return here "
2330 "when the kind of getAsRegion() and getAsSymbol() is different!");
2331 return std::nullopt;
2334std::optional<bugreporter::TrackingKind>
2337 return std::nullopt;
2342 return std::nullopt;
2343 return It->getSecond();
2346std::optional<bugreporter::TrackingKind>
2349 return std::nullopt;
2354 return It->getSecond();
2356 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2358 return std::nullopt;
2384 const Stmt *S =
nullptr;
2388 if (BE->getBlock() == &Exit)
2409 assert(
ErrorNode &&
"Cannot create a location with a null node.");
2429 if (
const auto *ME = dyn_cast<MemberExpr>(S))
2433 if (
const auto *B = dyn_cast<BinaryOperator>(S))
2439 if (S->getBeginLoc().isValid())
2465 assert(StrBugTypes.empty() &&
2466 "Destroying BugReporter before diagnostics are emitted!");
2469 for (
const auto I : EQClassesVector)
2476 for (
const auto EQ : EQClassesVector)
2483 StrBugTypes.clear();
2496 std::unique_ptr<ExplodedGraph> BugPath;
2503class BugPathGetter {
2504 std::unique_ptr<ExplodedGraph> TrimmedGraph;
2506 using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>;
2509 PriorityMapTy PriorityMap;
2513 using ReportNewNodePair =
2514 std::pair<PathSensitiveBugReport *, const ExplodedNode *>;
2517 BugPathInfo CurrentBugPath;
2520 template <
bool Descending>
2521 class PriorityCompare {
2522 const PriorityMapTy &PriorityMap;
2525 PriorityCompare(
const PriorityMapTy &M) : PriorityMap(M) {}
2528 PriorityMapTy::const_iterator LI = PriorityMap.find(LHS);
2529 PriorityMapTy::const_iterator RI = PriorityMap.find(RHS);
2530 PriorityMapTy::const_iterator E = PriorityMap.end();
2537 return Descending ? LI->second > RI->second
2538 : LI->second < RI->second;
2541 bool operator()(
const ReportNewNodePair &LHS,
2542 const ReportNewNodePair &RHS)
const {
2543 return (*
this)(LHS.second, RHS.second);
2551 BugPathInfo *getNextBugPath();
2556BugPathGetter::BugPathGetter(
const ExplodedGraph *OriginalGraph,
2559 for (
const auto I : bugReports) {
2560 assert(I->isValid() &&
2561 "We only allow BugReporterVisitors and BugReporter itself to "
2562 "invalidate reports!");
2563 Nodes.emplace_back(I->getErrorNode());
2569 TrimmedGraph = OriginalGraph->
trim(
Nodes, &ForwardMap);
2577 const ExplodedNode *NewNode = ForwardMap.lookup(Report->getErrorNode());
2579 "Failed to construct a trimmed graph that contains this error "
2581 ReportNodes.emplace_back(Report, NewNode);
2582 RemainingNodes.insert(NewNode);
2585 assert(!RemainingNodes.empty() &&
"No error node found in the trimmed graph");
2588 std::queue<const ExplodedNode *> WS;
2590 assert(TrimmedGraph->num_roots() == 1);
2591 WS.push(*TrimmedGraph->roots_begin());
2594 while (!WS.empty()) {
2598 PriorityMapTy::iterator PriorityEntry;
2600 std::tie(PriorityEntry, IsNew) = PriorityMap.insert({
Node,
Priority});
2604 assert(PriorityEntry->second <= Priority);
2608 if (RemainingNodes.erase(
Node))
2609 if (RemainingNodes.empty())
2617 llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap));
2620BugPathInfo *BugPathGetter::getNextBugPath() {
2621 if (ReportNodes.empty())
2625 std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val();
2626 assert(PriorityMap.contains(OrigN) &&
"error node not accessible from root");
2630 auto GNew = std::make_unique<ExplodedGraph>();
2646 CurrentBugPath.ErrorNode = NewN;
2652 GNew->addRoot(NewN);
2659 PriorityCompare<false>(PriorityMap));
2662 CurrentBugPath.BugPath = std::move(GNew);
2664 return &CurrentBugPath;
2671 using MacroStackTy = std::vector<
2672 std::pair<std::shared_ptr<PathDiagnosticMacroPiece>,
SourceLocation>>;
2674 using PiecesTy = std::vector<PathDiagnosticPieceRef>;
2676 MacroStackTy MacroStack;
2679 for (PathPieces::const_iterator I = path.begin(), E = path.end();
2681 const auto &piece = *I;
2684 if (
auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) {
2694 SM.getExpansionLoc(
Loc) :
2697 if (
Loc.isFileID()) {
2699 Pieces.push_back(piece);
2703 assert(
Loc.isMacroID());
2706 if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
2707 MacroStack.back().first->subPieces.push_back(piece);
2713 std::shared_ptr<PathDiagnosticMacroPiece> MacroGroup;
2716 SM.getExpansionLoc(
Loc) :
2720 while (!MacroStack.empty()) {
2721 if (InstantiationLoc == MacroStack.back().second) {
2722 MacroGroup = MacroStack.back().first;
2726 if (ParentInstantiationLoc == MacroStack.back().second) {
2727 MacroGroup = MacroStack.back().first;
2731 MacroStack.pop_back();
2734 if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
2736 auto NewGroup = std::make_shared<PathDiagnosticMacroPiece>(
2740 MacroGroup->subPieces.push_back(NewGroup);
2742 assert(InstantiationLoc.
isFileID());
2743 Pieces.push_back(NewGroup);
2746 MacroGroup = NewGroup;
2747 MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
2751 MacroGroup->subPieces.push_back(piece);
2757 path.insert(path.end(), Pieces.begin(), Pieces.end());
2763static std::unique_ptr<VisitorsDiagnosticsTy>
2767 std::unique_ptr<VisitorsDiagnosticsTy> Notes =
2768 std::make_unique<VisitorsDiagnosticsTy>();
2781 for (std::unique_ptr<BugReporterVisitor> &Visitor : R->
visitors())
2782 visitors.push_back(std::move(Visitor));
2789 for (
auto &
V : visitors) {
2790 V->finalizeVisitor(BRC, ErrorNode, *R);
2792 if (
auto Piece =
V->getEndPath(BRC, ErrorNode, *R)) {
2793 assert(!LastPiece &&
2794 "There can only be one final piece in a diagnostic.");
2796 "The final piece must contain a message!");
2797 LastPiece = std::move(Piece);
2798 (*Notes)[ErrorNode].push_back(LastPiece);
2804 for (
auto &
V : visitors) {
2805 auto P =
V->VisitNode(NextNode, BRC, *R);
2807 (*Notes)[NextNode].push_back(std::move(
P));
2819std::optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
2823 BugPathGetter BugGraph(&Reporter.
getGraph(), bugReports);
2825 while (BugPathInfo *BugPath = BugGraph.getNextBugPath()) {
2828 assert(R &&
"No original report found for sliced graph.");
2829 assert(R->
isValid() &&
"Report selected by trimmed graph marked invalid.");
2844 std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes =
2861 return PathDiagnosticBuilder(
2862 std::move(BRC), std::move(BugPath->BugPath), BugPath->Report,
2863 BugPath->ErrorNode, std::move(visitorNotes));
2870std::unique_ptr<DiagnosticForConsumerMapTy>
2874 assert(!bugReports.empty());
2876 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
2878 std::optional<PathDiagnosticBuilder> PDB =
2879 PathDiagnosticBuilder::findValidReport(bugReports, *
this);
2883 if (std::unique_ptr<PathDiagnostic> PD = PDB->generate(PC)) {
2884 (*Out)[PC] = std::move(PD);
2893 bool ValidSourceLoc = R->getLocation().isValid();
2894 assert(ValidSourceLoc);
2897 if (!ValidSourceLoc)
2901 llvm::FoldingSetNodeID ID;
2910 EQClasses.InsertNode(EQ, InsertPos);
2911 EQClassesVector.push_back(EQ);
2913 EQ->AddReport(std::move(R));
2917 if (
auto PR = dyn_cast<PathSensitiveBugReport>(R.get()))
2921 assert((E->isSink() || E->getLocation().getTag()) &&
2922 "Error node must either be a sink or have a tag");
2925 E->getLocationContext()->getAnalysisDeclContext();
2944struct FRIEC_WLItem {
2949 : N(n), I(N->succ_begin()), E(N->succ_end()) {}
2954BugReport *PathSensitiveBugReporter::findReportInEquivalenceClass(
2959 assert(
EQ.getReports().size() > 0);
2960 const BugType& BT =
EQ.getReports()[0]->getBugType();
2963 for (
auto &J :
EQ.getReports()) {
2964 if (
auto *PR = dyn_cast<PathSensitiveBugReport>(J.get())) {
2966 bugReports.push_back(PR);
2980 for (
const auto &I:
EQ.getReports()) {
2981 auto *R = dyn_cast<PathSensitiveBugReport>(I.get());
2986 if (errorNode->
isSink()) {
2988 "BugType::isSuppressSink() should not be 'true' for sink end nodes");
2992 bugReports.push_back(R);
3003 if (ErrorB->isInevitablySinking())
3008 using WLItem = FRIEC_WLItem;
3011 llvm::DenseMap<const ExplodedNode *, unsigned>
Visited;
3014 WL.push_back(errorNode);
3017 while (!WL.empty()) {
3018 WLItem &WI = WL.back();
3019 assert(!WI.N->succ_empty());
3021 for (; WI.I != WI.E; ++WI.I) {
3027 bugReports.push_back(R);
3038 unsigned &mark =
Visited[Succ];
3048 if (!WL.empty() && &WL.back() == &WI)
3055 return exampleReport;
3060 BugReport *report = findReportInEquivalenceClass(EQ, bugReports);
3065 for (
const std::string &CheckerOrPackage :
3073 std::unique_ptr<DiagnosticForConsumerMapTy> Diagnostics =
3076 for (
auto &
P : *Diagnostics) {
3078 std::unique_ptr<PathDiagnostic> &PD =
P.second;
3082 if (PD->path.empty()) {
3084 auto piece = std::make_unique<PathDiagnosticEventPiece>(
3087 piece->addRange(
Range);
3088 PD->setEndOfPath(std::move(piece));
3095 for (
const auto &I : llvm::reverse(report->
getNotes())) {
3097 auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
3100 ConvertedPiece->addRange(R);
3102 Pieces.push_front(std::move(ConvertedPiece));
3105 for (
const auto &I : llvm::reverse(report->
getNotes()))
3106 Pieces.push_front(I);
3109 for (
const auto &I : report->
getFixits())
3110 Pieces.back()->addFixit(I);
3124 if (
const auto FD = dyn_cast<FunctionDecl>(Signature)) {
3125 SignatureSourceRange = FD->getSourceRange();
3126 }
else if (
const auto OD = dyn_cast<ObjCMethodDecl>(Signature)) {
3127 SignatureSourceRange = OD->getSourceRange();
3133 : SignatureSourceRange.
getEnd();
3134 if (!Start.
isValid() || !End.isValid())
3136 unsigned StartLine =
SM.getExpansionLineNumber(Start);
3137 unsigned EndLine =
SM.getExpansionLineNumber(End);
3139 FileID FID =
SM.getFileID(
SM.getExpansionLoc(Start));
3140 for (
unsigned Line = StartLine;
Line <= EndLine;
Line++)
3141 ExecutedLines[FID].insert(
Line);
3151 FileID FID =
SM.getFileID(ExpansionLoc);
3152 unsigned LineNo =
SM.getExpansionLineNumber(ExpansionLoc);
3153 ExecutedLines[FID].insert(LineNo);
3158static std::unique_ptr<FilesToLineNumsMap>
3160 auto ExecutedLines = std::make_unique<FilesToLineNumsMap>();
3169 const Decl* D = CE->getCalleeContext()->getDecl();
3180 if (
const auto *RS = dyn_cast_or_null<ReturnStmt>(
P)) {
3185 if (isa_and_nonnull<SwitchCase, LabelStmt>(
P))
3191 return ExecutedLines;
3194std::unique_ptr<DiagnosticForConsumerMapTy>
3198 auto *basicReport = cast<BasicBugReport>(exampleReport);
3199 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
3200 for (
auto *Consumer : consumers)
3215 "The call piece should not be in a header file.");
3227 if (
auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get()))
3235 if (PD.
path.empty())
3244 if (
auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
3251 const auto *ND = dyn_cast<NamedDecl>(CP->
getCallee());
3254 llvm::raw_svector_ostream os(buf);
3255 os <<
" (within a call to '" << ND->getDeclName() <<
"')";
3270std::unique_ptr<DiagnosticForConsumerMapTy>
3271PathSensitiveBugReporter::generateDiagnosticForConsumerMap(
3274 std::vector<BasicBugReport *> BasicBugReports;
3275 std::vector<PathSensitiveBugReport *> PathSensitiveBugReports;
3276 if (isa<BasicBugReport>(exampleReport))
3278 consumers, bugReports);
3284 assert(!bugReports.empty());
3285 MaxBugClassSize.updateMax(bugReports.size());
3292 consumers, convertedArrayOfReports);
3297 MaxValidBugClassSize.updateMax(bugReports.size());
3302 for (
auto const &
P : *Out)
3303 if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.
AnalyzeAll)
3316 Loc, Ranges, Fixits);
3321 StringRef name, StringRef category,
3326 BugType *BT = getBugTypeForName(CheckName, name, category);
3327 auto R = std::make_unique<BasicBugReport>(*BT, str,
Loc);
3328 R->setDeclWithIssue(DeclWithIssue);
3329 for (
const auto &SR : Ranges)
3331 for (
const auto &FH : Fixits)
3337 StringRef name, StringRef category) {
3339 llvm::raw_svector_ostream(fullDesc) << CheckName.
getName() <<
":" << name
3341 std::unique_ptr<BugType> &BT = StrBugTypes[fullDesc];
3343 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 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)
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