clang: lib/StaticAnalyzer/Core/BugReporter.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
49#include "llvm/ADT/ArrayRef.h"
50#include "llvm/ADT/DenseMap.h"
51#include "llvm/ADT/DenseSet.h"
52#include "llvm/ADT/FoldingSet.h"
53#include "llvm/ADT/STLExtras.h"
54#include "llvm/ADT/SmallPtrSet.h"
55#include "llvm/ADT/SmallString.h"
56#include "llvm/ADT/SmallVector.h"
57#include "llvm/ADT/Statistic.h"
58#include "llvm/ADT/StringExtras.h"
59#include "llvm/ADT/StringRef.h"
60#include "llvm/ADT/iterator_range.h"
61#include "llvm/Support/Casting.h"
62#include "llvm/Support/Compiler.h"
63#include "llvm/Support/ErrorHandling.h"
64#include "llvm/Support/MemoryBuffer.h"
65#include "llvm/Support/raw_ostream.h"
66#include
67#include
68#include
69#include
70#include
71#include
72#include
73#include
74#include
75#include
76#include
77
78using namespace clang;
79using namespace ento;
80using namespace llvm;
81
82#define DEBUG_TYPE "BugReporter"
83
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)");
89
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 "
94 "oracle heuristic");
95STATISTIC(NumTimesReportEQClassWasExhausted,
96 "Number of times all reports of an equivalence class was refuted");
97
99
100void BugReporterContext::anchor() {}
101
102
103
104
105
106namespace {
107
108
109using CallWithEntry =
110 std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>;
112
113
114using VisitorsDiagnosticsTy =
115 llvm::DenseMap<const ExplodedNode *, std::vector>;
116
117
118
119using LocationContextMap =
120 llvm::DenseMap<const PathPieces *, const LocationContext *>;
121
122
123
124
125class PathDiagnosticConstruct {
126
128
129
131
132
133
134 LocationContextMap LCM;
136
137public:
138
139
140
141 CallWithEntryStack CallStack;
142
143
144
145 std::unique_ptr PD;
146
147public:
151 const Decl *AnalysisEntryPoint);
152
153
154
156 assert(CurrentNode && "Already reached the root!");
158 }
159
160
161
162
163 const LocationContext *getLocationContextForActivePath() const {
164 return LCM.find(&PD->getActivePath())->getSecond();
165 }
166
167 const ExplodedNode *getCurrentNode() const { return CurrentNode; }
168
169
170
171 bool ascendToPrevNode() {
173 return static_cast<bool>(CurrentNode);
174 }
175
176 const ParentMap &getParentMap() const {
177 return getCurrLocationContext()->getParentMap();
178 }
179
180 const SourceManager &getSourceManager() const { return SM; }
181
182 const Stmt *getParent(const Stmt *S) const {
183 return getParentMap().getParent(S);
184 }
185
187 assert(Path && LC);
188 LCM[Path] = LC;
189 }
190
192 assert(LCM.count(Path) &&
193 "Failed to find the context associated with these pieces!");
194 return LCM.find(Path)->getSecond();
195 }
196
197 bool isInLocCtxMap(const PathPieces *Path) const { return LCM.count(Path); }
198
199 PathPieces &getActivePath() { return PD->getActivePath(); }
200 PathPieces &getMutablePieces() { return PD->getMutablePieces(); }
201
202 bool shouldAddPathEdges() const { return Consumer->shouldAddPathEdges(); }
203 bool shouldAddControlNotes() const {
205 }
206 bool shouldGenerateDiagnostics() const {
208 }
209 bool supportsLogicalOpControlFlow() const {
211 }
212};
213
214
215
216
217
219
220 std::unique_ptr BugPath;
221
222
223
224
226
227
229
230
231 std::unique_ptr VisitorsDiagnostics;
232
233public:
234
235
236
237 static std::optional
240
241 PathDiagnosticBuilder(
244 std::unique_ptr VisitorsDiagnostics);
245
246
247
248
249
250
251
252
253
254
255
256 std::unique_ptr
258
259private:
261 const CallWithEntryStack &CallStack) const;
262 void generatePathDiagnosticsForNode(PathDiagnosticConstruct &C,
264
265 void generateMinimalDiagForBlockEdge(PathDiagnosticConstruct &C,
267
269 generateDiagForGotoOP(const PathDiagnosticConstruct &C, const Stmt *S,
271
273 generateDiagForSwitchOP(const PathDiagnosticConstruct &C, const CFGBlock *Dst,
275
277 generateDiagForBinaryOP(const PathDiagnosticConstruct &C, const Stmt *T,
279
281 ExecutionContinues(const PathDiagnosticConstruct &C) const;
282
284 ExecutionContinues(llvm::raw_string_ostream &os,
285 const PathDiagnosticConstruct &C) const;
286
288};
289
290}
291
292
293
294
295
297
299 if (!N)
301
304
305
307 const auto *CE = dyn_cast_or_null(CallSite);
308 if (!CE)
309 return {};
310
311
312 for (auto [Idx, ArgExpr] : llvm::enumerate(CE->arguments())) {
314
315
317 if (AS == Sym) {
319 }
320
321
323
324 if (ArgExpr->getType()->isVoidPointerType())
325 continue;
326 SVal PSV = N->getState()->getSVal(Reg->getRegion());
328 if (AS == Sym) {
330 }
331 }
332 }
333
334
337 if (RetSym == Sym) {
339 }
340
342}
343
345 unsigned ArgIndex) {
346
347 ++ArgIndex;
348
349 return (llvm::Twine(Msg) + " via " + std::to_string(ArgIndex) +
350 llvm::getOrdinalSuffix(ArgIndex) + " parameter").str();
351}
352
353
354
355
356
360
361
362
363
366
368 return nullptr;
369
370 if (X->getTag() == tagPreferred && Y->getTag() == tagLesser)
372
373 if (Y->getTag() == tagPreferred && X->getTag() == tagLesser)
375
376 return nullptr;
377}
378
379
380
381
382
383
385 unsigned N = path.size();
386 if (N < 2)
387 return;
388
389
390
391
392 for (unsigned i = 0; i < N; ++i) {
393 auto piece = std::move(path.front());
394 path.pop_front();
395
396 switch (piece->getKind()) {
399 break;
402 break;
404 if (i == N-1)
405 break;
406
407 if (auto *nextEvent =
408 dyn_cast(path.front().get())) {
409 auto *event = cast(piece.get());
410
411
412
413 if (auto *pieceToKeep =
415 piece = std::move(pieceToKeep == event ? piece : path.front());
416 path.pop_front();
417 ++i;
418 }
419 }
420 break;
421 }
425 break;
426 }
427 path.push_back(std::move(piece));
428 }
429}
430
431
432
433
437 bool IsInteresting = false) {
438 bool containsSomethingInteresting = IsInteresting;
439 const unsigned N = pieces.size();
440
441 for (unsigned i = 0 ; i < N ; ++i) {
442
443
444 auto piece = std::move(pieces.front());
445 pieces.pop_front();
446
447 switch (piece->getKind()) {
449 auto &call = cast(*piece);
450
452 C, call.path, R,
453 R->isInteresting(C.getLocationContextFor(&call.path))))
454 continue;
455
456 containsSomethingInteresting = true;
457 break;
458 }
460 auto ¯o = cast(*piece);
462 continue;
463 containsSomethingInteresting = true;
464 break;
465 }
467 auto &event = cast(*piece);
468
469
470
471 containsSomethingInteresting |= !event.isPrunable();
472 break;
473 }
477 break;
478 }
479
480 pieces.push_back(std::move(piece));
481 }
482
483 return containsSomethingInteresting;
484}
485
486
488 for (unsigned int i = 0; i < Path.size(); ++i) {
489 auto Piece = std::move(Path.front());
490 Path.pop_front();
491 if (!isa(*Piece))
492 Path.push_back(std::move(Piece));
493 }
494}
495
496
497
499 assert(D);
501}
502
503
504
505static void
508 for (const auto &I : Pieces) {
509 auto *Call = dyn_cast(I.get());
510
512 continue;
513
514 if (LastCallLocation) {
516 if (CallerIsImplicit || ->callEnter.asLocation().isValid())
517 Call->callEnter = *LastCallLocation;
518 if (CallerIsImplicit || ->callReturn.asLocation().isValid())
519 Call->callReturn = *LastCallLocation;
520 }
521
522
523
525 if (Call->callEnterWithin.asLocation().isValid() &&
527 ThisCallLocation = &Call->callEnterWithin;
528 else
529 ThisCallLocation = &Call->callEnter;
530
531 assert(ThisCallLocation && "Outermost call has an invalid location");
533 }
534}
535
536
537
538
540 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
541 if (auto *C = dyn_cast(I->get()))
543
544 if (auto *M = dyn_cast(I->get()))
546
547 if (auto *CF = dyn_cast(I->get())) {
548 const Stmt *Start = CF->getStartLocation().asStmt();
549 const Stmt *End = CF->getEndLocation().asStmt();
550 if (isa_and_nonnull(Start)) {
551 I = Pieces.erase(I);
552 continue;
553 } else if (isa_and_nonnull(End)) {
554 PathPieces::iterator Next = std::next(I);
555 if (Next != E) {
556 if (auto *NextCF =
557 dyn_cast(Next->get())) {
558 NextCF->setStartLocation(CF->getStartLocation());
559 }
560 }
561 I = Pieces.erase(I);
562 continue;
563 }
564 }
565
566 I++;
567 }
568}
569
570
571
572
574 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
575 if (auto *C = dyn_cast(I->get()))
577
578 if (auto *M = dyn_cast(I->get()))
580
581 if (!(*I)->getLocation().isValid() ||
582 !(*I)->getLocation().asLocation().isValid()) {
583 I = Pieces.erase(I);
584 continue;
585 }
586 I++;
587 }
588}
589
591 const PathDiagnosticConstruct &C) const {
592 if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics())
594 C.getCurrLocationContext());
595
597 getSourceManager());
598}
599
601 llvm::raw_string_ostream &os, const PathDiagnosticConstruct &C) const {
602
603 if (os.str().empty())
604 os << ' ';
605
607
608 if (Loc.asStmt())
609 os << "Execution continues on line "
610 << getSourceManager().getExpansionLineNumber(Loc.asLocation())
611 << '.';
612 else {
613 os << "Execution jumps to the end of the ";
614 const Decl *D = C.getCurrLocationContext()->getDecl();
615 if (isa(D))
616 os << "method";
617 else if (isa(D))
618 os << "function";
619 else {
620 assert(isa(D));
621 os << "anonymous block";
622 }
623 os << '.';
624 }
625
626 return Loc;
627}
628
630 if (isa(S) && PM.isConsumedExpr(cast(S)))
632
635 return nullptr;
636
637 switch (Parent->getStmtClass()) {
638 case Stmt::ForStmtClass:
639 case Stmt::DoStmtClass:
640 case Stmt::WhileStmtClass:
641 case Stmt::ObjCForCollectionStmtClass:
642 case Stmt::CXXForRangeStmtClass:
644 default:
645 break;
646 }
647
648 return nullptr;
649}
650
653 bool allowNestedContexts = false) {
654 if (!S)
655 return {};
656
658
660 switch (Parent->getStmtClass()) {
661 case Stmt::BinaryOperatorClass: {
662 const auto *B = cast(Parent);
663 if (B->isLogicalOp())
665 break;
666 }
667 case Stmt::CompoundStmtClass:
668 case Stmt::StmtExprClass:
670 case Stmt::ChooseExprClass:
671
672
673 if (allowNestedContexts || cast(Parent)->getCond() == S)
675 else
677 case Stmt::BinaryConditionalOperatorClass:
678 case Stmt::ConditionalOperatorClass:
679
680
681 if (allowNestedContexts ||
682 cast(Parent)->getCond() == S)
684 else
686 case Stmt::CXXForRangeStmtClass:
687 if (cast(Parent)->getBody() == S)
689 break;
690 case Stmt::DoStmtClass:
692 case Stmt::ForStmtClass:
693 if (cast(Parent)->getBody() == S)
695 break;
696 case Stmt::IfStmtClass:
697 if (cast(Parent)->getCond() != S)
699 break;
700 case Stmt::ObjCForCollectionStmtClass:
701 if (cast(Parent)->getBody() == S)
703 break;
704 case Stmt::WhileStmtClass:
705 if (cast(Parent)->getCond() != S)
707 break;
708 default:
709 break;
710 }
711
713 }
714
715 assert(S && "Cannot have null Stmt for PathDiagnosticLocation");
716
718}
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734void PathDiagnosticBuilder::updateStackPiecesWithMessage(
736 if (R->hasCallStackHint(P))
737 for (const auto &I : CallStack) {
740 std::string stackMsg = R->getCallStackMessage(P, N);
741
742
743
744
747 }
748}
749
752
754 const PathDiagnosticConstruct &C, const CFGBlock *Dst,
756
758
759 std::string sbuf;
760 llvm::raw_string_ostream os(sbuf);
762
765
766 switch (S->getStmtClass()) {
767 default:
768 os << "No cases match in the switch statement. "
769 "Control jumps to line "
770 << End.asLocation().getExpansionLineNumber();
771 break;
772 case Stmt::DefaultStmtClass:
773 os << "Control jumps to the 'default' case at line "
774 << End.asLocation().getExpansionLineNumber();
775 break;
776
777 case Stmt::CaseStmtClass: {
778 os << "Control jumps to 'case ";
779 const auto *Case = cast(S);
781
782
783 bool GetRawInt = true;
784
785 if (const auto *DR = dyn_cast(LHS)) {
786
787
788 const auto *D = dyn_cast(DR->getDecl());
789
790 if (D) {
791 GetRawInt = false;
792 os << *D;
793 }
794 }
795
796 if (GetRawInt)
798
799 os << ":' at line " << End.asLocation().getExpansionLineNumber();
800 break;
801 }
802 }
803 } else {
804 os << "'Default' branch taken. ";
805 End = ExecutionContinues(os, C);
806 }
807 return std::make_shared(Start, End, sbuf);
808}
809
811 const PathDiagnosticConstruct &C, const Stmt *S,
813 std::string sbuf;
814 llvm::raw_string_ostream os(sbuf);
817 os << "Control jumps to line " << End.asLocation().getExpansionLineNumber();
818 return std::make_shared(Start, End, sbuf);
819}
820
822 const PathDiagnosticConstruct &C, const Stmt *T, const CFGBlock *Src,
824
826
827 const auto *B = cast(T);
828 std::string sbuf;
829 llvm::raw_string_ostream os(sbuf);
830 os << "Left side of '";
832
833 if (B->getOpcode() == BO_LAnd) {
834 os << "&&"
835 << "' is ";
836
837 if (*(Src->succ_begin() + 1) == Dst) {
838 os << "false";
840 Start =
842 } else {
843 os << "true";
844 Start =
846 End = ExecutionContinues(C);
847 }
848 } else {
849 assert(B->getOpcode() == BO_LOr);
850 os << "||"
851 << "' is ";
852
853 if (*(Src->succ_begin() + 1) == Dst) {
854 os << "false";
855 Start =
857 End = ExecutionContinues(C);
858 } else {
859 os << "true";
861 Start =
863 }
864 }
865 return std::make_shared(Start, End, sbuf);
866}
867
868void PathDiagnosticBuilder::generateMinimalDiagForBlockEdge(
869 PathDiagnosticConstruct &C, BlockEdge BE) const {
875 if ()
876 return;
877
879 switch (T->getStmtClass()) {
880 default:
881 break;
882
883 case Stmt::GotoStmtClass:
884 case Stmt::IndirectGotoStmtClass: {
885 if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics())
886 C.getActivePath().push_front(generateDiagForGotoOP(C, S, Start));
887 break;
888 }
889
890 case Stmt::SwitchStmtClass: {
891 C.getActivePath().push_front(generateDiagForSwitchOP(C, Dst, Start));
892 break;
893 }
894
895 case Stmt::BreakStmtClass:
896 case Stmt::ContinueStmtClass: {
897 std::string sbuf;
898 llvm::raw_string_ostream os(sbuf);
900 C.getActivePath().push_front(
901 std::make_shared(Start, End, sbuf));
902 break;
903 }
904
905
906 case Stmt::BinaryConditionalOperatorClass:
907 case Stmt::ConditionalOperatorClass: {
908 std::string sbuf;
909 llvm::raw_string_ostream os(sbuf);
910 os << "'?' condition is ";
911
913 os << "false";
914 else
915 os << "true";
916
918
919 if (const Stmt *S = End.asStmt())
921
922 C.getActivePath().push_front(
923 std::make_shared(Start, End, sbuf));
924 break;
925 }
926
927
928 case Stmt::BinaryOperatorClass: {
929 if (.supportsLogicalOpControlFlow())
930 break;
931
932 C.getActivePath().push_front(generateDiagForBinaryOP(C, T, Src, Dst));
933 break;
934 }
935
936 case Stmt::DoStmtClass:
938 std::string sbuf;
939 llvm::raw_string_ostream os(sbuf);
940
941 os << "Loop condition is true. ";
943
944 if (const Stmt *S = End.asStmt())
946
947 C.getActivePath().push_front(
948 std::make_shared(Start, End, sbuf));
949 } else {
951
952 if (const Stmt *S = End.asStmt())
954
955 C.getActivePath().push_front(
956 std::make_shared(
957 Start, End, "Loop condition is false. Exiting loop"));
958 }
959 break;
960
961 case Stmt::WhileStmtClass:
962 case Stmt::ForStmtClass:
963 if (*(Src->succ_begin() + 1) == Dst) {
964 std::string sbuf;
965 llvm::raw_string_ostream os(sbuf);
966
967 os << "Loop condition is false. ";
969 if (const Stmt *S = End.asStmt())
971
972 C.getActivePath().push_front(
973 std::make_shared(Start, End, sbuf));
974 } else {
976 if (const Stmt *S = End.asStmt())
978
979 C.getActivePath().push_front(
980 std::make_shared(
981 Start, End, "Loop condition is true. Entering loop body"));
982 }
983
984 break;
985
986 case Stmt::IfStmtClass: {
988
989 if (const Stmt *S = End.asStmt())
991
993 C.getActivePath().push_front(
994 std::make_shared(
995 Start, End, "Taking false branch"));
996 else
997 C.getActivePath().push_front(
998 std::make_shared(
999 Start, End, "Taking true branch"));
1000
1001 break;
1002 }
1003 }
1004}
1005
1006
1007
1008
1009
1012 case Stmt::ForStmtClass:
1013 case Stmt::WhileStmtClass:
1014 case Stmt::ObjCForCollectionStmtClass:
1015 case Stmt::CXXForRangeStmtClass:
1016 return true;
1017 default:
1018
1019 return false;
1020 }
1021}
1022
1027}
1028
1030 const Stmt *SubS) {
1031 while (SubS) {
1032 if (SubS == S)
1033 return true;
1035 }
1036 return false;
1037}
1038
1041 while (N) {
1043 if (SP) {
1044 const Stmt *S = SP->getStmt();
1046 return S;
1047 }
1049 }
1050 return nullptr;
1051}
1052
1054 const Stmt *LoopBody = nullptr;
1056 case Stmt::CXXForRangeStmtClass: {
1057 const auto *FR = cast(Term);
1059 return true;
1061 return true;
1062 LoopBody = FR->getBody();
1063 break;
1064 }
1065 case Stmt::ForStmtClass: {
1066 const auto *FS = cast(Term);
1068 return true;
1069 LoopBody = FS->getBody();
1070 break;
1071 }
1072 case Stmt::ObjCForCollectionStmtClass: {
1073 const auto *FC = cast(Term);
1074 LoopBody = FC->getBody();
1075 break;
1076 }
1077 case Stmt::WhileStmtClass:
1078 LoopBody = cast(Term)->getBody();
1079 break;
1080 default:
1081 return false;
1082 }
1084}
1085
1086
1091 return;
1092
1095 return;
1096
1098 PrevLoc = NewLoc;
1099 return;
1100 }
1101
1102
1103
1105 return;
1106
1107 path.push_front(
1108 std::make_shared(NewLoc, PrevLoc));
1109 PrevLoc = NewLoc;
1110}
1111
1112
1113
1116 if (const auto *FS = dyn_cast_or_null(S))
1117 return FS->getElement();
1118 return S;
1119}
1120
1122constexpr llvm::StringLiteral StrLoopBodyZero = "Loop body executed 0 times";
1124 "Loop body skipped when range is empty";
1126 "Loop body skipped when collection is empty";
1127
1128static std::unique_ptr
1130
1131void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
1135
1136
1137
1138
1139
1140 if (auto CE = P.getAs<CallEnter>()) {
1141
1142 if (C.shouldAddPathEdges()) {
1143
1146
1147
1148
1149
1150
1151
1155 }
1156
1157
1158 bool VisitedEntireCall = C.PD->isWithinCall();
1159 C.PD->popActivePath();
1160
1162 if (VisitedEntireCall) {
1163 Call = cast(C.getActivePath().front().get());
1164 } else {
1165
1166
1167 const Decl *Caller = CE->getLocationContext()->getDecl();
1169 assert(C.getActivePath().size() == 1 &&
1170 C.getActivePath().front().get() == Call);
1171
1172
1173
1174 assert(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());
1179
1180
1181 assert(.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());
1185 }
1186 Call->setCallee(*CE, SM);
1187
1188
1189 PrevLoc = Call->getLocation();
1190
1191 if (.CallStack.empty()) {
1192 assert(C.CallStack.back().first == Call);
1193 C.CallStack.pop_back();
1194 }
1195 return;
1196 }
1197
1198 assert(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!");
1201
1202
1203 if (std::optional CE = P.getAs<CallExitEnd>()) {
1204
1205
1206
1208
1209 assert(.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());
1213
1214 if (C.shouldAddPathEdges()) {
1215
1218 }
1219
1221 C.getActivePath().push_front(std::move(Call));
1222
1223
1224 C.PD->pushActivePath(&P->path);
1225 C.CallStack.push_back(CallWithEntry(P, C.getCurrentNode()));
1226 return;
1227 }
1228
1229 if (auto PS = P.getAs<PostStmt>()) {
1230 if (.shouldAddPathEdges())
1231 return;
1232
1233
1234
1235
1236 if (!isa(PS->getStmt())) {
1240 }
1241
1242 } else if (auto BE = P.getAs<BlockEdge>()) {
1243
1244 if (C.shouldAddControlNotes()) {
1245 generateMinimalDiagForBlockEdge(C, *BE);
1246 }
1247
1248 if (.shouldAddPathEdges()) {
1249 return;
1250 }
1251
1252
1255 const Stmt *Body = nullptr;
1256
1257 if (const auto *FS = dyn_cast(Loop))
1258 Body = FS->getBody();
1259 else if (const auto *WS = dyn_cast(Loop))
1260 Body = WS->getBody();
1261 else if (const auto *OFS = dyn_cast(Loop)) {
1262 Body = OFS->getBody();
1263 } else if (const auto *FRS = dyn_cast(Loop)) {
1264 Body = FRS->getBody();
1265 }
1266
1267
1268 auto p = std::make_shared(
1269 L, "Looping back to the head of the loop");
1270 p->setPrunable(true);
1271
1272 addEdgeToPath(C.getActivePath(), PrevLoc, p->getLocation());
1273
1274 if (.shouldAddControlNotes()) {
1275 C.getActivePath().push_front(std::move(p));
1276 }
1277
1278 if (const auto *CS = dyn_cast_or_null(Body)) {
1281 }
1282 }
1283
1285 const ParentMap &PM = C.getParentMap();
1286
1288
1289
1294
1295 StringRef str;
1296
1298 if (!IsInLoopBody) {
1299 if (isa(Term)) {
1301 } else if (isa(Term)) {
1303 } else {
1305 }
1306 }
1307 } else {
1309 }
1310
1311 if (!str.empty()) {
1313 C.getCurrLocationContext());
1314 auto PE = std::make_shared(L, str);
1315 PE->setPrunable(true);
1316 addEdgeToPath(C.getActivePath(), PrevLoc, PE->getLocation());
1317
1318
1319 if (.shouldAddControlNotes()) {
1320 C.getActivePath().push_front(std::move(PE));
1321 }
1322 }
1323 } else if (isa<BreakStmt, ContinueStmt, GotoStmt>(Term)) {
1326 }
1327 }
1328 }
1329}
1330
1331static std::unique_ptr
1333 const Decl *AnalysisEntryPoint) {
1335 return std::make_unique(
1339 AnalysisEntryPoint, std::make_unique());
1340}
1341
1342static std::unique_ptr
1345 const Decl *AnalysisEntryPoint) {
1347 return std::make_unique(
1352}
1353
1355 if (!S)
1356 return nullptr;
1357
1358 while (true) {
1360
1361 if (!S)
1362 break;
1363
1364 if (isa<FullExpr, CXXBindTemporaryExpr, SubstNonTypeTemplateParmExpr>(S))
1365 continue;
1366
1367 break;
1368 }
1369
1370 return S;
1371}
1372
1374 switch (S->getStmtClass()) {
1375 case Stmt::BinaryOperatorClass: {
1376 const auto *BO = cast(S);
1377 if (!BO->isLogicalOp())
1378 return false;
1379 return BO->getLHS() == Cond || BO->getRHS() == Cond;
1380 }
1381 case Stmt::IfStmtClass:
1382 return cast(S)->getCond() == Cond;
1383 case Stmt::ForStmtClass:
1384 return cast(S)->getCond() == Cond;
1385 case Stmt::WhileStmtClass:
1386 return cast(S)->getCond() == Cond;
1387 case Stmt::DoStmtClass:
1388 return cast(S)->getCond() == Cond;
1389 case Stmt::ChooseExprClass:
1390 return cast(S)->getCond() == Cond;
1391 case Stmt::IndirectGotoStmtClass:
1392 return cast(S)->getTarget() == Cond;
1393 case Stmt::SwitchStmtClass:
1394 return cast(S)->getCond() == Cond;
1395 case Stmt::BinaryConditionalOperatorClass:
1396 return cast(S)->getCond() == Cond;
1397 case Stmt::ConditionalOperatorClass: {
1398 const auto *CO = cast(S);
1399 return CO->getCond() == Cond ||
1400 CO->getLHS() == Cond ||
1401 CO->getRHS() == Cond;
1402 }
1403 case Stmt::ObjCForCollectionStmtClass:
1404 return cast(S)->getElement() == Cond;
1405 case Stmt::CXXForRangeStmtClass: {
1406 const auto *FRS = cast(S);
1407 return FRS->getCond() == Cond || FRS->getRangeInit() == Cond;
1408 }
1409 default:
1410 return false;
1411 }
1412}
1413
1415 if (const auto *FS = dyn_cast(FL))
1416 return FS->getInc() == S || FS->getInit() == S;
1417 if (const auto *FRS = dyn_cast(FL))
1418 return FRS->getInc() == S || FRS->getRangeStmt() == S ||
1419 FRS->getLoopVarStmt() || FRS->getRangeInit() == S;
1420 return false;
1421}
1422
1424
1425
1426
1427
1428
1429
1432 PathPieces::iterator Prev = pieces.end();
1433 for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E;
1434 Prev = I, ++I) {
1435 auto *Piece = dyn_cast(I->get());
1436
1437 if (!Piece)
1438 continue;
1439
1442
1444 const Stmt *InnerStmt = nullptr;
1445 while (NextSrcContext.isValid() && NextSrcContext.asStmt() != InnerStmt) {
1446 SrcContexts.push_back(NextSrcContext);
1447 InnerStmt = NextSrcContext.asStmt();
1449 true);
1450 }
1451
1452
1453
1454
1455 while (true) {
1456 const Stmt *Dst = Piece->getEndLocation().getStmtOrNull();
1457
1458
1459
1462 if (!DstContext.isValid() || DstContext.asStmt() == Dst)
1463 break;
1464
1465
1466 if (llvm::is_contained(SrcContexts, DstContext))
1467 break;
1468
1469
1470 Piece->setStartLocation(DstContext);
1471
1472
1473
1474 if (Prev != E) {
1475 auto *PrevPiece = dyn_cast(Prev->get());
1476
1477 if (PrevPiece) {
1478 if (const Stmt *PrevSrc =
1479 PrevPiece->getStartLocation().getStmtOrNull()) {
1481 if (PrevSrcParent ==
1483 PrevPiece->setEndLocation(DstContext);
1484 break;
1485 }
1486 }
1487 }
1488 }
1489
1490
1491
1492
1493 auto P =
1494 std::make_shared(SrcLoc, DstContext);
1495 Piece = P.get();
1496 I = pieces.insert(I, std::move(P));
1497 }
1498 }
1499}
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1512 for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) {
1513 const auto *PieceI = dyn_cast(I->get());
1514
1515 if (!PieceI)
1516 continue;
1517
1518 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1519 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1520
1521 if (!s1Start || !s1End)
1522 continue;
1523
1524 PathPieces::iterator NextI = I; ++NextI;
1525 if (NextI == E)
1526 break;
1527
1529
1530 while (true) {
1531 if (NextI == E)
1532 break;
1533
1534 const auto *EV = dyn_cast(NextI->get());
1535 if (EV) {
1536 StringRef S = EV->getString();
1539 ++NextI;
1540 continue;
1541 }
1542 break;
1543 }
1544
1545 PieceNextI = dyn_cast(NextI->get());
1546 break;
1547 }
1548
1549 if (!PieceNextI)
1550 continue;
1551
1554
1555 if (!s2Start || !s2End || s1End != s2Start)
1556 continue;
1557
1558
1559
1562 continue;
1563
1564
1566 continue;
1567
1568
1569
1571 I = pieces.erase(I);
1572 }
1573}
1574
1575
1576
1577
1578
1579
1580
1584 SM.getExpansionRange(Range.getEnd()).getEnd());
1585
1587 if (FID != SM.getFileID(ExpansionRange.getEnd()))
1588 return std::nullopt;
1589
1590 std::optional Buffer = SM.getBufferOrNone(FID);
1591 if (!Buffer)
1592 return std::nullopt;
1593
1594 unsigned BeginOffset = SM.getFileOffset(ExpansionRange.getBegin());
1595 unsigned EndOffset = SM.getFileOffset(ExpansionRange.getEnd());
1596 StringRef Snippet = Buffer->getBuffer().slice(BeginOffset, EndOffset);
1597
1598
1599
1600
1601
1602 if (Snippet.find_first_of("\r\n") != StringRef::npos)
1603 return std::nullopt;
1604
1605
1606 return Snippet.size();
1607}
1608
1609
1611 const Stmt *S) {
1613}
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1632 for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) {
1633
1634 const auto *PieceI = dyn_cast(I->get());
1635
1636 if (!PieceI) {
1637 ++I;
1638 continue;
1639 }
1640
1641 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1642 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1643
1644 PathPieces::iterator NextI = I; ++NextI;
1645 if (NextI == E)
1646 break;
1647
1648 const auto *PieceNextI =
1649 dyn_cast(NextI->get());
1650
1651 if (!PieceNextI) {
1652 if (isa(NextI->get())) {
1653 ++NextI;
1654 if (NextI == E)
1655 break;
1656 PieceNextI = dyn_cast(NextI->get());
1657 }
1658
1659 if (!PieceNextI) {
1660 ++I;
1661 continue;
1662 }
1663 }
1664
1665 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1666 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1667
1668 if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
1669 const size_t MAX_SHORT_LINE_LENGTH = 80;
1671 if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) {
1673 if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) {
1674 Path.erase(I);
1675 I = Path.erase(NextI);
1676 continue;
1677 }
1678 }
1679 }
1680
1681 ++I;
1682 }
1683}
1684
1685
1687 while (X) {
1688 if (X == Y)
1689 return true;
1691 }
1692 return false;
1693}
1694
1695
1698 bool erased = false;
1699
1700 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E;
1701 erased ? I : ++I) {
1702 erased = false;
1703
1704 const auto *PieceI = dyn_cast(I->get());
1705
1706 if (!PieceI)
1707 continue;
1708
1709 const Stmt *start = PieceI->getStartLocation().getStmtOrNull();
1710 const Stmt *end = PieceI->getEndLocation().getStmtOrNull();
1711
1712 if (!start || !end)
1713 continue;
1714
1716 if (!endParent)
1717 continue;
1718
1720 continue;
1721
1724
1725 if (.isWrittenInSameFile(FirstLoc, SecondLoc))
1726 continue;
1727 if (SM.isBeforeInTranslationUnit(SecondLoc, FirstLoc))
1728 std::swap(SecondLoc, FirstLoc);
1729
1730 SourceRange EdgeRange(FirstLoc, SecondLoc);
1732
1733
1734 if (!ByteWidth)
1735 continue;
1736
1737 const size_t MAX_PUNY_EDGE_LENGTH = 2;
1738 if (*ByteWidth <= MAX_PUNY_EDGE_LENGTH) {
1739
1740
1741
1742 I = path.erase(I);
1743 erased = true;
1744 continue;
1745 }
1746 }
1747}
1748
1750 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ++I) {
1751 const auto *PieceI = dyn_cast(I->get());
1752
1753 if (!PieceI)
1754 continue;
1755
1756 PathPieces::iterator NextI = I; ++NextI;
1757 if (NextI == E)
1758 return;
1759
1760 const auto *PieceNextI = dyn_cast(NextI->get());
1761
1762 if (!PieceNextI)
1763 continue;
1764
1765
1766 if (PieceI->getString() == PieceNextI->getString()) {
1767 path.erase(NextI);
1768 }
1769 }
1770}
1771
1774 bool hasChanges = false;
1776 assert(LC);
1779
1780 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) {
1781
1782 if (auto *CallI = dyn_cast(I->get())) {
1783
1784
1785 if (!OCS.count(CallI)) {
1787 }
1788 OCS.insert(CallI);
1789 }
1790 ++I;
1791 continue;
1792 }
1793
1794
1795 auto *PieceI = dyn_cast(I->get());
1796
1797 if (!PieceI) {
1798 ++I;
1799 continue;
1800 }
1801
1802 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1803 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1806
1807 PathPieces::iterator NextI = I; ++NextI;
1808 if (NextI == E)
1809 break;
1810
1811 const auto *PieceNextI = dyn_cast(NextI->get());
1812
1813 if (!PieceNextI) {
1814 ++I;
1815 continue;
1816 }
1817
1818 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1819 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837 if (level1 && level1 == level2 && level1 == level3 && level1 == level4) {
1838 PieceI->setEndLocation(PieceNextI->getEndLocation());
1839 path.erase(NextI);
1840 hasChanges = true;
1841 continue;
1842 }
1843
1844
1845
1846
1847
1848
1849
1850
1851 if (s1End && s1End == s2Start && level2) {
1852 bool removeEdge = false;
1853
1854
1855
1857 removeEdge = true;
1858
1859
1860
1862
1863
1864 if (isa(s1End) && PM.isConsumedExpr(cast(s1End))) {
1865 removeEdge = true;
1866 }
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878 else if (s1Start && s2End &&
1881 removeEdge = true;
1882 }
1883
1884
1885
1886
1887
1888
1889
1890
1891 else if (s1Start && s2End &&
1893 SourceRange EdgeRange(PieceI->getEndLocation().asLocation(),
1894 PieceI->getStartLocation().asLocation());
1896 removeEdge = true;
1897 }
1898 }
1899
1900 if (removeEdge) {
1901 PieceI->setEndLocation(PieceNextI->getEndLocation());
1902 path.erase(NextI);
1903 hasChanges = true;
1904 continue;
1905 }
1906 }
1907
1908
1909
1910
1911
1912
1913
1914
1915 if (s1End == s2Start) {
1916 const auto *FS = dyn_cast_or_null(level3);
1917 if (FS && FS->getCollection()->IgnoreParens() == s2Start &&
1918 s2End == FS->getElement()) {
1919 PieceI->setEndLocation(PieceNextI->getEndLocation());
1920 path.erase(NextI);
1921 hasChanges = true;
1922 continue;
1923 }
1924 }
1925
1926
1927 ++I;
1928 }
1929
1930 if (!hasChanges) {
1931
1932
1934
1936
1937
1939
1941
1943 }
1944
1945 return hasChanges;
1946}
1947
1948
1949
1950
1951
1952
1953
1956 const auto *FirstEdge =
1957 dyn_cast(Path.front().get());
1958 if (!FirstEdge)
1959 return;
1960
1961 const Decl *D = C.getLocationContextFor(&Path)->getDecl();
1964 if (FirstEdge->getStartLocation() != EntryLoc)
1965 return;
1966
1967 Path.pop_front();
1968}
1969
1970
1972
1975
1976 for (const auto &P : path) {
1977 FullSourceLoc Loc = P->getLocation().asLocation().getExpansionLoc();
1979 unsigned LineNo = Loc.getLineNumber();
1981 ExecutedLines[FID].insert(LineNo);
1982 }
1983}
1984
1985PathDiagnosticConstruct::PathDiagnosticConstruct(
1988 : Consumer(PDC), CurrentNode(ErrorNode),
1989 SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()),
1991 AnalysisEntryPoint)) {
1993}
1994
1995PathDiagnosticBuilder::PathDiagnosticBuilder(
1998 std::unique_ptr VisitorsDiagnostics)
2000 ErrorNode(ErrorNode),
2001 VisitorsDiagnostics(std::move(VisitorsDiagnostics)) {}
2002
2003std::unique_ptr
2005 const Decl *EntryPoint = getBugReporter().getAnalysisEntryPoint();
2006 PathDiagnosticConstruct Construct(PDC, ErrorNode, R, EntryPoint);
2007
2010
2013
2014
2015 auto EndNotes = VisitorsDiagnostics->find(ErrorNode);
2017 if (EndNotes != VisitorsDiagnostics->end()) {
2018 assert(!EndNotes->second.empty());
2019 LastPiece = EndNotes->second[0];
2020 } else {
2022 *getBugReport());
2023 }
2024 Construct.PD->setEndOfPath(LastPiece);
2025
2027
2028
2029 while (Construct.ascendToPrevNode()) {
2030 generatePathDiagnosticsForNode(Construct, PrevLoc);
2031
2032 auto VisitorNotes = VisitorsDiagnostics->find(Construct.getCurrentNode());
2033 if (VisitorNotes == VisitorsDiagnostics->end())
2034 continue;
2035
2036
2037
2038 std::setllvm::FoldingSetNodeID DeduplicationSet;
2039
2040
2042 llvm::FoldingSetNodeID ID;
2043 Note->Profile(ID);
2044 if (!DeduplicationSet.insert(ID).second)
2045 continue;
2046
2048 addEdgeToPath(Construct.getActivePath(), PrevLoc, Note->getLocation());
2049 updateStackPiecesWithMessage(Note, Construct.CallStack);
2050 Construct.getActivePath().push_front(Note);
2051 }
2052 }
2053
2055
2056
2058 Construct.getLocationContextForActivePath()->getStackFrame();
2060 addEdgeToPath(Construct.getActivePath(), PrevLoc,
2062 }
2063
2064
2065
2066 if (!Construct.PD->path.empty()) {
2068 bool stillHasNotes =
2070 assert(stillHasNotes);
2071 (void)stillHasNotes;
2072 }
2073
2074
2075 if (!Opts.ShouldAddPopUpNotes)
2077
2078
2081
2083
2084
2085
2086
2088 while (optimizeEdges(Construct, Construct.getMutablePieces(), OCS)) {
2089 }
2090
2091
2092
2094 }
2095
2096
2097
2098
2101 }
2102
2103 if (Opts.ShouldDisplayMacroExpansions)
2105
2106 return std::move(Construct.PD);
2107}
2108
2109
2110
2111
2112
2113void BugType::anchor() {}
2114
2115
2116
2117
2118
2119LLVM_ATTRIBUTE_USED static bool
2121 for (const std::pair<StringRef, StringRef> &Pair : Registry.Dependencies) {
2122 if (Pair.second == CheckerName)
2123 return true;
2124 }
2125 return false;
2126}
2127
2129 StringRef CheckerName) {
2131 if (Checker.FullName == CheckerName)
2132 return Checker.IsHidden;
2133 }
2134 llvm_unreachable(
2135 "Checker name not found in CheckerRegistry -- did you retrieve it "
2136 "correctly from CheckerManager::getCurrentCheckerName?");
2137}
2138
2140 const BugType &bt, StringRef shortDesc, StringRef desc,
2142 const Decl *DeclToUnique)
2143 : BugReport(Kind::PathSensitive, bt, shortDesc, desc), ErrorNode(errorNode),
2144 ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() : SourceRange()),
2145 UniqueingLocation(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*.");
2154
2157 ->getAnalysisManager()
2158 .getCheckerManager()
2159 ->getCheckerRegistryData(),
2161 "Hidden checkers musn't emit diagnostics as they are by definition "
2162 "non-user facing!");
2163}
2164
2166 std::unique_ptr visitor) {
2167 if (!visitor)
2168 return;
2169
2170 llvm::FoldingSetNodeID ID;
2171 visitor->Profile(ID);
2172
2173 void *InsertPos = nullptr;
2174 if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) {
2175 return;
2176 }
2177
2178 Callbacks.push_back(std::move(visitor));
2179}
2180
2183}
2184
2187 if (!N)
2188 return nullptr;
2189
2192}
2193
2195 hash.AddInteger(static_cast<int>(getKind()));
2196 hash.AddPointer(&BT);
2198 assert(Location.isValid());
2200
2202 if (!range.isValid())
2203 continue;
2204 hash.Add(range.getBegin());
2205 hash.Add(range.getEnd());
2206 }
2207}
2208
2210 hash.AddInteger(static_cast<int>(getKind()));
2211 hash.AddPointer(&BT);
2216 } else {
2217
2218
2219
2220
2222 }
2223
2225 if (!range.isValid())
2226 continue;
2227 hash.Add(range.getBegin());
2228 hash.Add(range.getEnd());
2229 }
2230}
2231
2232template
2234 llvm::DenseMap<T, bugreporter::TrackingKind> &InterestingnessMap, T Val,
2236 auto Result = InterestingnessMap.insert({Val, TKind});
2237
2239 return;
2240
2241
2242
2243
2244
2245
2246
2247 switch (TKind) {
2250 return;
2252 return;
2253 }
2254
2255 llvm_unreachable(
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!");
2259}
2260
2263 if (!sym)
2264 return;
2265
2267
2268
2269
2270 if (const auto *meta = dyn_cast(sym))
2272}
2273
2275 if (!sym)
2276 return;
2278
2279
2280
2281
2282 if (const auto *meta = dyn_cast(sym))
2284}
2285
2288 if (!R)
2289 return;
2290
2293
2294 if (const auto *SR = dyn_cast(R))
2296}
2297
2299 if (!R)
2300 return;
2301
2304
2305 if (const auto *SR = dyn_cast(R))
2307}
2308
2313}
2314
2316 if (!LC)
2317 return;
2319}
2320
2321std::optionalbugreporter::TrackingKind
2325 if (!RKind)
2326 return SKind;
2327 if (!SKind)
2328 return RKind;
2329
2330
2331
2332 switch(*RKind) {
2334 return RKind;
2336 return SKind;
2337 }
2338
2339 llvm_unreachable(
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 return std::nullopt;
2344}
2345
2346std::optionalbugreporter::TrackingKind
2348 if (!sym)
2349 return std::nullopt;
2350
2351
2354 return std::nullopt;
2355 return It->getSecond();
2356}
2357
2358std::optionalbugreporter::TrackingKind
2360 if (!R)
2361 return std::nullopt;
2362
2366 return It->getSecond();
2367
2368 if (const auto *SR = dyn_cast(R))
2370 return std::nullopt;
2371}
2372
2375}
2376
2379}
2380
2383}
2384
2386 if (!LC)
2387 return false;
2389}
2390
2393 return nullptr;
2394
2396 const Stmt *S = nullptr;
2397
2398 if (std::optional BE = ProgP.getAs<BlockEntrance>()) {
2400 if (BE->getBlock() == &Exit)
2402 }
2403 if (!S)
2405
2406 return S;
2407}
2408
2411
2412
2413 if (Ranges.empty() && isa_and_nonnull(getStmt()))
2415
2417}
2418
2420
2421
2424 }
2426}
2427
2428static const Stmt *
2431
2432
2434 return S;
2435
2436
2437 }
2439}
2440
2443 assert(ErrorNode && "Cannot create a location with a null node.");
2448 ErrorNode->getState()->getStateManager().getContext().getSourceManager();
2449
2450 if (!S) {
2451
2452 if (std::optional PIE = P.getAs<PreImplicitCall>())
2455 if (const ReturnStmt *RS = FE->getStmt())
2457
2459 }
2460 if (!S)
2462 }
2463
2464 if (S) {
2465
2466
2467
2468 if (const auto *AS = dyn_cast(S))
2469 S = AS->getSubStmt();
2470
2471
2472 if (const auto *ME = dyn_cast(S))
2474
2475
2476 if (const auto *B = dyn_cast(S))
2478
2481
2482 if (S->getBeginLoc().isValid())
2484
2487 }
2488
2490 SM);
2491}
2492
2493
2494
2495
2496
2499}
2500
2503}
2504
2506 : D(D), UserSuppressions(D.getASTContext()) {}
2507
2509
2510 assert(StrBugTypes.empty() &&
2511 "Destroying BugReporter before diagnostics are emitted!");
2512
2513
2514 for (const auto I : EQClassesVector)
2515 delete I;
2516}
2517
2519
2520
2521 for (const auto EQ : EQClassesVector)
2522 FlushReport(*EQ);
2523
2524
2525
2526
2527
2528 StrBugTypes.clear();
2529}
2530
2531
2532
2533
2534
2535namespace {
2536
2537
2538
2539class BugPathInfo {
2540public:
2541 std::unique_ptr BugPath;
2544};
2545
2546
2547
2548class BugPathGetter {
2549 std::unique_ptr TrimmedGraph;
2550
2551 using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>;
2552
2553
2554 PriorityMapTy PriorityMap;
2555
2556
2557
2558 using ReportNewNodePair =
2559 std::pair<PathSensitiveBugReport *, const ExplodedNode *>;
2561
2562 BugPathInfo CurrentBugPath;
2563
2564
2565 template
2566 class PriorityCompare {
2567 const PriorityMapTy &PriorityMap;
2568
2569 public:
2570 PriorityCompare(const PriorityMapTy &M) : PriorityMap(M) {}
2571
2573 PriorityMapTy::const_iterator LI = PriorityMap.find(LHS);
2574 PriorityMapTy::const_iterator RI = PriorityMap.find(RHS);
2575 PriorityMapTy::const_iterator E = PriorityMap.end();
2576
2577 if (LI == E)
2578 return Descending;
2579 if (RI == E)
2580 return !Descending;
2581
2582 return Descending ? LI->second > RI->second
2583 : LI->second < RI->second;
2584 }
2585
2586 bool operator()(const ReportNewNodePair &LHS,
2587 const ReportNewNodePair &RHS) const {
2588 return (*this)(LHS.second, RHS.second);
2589 }
2590 };
2591
2592public:
2593 BugPathGetter(const ExplodedGraph *OriginalGraph,
2595
2596 BugPathInfo *getNextBugPath();
2597};
2598
2599}
2600
2601BugPathGetter::BugPathGetter(const ExplodedGraph *OriginalGraph,
2604 for (const auto I : bugReports) {
2605 assert(I->isValid() &&
2606 "We only allow BugReporterVisitors and BugReporter itself to "
2607 "invalidate reports!");
2608 Nodes.emplace_back(I->getErrorNode());
2609 }
2610
2611
2612
2614 TrimmedGraph = OriginalGraph->trim(Nodes, &ForwardMap);
2615
2616
2617
2618
2620
2622 const ExplodedNode *NewNode = ForwardMap.lookup(Report->getErrorNode());
2623 assert(NewNode &&
2624 "Failed to construct a trimmed graph that contains this error "
2625 "node!");
2626 ReportNodes.emplace_back(Report, NewNode);
2627 RemainingNodes.insert(NewNode);
2628 }
2629
2630 assert(!RemainingNodes.empty() && "No error node found in the trimmed graph");
2631
2632
2633 std::queue<const ExplodedNode *> WS;
2634
2635 assert(TrimmedGraph->num_roots() == 1);
2636 WS.push(*TrimmedGraph->roots_begin());
2638
2639 while (!WS.empty()) {
2641 WS.pop();
2642
2643 PriorityMapTy::iterator PriorityEntry;
2644 bool IsNew;
2645 std::tie(PriorityEntry, IsNew) = PriorityMap.insert({Node, Priority});
2647
2648 if (!IsNew) {
2649 assert(PriorityEntry->second <= Priority);
2650 continue;
2651 }
2652
2653 if (RemainingNodes.erase(Node))
2654 if (RemainingNodes.empty())
2655 break;
2656
2658 WS.push(Succ);
2659 }
2660
2661
2662 llvm::sort(ReportNodes, PriorityCompare(PriorityMap));
2663}
2664
2665BugPathInfo *BugPathGetter::getNextBugPath() {
2666 if (ReportNodes.empty())
2667 return nullptr;
2668
2670 std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val();
2671 assert(PriorityMap.contains(OrigN) && "error node not accessible from root");
2672
2673
2674
2675 auto GNew = std::make_unique();
2676
2677
2678
2680 while (true) {
2681
2682
2683 ExplodedNode *NewN = GNew->createUncachedNode(
2686
2687
2688 if (Succ)
2690 else
2691 CurrentBugPath.ErrorNode = NewN;
2692
2693 Succ = NewN;
2694
2695
2697 GNew->addRoot(NewN);
2698 break;
2699 }
2700
2701
2702
2704 PriorityCompare(PriorityMap));
2705 }
2706
2707 CurrentBugPath.BugPath = std::move(GNew);
2708
2709 return &CurrentBugPath;
2710}
2711
2712
2713
2716 using MacroStackTy = std::vector<
2717 std::pair<std::shared_ptr, SourceLocation>>;
2718
2719 using PiecesTy = std::vector;
2720
2721 MacroStackTy MacroStack;
2722 PiecesTy Pieces;
2723
2724 for (PathPieces::const_iterator I = path.begin(), E = path.end();
2725 I != E; ++I) {
2726 const auto &piece = *I;
2727
2728
2729 if (auto *call = dyn_cast(&*piece)) {
2731 }
2732
2733
2734 const FullSourceLoc Loc = piece->getLocation().asLocation();
2735
2736
2737
2739 SM.getExpansionLoc(Loc) :
2741
2742 if (Loc.isFileID()) {
2743 MacroStack.clear();
2744 Pieces.push_back(piece);
2745 continue;
2746 }
2747
2748 assert(Loc.isMacroID());
2749
2750
2751 if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
2752 MacroStack.back().first->subPieces.push_back(piece);
2753 continue;
2754 }
2755
2756
2757
2758 std::shared_ptr MacroGroup;
2759
2761 SM.getExpansionLoc(Loc) :
2763
2764
2765 while (!MacroStack.empty()) {
2766 if (InstantiationLoc == MacroStack.back().second) {
2767 MacroGroup = MacroStack.back().first;
2768 break;
2769 }
2770
2771 if (ParentInstantiationLoc == MacroStack.back().second) {
2772 MacroGroup = MacroStack.back().first;
2773 break;
2774 }
2775
2776 MacroStack.pop_back();
2777 }
2778
2779 if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
2780
2781 auto NewGroup = std::make_shared(
2783
2784 if (MacroGroup)
2785 MacroGroup->subPieces.push_back(NewGroup);
2786 else {
2787 assert(InstantiationLoc.isFileID());
2788 Pieces.push_back(NewGroup);
2789 }
2790
2791 MacroGroup = NewGroup;
2792 MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
2793 }
2794
2795
2796 MacroGroup->subPieces.push_back(piece);
2797 }
2798
2799
2800 path.clear();
2801
2802 path.insert(path.end(), Pieces.begin(), Pieces.end());
2803}
2804
2805
2806
2807
2808static std::unique_ptr
2812 std::unique_ptr Notes =
2813 std::make_unique();
2815
2816
2817
2819 while (NextNode) {
2820
2821
2822
2823
2824
2825
2826 for (std::unique_ptr &Visitor : R->visitors())
2827 visitors.push_back(std::move(Visitor));
2828
2830
2832 if (!Pred) {
2834 for (auto &V : visitors) {
2835 V->finalizeVisitor(BRC, ErrorNode, *R);
2836
2837 if (auto Piece = V->getEndPath(BRC, ErrorNode, *R)) {
2838 assert(!LastPiece &&
2839 "There can only be one final piece in a diagnostic.");
2841 "The final piece must contain a message!");
2842 LastPiece = std::move(Piece);
2843 (*Notes)[ErrorNode].push_back(LastPiece);
2844 }
2845 }
2846 break;
2847 }
2848
2849 for (auto &V : visitors) {
2850 auto P = V->VisitNode(NextNode, BRC, *R);
2851 if (P)
2852 (*Notes)[NextNode].push_back(std::move(P));
2853 }
2854
2856 break;
2857
2858 NextNode = Pred;
2859 }
2860
2861 return Notes;
2862}
2863
2864std::optional PathDiagnosticBuilder::findValidReport(
2868
2869 BugPathGetter BugGraph(&Reporter.getGraph(), bugReports);
2870
2871 while (BugPathInfo *BugPath = BugGraph.getNextBugPath()) {
2872
2874 assert(R && "No original report found for sliced graph.");
2875 assert(R->isValid() && "Report selected by trimmed graph marked invalid.");
2876 const ExplodedNode *ErrorNode = BugPath->ErrorNode;
2877
2878
2879
2881
2882
2886
2888
2889
2890 std::unique_ptr visitorNotes =
2892
2895
2896
2901
2902
2903
2905 switch (Z3Oracle.interpretQueryResult(CrosscheckResult)) {
2907 ++NumTimesReportRefuted;
2908 R->markInvalid("Infeasible constraints", nullptr);
2909 continue;
2911 ++NumTimesReportEQClassAborted;
2912 return {};
2914 ++NumTimesReportPassesZ3;
2915 break;
2916 }
2917 }
2918
2920 return PathDiagnosticBuilder(std::move(BRC), std::move(BugPath->BugPath),
2921 BugPath->Report, BugPath->ErrorNode,
2922 std::move(visitorNotes));
2923 }
2924 }
2925
2926 ++NumTimesReportEQClassWasExhausted;
2927 return {};
2928}
2929
2930std::unique_ptr
2934 assert(!bugReports.empty());
2935
2936 auto Out = std::make_unique();
2937
2938 std::optional PDB =
2939 PathDiagnosticBuilder::findValidReport(bugReports, *this);
2940
2941 if (PDB) {
2943 if (std::unique_ptr PD = PDB->generate(PC)) {
2944 (*Out)[PC] = std::move(PD);
2945 }
2946 }
2947 }
2948
2949 return Out;
2950}
2951
2953 bool ValidSourceLoc = R->getLocation().isValid();
2954 assert(ValidSourceLoc);
2955
2956
2957 if (!ValidSourceLoc)
2958 return;
2959
2960
2962 return;
2963
2964
2965 llvm::FoldingSetNodeID ID;
2966 R->Profile(ID);
2967
2968
2969 void *InsertPos;
2971
2972 if (!EQ) {
2974 EQClasses.InsertNode(EQ, InsertPos);
2975 EQClassesVector.push_back(EQ);
2976 } else
2977 EQ->AddReport(std::move(R));
2978}
2979
2981 if (auto PR = dyn_cast(R.get()))
2982 if (const ExplodedNode *E = PR->getErrorNode()) {
2983
2984
2985 assert((E->isSink() || E->getLocation().getTag()) &&
2986 "Error node must either be a sink or have a tag");
2987
2989 E->getLocationContext()->getAnalysisDeclContext();
2990
2991
2992
2993
2996 return;
2997 }
2998
3000}
3001
3002
3003
3004
3005
3006namespace {
3007
3008struct FRIEC_WLItem {
3011
3013 : N(n), I(N->succ_begin()), E(N->succ_end()) {}
3014};
3015
3016}
3017
3018BugReport *PathSensitiveBugReporter::findReportInEquivalenceClass(
3020
3021
3022
3023 assert(EQ.getReports().size() > 0);
3024 const BugType& BT = EQ.getReports()[0]->getBugType();
3026 BugReport *R = EQ.getReports()[0].get();
3027 for (auto &J : EQ.getReports()) {
3028 if (auto *PR = dyn_cast(J.get())) {
3029 R = PR;
3030 bugReports.push_back(PR);
3031 }
3032 }
3033 return R;
3034 }
3035
3036
3037
3038
3039
3040
3041
3042 BugReport *exampleReport = nullptr;
3043
3044 for (const auto &I: EQ.getReports()) {
3045 auto *R = dyn_cast(I.get());
3046 if (!R)
3047 continue;
3048
3049 const ExplodedNode *errorNode = R->getErrorNode();
3050 if (errorNode->isSink()) {
3051 llvm_unreachable(
3052 "BugType::isSuppressSink() should not be 'true' for sink end nodes");
3053 }
3054
3056 bugReports.push_back(R);
3057 if (!exampleReport)
3058 exampleReport = R;
3059 continue;
3060 }
3061
3062
3063
3064
3065
3067 if (ErrorB->isInevitablySinking())
3068 continue;
3069
3070
3071
3072 using WLItem = FRIEC_WLItem;
3074
3075 llvm::DenseMap<const ExplodedNode *, unsigned> Visited;
3076
3077 DFSWorkList WL;
3078 WL.push_back(errorNode);
3080
3081 while (!WL.empty()) {
3082 WLItem &WI = WL.back();
3083 assert(!WI.N->succ_empty());
3084
3085 for (; WI.I != WI.E; ++WI.I) {
3087
3089
3090 if (!Succ->isSink()) {
3091 bugReports.push_back(R);
3092 if (!exampleReport)
3093 exampleReport = R;
3094 WL.clear();
3095 break;
3096 }
3097
3098 continue;
3099 }
3100
3101
3102 unsigned &mark = Visited[Succ];
3103 if (!mark) {
3104 mark = 1;
3105 WL.push_back(Succ);
3106 break;
3107 }
3108 }
3109
3110
3111
3112 if (!WL.empty() && &WL.back() == &WI)
3113 WL.pop_back();
3114 }
3115 }
3116
3117
3118
3119 return exampleReport;
3120}
3121
3124 BugReport *report = findReportInEquivalenceClass(EQ, bugReports);
3125 if (!report)
3126 return;
3127
3128
3129 for (const std::string &CheckerOrPackage :
3132 return;
3133 }
3134
3136 std::unique_ptr Diagnostics =
3138
3139 for (auto &P : *Diagnostics) {
3141 std::unique_ptr &PD = P.second;
3142
3143
3144
3145 if (PD->path.empty()) {
3147 auto piece = std::make_unique(
3150 piece->addRange(Range);
3151 PD->setEndOfPath(std::move(piece));
3152 }
3153
3154 PathPieces &Pieces = PD->getMutablePieces();
3156
3157
3158 for (const auto &I : llvm::reverse(report->getNotes())) {
3160 auto ConvertedPiece = std::make_shared(
3162 for (const auto &R: Piece->getRanges())
3163 ConvertedPiece->addRange(R);
3164
3165 Pieces.push_front(std::move(ConvertedPiece));
3166 }
3167 } else {
3168 for (const auto &I : llvm::reverse(report->getNotes()))
3169 Pieces.push_front(I);
3170 }
3171
3172 for (const auto &I : report->getFixits())
3173 Pieces.back()->addFixit(I);
3174
3176
3177
3181 Pieces.push_front(std::make_shared(
3183 "[debug] analyzing from " +
3185 }
3187 }
3188}
3189
3190
3191
3196 const Stmt* Body = Signature->getBody();
3197 if (const auto FD = dyn_cast(Signature)) {
3198 SignatureSourceRange = FD->getSourceRange();
3199 } else if (const auto OD = dyn_cast(Signature)) {
3200 SignatureSourceRange = OD->getSourceRange();
3201 } else {
3202 return;
3203 }
3206 : SignatureSourceRange.getEnd();
3207 if (!Start.isValid() || !End.isValid())
3208 return;
3209 unsigned StartLine = SM.getExpansionLineNumber(Start);
3210 unsigned EndLine = SM.getExpansionLineNumber(End);
3211
3212 FileID FID = SM.getFileID(SM.getExpansionLoc(Start));
3213 for (unsigned Line = StartLine; Line <= EndLine; Line++)
3214 ExecutedLines[FID].insert(Line);
3215}
3216
3222 return;
3224 FileID FID = SM.getFileID(ExpansionLoc);
3225 unsigned LineNo = SM.getExpansionLineNumber(ExpansionLoc);
3226 ExecutedLines[FID].insert(LineNo);
3227}
3228
3229
3230
3231static std::unique_ptr
3233 auto ExecutedLines = std::make_unique();
3234
3235 while (N) {
3237
3241
3242 const Decl* D = CE->getCalleeContext()->getDecl();
3246
3247
3249
3250
3251
3252
3253 if (const auto *RS = dyn_cast_or_null(P)) {
3256 }
3257
3258 if (isa_and_nonnull<SwitchCase, LabelStmt>(P))
3260 }
3261
3263 }
3264 return ExecutedLines;
3265}
3266
3267std::unique_ptr
3271 auto *basicReport = cast(exampleReport);
3272 auto Out = std::make_unique();
3273 for (auto *Consumer : consumers)
3274 (*Out)[Consumer] =
3276 return Out;
3277}
3278
3283
3284
3286 return nullptr;
3287
3289 "The call piece should not be in a header file.");
3290
3291
3293 return CP;
3294
3296 if (Path.empty())
3297 return nullptr;
3298
3299
3300
3301 if (auto *CPInner = dyn_cast(Path.back().get()))
3303
3304
3305 return nullptr;
3306}
3307
3309 if (PD.path.empty())
3310 return;
3311
3313 assert(LastP);
3315
3316
3317
3318 if (auto *CP = dyn_cast(LastP)) {
3320 if (CP) {
3321
3323
3324
3325 const auto *ND = dyn_cast(CP->getCallee());
3326 if (ND) {
3328 llvm::raw_svector_ostream os(buf);
3329 os << " (within a call to '" << ND->getDeclName() << "')";
3331 }
3332
3333
3336
3337 return;
3338 }
3339 }
3340}
3341
3342
3343
3344std::unique_ptr
3345PathSensitiveBugReporter::generateDiagnosticForConsumerMap(
3348 std::vector<BasicBugReport *> BasicBugReports;
3349 std::vector<PathSensitiveBugReport *> PathSensitiveBugReports;
3350 if (isa(exampleReport))
3352 consumers, bugReports);
3353
3354
3355
3356
3357
3358 assert(!bugReports.empty());
3359 MaxBugClassSize.updateMax(bugReports.size());
3360
3361
3366 consumers, convertedArrayOfReports);
3367
3368 if (Out->empty())
3369 return Out;
3370
3371 MaxValidBugClassSize.updateMax(bugReports.size());
3372
3373
3374
3376 for (auto const &P : *Out)
3377 if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.AnalyzeAll)
3379
3380 return Out;
3381}
3382
3385 StringRef Category, StringRef Str,
3390 Loc, Ranges, Fixits);
3391}
3392
3395 StringRef name, StringRef category,
3399
3400 BugType *BT = getBugTypeForName(CheckName, name, category);
3401 auto R = std::make_unique(*BT, str, Loc);
3402 R->setDeclWithIssue(DeclWithIssue);
3403 for (const auto &SR : Ranges)
3405 for (const auto &FH : Fixits)
3408}
3409
3411 StringRef name, StringRef category) {
3413 llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name
3414 << ":" << category;
3415 std::unique_ptr &BT = StrBugTypes[fullDesc];
3416 if (!BT)
3417 BT = std::make_unique(CheckName, name, category);
3418 return BT.get();
3419}
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