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