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 unsigned ArgIndex = 0;
301 E = CE->arg_end(); I != E; ++I, ++ArgIndex){
313 if ((*I)->getType()->isVoidPointerType())
338 return (llvm::Twine(Msg) +
" via " + std::to_string(ArgIndex) +
339 llvm::getOrdinalSuffix(ArgIndex) +
" parameter").str();
359 if (
X->getTag() == tagPreferred && Y->
getTag() == tagLesser)
362 if (Y->
getTag() == tagPreferred &&
X->getTag() == tagLesser)
374 unsigned N = path.size();
381 for (
unsigned i = 0; i < N; ++i) {
382 auto piece = std::move(path.front());
385 switch (piece->getKind()) {
396 if (
auto *nextEvent =
397 dyn_cast<PathDiagnosticEventPiece>(path.front().get())) {
398 auto *
event = cast<PathDiagnosticEventPiece>(piece.get());
402 if (
auto *pieceToKeep =
404 piece = std::move(pieceToKeep == event ? piece : path.front());
416 path.push_back(std::move(piece));
426 bool IsInteresting =
false) {
427 bool containsSomethingInteresting = IsInteresting;
428 const unsigned N = pieces.size();
430 for (
unsigned i = 0 ; i < N ; ++i) {
433 auto piece = std::move(pieces.front());
436 switch (piece->getKind()) {
438 auto &call = cast<PathDiagnosticCallPiece>(*piece);
445 containsSomethingInteresting =
true;
449 auto ¯o = cast<PathDiagnosticMacroPiece>(*piece);
452 containsSomethingInteresting =
true;
456 auto &
event = cast<PathDiagnosticEventPiece>(*piece);
460 containsSomethingInteresting |= !
event.isPrunable();
469 pieces.push_back(std::move(piece));
472 return containsSomethingInteresting;
477 for (
unsigned int i = 0; i < Path.size(); ++i) {
478 auto Piece = std::move(Path.front());
480 if (!isa<PathDiagnosticPopUpPiece>(*Piece))
481 Path.push_back(std::move(Piece));
497 for (
const auto &I : Pieces) {
498 auto *Call = dyn_cast<PathDiagnosticCallPiece>(I.get());
503 if (LastCallLocation) {
505 if (CallerIsImplicit || !Call->callEnter.asLocation().isValid())
506 Call->callEnter = *LastCallLocation;
507 if (CallerIsImplicit || !Call->callReturn.asLocation().isValid())
508 Call->callReturn = *LastCallLocation;
514 if (Call->callEnterWithin.asLocation().isValid() &&
516 ThisCallLocation = &Call->callEnterWithin;
518 ThisCallLocation = &Call->callEnter;
520 assert(ThisCallLocation &&
"Outermost call has an invalid location");
529 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
530 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
533 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
536 if (
auto *
CF = dyn_cast<PathDiagnosticControlFlowPiece>(I->get())) {
537 const Stmt *Start =
CF->getStartLocation().asStmt();
538 const Stmt *End =
CF->getEndLocation().asStmt();
539 if (isa_and_nonnull<CXXDefaultInitExpr>(Start)) {
542 }
else if (isa_and_nonnull<CXXDefaultInitExpr>(End)) {
543 PathPieces::iterator Next = std::next(I);
546 dyn_cast<PathDiagnosticControlFlowPiece>(Next->get())) {
547 NextCF->setStartLocation(
CF->getStartLocation());
563 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
564 if (
auto *
C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
567 if (
auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
570 if (!(*I)->getLocation().isValid() ||
571 !(*I)->getLocation().asLocation().isValid()) {
580 const PathDiagnosticConstruct &
C)
const {
581 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
583 C.getCurrLocationContext());
590 llvm::raw_string_ostream &os,
const PathDiagnosticConstruct &
C)
const {
592 if (os.str().empty())
598 os <<
"Execution continues on line "
599 << getSourceManager().getExpansionLineNumber(
Loc.asLocation())
602 os <<
"Execution jumps to the end of the ";
603 const Decl *D =
C.getCurrLocationContext()->getDecl();
604 if (isa<ObjCMethodDecl>(D))
606 else if (isa<FunctionDecl>(D))
609 assert(isa<BlockDecl>(D));
610 os <<
"anonymous block";
626 switch (
Parent->getStmtClass()) {
627 case Stmt::ForStmtClass:
628 case Stmt::DoStmtClass:
629 case Stmt::WhileStmtClass:
630 case Stmt::ObjCForCollectionStmtClass:
631 case Stmt::CXXForRangeStmtClass:
642 bool allowNestedContexts =
false) {
649 switch (
Parent->getStmtClass()) {
650 case Stmt::BinaryOperatorClass: {
651 const auto *B = cast<BinaryOperator>(
Parent);
652 if (B->isLogicalOp())
656 case Stmt::CompoundStmtClass:
657 case Stmt::StmtExprClass:
659 case Stmt::ChooseExprClass:
662 if (allowNestedContexts || cast<ChooseExpr>(
Parent)->getCond() == S)
666 case Stmt::BinaryConditionalOperatorClass:
667 case Stmt::ConditionalOperatorClass:
670 if (allowNestedContexts ||
671 cast<AbstractConditionalOperator>(
Parent)->getCond() == S)
675 case Stmt::CXXForRangeStmtClass:
676 if (cast<CXXForRangeStmt>(
Parent)->getBody() == S)
679 case Stmt::DoStmtClass:
681 case Stmt::ForStmtClass:
682 if (cast<ForStmt>(
Parent)->getBody() == S)
685 case Stmt::IfStmtClass:
686 if (cast<IfStmt>(
Parent)->getCond() != S)
689 case Stmt::ObjCForCollectionStmtClass:
690 if (cast<ObjCForCollectionStmt>(
Parent)->getBody() == S)
693 case Stmt::WhileStmtClass:
694 if (cast<WhileStmt>(
Parent)->getCond() != S)
704 assert(S &&
"Cannot have null Stmt for PathDiagnosticLocation");
723void PathDiagnosticBuilder::updateStackPiecesWithMessage(
725 if (R->hasCallStackHint(
P))
726 for (
const auto &I : CallStack) {
729 std::string stackMsg = R->getCallStackMessage(
P, N);
743 const PathDiagnosticConstruct &
C,
const CFGBlock *Dst,
749 llvm::raw_string_ostream os(sbuf);
755 switch (S->getStmtClass()) {
757 os <<
"No cases match in the switch statement. "
758 "Control jumps to line "
759 << End.asLocation().getExpansionLineNumber();
761 case Stmt::DefaultStmtClass:
762 os <<
"Control jumps to the 'default' case at line "
763 << End.asLocation().getExpansionLineNumber();
766 case Stmt::CaseStmtClass: {
767 os <<
"Control jumps to 'case ";
768 const auto *Case = cast<CaseStmt>(S);
772 bool GetRawInt =
true;
774 if (
const auto *DR = dyn_cast<DeclRefExpr>(LHS)) {
777 const auto *D = dyn_cast<EnumConstantDecl>(DR->getDecl());
788 os <<
":' at line " << End.asLocation().getExpansionLineNumber();
793 os <<
"'Default' branch taken. ";
794 End = ExecutionContinues(os,
C);
796 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
801 const PathDiagnosticConstruct &
C,
const Stmt *S,
804 llvm::raw_string_ostream os(sbuf);
807 os <<
"Control jumps to line " << End.asLocation().getExpansionLineNumber();
808 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str());
812 const PathDiagnosticConstruct &
C,
const Stmt *T,
const CFGBlock *Src,
817 const auto *B = cast<BinaryOperator>(T);
819 llvm::raw_string_ostream os(sbuf);
820 os <<
"Left side of '";
823 if (B->getOpcode() == BO_LAnd) {
836 End = ExecutionContinues(
C);
839 assert(B->getOpcode() == BO_LOr);
847 End = ExecutionContinues(
C);
855 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
859void PathDiagnosticBuilder::generateMinimalDiagForBlockEdge(
860 PathDiagnosticConstruct &
C,
BlockEdge BE)
const {
874 case Stmt::GotoStmtClass:
875 case Stmt::IndirectGotoStmtClass: {
876 if (
const Stmt *S =
C.getCurrentNode()->getNextStmtForDiagnostics())
877 C.getActivePath().push_front(generateDiagForGotoOP(
C, S, Start));
881 case Stmt::SwitchStmtClass: {
882 C.getActivePath().push_front(generateDiagForSwitchOP(
C, Dst, Start));
886 case Stmt::BreakStmtClass:
887 case Stmt::ContinueStmtClass: {
889 llvm::raw_string_ostream os(sbuf);
891 C.getActivePath().push_front(
892 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
897 case Stmt::BinaryConditionalOperatorClass:
898 case Stmt::ConditionalOperatorClass: {
900 llvm::raw_string_ostream os(sbuf);
901 os <<
"'?' condition is ";
910 if (
const Stmt *S = End.asStmt())
913 C.getActivePath().push_front(
914 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
919 case Stmt::BinaryOperatorClass: {
920 if (!
C.supportsLogicalOpControlFlow())
923 C.getActivePath().push_front(generateDiagForBinaryOP(
C, T, Src, Dst));
927 case Stmt::DoStmtClass:
930 llvm::raw_string_ostream os(sbuf);
932 os <<
"Loop condition is true. ";
935 if (
const Stmt *S = End.asStmt())
938 C.getActivePath().push_front(
939 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
944 if (
const Stmt *S = End.asStmt())
947 C.getActivePath().push_front(
948 std::make_shared<PathDiagnosticControlFlowPiece>(
949 Start, End,
"Loop condition is false. Exiting loop"));
953 case Stmt::WhileStmtClass:
954 case Stmt::ForStmtClass:
957 llvm::raw_string_ostream os(sbuf);
959 os <<
"Loop condition is false. ";
961 if (
const Stmt *S = End.asStmt())
964 C.getActivePath().push_front(
965 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
969 if (
const Stmt *S = End.asStmt())
972 C.getActivePath().push_front(
973 std::make_shared<PathDiagnosticControlFlowPiece>(
974 Start, End,
"Loop condition is true. Entering loop body"));
979 case Stmt::IfStmtClass: {
982 if (
const Stmt *S = End.asStmt())
986 C.getActivePath().push_front(
987 std::make_shared<PathDiagnosticControlFlowPiece>(
988 Start, End,
"Taking false branch"));
990 C.getActivePath().push_front(
991 std::make_shared<PathDiagnosticControlFlowPiece>(
992 Start, End,
"Taking true branch"));
1005 case Stmt::ForStmtClass:
1006 case Stmt::WhileStmtClass:
1007 case Stmt::ObjCForCollectionStmtClass:
1008 case Stmt::CXXForRangeStmtClass:
1037 const Stmt *S = SP->getStmt();
1047 const Stmt *LoopBody =
nullptr;
1049 case Stmt::CXXForRangeStmtClass: {
1050 const auto *FR = cast<CXXForRangeStmt>(Term);
1055 LoopBody = FR->getBody();
1058 case Stmt::ForStmtClass: {
1059 const auto *FS = cast<ForStmt>(Term);
1062 LoopBody = FS->getBody();
1065 case Stmt::ObjCForCollectionStmtClass: {
1066 const auto *FC = cast<ObjCForCollectionStmt>(Term);
1067 LoopBody = FC->getBody();
1070 case Stmt::WhileStmtClass:
1071 LoopBody = cast<WhileStmt>(Term)->getBody();
1101 std::make_shared<PathDiagnosticControlFlowPiece>(NewLoc, PrevLoc));
1109 if (
const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(S))
1110 return FS->getElement();
1117 "Loop body skipped when range is empty";
1119 "Loop body skipped when collection is empty";
1121static std::unique_ptr<FilesToLineNumsMap>
1124void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
1135 if (
C.shouldAddPathEdges()) {
1151 bool VisitedEntireCall =
C.PD->isWithinCall();
1152 C.PD->popActivePath();
1155 if (VisitedEntireCall) {
1156 Call = cast<PathDiagnosticCallPiece>(
C.getActivePath().front().get());
1160 const Decl *Caller = CE->getLocationContext()->getDecl();
1162 assert(
C.getActivePath().size() == 1 &&
1163 C.getActivePath().front().get() == Call);
1167 assert(
C.isInLocCtxMap(&
C.getActivePath()) &&
1168 "When we ascend to a previously unvisited call, the active path's "
1169 "address shouldn't change, but rather should be compacted into "
1170 "a single CallEvent!");
1171 C.updateLocCtxMap(&
C.getActivePath(),
C.getCurrLocationContext());
1174 assert(!
C.isInLocCtxMap(&
Call->path) &&
1175 "When we ascend to a previously unvisited call, this must be the "
1176 "first time we encounter the caller context!");
1177 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1179 Call->setCallee(*CE,
SM);
1182 PrevLoc =
Call->getLocation();
1184 if (!
C.CallStack.empty()) {
1185 assert(
C.CallStack.back().first == Call);
1186 C.CallStack.pop_back();
1191 assert(
C.getCurrLocationContext() ==
C.getLocationContextForActivePath() &&
1192 "The current position in the bug path is out of sync with the "
1193 "location context associated with the active path!");
1196 if (std::optional<CallExitEnd> CE =
P.getAs<
CallExitEnd>()) {
1202 assert(!
C.isInLocCtxMap(&
Call->path) &&
1203 "We just entered a call, this must've been the first time we "
1204 "encounter its context!");
1205 C.updateLocCtxMap(&
Call->path, CE->getCalleeContext());
1207 if (
C.shouldAddPathEdges()) {
1213 auto *
P =
Call.get();
1214 C.getActivePath().push_front(std::move(Call));
1217 C.PD->pushActivePath(&
P->path);
1218 C.CallStack.push_back(CallWithEntry(
P,
C.getCurrentNode()));
1223 if (!
C.shouldAddPathEdges())
1229 if (!isa<ObjCForCollectionStmt>(PS->getStmt())) {
1237 if (
C.shouldAddControlNotes()) {
1238 generateMinimalDiagForBlockEdge(
C, *BE);
1241 if (!
C.shouldAddPathEdges()) {
1248 const Stmt *Body =
nullptr;
1250 if (
const auto *FS = dyn_cast<ForStmt>(Loop))
1251 Body = FS->getBody();
1252 else if (
const auto *WS = dyn_cast<WhileStmt>(Loop))
1253 Body = WS->getBody();
1254 else if (
const auto *OFS = dyn_cast<ObjCForCollectionStmt>(Loop)) {
1255 Body = OFS->getBody();
1256 }
else if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(Loop)) {
1257 Body = FRS->getBody();
1261 auto p = std::make_shared<PathDiagnosticEventPiece>(
1262 L,
"Looping back to the head of the loop");
1263 p->setPrunable(
true);
1267 if (!
C.shouldAddControlNotes()) {
1268 C.getActivePath().push_front(std::move(p));
1271 if (
const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
1291 if (!IsInLoopBody) {
1292 if (isa<ObjCForCollectionStmt>(Term)) {
1294 }
else if (isa<CXXForRangeStmt>(Term)) {
1306 C.getCurrLocationContext());
1307 auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
1308 PE->setPrunable(
true);
1312 if (!
C.shouldAddControlNotes()) {
1313 C.getActivePath().push_front(std::move(PE));
1316 }
else if (isa<BreakStmt, ContinueStmt, GotoStmt>(Term)) {
1324static std::unique_ptr<PathDiagnostic>
1327 return std::make_unique<PathDiagnostic>(
1331 std::make_unique<FilesToLineNumsMap>());
1334static std::unique_ptr<PathDiagnostic>
1338 return std::make_unique<PathDiagnostic>(
1355 if (isa<FullExpr, CXXBindTemporaryExpr, SubstNonTypeTemplateParmExpr>(S))
1365 switch (S->getStmtClass()) {
1366 case Stmt::BinaryOperatorClass: {
1367 const auto *BO = cast<BinaryOperator>(S);
1368 if (!BO->isLogicalOp())
1370 return BO->getLHS() == Cond || BO->getRHS() == Cond;
1372 case Stmt::IfStmtClass:
1373 return cast<IfStmt>(S)->getCond() == Cond;
1374 case Stmt::ForStmtClass:
1375 return cast<ForStmt>(S)->getCond() == Cond;
1376 case Stmt::WhileStmtClass:
1377 return cast<WhileStmt>(S)->getCond() == Cond;
1378 case Stmt::DoStmtClass:
1379 return cast<DoStmt>(S)->getCond() == Cond;
1380 case Stmt::ChooseExprClass:
1381 return cast<ChooseExpr>(S)->getCond() == Cond;
1382 case Stmt::IndirectGotoStmtClass:
1383 return cast<IndirectGotoStmt>(S)->getTarget() == Cond;
1384 case Stmt::SwitchStmtClass:
1385 return cast<SwitchStmt>(S)->getCond() == Cond;
1386 case Stmt::BinaryConditionalOperatorClass:
1387 return cast<BinaryConditionalOperator>(S)->getCond() == Cond;
1388 case Stmt::ConditionalOperatorClass: {
1389 const auto *CO = cast<ConditionalOperator>(S);
1390 return CO->getCond() == Cond ||
1391 CO->getLHS() == Cond ||
1392 CO->getRHS() == Cond;
1394 case Stmt::ObjCForCollectionStmtClass:
1395 return cast<ObjCForCollectionStmt>(S)->getElement() == Cond;
1396 case Stmt::CXXForRangeStmtClass: {
1397 const auto *FRS = cast<CXXForRangeStmt>(S);
1398 return FRS->getCond() == Cond || FRS->getRangeInit() == Cond;
1406 if (
const auto *FS = dyn_cast<ForStmt>(FL))
1407 return FS->getInc() == S || FS->getInit() == S;
1408 if (
const auto *FRS = dyn_cast<CXXForRangeStmt>(FL))
1409 return FRS->getInc() == S || FRS->getRangeStmt() == S ||
1410 FRS->getLoopVarStmt() || FRS->getRangeInit() == S;
1423 PathPieces::iterator Prev = pieces.end();
1424 for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E;
1426 auto *Piece = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1435 const Stmt *InnerStmt =
nullptr;
1436 while (NextSrcContext.
isValid() && NextSrcContext.
asStmt() != InnerStmt) {
1437 SrcContexts.push_back(NextSrcContext);
1438 InnerStmt = NextSrcContext.
asStmt();
1447 const Stmt *Dst = Piece->getEndLocation().getStmtOrNull();
1457 if (llvm::is_contained(SrcContexts, DstContext))
1461 Piece->setStartLocation(DstContext);
1466 auto *PrevPiece = dyn_cast<PathDiagnosticControlFlowPiece>(Prev->get());
1469 if (
const Stmt *PrevSrc =
1470 PrevPiece->getStartLocation().getStmtOrNull()) {
1472 if (PrevSrcParent ==
1474 PrevPiece->setEndLocation(DstContext);
1485 std::make_shared<PathDiagnosticControlFlowPiece>(SrcLoc, DstContext);
1487 I = pieces.insert(I, std::move(
P));
1503 for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) {
1504 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1509 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1510 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1512 if (!s1Start || !s1End)
1515 PathPieces::iterator NextI = I; ++NextI;
1525 const auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1527 StringRef S = EV->getString();
1536 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1546 if (!s2Start || !s2End || s1End != s2Start)
1562 I = pieces.erase(I);
1575 SM.getExpansionRange(
Range.getEnd()).getEnd());
1578 if (FID !=
SM.getFileID(ExpansionRange.
getEnd()))
1579 return std::nullopt;
1581 std::optional<MemoryBufferRef> Buffer =
SM.getBufferOrNone(FID);
1583 return std::nullopt;
1585 unsigned BeginOffset =
SM.getFileOffset(ExpansionRange.
getBegin());
1586 unsigned EndOffset =
SM.getFileOffset(ExpansionRange.
getEnd());
1587 StringRef Snippet = Buffer->getBuffer().slice(BeginOffset, EndOffset);
1593 if (Snippet.find_first_of(
"\r\n") != StringRef::npos)
1594 return std::nullopt;
1597 return Snippet.size();
1623 for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) {
1625 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1632 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1633 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1635 PathPieces::iterator NextI = I; ++NextI;
1639 const auto *PieceNextI =
1640 dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1643 if (isa<PathDiagnosticEventPiece>(NextI->get())) {
1647 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1656 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1657 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1659 if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
1660 const size_t MAX_SHORT_LINE_LENGTH = 80;
1662 if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) {
1664 if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) {
1666 I = Path.erase(NextI);
1689 bool erased =
false;
1691 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E;
1695 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1700 const Stmt *start = PieceI->getStartLocation().getStmtOrNull();
1701 const Stmt *end = PieceI->getEndLocation().getStmtOrNull();
1716 if (!
SM.isWrittenInSameFile(FirstLoc, SecondLoc))
1718 if (
SM.isBeforeInTranslationUnit(SecondLoc, FirstLoc))
1719 std::swap(SecondLoc, FirstLoc);
1728 const size_t MAX_PUNY_EDGE_LENGTH = 2;
1729 if (*ByteWidth <= MAX_PUNY_EDGE_LENGTH) {
1741 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ++I) {
1742 const auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get());
1747 PathPieces::iterator NextI = I; ++NextI;
1751 const auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1757 if (PieceI->getString() == PieceNextI->getString()) {
1765 bool hasChanges =
false;
1771 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) {
1773 if (
auto *CallI = dyn_cast<PathDiagnosticCallPiece>(I->get())) {
1776 if (!OCS.count(CallI)) {
1786 auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1793 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1794 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1798 PathPieces::iterator NextI = I; ++NextI;
1802 const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1809 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1810 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1828 if (level1 && level1 == level2 && level1 == level3 && level1 == level4) {
1829 PieceI->setEndLocation(PieceNextI->getEndLocation());
1842 if (s1End && s1End == s2Start && level2) {
1843 bool removeEdge =
false;
1869 else if (s1Start && s2End &&
1882 else if (s1Start && s2End &&
1884 SourceRange EdgeRange(PieceI->getEndLocation().asLocation(),
1885 PieceI->getStartLocation().asLocation());
1892 PieceI->setEndLocation(PieceNextI->getEndLocation());
1906 if (s1End == s2Start) {
1907 const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(level3);
1908 if (FS && FS->getCollection()->IgnoreParens() == s2Start &&
1909 s2End == FS->getElement()) {
1910 PieceI->setEndLocation(PieceNextI->getEndLocation());
1947 const auto *FirstEdge =
1948 dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get());
1952 const Decl *D =
C.getLocationContextFor(&Path)->getDecl();
1955 if (FirstEdge->getStartLocation() != EntryLoc)
1967 for (
const auto &
P : path) {
1970 unsigned LineNo =
Loc.getLineNumber();
1972 ExecutedLines[FID].insert(LineNo);
1976PathDiagnosticConstruct::PathDiagnosticConstruct(
1979 : Consumer(PDC), CurrentNode(ErrorNode),
1980 SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()),
1985PathDiagnosticBuilder::PathDiagnosticBuilder(
1988 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics)
1990 ErrorNode(ErrorNode),
1991 VisitorsDiagnostics(
std::move(VisitorsDiagnostics)) {}
1993std::unique_ptr<PathDiagnostic>
1995 PathDiagnosticConstruct Construct(PDC, ErrorNode, R);
2004 auto EndNotes = VisitorsDiagnostics->find(ErrorNode);
2006 if (EndNotes != VisitorsDiagnostics->end()) {
2007 assert(!EndNotes->second.empty());
2008 LastPiece = EndNotes->second[0];
2013 Construct.PD->setEndOfPath(LastPiece);
2018 while (Construct.ascendToPrevNode()) {
2019 generatePathDiagnosticsForNode(Construct, PrevLoc);
2021 auto VisitorNotes = VisitorsDiagnostics->find(Construct.getCurrentNode());
2022 if (VisitorNotes == VisitorsDiagnostics->end())
2027 std::set<llvm::FoldingSetNodeID> DeduplicationSet;
2031 llvm::FoldingSetNodeID
ID;
2033 if (!DeduplicationSet.insert(ID).second)
2038 updateStackPiecesWithMessage(
Note, Construct.CallStack);
2039 Construct.getActivePath().push_front(
Note);
2047 Construct.getLocationContextForActivePath()->
getStackFrame();
2055 if (!Construct.PD->path.empty()) {
2057 bool stillHasNotes =
2059 assert(stillHasNotes);
2060 (void)stillHasNotes;
2064 if (!Opts.ShouldAddPopUpNotes)
2077 while (
optimizeEdges(Construct, Construct.getMutablePieces(), OCS)) {
2092 if (Opts.ShouldDisplayMacroExpansions)
2095 return std::move(Construct.PD);
2102void BugType::anchor() {}
2104void BuiltinBug::anchor() {}
2110LLVM_ATTRIBUTE_USED
static bool
2112 for (
const std::pair<StringRef, StringRef> &Pair : Registry.
Dependencies) {
2113 if (Pair.second == CheckerName)
2120 StringRef CheckerName) {
2122 if (
Checker.FullName == CheckerName)
2126 "Checker name not found in CheckerRegistry -- did you retrieve it "
2127 "correctly from CheckerManager::getCurrentCheckerName?");
2131 const BugType &bt, StringRef shortDesc, StringRef desc,
2133 const Decl *DeclToUnique)
2134 :
BugReport(
Kind::PathSensitive, bt, shortDesc, desc), ErrorNode(errorNode),
2135 ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() :
SourceRange()),
2136 UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) {
2138 ->getAnalysisManager()
2139 .getCheckerManager()
2140 ->getCheckerRegistryData(),
2142 "Some checkers depend on this one! We don't allow dependency "
2143 "checkers to emit warnings, because checkers should depend on "
2144 "*modeling*, not *diagnostics*.");
2149 ->getAnalysisManager()
2150 .getCheckerManager()
2151 ->getCheckerRegistryData(),
2153 "Hidden checkers musn't emit diagnostics as they are by definition "
2154 "non-user facing!");
2158 std::unique_ptr<BugReporterVisitor> visitor) {
2162 llvm::FoldingSetNodeID ID;
2163 visitor->Profile(ID);
2165 void *InsertPos =
nullptr;
2170 Callbacks.push_back(std::move(visitor));
2187 hash.AddInteger(
static_cast<int>(
getKind()));
2188 hash.AddPointer(&
BT);
2194 if (!range.isValid())
2196 hash.Add(range.getBegin());
2197 hash.Add(range.getEnd());
2202 hash.AddInteger(
static_cast<int>(
getKind()));
2203 hash.AddPointer(&
BT);
2217 if (!range.isValid())
2219 hash.Add(range.getBegin());
2220 hash.Add(range.getEnd());
2226 llvm::DenseMap<T, bugreporter::TrackingKind> &InterestingnessMap, T Val,
2228 auto Result = InterestingnessMap.insert({Val, TKind});
2248 "BugReport::markInteresting currently can only handle 2 different "
2249 "tracking kinds! Please define what tracking kind should this entitiy"
2250 "have, if it was already marked as interesting with a different kind!");
2262 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2274 if (
const auto *meta = dyn_cast<SymbolMetadata>(sym))
2286 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2297 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2313std::optional<bugreporter::TrackingKind>
2332 "BugReport::getInterestingnessKind currently can only handle 2 different "
2333 "tracking kinds! Please define what tracking kind should we return here "
2334 "when the kind of getAsRegion() and getAsSymbol() is different!");
2335 return std::nullopt;
2338std::optional<bugreporter::TrackingKind>
2341 return std::nullopt;
2346 return std::nullopt;
2347 return It->getSecond();
2350std::optional<bugreporter::TrackingKind>
2353 return std::nullopt;
2358 return It->getSecond();
2360 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
2362 return std::nullopt;
2388 const Stmt *S =
nullptr;
2392 if (BE->getBlock() == &Exit)
2413 assert(
ErrorNode &&
"Cannot create a location with a null node.");
2433 if (
const auto *ME = dyn_cast<MemberExpr>(S))
2437 if (
const auto *B = dyn_cast<BinaryOperator>(S))
2443 if (S->getBeginLoc().isValid())
2469 assert(StrBugTypes.empty() &&
2470 "Destroying BugReporter before diagnostics are emitted!");
2473 for (
const auto I : EQClassesVector)
2480 for (
const auto EQ : EQClassesVector)
2487 StrBugTypes.clear();
2500 std::unique_ptr<ExplodedGraph> BugPath;
2507class BugPathGetter {
2508 std::unique_ptr<ExplodedGraph> TrimmedGraph;
2510 using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>;
2513 PriorityMapTy PriorityMap;
2517 using ReportNewNodePair =
2518 std::pair<PathSensitiveBugReport *, const ExplodedNode *>;
2521 BugPathInfo CurrentBugPath;
2524 template <
bool Descending>
2525 class PriorityCompare {
2526 const PriorityMapTy &PriorityMap;
2529 PriorityCompare(
const PriorityMapTy &M) : PriorityMap(M) {}
2532 PriorityMapTy::const_iterator LI = PriorityMap.find(LHS);
2533 PriorityMapTy::const_iterator RI = PriorityMap.find(RHS);
2534 PriorityMapTy::const_iterator E = PriorityMap.end();
2541 return Descending ? LI->second > RI->second
2542 : LI->second < RI->second;
2545 bool operator()(
const ReportNewNodePair &LHS,
2546 const ReportNewNodePair &RHS)
const {
2547 return (*
this)(LHS.second, RHS.second);
2555 BugPathInfo *getNextBugPath();
2560BugPathGetter::BugPathGetter(
const ExplodedGraph *OriginalGraph,
2563 for (
const auto I : bugReports) {
2564 assert(I->isValid() &&
2565 "We only allow BugReporterVisitors and BugReporter itself to "
2566 "invalidate reports!");
2567 Nodes.emplace_back(I->getErrorNode());
2573 TrimmedGraph = OriginalGraph->
trim(
Nodes, &ForwardMap);
2581 const ExplodedNode *NewNode = ForwardMap.lookup(Report->getErrorNode());
2583 "Failed to construct a trimmed graph that contains this error "
2585 ReportNodes.emplace_back(Report, NewNode);
2586 RemainingNodes.insert(NewNode);
2589 assert(!RemainingNodes.empty() &&
"No error node found in the trimmed graph");
2592 std::queue<const ExplodedNode *> WS;
2594 assert(TrimmedGraph->num_roots() == 1);
2595 WS.push(*TrimmedGraph->roots_begin());
2598 while (!WS.empty()) {
2602 PriorityMapTy::iterator PriorityEntry;
2604 std::tie(PriorityEntry, IsNew) = PriorityMap.insert({
Node,
Priority});
2608 assert(PriorityEntry->second <= Priority);
2612 if (RemainingNodes.erase(
Node))
2613 if (RemainingNodes.empty())
2621 llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap));
2624BugPathInfo *BugPathGetter::getNextBugPath() {
2625 if (ReportNodes.empty())
2629 std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val();
2630 assert(PriorityMap.contains(OrigN) &&
"error node not accessible from root");
2634 auto GNew = std::make_unique<ExplodedGraph>();
2650 CurrentBugPath.ErrorNode = NewN;
2656 GNew->addRoot(NewN);
2663 PriorityCompare<false>(PriorityMap));
2666 CurrentBugPath.BugPath = std::move(GNew);
2668 return &CurrentBugPath;
2675 using MacroStackTy = std::vector<
2676 std::pair<std::shared_ptr<PathDiagnosticMacroPiece>,
SourceLocation>>;
2678 using PiecesTy = std::vector<PathDiagnosticPieceRef>;
2680 MacroStackTy MacroStack;
2683 for (PathPieces::const_iterator I = path.begin(), E = path.end();
2685 const auto &piece = *I;
2688 if (
auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) {
2698 SM.getExpansionLoc(
Loc) :
2701 if (
Loc.isFileID()) {
2703 Pieces.push_back(piece);
2707 assert(
Loc.isMacroID());
2710 if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
2711 MacroStack.back().first->subPieces.push_back(piece);
2717 std::shared_ptr<PathDiagnosticMacroPiece> MacroGroup;
2720 SM.getExpansionLoc(
Loc) :
2724 while (!MacroStack.empty()) {
2725 if (InstantiationLoc == MacroStack.back().second) {
2726 MacroGroup = MacroStack.back().first;
2730 if (ParentInstantiationLoc == MacroStack.back().second) {
2731 MacroGroup = MacroStack.back().first;
2735 MacroStack.pop_back();
2738 if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
2740 auto NewGroup = std::make_shared<PathDiagnosticMacroPiece>(
2744 MacroGroup->subPieces.push_back(NewGroup);
2746 assert(InstantiationLoc.
isFileID());
2747 Pieces.push_back(NewGroup);
2750 MacroGroup = NewGroup;
2751 MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
2755 MacroGroup->subPieces.push_back(piece);
2761 path.insert(path.end(), Pieces.begin(), Pieces.end());
2767static std::unique_ptr<VisitorsDiagnosticsTy>
2771 std::unique_ptr<VisitorsDiagnosticsTy> Notes =
2772 std::make_unique<VisitorsDiagnosticsTy>();
2785 for (std::unique_ptr<BugReporterVisitor> &Visitor : R->
visitors())
2786 visitors.push_back(std::move(Visitor));
2793 for (
auto &
V : visitors) {
2794 V->finalizeVisitor(BRC, ErrorNode, *R);
2796 if (
auto Piece =
V->getEndPath(BRC, ErrorNode, *R)) {
2797 assert(!LastPiece &&
2798 "There can only be one final piece in a diagnostic.");
2800 "The final piece must contain a message!");
2801 LastPiece = std::move(Piece);
2802 (*Notes)[ErrorNode].push_back(LastPiece);
2808 for (
auto &
V : visitors) {
2809 auto P =
V->VisitNode(NextNode, BRC, *R);
2811 (*Notes)[NextNode].push_back(std::move(
P));
2823std::optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
2827 BugPathGetter BugGraph(&Reporter.
getGraph(), bugReports);
2829 while (BugPathInfo *BugPath = BugGraph.getNextBugPath()) {
2832 assert(R &&
"No original report found for sliced graph.");
2833 assert(R->
isValid() &&
"Report selected by trimmed graph marked invalid.");
2848 std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes =
2865 return PathDiagnosticBuilder(
2866 std::move(BRC), std::move(BugPath->BugPath), BugPath->Report,
2867 BugPath->ErrorNode, std::move(visitorNotes));
2874std::unique_ptr<DiagnosticForConsumerMapTy>
2878 assert(!bugReports.empty());
2880 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
2882 std::optional<PathDiagnosticBuilder> PDB =
2883 PathDiagnosticBuilder::findValidReport(bugReports, *
this);
2887 if (std::unique_ptr<PathDiagnostic> PD = PDB->generate(PC)) {
2888 (*Out)[PC] = std::move(PD);
2897 bool ValidSourceLoc = R->getLocation().isValid();
2898 assert(ValidSourceLoc);
2901 if (!ValidSourceLoc)
2905 llvm::FoldingSetNodeID ID;
2914 EQClasses.InsertNode(EQ, InsertPos);
2915 EQClassesVector.push_back(EQ);
2917 EQ->AddReport(std::move(R));
2921 if (
auto PR = dyn_cast<PathSensitiveBugReport>(R.get()))
2925 assert((E->isSink() || E->getLocation().getTag()) &&
2926 "Error node must either be a sink or have a tag");
2929 E->getLocationContext()->getAnalysisDeclContext();
2948struct FRIEC_WLItem {
2953 : N(n), I(N->succ_begin()), E(N->succ_end()) {}
2958BugReport *PathSensitiveBugReporter::findReportInEquivalenceClass(
2963 assert(
EQ.getReports().size() > 0);
2964 const BugType& BT =
EQ.getReports()[0]->getBugType();
2967 for (
auto &J :
EQ.getReports()) {
2968 if (
auto *PR = dyn_cast<PathSensitiveBugReport>(J.get())) {
2970 bugReports.push_back(PR);
2984 for (
const auto &I:
EQ.getReports()) {
2985 auto *R = dyn_cast<PathSensitiveBugReport>(I.get());
2990 if (errorNode->
isSink()) {
2992 "BugType::isSuppressSink() should not be 'true' for sink end nodes");
2996 bugReports.push_back(R);
3007 if (ErrorB->isInevitablySinking())
3012 using WLItem = FRIEC_WLItem;
3015 llvm::DenseMap<const ExplodedNode *, unsigned> Visited;
3018 WL.push_back(errorNode);
3019 Visited[errorNode] = 1;
3021 while (!WL.empty()) {
3022 WLItem &WI = WL.back();
3023 assert(!WI.N->succ_empty());
3025 for (; WI.I != WI.E; ++WI.I) {
3031 bugReports.push_back(R);
3042 unsigned &mark = Visited[Succ];
3052 if (!WL.empty() && &WL.back() == &WI)
3059 return exampleReport;
3064 BugReport *report = findReportInEquivalenceClass(EQ, bugReports);
3069 for (
const std::string &CheckerOrPackage :
3077 std::unique_ptr<DiagnosticForConsumerMapTy> Diagnostics =
3080 for (
auto &
P : *Diagnostics) {
3082 std::unique_ptr<PathDiagnostic> &PD =
P.second;
3086 if (PD->path.empty()) {
3088 auto piece = std::make_unique<PathDiagnosticEventPiece>(
3091 piece->addRange(
Range);
3092 PD->setEndOfPath(std::move(piece));
3099 for (
const auto &I : llvm::reverse(report->
getNotes())) {
3101 auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
3104 ConvertedPiece->addRange(R);
3106 Pieces.push_front(std::move(ConvertedPiece));
3109 for (
const auto &I : llvm::reverse(report->
getNotes()))
3110 Pieces.push_front(I);
3113 for (
const auto &I : report->
getFixits())
3114 Pieces.back()->addFixit(I);
3128 if (
const auto FD = dyn_cast<FunctionDecl>(Signature)) {
3129 SignatureSourceRange = FD->getSourceRange();
3130 }
else if (
const auto OD = dyn_cast<ObjCMethodDecl>(Signature)) {
3131 SignatureSourceRange = OD->getSourceRange();
3137 : SignatureSourceRange.
getEnd();
3138 if (!Start.
isValid() || !End.isValid())
3140 unsigned StartLine =
SM.getExpansionLineNumber(Start);
3141 unsigned EndLine =
SM.getExpansionLineNumber(End);
3143 FileID FID =
SM.getFileID(
SM.getExpansionLoc(Start));
3144 for (
unsigned Line = StartLine; Line <= EndLine; Line++)
3145 ExecutedLines[FID].insert(Line);
3155 FileID FID =
SM.getFileID(ExpansionLoc);
3156 unsigned LineNo =
SM.getExpansionLineNumber(ExpansionLoc);
3157 ExecutedLines[FID].insert(LineNo);
3162static std::unique_ptr<FilesToLineNumsMap>
3164 auto ExecutedLines = std::make_unique<FilesToLineNumsMap>();
3173 const Decl* D = CE->getCalleeContext()->getDecl();
3184 if (
const auto *RS = dyn_cast_or_null<ReturnStmt>(
P)) {
3189 if (isa_and_nonnull<SwitchCase, LabelStmt>(
P))
3195 return ExecutedLines;
3198std::unique_ptr<DiagnosticForConsumerMapTy>
3202 auto *basicReport = cast<BasicBugReport>(exampleReport);
3203 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
3204 for (
auto *Consumer : consumers)
3219 "The call piece should not be in a header file.");
3231 if (
auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get()))
3239 if (PD.
path.empty())
3248 if (
auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
3255 const auto *ND = dyn_cast<NamedDecl>(CP->
getCallee());
3258 llvm::raw_svector_ostream os(buf);
3259 os <<
" (within a call to '" << ND->getDeclName() <<
"')";
3274std::unique_ptr<DiagnosticForConsumerMapTy>
3275PathSensitiveBugReporter::generateDiagnosticForConsumerMap(
3278 std::vector<BasicBugReport *> BasicBugReports;
3279 std::vector<PathSensitiveBugReport *> PathSensitiveBugReports;
3280 if (isa<BasicBugReport>(exampleReport))
3282 consumers, bugReports);
3288 assert(!bugReports.empty());
3289 MaxBugClassSize.updateMax(bugReports.size());
3296 consumers, convertedArrayOfReports);
3301 MaxValidBugClassSize.updateMax(bugReports.size());
3306 for (
auto const &
P : *Out)
3307 if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.
AnalyzeAll)
3320 Loc, Ranges, Fixits);
3325 StringRef name, StringRef category,
3330 BugType *BT = getBugTypeForName(CheckName, name, category);
3331 auto R = std::make_unique<BasicBugReport>(*BT, str,
Loc);
3332 R->setDeclWithIssue(DeclWithIssue);
3333 for (
const auto &SR : Ranges)
3335 for (
const auto &FH : Fixits)
3341 StringRef name, StringRef category) {
3343 llvm::raw_svector_ostream(fullDesc) << CheckName.
getName() <<
":" << name
3345 std::unique_ptr<BugType> &BT = StrBugTypes[fullDesc];
3347 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)
static CompilationDatabasePluginRegistry::Add< FixedCompilationDatabasePlugin > X("fixed-compilation-database", "Reads plain-text flags file")
Defines the clang::Expr interface and subclasses for C++ expressions.
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 Call(InterpState &S, CodePtr &PC, const Function *Func)
bool EQ(InterpState &S, CodePtr OpPC)
bool isa(CodeGen::Address addr)
@ C
Languages that the frontend can parse and compile.
@ Result
The result type of a method or function.
YAML serialization mapping.
Iterator for iterating over Stmt * arrays that contain only T *.
llvm::SmallVector< std::pair< StringRef, StringRef >, 0 > Dependencies