clang 22.0.0git
BugReporter.cpp
Go to the documentation of this file.
1//===- BugReporter.cpp - Generate PathDiagnostics for bugs ----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines BugReporter, a utility class for generating
10// PathDiagnostics.
11//
12//===----------------------------------------------------------------------===//
13
16#include "clang/AST/Attr.h"
17#include "clang/AST/Decl.h"
18#include "clang/AST/DeclBase.h"
19#include "clang/AST/DeclObjC.h"
20#include "clang/AST/Expr.h"
21#include "clang/AST/ExprCXX.h"
22#include "clang/AST/ParentMap.h"
23#include "clang/AST/Stmt.h"
24#include "clang/AST/StmtCXX.h"
25#include "clang/AST/StmtObjC.h"
27#include "clang/Analysis/CFG.h"
30#include "clang/Basic/LLVM.h"
47#include "llvm/ADT/ArrayRef.h"
48#include "llvm/ADT/DenseMap.h"
49#include "llvm/ADT/DenseSet.h"
50#include "llvm/ADT/FoldingSet.h"
51#include "llvm/ADT/STLExtras.h"
52#include "llvm/ADT/SmallPtrSet.h"
53#include "llvm/ADT/StringExtras.h"
54#include "llvm/ADT/StringRef.h"
55#include "llvm/Support/Compiler.h"
56#include "llvm/Support/ErrorHandling.h"
57#include "llvm/Support/TimeProfiler.h"
58#include "llvm/Support/raw_ostream.h"
59#include <algorithm>
60#include <cassert>
61#include <cstddef>
62#include <iterator>
63#include <memory>
64#include <optional>
65#include <queue>
66#include <string>
67#include <tuple>
68#include <utility>
69#include <vector>
70
71using namespace clang;
72using namespace ento;
73using namespace llvm;
74
75#define DEBUG_TYPE "BugReporter"
76
77STAT_MAX(MaxBugClassSize,
78 "The maximum number of bug reports in the same equivalence class");
79STAT_MAX(MaxValidBugClassSize,
80 "The maximum number of bug reports in the same equivalence class "
81 "where at least one report is valid (not suppressed)");
82
83STAT_COUNTER(NumTimesReportPassesZ3, "Number of reports passed Z3");
84STAT_COUNTER(NumTimesReportRefuted, "Number of reports refuted by Z3");
85STAT_COUNTER(NumTimesReportEQClassAborted,
86 "Number of times a report equivalence class was aborted by the Z3 "
87 "oracle heuristic");
88STAT_COUNTER(NumTimesReportEQClassWasExhausted,
89 "Number of times all reports of an equivalence class was refuted");
90
92
93void BugReporterContext::anchor() {}
94
95//===----------------------------------------------------------------------===//
96// PathDiagnosticBuilder and its associated routines and helper objects.
97//===----------------------------------------------------------------------===//
98
99namespace {
100
101/// A (CallPiece, node assiciated with its CallEnter) pair.
102using CallWithEntry =
103 std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>;
104using CallWithEntryStack = SmallVector<CallWithEntry, 6>;
105
106/// Map from each node to the diagnostic pieces visitors emit for them.
107using VisitorsDiagnosticsTy =
108 llvm::DenseMap<const ExplodedNode *, std::vector<PathDiagnosticPieceRef>>;
109
110/// A map from PathDiagnosticPiece to the LocationContext of the inlined
111/// function call it represents.
112using LocationContextMap =
113 llvm::DenseMap<const PathPieces *, const LocationContext *>;
114
115/// A helper class that contains everything needed to construct a
116/// PathDiagnostic object. It does no much more then providing convenient
117/// getters and some well placed asserts for extra security.
118class PathDiagnosticConstruct {
119 /// The consumer we're constructing the bug report for.
120 const PathDiagnosticConsumer *Consumer;
121 /// Our current position in the bug path, which is owned by
122 /// PathDiagnosticBuilder.
123 const ExplodedNode *CurrentNode;
124 /// A mapping from parts of the bug path (for example, a function call, which
125 /// would span backwards from a CallExit to a CallEnter with the nodes in
126 /// between them) with the location contexts it is associated with.
127 LocationContextMap LCM;
128 const SourceManager &SM;
129
130public:
131 /// We keep stack of calls to functions as we're ascending the bug path.
132 /// TODO: PathDiagnostic has a stack doing the same thing, shouldn't we use
133 /// that instead?
134 CallWithEntryStack CallStack;
135 /// The bug report we're constructing. For ease of use, this field is kept
136 /// public, though some "shortcut" getters are provided for commonly used
137 /// methods of PathDiagnostic.
138 std::unique_ptr<PathDiagnostic> PD;
139
140public:
141 PathDiagnosticConstruct(const PathDiagnosticConsumer *PDC,
142 const ExplodedNode *ErrorNode,
143 const PathSensitiveBugReport *R,
144 const Decl *AnalysisEntryPoint);
145
146 /// \returns the location context associated with the current position in the
147 /// bug path.
148 const LocationContext *getCurrLocationContext() const {
149 assert(CurrentNode && "Already reached the root!");
150 return CurrentNode->getLocationContext();
151 }
152
153 /// Same as getCurrLocationContext (they should always return the same
154 /// location context), but works after reaching the root of the bug path as
155 /// well.
156 const LocationContext *getLocationContextForActivePath() const {
157 return LCM.find(&PD->getActivePath())->getSecond();
158 }
159
160 const ExplodedNode *getCurrentNode() const { return CurrentNode; }
161
162 /// Steps the current node to its predecessor.
163 /// \returns whether we reached the root of the bug path.
164 bool ascendToPrevNode() {
165 CurrentNode = CurrentNode->getFirstPred();
166 return static_cast<bool>(CurrentNode);
167 }
168
169 const ParentMap &getParentMap() const {
170 return getCurrLocationContext()->getParentMap();
171 }
172
173 const SourceManager &getSourceManager() const { return SM; }
174
175 const Stmt *getParent(const Stmt *S) const {
176 return getParentMap().getParent(S);
177 }
178
179 void updateLocCtxMap(const PathPieces *Path, const LocationContext *LC) {
180 assert(Path && LC);
181 LCM[Path] = LC;
182 }
183
184 const LocationContext *getLocationContextFor(const PathPieces *Path) const {
185 assert(LCM.count(Path) &&
186 "Failed to find the context associated with these pieces!");
187 return LCM.find(Path)->getSecond();
188 }
189
190 bool isInLocCtxMap(const PathPieces *Path) const { return LCM.count(Path); }
191
192 PathPieces &getActivePath() { return PD->getActivePath(); }
193 PathPieces &getMutablePieces() { return PD->getMutablePieces(); }
194
195 bool shouldAddPathEdges() const { return Consumer->shouldAddPathEdges(); }
196 bool shouldAddControlNotes() const {
197 return Consumer->shouldAddControlNotes();
198 }
199 bool shouldGenerateDiagnostics() const {
200 return Consumer->shouldGenerateDiagnostics();
201 }
202 bool supportsLogicalOpControlFlow() const {
203 return Consumer->supportsLogicalOpControlFlow();
204 }
205};
206
207/// Contains every contextual information needed for constructing a
208/// PathDiagnostic object for a given bug report. This class and its fields are
209/// immutable, and passes a BugReportConstruct object around during the
210/// construction.
211class PathDiagnosticBuilder : public BugReporterContext {
212 /// A linear path from the error node to the root.
213 std::unique_ptr<const ExplodedGraph> BugPath;
214 /// The bug report we're describing. Visitors create their diagnostics with
215 /// them being the last entities being able to modify it (for example,
216 /// changing interestingness here would cause inconsistencies as to how this
217 /// file and visitors construct diagnostics), hence its const.
218 const PathSensitiveBugReport *R;
219 /// The leaf of the bug path. This isn't the same as the bug reports error
220 /// node, which refers to the *original* graph, not the bug path.
221 const ExplodedNode *const ErrorNode;
222 /// The diagnostic pieces visitors emitted, which is expected to be collected
223 /// by the time this builder is constructed.
224 std::unique_ptr<const VisitorsDiagnosticsTy> VisitorsDiagnostics;
225
226public:
227 /// Find a non-invalidated report for a given equivalence class, and returns
228 /// a PathDiagnosticBuilder able to construct bug reports for different
229 /// consumers. Returns std::nullopt if no valid report is found.
230 static std::optional<PathDiagnosticBuilder>
231 findValidReport(ArrayRef<PathSensitiveBugReport *> &bugReports,
232 PathSensitiveBugReporter &Reporter);
233
234 PathDiagnosticBuilder(
235 BugReporterContext BRC, std::unique_ptr<ExplodedGraph> BugPath,
236 PathSensitiveBugReport *r, const ExplodedNode *ErrorNode,
237 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics);
238
239 /// This function is responsible for generating diagnostic pieces that are
240 /// *not* provided by bug report visitors.
241 /// These diagnostics may differ depending on the consumer's settings,
242 /// and are therefore constructed separately for each consumer.
243 ///
244 /// There are two path diagnostics generation modes: with adding edges (used
245 /// for plists) and without (used for HTML and text). When edges are added,
246 /// the path is modified to insert artificially generated edges.
247 /// Otherwise, more detailed diagnostics is emitted for block edges,
248 /// explaining the transitions in words.
249 std::unique_ptr<PathDiagnostic>
250 generate(const PathDiagnosticConsumer *PDC) const;
251
252private:
253 void updateStackPiecesWithMessage(PathDiagnosticPieceRef P,
254 const CallWithEntryStack &CallStack) const;
255 void generatePathDiagnosticsForNode(PathDiagnosticConstruct &C,
256 PathDiagnosticLocation &PrevLoc) const;
257
258 void generateMinimalDiagForBlockEdge(PathDiagnosticConstruct &C,
259 BlockEdge BE) const;
260
262 generateDiagForGotoOP(const PathDiagnosticConstruct &C, const Stmt *S,
263 PathDiagnosticLocation &Start) const;
264
266 generateDiagForSwitchOP(const PathDiagnosticConstruct &C, const CFGBlock *Dst,
267 PathDiagnosticLocation &Start) const;
268
270 generateDiagForBinaryOP(const PathDiagnosticConstruct &C, const Stmt *T,
271 const CFGBlock *Src, const CFGBlock *DstC) const;
272
273 PathDiagnosticLocation
274 ExecutionContinues(const PathDiagnosticConstruct &C) const;
275
276 PathDiagnosticLocation
277 ExecutionContinues(llvm::raw_string_ostream &os,
278 const PathDiagnosticConstruct &C) const;
279
280 const PathSensitiveBugReport *getBugReport() const { return R; }
281};
282
283std::string timeTraceName(const BugReportEquivClass &EQ) {
284 if (!llvm::timeTraceProfilerEnabled())
285 return "";
286 const auto &BugReports = EQ.getReports();
287 if (BugReports.empty())
288 return "Empty Equivalence Class";
289 const BugReport *R = BugReports.front().get();
290 const auto &BT = R->getBugType();
291 return ("Flushing EQC " + BT.getDescription()).str();
292}
293
294llvm::TimeTraceMetadata timeTraceMetadata(const BugReportEquivClass &EQ,
295 const SourceManager &SM) {
296 // Must be called only when constructing non-bogus TimeTraceScope
297 assert(llvm::timeTraceProfilerEnabled());
298
299 const auto &BugReports = EQ.getReports();
300 if (BugReports.empty())
301 return {};
302 const BugReport *R = BugReports.front().get();
303 const auto &BT = R->getBugType();
304 auto Loc = R->getLocation().asLocation();
305 std::string File = SM.getFilename(Loc).str();
306 return {BT.getCheckerName().str(), std::move(File),
307 static_cast<int>(Loc.getLineNumber())};
308}
309
310} // namespace
311
312//===----------------------------------------------------------------------===//
313// Base implementation of stack hint generators.
314//===----------------------------------------------------------------------===//
315
317
319 if (!N)
321
322 ProgramPoint P = N->getLocation();
323 CallExitEnd CExit = P.castAs<CallExitEnd>();
324
325 // FIXME: Use CallEvent to abstract this over all calls.
326 const Stmt *CallSite = CExit.getCalleeContext()->getCallSite();
327 const auto *CE = dyn_cast_or_null<CallExpr>(CallSite);
328 if (!CE)
329 return {};
330
331 // Check if one of the parameters are set to the interesting symbol.
332 for (auto [Idx, ArgExpr] : llvm::enumerate(CE->arguments())) {
333 SVal SV = N->getSVal(ArgExpr);
334
335 // Check if the variable corresponding to the symbol is passed by value.
336 SymbolRef AS = SV.getAsLocSymbol();
337 if (AS == Sym) {
338 return getMessageForArg(ArgExpr, Idx);
339 }
340
341 // Check if the parameter is a pointer to the symbol.
342 if (std::optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) {
343 // Do not attempt to dereference void*.
344 if (ArgExpr->getType()->isVoidPointerType())
345 continue;
346 SVal PSV = N->getState()->getSVal(Reg->getRegion());
347 SymbolRef AS = PSV.getAsLocSymbol();
348 if (AS == Sym) {
349 return getMessageForArg(ArgExpr, Idx);
350 }
351 }
352 }
353
354 // Check if we are returning the interesting symbol.
355 SVal SV = N->getSVal(CE);
356 SymbolRef RetSym = SV.getAsLocSymbol();
357 if (RetSym == Sym) {
358 return getMessageForReturn(CE);
359 }
360
362}
363
365 unsigned ArgIndex) {
366 // Printed parameters start at 1, not 0.
367 ++ArgIndex;
368
369 return (llvm::Twine(Msg) + " via " + std::to_string(ArgIndex) +
370 llvm::getOrdinalSuffix(ArgIndex) + " parameter").str();
371}
372
373//===----------------------------------------------------------------------===//
374// Diagnostic cleanup.
375//===----------------------------------------------------------------------===//
376
380 // Prefer diagnostics that come from ConditionBRVisitor over
381 // those that came from TrackConstraintBRVisitor,
382 // unless the one from ConditionBRVisitor is
383 // its generic fallback diagnostic.
384 const void *tagPreferred = ConditionBRVisitor::getTag();
385 const void *tagLesser = TrackConstraintBRVisitor::getTag();
386
387 if (X->getLocation() != Y->getLocation())
388 return nullptr;
389
390 if (X->getTag() == tagPreferred && Y->getTag() == tagLesser)
392
393 if (Y->getTag() == tagPreferred && X->getTag() == tagLesser)
395
396 return nullptr;
397}
398
399/// An optimization pass over PathPieces that removes redundant diagnostics
400/// generated by both ConditionBRVisitor and TrackConstraintBRVisitor. Both
401/// BugReporterVisitors use different methods to generate diagnostics, with
402/// one capable of emitting diagnostics in some cases but not in others. This
403/// can lead to redundant diagnostic pieces at the same point in a path.
404static void removeRedundantMsgs(PathPieces &path) {
405 unsigned N = path.size();
406 if (N < 2)
407 return;
408 // NOTE: this loop intentionally is not using an iterator. Instead, we
409 // are streaming the path and modifying it in place. This is done by
410 // grabbing the front, processing it, and if we decide to keep it append
411 // it to the end of the path. The entire path is processed in this way.
412 for (unsigned i = 0; i < N; ++i) {
413 auto piece = std::move(path.front());
414 path.pop_front();
415
416 switch (piece->getKind()) {
419 break;
422 break;
424 if (i == N-1)
425 break;
426
427 if (auto *nextEvent =
428 dyn_cast<PathDiagnosticEventPiece>(path.front().get())) {
429 auto *event = cast<PathDiagnosticEventPiece>(piece.get());
430 // Check to see if we should keep one of the two pieces. If we
431 // come up with a preference, record which piece to keep, and consume
432 // another piece from the path.
433 if (auto *pieceToKeep =
434 eventsDescribeSameCondition(event, nextEvent)) {
435 piece = std::move(pieceToKeep == event ? piece : path.front());
436 path.pop_front();
437 ++i;
438 }
439 }
440 break;
441 }
445 break;
446 }
447 path.push_back(std::move(piece));
448 }
449}
450
451/// Recursively scan through a path and prune out calls and macros pieces
452/// that aren't needed. Return true if afterwards the path contains
453/// "interesting stuff" which means it shouldn't be pruned from the parent path.
454static bool removeUnneededCalls(const PathDiagnosticConstruct &C,
455 PathPieces &pieces,
456 const PathSensitiveBugReport *R,
457 bool IsInteresting = false) {
458 bool containsSomethingInteresting = IsInteresting;
459 const unsigned N = pieces.size();
460
461 for (unsigned i = 0 ; i < N ; ++i) {
462 // Remove the front piece from the path. If it is still something we
463 // want to keep once we are done, we will push it back on the end.
464 auto piece = std::move(pieces.front());
465 pieces.pop_front();
466
467 switch (piece->getKind()) {
469 auto &call = cast<PathDiagnosticCallPiece>(*piece);
470 // Check if the location context is interesting.
472 C, call.path, R,
473 R->isInteresting(C.getLocationContextFor(&call.path))))
474 continue;
475
476 containsSomethingInteresting = true;
477 break;
478 }
480 auto &macro = cast<PathDiagnosticMacroPiece>(*piece);
481 if (!removeUnneededCalls(C, macro.subPieces, R, IsInteresting))
482 continue;
483 containsSomethingInteresting = true;
484 break;
485 }
487 auto &event = cast<PathDiagnosticEventPiece>(*piece);
488
489 // We never throw away an event, but we do throw it away wholesale
490 // as part of a path if we throw the entire path away.
491 containsSomethingInteresting |= !event.isPrunable();
492 break;
493 }
497 break;
498 }
499
500 pieces.push_back(std::move(piece));
501 }
502
503 return containsSomethingInteresting;
504}
505
506/// Same logic as above to remove extra pieces.
507static void removePopUpNotes(PathPieces &Path) {
508 for (unsigned int i = 0; i < Path.size(); ++i) {
509 auto Piece = std::move(Path.front());
510 Path.pop_front();
512 Path.push_back(std::move(Piece));
513 }
514}
515
516/// Returns true if the given decl has been implicitly given a body, either by
517/// the analyzer or by the compiler proper.
518static bool hasImplicitBody(const Decl *D) {
519 assert(D);
520 return D->isImplicit() || !D->hasBody();
521}
522
523/// Recursively scan through a path and make sure that all call pieces have
524/// valid locations.
525static void
527 PathDiagnosticLocation *LastCallLocation = nullptr) {
528 for (const auto &I : Pieces) {
529 auto *Call = dyn_cast<PathDiagnosticCallPiece>(I.get());
530
531 if (!Call)
532 continue;
533
534 if (LastCallLocation) {
535 bool CallerIsImplicit = hasImplicitBody(Call->getCaller());
536 if (CallerIsImplicit || !Call->callEnter.asLocation().isValid())
537 Call->callEnter = *LastCallLocation;
538 if (CallerIsImplicit || !Call->callReturn.asLocation().isValid())
539 Call->callReturn = *LastCallLocation;
540 }
541
542 // Recursively clean out the subclass. Keep this call around if
543 // it contains any informative diagnostics.
544 PathDiagnosticLocation *ThisCallLocation;
545 if (Call->callEnterWithin.asLocation().isValid() &&
546 !hasImplicitBody(Call->getCallee()))
547 ThisCallLocation = &Call->callEnterWithin;
548 else
549 ThisCallLocation = &Call->callEnter;
550
551 assert(ThisCallLocation && "Outermost call has an invalid location");
552 adjustCallLocations(Call->path, ThisCallLocation);
553 }
554}
555
556/// Remove edges in and out of C++ default initializer expressions. These are
557/// for fields that have in-class initializers, as opposed to being initialized
558/// explicitly in a constructor or braced list.
560 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
561 if (auto *C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
563
564 if (auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
566
567 if (auto *CF = dyn_cast<PathDiagnosticControlFlowPiece>(I->get())) {
568 const Stmt *Start = CF->getStartLocation().asStmt();
569 const Stmt *End = CF->getEndLocation().asStmt();
570 if (isa_and_nonnull<CXXDefaultInitExpr>(Start)) {
571 I = Pieces.erase(I);
572 continue;
573 } else if (isa_and_nonnull<CXXDefaultInitExpr>(End)) {
574 PathPieces::iterator Next = std::next(I);
575 if (Next != E) {
576 if (auto *NextCF =
577 dyn_cast<PathDiagnosticControlFlowPiece>(Next->get())) {
578 NextCF->setStartLocation(CF->getStartLocation());
579 }
580 }
581 I = Pieces.erase(I);
582 continue;
583 }
584 }
585
586 I++;
587 }
588}
589
590/// Remove all pieces with invalid locations as these cannot be serialized.
591/// We might have pieces with invalid locations as a result of inlining Body
592/// Farm generated functions.
594 for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
595 if (auto *C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
597
598 if (auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
600
601 if (!(*I)->getLocation().isValid() ||
602 !(*I)->getLocation().asLocation().isValid()) {
603 I = Pieces.erase(I);
604 continue;
605 }
606 I++;
607 }
608}
609
610PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues(
611 const PathDiagnosticConstruct &C) const {
612 if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics())
613 return PathDiagnosticLocation(S, getSourceManager(),
614 C.getCurrLocationContext());
615
616 return PathDiagnosticLocation::createDeclEnd(C.getCurrLocationContext(),
617 getSourceManager());
618}
619
620PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues(
621 llvm::raw_string_ostream &os, const PathDiagnosticConstruct &C) const {
622 // Slow, but probably doesn't matter.
623 if (os.str().empty())
624 os << ' ';
625
626 const PathDiagnosticLocation &Loc = ExecutionContinues(C);
627
628 if (Loc.asStmt())
629 os << "Execution continues on line "
630 << getSourceManager().getExpansionLineNumber(Loc.asLocation())
631 << '.';
632 else {
633 os << "Execution jumps to the end of the ";
634 const Decl *D = C.getCurrLocationContext()->getDecl();
635 if (isa<ObjCMethodDecl>(D))
636 os << "method";
637 else if (isa<FunctionDecl>(D))
638 os << "function";
639 else {
640 assert(isa<BlockDecl>(D));
641 os << "anonymous block";
642 }
643 os << '.';
644 }
645
646 return Loc;
647}
648
649static const Stmt *getEnclosingParent(const Stmt *S, const ParentMap &PM) {
650 if (isa<Expr>(S) && PM.isConsumedExpr(cast<Expr>(S)))
651 return PM.getParentIgnoreParens(S);
652
653 const Stmt *Parent = PM.getParentIgnoreParens(S);
654 if (!Parent)
655 return nullptr;
656
657 switch (Parent->getStmtClass()) {
658 case Stmt::ForStmtClass:
659 case Stmt::DoStmtClass:
660 case Stmt::WhileStmtClass:
661 case Stmt::ObjCForCollectionStmtClass:
662 case Stmt::CXXForRangeStmtClass:
663 return Parent;
664 default:
665 break;
666 }
667
668 return nullptr;
669}
670
673 bool allowNestedContexts = false) {
674 if (!S)
675 return {};
676
677 const SourceManager &SMgr = LC->getDecl()->getASTContext().getSourceManager();
678
679 while (const Stmt *Parent = getEnclosingParent(S, LC->getParentMap())) {
680 switch (Parent->getStmtClass()) {
681 case Stmt::BinaryOperatorClass: {
682 const auto *B = cast<BinaryOperator>(Parent);
683 if (B->isLogicalOp())
684 return PathDiagnosticLocation(allowNestedContexts ? B : S, SMgr, LC);
685 break;
686 }
687 case Stmt::CompoundStmtClass:
688 case Stmt::StmtExprClass:
689 return PathDiagnosticLocation(S, SMgr, LC);
690 case Stmt::ChooseExprClass:
691 // Similar to '?' if we are referring to condition, just have the edge
692 // point to the entire choose expression.
693 if (allowNestedContexts || cast<ChooseExpr>(Parent)->getCond() == S)
694 return PathDiagnosticLocation(Parent, SMgr, LC);
695 else
696 return PathDiagnosticLocation(S, SMgr, LC);
697 case Stmt::BinaryConditionalOperatorClass:
698 case Stmt::ConditionalOperatorClass:
699 // For '?', if we are referring to condition, just have the edge point
700 // to the entire '?' expression.
701 if (allowNestedContexts ||
702 cast<AbstractConditionalOperator>(Parent)->getCond() == S)
703 return PathDiagnosticLocation(Parent, SMgr, LC);
704 else
705 return PathDiagnosticLocation(S, SMgr, LC);
706 case Stmt::CXXForRangeStmtClass:
707 if (cast<CXXForRangeStmt>(Parent)->getBody() == S)
708 return PathDiagnosticLocation(S, SMgr, LC);
709 break;
710 case Stmt::DoStmtClass:
711 return PathDiagnosticLocation(S, SMgr, LC);
712 case Stmt::ForStmtClass:
713 if (cast<ForStmt>(Parent)->getBody() == S)
714 return PathDiagnosticLocation(S, SMgr, LC);
715 break;
716 case Stmt::IfStmtClass:
717 if (cast<IfStmt>(Parent)->getCond() != S)
718 return PathDiagnosticLocation(S, SMgr, LC);
719 break;
720 case Stmt::ObjCForCollectionStmtClass:
721 if (cast<ObjCForCollectionStmt>(Parent)->getBody() == S)
722 return PathDiagnosticLocation(S, SMgr, LC);
723 break;
724 case Stmt::WhileStmtClass:
725 if (cast<WhileStmt>(Parent)->getCond() != S)
726 return PathDiagnosticLocation(S, SMgr, LC);
727 break;
728 default:
729 break;
730 }
731
732 S = Parent;
733 }
734
735 assert(S && "Cannot have null Stmt for PathDiagnosticLocation");
736
737 return PathDiagnosticLocation(S, SMgr, LC);
738}
739
740//===----------------------------------------------------------------------===//
741// "Minimal" path diagnostic generation algorithm.
742//===----------------------------------------------------------------------===//
743
744/// If the piece contains a special message, add it to all the call pieces on
745/// the active stack. For example, my_malloc allocated memory, so MallocChecker
746/// will construct an event at the call to malloc(), and add a stack hint that
747/// an allocated memory was returned. We'll use this hint to construct a message
748/// when returning from the call to my_malloc
749///
750/// void *my_malloc() { return malloc(sizeof(int)); }
751/// void fishy() {
752/// void *ptr = my_malloc(); // returned allocated memory
753/// } // leak
754void PathDiagnosticBuilder::updateStackPiecesWithMessage(
755 PathDiagnosticPieceRef P, const CallWithEntryStack &CallStack) const {
756 if (R->hasCallStackHint(P))
757 for (const auto &I : CallStack) {
758 PathDiagnosticCallPiece *CP = I.first;
759 const ExplodedNode *N = I.second;
760 std::string stackMsg = R->getCallStackMessage(P, N);
761
762 // The last message on the path to final bug is the most important
763 // one. Since we traverse the path backwards, do not add the message
764 // if one has been previously added.
765 if (!CP->hasCallStackMessage())
766 CP->setCallStackMessage(stackMsg);
767 }
768}
769
770static void CompactMacroExpandedPieces(PathPieces &path,
771 const SourceManager& SM);
772
773PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForSwitchOP(
774 const PathDiagnosticConstruct &C, const CFGBlock *Dst,
775 PathDiagnosticLocation &Start) const {
776
777 const SourceManager &SM = getSourceManager();
778 // Figure out what case arm we took.
779 std::string sbuf;
780 llvm::raw_string_ostream os(sbuf);
782
783 if (const Stmt *S = Dst->getLabel()) {
784 End = PathDiagnosticLocation(S, SM, C.getCurrLocationContext());
785
786 switch (S->getStmtClass()) {
787 default:
788 os << "No cases match in the switch statement. "
789 "Control jumps to line "
791 break;
792 case Stmt::DefaultStmtClass:
793 os << "Control jumps to the 'default' case at line "
795 break;
796
797 case Stmt::CaseStmtClass: {
798 os << "Control jumps to 'case ";
799 const auto *Case = cast<CaseStmt>(S);
800 const Expr *LHS = Case->getLHS()->IgnoreParenImpCasts();
801
802 // Determine if it is an enum.
803 bool GetRawInt = true;
804
805 if (const auto *DR = dyn_cast<DeclRefExpr>(LHS)) {
806 // FIXME: Maybe this should be an assertion. Are there cases
807 // were it is not an EnumConstantDecl?
808 const auto *D = dyn_cast<EnumConstantDecl>(DR->getDecl());
809
810 if (D) {
811 GetRawInt = false;
812 os << *D;
813 }
814 }
815
816 if (GetRawInt)
817 os << LHS->EvaluateKnownConstInt(getASTContext());
818
819 os << ":' at line " << End.asLocation().getExpansionLineNumber();
820 break;
821 }
822 }
823 } else {
824 os << "'Default' branch taken. ";
825 End = ExecutionContinues(os, C);
826 }
827 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf);
828}
829
830PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForGotoOP(
831 const PathDiagnosticConstruct &C, const Stmt *S,
832 PathDiagnosticLocation &Start) const {
833 std::string sbuf;
834 llvm::raw_string_ostream os(sbuf);
835 const PathDiagnosticLocation &End =
836 getEnclosingStmtLocation(S, C.getCurrLocationContext());
837 os << "Control jumps to line " << End.asLocation().getExpansionLineNumber();
838 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf);
839}
840
841PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForBinaryOP(
842 const PathDiagnosticConstruct &C, const Stmt *T, const CFGBlock *Src,
843 const CFGBlock *Dst) const {
844
845 const SourceManager &SM = getSourceManager();
846
847 const auto *B = cast<BinaryOperator>(T);
848 std::string sbuf;
849 llvm::raw_string_ostream os(sbuf);
850 os << "Left side of '";
851 PathDiagnosticLocation Start, End;
852
853 if (B->getOpcode() == BO_LAnd) {
854 os << "&&"
855 << "' is ";
856
857 if (*(Src->succ_begin() + 1) == Dst) {
858 os << "false";
859 End = PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext());
860 Start =
862 } else {
863 os << "true";
864 Start =
865 PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext());
866 End = ExecutionContinues(C);
867 }
868 } else {
869 assert(B->getOpcode() == BO_LOr);
870 os << "||"
871 << "' is ";
872
873 if (*(Src->succ_begin() + 1) == Dst) {
874 os << "false";
875 Start =
876 PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext());
877 End = ExecutionContinues(C);
878 } else {
879 os << "true";
880 End = PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext());
881 Start =
883 }
884 }
885 return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf);
886}
887
888void PathDiagnosticBuilder::generateMinimalDiagForBlockEdge(
889 PathDiagnosticConstruct &C, BlockEdge BE) const {
890 const SourceManager &SM = getSourceManager();
891 const LocationContext *LC = C.getCurrLocationContext();
892 const CFGBlock *Src = BE.getSrc();
893 const CFGBlock *Dst = BE.getDst();
894 const Stmt *T = Src->getTerminatorStmt();
895 if (!T)
896 return;
897
898 auto Start = PathDiagnosticLocation::createBegin(T, SM, LC);
899 switch (T->getStmtClass()) {
900 default:
901 break;
902
903 case Stmt::GotoStmtClass:
904 case Stmt::IndirectGotoStmtClass: {
905 if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics())
906 C.getActivePath().push_front(generateDiagForGotoOP(C, S, Start));
907 break;
908 }
909
910 case Stmt::SwitchStmtClass: {
911 C.getActivePath().push_front(generateDiagForSwitchOP(C, Dst, Start));
912 break;
913 }
914
915 case Stmt::BreakStmtClass:
916 case Stmt::ContinueStmtClass: {
917 std::string sbuf;
918 llvm::raw_string_ostream os(sbuf);
919 PathDiagnosticLocation End = ExecutionContinues(os, C);
920 C.getActivePath().push_front(
921 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf));
922 break;
923 }
924
925 // Determine control-flow for ternary '?'.
926 case Stmt::BinaryConditionalOperatorClass:
927 case Stmt::ConditionalOperatorClass: {
928 std::string sbuf;
929 llvm::raw_string_ostream os(sbuf);
930 os << "'?' condition is ";
931
932 if (*(Src->succ_begin() + 1) == Dst)
933 os << "false";
934 else
935 os << "true";
936
937 PathDiagnosticLocation End = ExecutionContinues(C);
938
939 if (const Stmt *S = End.asStmt())
940 End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
941
942 C.getActivePath().push_front(
943 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf));
944 break;
945 }
946
947 // Determine control-flow for short-circuited '&&' and '||'.
948 case Stmt::BinaryOperatorClass: {
949 if (!C.supportsLogicalOpControlFlow())
950 break;
951
952 C.getActivePath().push_front(generateDiagForBinaryOP(C, T, Src, Dst));
953 break;
954 }
955
956 case Stmt::DoStmtClass:
957 if (*(Src->succ_begin()) == Dst) {
958 std::string sbuf;
959 llvm::raw_string_ostream os(sbuf);
960
961 os << "Loop condition is true. ";
962 PathDiagnosticLocation End = ExecutionContinues(os, C);
963
964 if (const Stmt *S = End.asStmt())
965 End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
966
967 C.getActivePath().push_front(
968 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf));
969 } else {
970 PathDiagnosticLocation End = ExecutionContinues(C);
971
972 if (const Stmt *S = End.asStmt())
973 End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
974
975 C.getActivePath().push_front(
976 std::make_shared<PathDiagnosticControlFlowPiece>(
977 Start, End, "Loop condition is false. Exiting loop"));
978 }
979 break;
980
981 case Stmt::WhileStmtClass:
982 case Stmt::ForStmtClass:
983 if (*(Src->succ_begin() + 1) == Dst) {
984 std::string sbuf;
985 llvm::raw_string_ostream os(sbuf);
986
987 os << "Loop condition is false. ";
988 PathDiagnosticLocation End = ExecutionContinues(os, C);
989 if (const Stmt *S = End.asStmt())
990 End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
991
992 C.getActivePath().push_front(
993 std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, sbuf));
994 } else {
995 PathDiagnosticLocation End = ExecutionContinues(C);
996 if (const Stmt *S = End.asStmt())
997 End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
998
999 C.getActivePath().push_front(
1000 std::make_shared<PathDiagnosticControlFlowPiece>(
1001 Start, End, "Loop condition is true. Entering loop body"));
1002 }
1003
1004 break;
1005
1006 case Stmt::IfStmtClass: {
1007 PathDiagnosticLocation End = ExecutionContinues(C);
1008
1009 if (const Stmt *S = End.asStmt())
1010 End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
1011
1012 if (*(Src->succ_begin() + 1) == Dst)
1013 C.getActivePath().push_front(
1014 std::make_shared<PathDiagnosticControlFlowPiece>(
1015 Start, End, "Taking false branch"));
1016 else
1017 C.getActivePath().push_front(
1018 std::make_shared<PathDiagnosticControlFlowPiece>(
1019 Start, End, "Taking true branch"));
1020
1021 break;
1022 }
1023 }
1024}
1025
1026//===----------------------------------------------------------------------===//
1027// Functions for determining if a loop was executed 0 times.
1028//===----------------------------------------------------------------------===//
1029
1030static bool isLoop(const Stmt *Term) {
1031 switch (Term->getStmtClass()) {
1032 case Stmt::ForStmtClass:
1033 case Stmt::WhileStmtClass:
1034 case Stmt::ObjCForCollectionStmtClass:
1035 case Stmt::CXXForRangeStmtClass:
1036 return true;
1037 default:
1038 // Note that we intentionally do not include do..while here.
1039 return false;
1040 }
1041}
1042
1043static bool isJumpToFalseBranch(const BlockEdge *BE) {
1044 const CFGBlock *Src = BE->getSrc();
1045 assert(Src->succ_size() == 2);
1046 return (*(Src->succ_begin()+1) == BE->getDst());
1047}
1048
1049static bool isContainedByStmt(const ParentMap &PM, const Stmt *S,
1050 const Stmt *SubS) {
1051 while (SubS) {
1052 if (SubS == S)
1053 return true;
1054 SubS = PM.getParent(SubS);
1055 }
1056 return false;
1057}
1058
1059static const Stmt *getStmtBeforeCond(const ParentMap &PM, const Stmt *Term,
1060 const ExplodedNode *N) {
1061 while (N) {
1062 std::optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>();
1063 if (SP) {
1064 const Stmt *S = SP->getStmt();
1065 if (!isContainedByStmt(PM, Term, S))
1066 return S;
1067 }
1068 N = N->getFirstPred();
1069 }
1070 return nullptr;
1071}
1072
1073static bool isInLoopBody(const ParentMap &PM, const Stmt *S, const Stmt *Term) {
1074 const Stmt *LoopBody = nullptr;
1075 switch (Term->getStmtClass()) {
1076 case Stmt::CXXForRangeStmtClass: {
1077 const auto *FR = cast<CXXForRangeStmt>(Term);
1078 if (isContainedByStmt(PM, FR->getInc(), S))
1079 return true;
1080 if (isContainedByStmt(PM, FR->getLoopVarStmt(), S))
1081 return true;
1082 LoopBody = FR->getBody();
1083 break;
1084 }
1085 case Stmt::ForStmtClass: {
1086 const auto *FS = cast<ForStmt>(Term);
1087 if (isContainedByStmt(PM, FS->getInc(), S))
1088 return true;
1089 LoopBody = FS->getBody();
1090 break;
1091 }
1092 case Stmt::ObjCForCollectionStmtClass: {
1093 const auto *FC = cast<ObjCForCollectionStmt>(Term);
1094 LoopBody = FC->getBody();
1095 break;
1096 }
1097 case Stmt::WhileStmtClass:
1098 LoopBody = cast<WhileStmt>(Term)->getBody();
1099 break;
1100 default:
1101 return false;
1102 }
1103 return isContainedByStmt(PM, LoopBody, S);
1104}
1105
1106/// Adds a sanitized control-flow diagnostic edge to a path.
1107static void addEdgeToPath(PathPieces &path,
1108 PathDiagnosticLocation &PrevLoc,
1109 PathDiagnosticLocation NewLoc) {
1110 if (!NewLoc.isValid())
1111 return;
1112
1113 SourceLocation NewLocL = NewLoc.asLocation();
1114 if (NewLocL.isInvalid())
1115 return;
1116
1117 if (!PrevLoc.isValid() || !PrevLoc.asLocation().isValid()) {
1118 PrevLoc = NewLoc;
1119 return;
1120 }
1121
1122 // Ignore self-edges, which occur when there are multiple nodes at the same
1123 // statement.
1124 if (NewLoc.asStmt() && NewLoc.asStmt() == PrevLoc.asStmt())
1125 return;
1126
1127 path.push_front(
1128 std::make_shared<PathDiagnosticControlFlowPiece>(NewLoc, PrevLoc));
1129 PrevLoc = NewLoc;
1130}
1131
1132/// A customized wrapper for CFGBlock::getTerminatorCondition()
1133/// which returns the element for ObjCForCollectionStmts.
1134static const Stmt *getTerminatorCondition(const CFGBlock *B) {
1135 const Stmt *S = B->getTerminatorCondition();
1136 if (const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(S))
1137 return FS->getElement();
1138 return S;
1139}
1140
1141constexpr llvm::StringLiteral StrEnteringLoop = "Entering loop body";
1142constexpr llvm::StringLiteral StrLoopBodyZero = "Loop body executed 0 times";
1143constexpr llvm::StringLiteral StrLoopRangeEmpty =
1144 "Loop body skipped when range is empty";
1145constexpr llvm::StringLiteral StrLoopCollectionEmpty =
1146 "Loop body skipped when collection is empty";
1147
1148static std::unique_ptr<FilesToLineNumsMap>
1150
1151void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
1152 PathDiagnosticConstruct &C, PathDiagnosticLocation &PrevLoc) const {
1153 ProgramPoint P = C.getCurrentNode()->getLocation();
1154 const SourceManager &SM = getSourceManager();
1155
1156 // Have we encountered an entrance to a call? It may be
1157 // the case that we have not encountered a matching
1158 // call exit before this point. This means that the path
1159 // terminated within the call itself.
1160 if (auto CE = P.getAs<CallEnter>()) {
1161
1162 if (C.shouldAddPathEdges()) {
1163 // Add an edge to the start of the function.
1164 const StackFrameContext *CalleeLC = CE->getCalleeContext();
1165 const Decl *D = CalleeLC->getDecl();
1166 // Add the edge only when the callee has body. We jump to the beginning
1167 // of the *declaration*, however we expect it to be followed by the
1168 // body. This isn't the case for autosynthesized property accessors in
1169 // Objective-C. No need for a similar extra check for CallExit points
1170 // because the exit edge comes from a statement (i.e. return),
1171 // not from declaration.
1172 if (D->hasBody())
1173 addEdgeToPath(C.getActivePath(), PrevLoc,
1175 }
1176
1177 // Did we visit an entire call?
1178 bool VisitedEntireCall = C.PD->isWithinCall();
1179 C.PD->popActivePath();
1180
1182 if (VisitedEntireCall) {
1183 Call = cast<PathDiagnosticCallPiece>(C.getActivePath().front().get());
1184 } else {
1185 // The path terminated within a nested location context, create a new
1186 // call piece to encapsulate the rest of the path pieces.
1187 const Decl *Caller = CE->getLocationContext()->getDecl();
1188 Call = PathDiagnosticCallPiece::construct(C.getActivePath(), Caller);
1189 assert(C.getActivePath().size() == 1 &&
1190 C.getActivePath().front().get() == Call);
1191
1192 // Since we just transferred the path over to the call piece, reset the
1193 // mapping of the active path to the current location context.
1194 assert(C.isInLocCtxMap(&C.getActivePath()) &&
1195 "When we ascend to a previously unvisited call, the active path's "
1196 "address shouldn't change, but rather should be compacted into "
1197 "a single CallEvent!");
1198 C.updateLocCtxMap(&C.getActivePath(), C.getCurrLocationContext());
1199
1200 // Record the location context mapping for the path within the call.
1201 assert(!C.isInLocCtxMap(&Call->path) &&
1202 "When we ascend to a previously unvisited call, this must be the "
1203 "first time we encounter the caller context!");
1204 C.updateLocCtxMap(&Call->path, CE->getCalleeContext());
1205 }
1206 Call->setCallee(*CE, SM);
1207
1208 // Update the previous location in the active path.
1209 PrevLoc = Call->getLocation();
1210
1211 if (!C.CallStack.empty()) {
1212 assert(C.CallStack.back().first == Call);
1213 C.CallStack.pop_back();
1214 }
1215 return;
1216 }
1217
1218 assert(C.getCurrLocationContext() == C.getLocationContextForActivePath() &&
1219 "The current position in the bug path is out of sync with the "
1220 "location context associated with the active path!");
1221
1222 // Have we encountered an exit from a function call?
1223 if (std::optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
1224
1225 // We are descending into a call (backwards). Construct
1226 // a new call piece to contain the path pieces for that call.
1228 // Record the mapping from call piece to LocationContext.
1229 assert(!C.isInLocCtxMap(&Call->path) &&
1230 "We just entered a call, this must've been the first time we "
1231 "encounter its context!");
1232 C.updateLocCtxMap(&Call->path, CE->getCalleeContext());
1233
1234 if (C.shouldAddPathEdges()) {
1235 // Add the edge to the return site.
1236 addEdgeToPath(C.getActivePath(), PrevLoc, Call->callReturn);
1237 PrevLoc.invalidate();
1238 }
1239
1240 auto *P = Call.get();
1241 C.getActivePath().push_front(std::move(Call));
1242
1243 // Make the contents of the call the active path for now.
1244 C.PD->pushActivePath(&P->path);
1245 C.CallStack.push_back(CallWithEntry(P, C.getCurrentNode()));
1246 return;
1247 }
1248
1249 if (auto PS = P.getAs<PostStmt>()) {
1250 if (!C.shouldAddPathEdges())
1251 return;
1252
1253 // Add an edge. If this is an ObjCForCollectionStmt do
1254 // not add an edge here as it appears in the CFG both
1255 // as a terminator and as a terminator condition.
1256 if (!isa<ObjCForCollectionStmt>(PS->getStmt())) {
1258 PathDiagnosticLocation(PS->getStmt(), SM, C.getCurrLocationContext());
1259 addEdgeToPath(C.getActivePath(), PrevLoc, L);
1260 }
1261
1262 } else if (auto BE = P.getAs<BlockEdge>()) {
1263
1264 if (C.shouldAddControlNotes()) {
1265 generateMinimalDiagForBlockEdge(C, *BE);
1266 }
1267
1268 if (!C.shouldAddPathEdges()) {
1269 return;
1270 }
1271
1272 // Are we jumping to the head of a loop? Add a special diagnostic.
1273 if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) {
1274 PathDiagnosticLocation L(Loop, SM, C.getCurrLocationContext());
1275 const Stmt *Body = nullptr;
1276
1277 if (const auto *FS = dyn_cast<ForStmt>(Loop))
1278 Body = FS->getBody();
1279 else if (const auto *WS = dyn_cast<WhileStmt>(Loop))
1280 Body = WS->getBody();
1281 else if (const auto *OFS = dyn_cast<ObjCForCollectionStmt>(Loop)) {
1282 Body = OFS->getBody();
1283 } else if (const auto *FRS = dyn_cast<CXXForRangeStmt>(Loop)) {
1284 Body = FRS->getBody();
1285 }
1286 // do-while statements are explicitly excluded here
1287
1288 auto p = std::make_shared<PathDiagnosticEventPiece>(
1289 L, "Looping back to the head of the loop");
1290 p->setPrunable(true);
1291
1292 addEdgeToPath(C.getActivePath(), PrevLoc, p->getLocation());
1293 // We might've added a very similar control node already
1294 if (!C.shouldAddControlNotes()) {
1295 C.getActivePath().push_front(std::move(p));
1296 }
1297
1298 if (const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
1299 addEdgeToPath(C.getActivePath(), PrevLoc,
1301 }
1302 }
1303
1304 const CFGBlock *BSrc = BE->getSrc();
1305 const ParentMap &PM = C.getParentMap();
1306
1307 if (const Stmt *Term = BSrc->getTerminatorStmt()) {
1308 // Are we jumping past the loop body without ever executing the
1309 // loop (because the condition was false)?
1310 if (isLoop(Term)) {
1311 const Stmt *TermCond = getTerminatorCondition(BSrc);
1312 bool IsInLoopBody = isInLoopBody(
1313 PM, getStmtBeforeCond(PM, TermCond, C.getCurrentNode()), Term);
1314
1315 StringRef str;
1316
1317 if (isJumpToFalseBranch(&*BE)) {
1318 if (!IsInLoopBody) {
1319 if (isa<ObjCForCollectionStmt>(Term)) {
1321 } else if (isa<CXXForRangeStmt>(Term)) {
1322 str = StrLoopRangeEmpty;
1323 } else {
1324 str = StrLoopBodyZero;
1325 }
1326 }
1327 } else {
1328 str = StrEnteringLoop;
1329 }
1330
1331 if (!str.empty()) {
1332 PathDiagnosticLocation L(TermCond ? TermCond : Term, SM,
1333 C.getCurrLocationContext());
1334 auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
1335 PE->setPrunable(true);
1336 addEdgeToPath(C.getActivePath(), PrevLoc, PE->getLocation());
1337
1338 // We might've added a very similar control node already
1339 if (!C.shouldAddControlNotes()) {
1340 C.getActivePath().push_front(std::move(PE));
1341 }
1342 }
1343 } else if (isa<BreakStmt, ContinueStmt, GotoStmt>(Term)) {
1344 PathDiagnosticLocation L(Term, SM, C.getCurrLocationContext());
1345 addEdgeToPath(C.getActivePath(), PrevLoc, L);
1346 }
1347 }
1348 }
1349}
1350
1351static std::unique_ptr<PathDiagnostic>
1353 const Decl *AnalysisEntryPoint) {
1354 const BugType &BT = R->getBugType();
1355 return std::make_unique<PathDiagnostic>(
1357 R->getDescription(), R->getShortDescription(/*UseFallback=*/false),
1359 AnalysisEntryPoint, std::make_unique<FilesToLineNumsMap>());
1360}
1361
1362static std::unique_ptr<PathDiagnostic>
1364 const SourceManager &SM,
1365 const Decl *AnalysisEntryPoint) {
1366 const BugType &BT = R->getBugType();
1367 return std::make_unique<PathDiagnostic>(
1369 R->getDescription(), R->getShortDescription(/*UseFallback=*/false),
1371 AnalysisEntryPoint, findExecutedLines(SM, R->getErrorNode()));
1372}
1373
1374static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) {
1375 if (!S)
1376 return nullptr;
1377
1378 while (true) {
1379 S = PM.getParentIgnoreParens(S);
1380
1381 if (!S)
1382 break;
1383
1385 continue;
1386
1387 break;
1388 }
1389
1390 return S;
1391}
1392
1393static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) {
1394 switch (S->getStmtClass()) {
1395 case Stmt::BinaryOperatorClass: {
1396 const auto *BO = cast<BinaryOperator>(S);
1397 if (!BO->isLogicalOp())
1398 return false;
1399 return BO->getLHS() == Cond || BO->getRHS() == Cond;
1400 }
1401 case Stmt::IfStmtClass:
1402 return cast<IfStmt>(S)->getCond() == Cond;
1403 case Stmt::ForStmtClass:
1404 return cast<ForStmt>(S)->getCond() == Cond;
1405 case Stmt::WhileStmtClass:
1406 return cast<WhileStmt>(S)->getCond() == Cond;
1407 case Stmt::DoStmtClass:
1408 return cast<DoStmt>(S)->getCond() == Cond;
1409 case Stmt::ChooseExprClass:
1410 return cast<ChooseExpr>(S)->getCond() == Cond;
1411 case Stmt::IndirectGotoStmtClass:
1412 return cast<IndirectGotoStmt>(S)->getTarget() == Cond;
1413 case Stmt::SwitchStmtClass:
1414 return cast<SwitchStmt>(S)->getCond() == Cond;
1415 case Stmt::BinaryConditionalOperatorClass:
1416 return cast<BinaryConditionalOperator>(S)->getCond() == Cond;
1417 case Stmt::ConditionalOperatorClass: {
1418 const auto *CO = cast<ConditionalOperator>(S);
1419 return CO->getCond() == Cond ||
1420 CO->getLHS() == Cond ||
1421 CO->getRHS() == Cond;
1422 }
1423 case Stmt::ObjCForCollectionStmtClass:
1424 return cast<ObjCForCollectionStmt>(S)->getElement() == Cond;
1425 case Stmt::CXXForRangeStmtClass: {
1426 const auto *FRS = cast<CXXForRangeStmt>(S);
1427 return FRS->getCond() == Cond || FRS->getRangeInit() == Cond;
1428 }
1429 default:
1430 return false;
1431 }
1432}
1433
1434static bool isIncrementOrInitInForLoop(const Stmt *S, const Stmt *FL) {
1435 if (const auto *FS = dyn_cast<ForStmt>(FL))
1436 return FS->getInc() == S || FS->getInit() == S;
1437 if (const auto *FRS = dyn_cast<CXXForRangeStmt>(FL))
1438 return FRS->getInc() == S || FRS->getRangeStmt() == S ||
1439 FRS->getLoopVarStmt() || FRS->getRangeInit() == S;
1440 return false;
1441}
1442
1443using OptimizedCallsSet = llvm::DenseSet<const PathDiagnosticCallPiece *>;
1444
1445/// Adds synthetic edges from top-level statements to their subexpressions.
1446///
1447/// This avoids a "swoosh" effect, where an edge from a top-level statement A
1448/// points to a sub-expression B.1 that's not at the start of B. In these cases,
1449/// we'd like to see an edge from A to B, then another one from B to B.1.
1450static void addContextEdges(PathPieces &pieces, const LocationContext *LC) {
1451 const ParentMap &PM = LC->getParentMap();
1452 PathPieces::iterator Prev = pieces.end();
1453 for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E;
1454 Prev = I, ++I) {
1455 auto *Piece = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1456
1457 if (!Piece)
1458 continue;
1459
1460 PathDiagnosticLocation SrcLoc = Piece->getStartLocation();
1462
1463 PathDiagnosticLocation NextSrcContext = SrcLoc;
1464 const Stmt *InnerStmt = nullptr;
1465 while (NextSrcContext.isValid() && NextSrcContext.asStmt() != InnerStmt) {
1466 SrcContexts.push_back(NextSrcContext);
1467 InnerStmt = NextSrcContext.asStmt();
1468 NextSrcContext = getEnclosingStmtLocation(InnerStmt, LC,
1469 /*allowNested=*/true);
1470 }
1471
1472 // Repeatedly split the edge as necessary.
1473 // This is important for nested logical expressions (||, &&, ?:) where we
1474 // want to show all the levels of context.
1475 while (true) {
1476 const Stmt *Dst = Piece->getEndLocation().getStmtOrNull();
1477
1478 // We are looking at an edge. Is the destination within a larger
1479 // expression?
1480 PathDiagnosticLocation DstContext =
1481 getEnclosingStmtLocation(Dst, LC, /*allowNested=*/true);
1482 if (!DstContext.isValid() || DstContext.asStmt() == Dst)
1483 break;
1484
1485 // If the source is in the same context, we're already good.
1486 if (llvm::is_contained(SrcContexts, DstContext))
1487 break;
1488
1489 // Update the subexpression node to point to the context edge.
1490 Piece->setStartLocation(DstContext);
1491
1492 // Try to extend the previous edge if it's at the same level as the source
1493 // context.
1494 if (Prev != E) {
1495 auto *PrevPiece = dyn_cast<PathDiagnosticControlFlowPiece>(Prev->get());
1496
1497 if (PrevPiece) {
1498 if (const Stmt *PrevSrc =
1499 PrevPiece->getStartLocation().getStmtOrNull()) {
1500 const Stmt *PrevSrcParent = getStmtParent(PrevSrc, PM);
1501 if (PrevSrcParent ==
1502 getStmtParent(DstContext.getStmtOrNull(), PM)) {
1503 PrevPiece->setEndLocation(DstContext);
1504 break;
1505 }
1506 }
1507 }
1508 }
1509
1510 // Otherwise, split the current edge into a context edge and a
1511 // subexpression edge. Note that the context statement may itself have
1512 // context.
1513 auto P =
1514 std::make_shared<PathDiagnosticControlFlowPiece>(SrcLoc, DstContext);
1515 Piece = P.get();
1516 I = pieces.insert(I, std::move(P));
1517 }
1518 }
1519}
1520
1521/// Move edges from a branch condition to a branch target
1522/// when the condition is simple.
1523///
1524/// This restructures some of the work of addContextEdges. That function
1525/// creates edges this may destroy, but they work together to create a more
1526/// aesthetically set of edges around branches. After the call to
1527/// addContextEdges, we may have (1) an edge to the branch, (2) an edge from
1528/// the branch to the branch condition, and (3) an edge from the branch
1529/// condition to the branch target. We keep (1), but may wish to remove (2)
1530/// and move the source of (3) to the branch if the branch condition is simple.
1532 for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) {
1533 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1534
1535 if (!PieceI)
1536 continue;
1537
1538 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1539 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1540
1541 if (!s1Start || !s1End)
1542 continue;
1543
1544 PathPieces::iterator NextI = I; ++NextI;
1545 if (NextI == E)
1546 break;
1547
1548 PathDiagnosticControlFlowPiece *PieceNextI = nullptr;
1549
1550 while (true) {
1551 if (NextI == E)
1552 break;
1553
1554 const auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1555 if (EV) {
1556 StringRef S = EV->getString();
1557 if (S == StrEnteringLoop || S == StrLoopBodyZero ||
1559 ++NextI;
1560 continue;
1561 }
1562 break;
1563 }
1564
1565 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1566 break;
1567 }
1568
1569 if (!PieceNextI)
1570 continue;
1571
1572 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1573 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1574
1575 if (!s2Start || !s2End || s1End != s2Start)
1576 continue;
1577
1578 // We only perform this transformation for specific branch kinds.
1579 // We don't want to do this for do..while, for example.
1581 CXXForRangeStmt>(s1Start))
1582 continue;
1583
1584 // Is s1End the branch condition?
1585 if (!isConditionForTerminator(s1Start, s1End))
1586 continue;
1587
1588 // Perform the hoisting by eliminating (2) and changing the start
1589 // location of (3).
1590 PieceNextI->setStartLocation(PieceI->getStartLocation());
1591 I = pieces.erase(I);
1592 }
1593}
1594
1595/// Returns the number of bytes in the given (character-based) SourceRange.
1596///
1597/// If the locations in the range are not on the same line, returns
1598/// std::nullopt.
1599///
1600/// Note that this does not do a precise user-visible character or column count.
1601static std::optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
1603 SourceRange ExpansionRange(SM.getExpansionLoc(Range.getBegin()),
1604 SM.getExpansionRange(Range.getEnd()).getEnd());
1605
1606 FileID FID = SM.getFileID(ExpansionRange.getBegin());
1607 if (FID != SM.getFileID(ExpansionRange.getEnd()))
1608 return std::nullopt;
1609
1610 std::optional<MemoryBufferRef> Buffer = SM.getBufferOrNone(FID);
1611 if (!Buffer)
1612 return std::nullopt;
1613
1614 unsigned BeginOffset = SM.getFileOffset(ExpansionRange.getBegin());
1615 unsigned EndOffset = SM.getFileOffset(ExpansionRange.getEnd());
1616 StringRef Snippet = Buffer->getBuffer().slice(BeginOffset, EndOffset);
1617
1618 // We're searching the raw bytes of the buffer here, which might include
1619 // escaped newlines and such. That's okay; we're trying to decide whether the
1620 // SourceRange is covering a large or small amount of space in the user's
1621 // editor.
1622 if (Snippet.find_first_of("\r\n") != StringRef::npos)
1623 return std::nullopt;
1624
1625 // This isn't Unicode-aware, but it doesn't need to be.
1626 return Snippet.size();
1627}
1628
1629/// \sa getLengthOnSingleLine(SourceManager, SourceRange)
1630static std::optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
1631 const Stmt *S) {
1633}
1634
1635/// Eliminate two-edge cycles created by addContextEdges().
1636///
1637/// Once all the context edges are in place, there are plenty of cases where
1638/// there's a single edge from a top-level statement to a subexpression,
1639/// followed by a single path note, and then a reverse edge to get back out to
1640/// the top level. If the statement is simple enough, the subexpression edges
1641/// just add noise and make it harder to understand what's going on.
1642///
1643/// This function only removes edges in pairs, because removing only one edge
1644/// might leave other edges dangling.
1645///
1646/// This will not remove edges in more complicated situations:
1647/// - if there is more than one "hop" leading to or from a subexpression.
1648/// - if there is an inlined call between the edges instead of a single event.
1649/// - if the whole statement is large enough that having subexpression arrows
1650/// might be helpful.
1652 for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) {
1653 // Pattern match the current piece and its successor.
1654 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1655
1656 if (!PieceI) {
1657 ++I;
1658 continue;
1659 }
1660
1661 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1662 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1663
1664 PathPieces::iterator NextI = I; ++NextI;
1665 if (NextI == E)
1666 break;
1667
1668 const auto *PieceNextI =
1669 dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1670
1671 if (!PieceNextI) {
1672 if (isa<PathDiagnosticEventPiece>(NextI->get())) {
1673 ++NextI;
1674 if (NextI == E)
1675 break;
1676 PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1677 }
1678
1679 if (!PieceNextI) {
1680 ++I;
1681 continue;
1682 }
1683 }
1684
1685 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1686 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1687
1688 if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
1689 const size_t MAX_SHORT_LINE_LENGTH = 80;
1690 std::optional<size_t> s1Length = getLengthOnSingleLine(SM, s1Start);
1691 if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) {
1692 std::optional<size_t> s2Length = getLengthOnSingleLine(SM, s2Start);
1693 if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) {
1694 Path.erase(I);
1695 I = Path.erase(NextI);
1696 continue;
1697 }
1698 }
1699 }
1700
1701 ++I;
1702 }
1703}
1704
1705/// Return true if X is contained by Y.
1706static bool lexicalContains(const ParentMap &PM, const Stmt *X, const Stmt *Y) {
1707 while (X) {
1708 if (X == Y)
1709 return true;
1710 X = PM.getParent(X);
1711 }
1712 return false;
1713}
1714
1715// Remove short edges on the same line less than 3 columns in difference.
1716static void removePunyEdges(PathPieces &path, const SourceManager &SM,
1717 const ParentMap &PM) {
1718 bool erased = false;
1719
1720 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E;
1721 erased ? I : ++I) {
1722 erased = false;
1723
1724 const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1725
1726 if (!PieceI)
1727 continue;
1728
1729 const Stmt *start = PieceI->getStartLocation().getStmtOrNull();
1730 const Stmt *end = PieceI->getEndLocation().getStmtOrNull();
1731
1732 if (!start || !end)
1733 continue;
1734
1735 const Stmt *endParent = PM.getParent(end);
1736 if (!endParent)
1737 continue;
1738
1739 if (isConditionForTerminator(end, endParent))
1740 continue;
1741
1742 SourceLocation FirstLoc = start->getBeginLoc();
1743 SourceLocation SecondLoc = end->getBeginLoc();
1744
1745 if (!SM.isWrittenInSameFile(FirstLoc, SecondLoc))
1746 continue;
1747 if (SM.isBeforeInTranslationUnit(SecondLoc, FirstLoc))
1748 std::swap(SecondLoc, FirstLoc);
1749
1750 SourceRange EdgeRange(FirstLoc, SecondLoc);
1751 std::optional<size_t> ByteWidth = getLengthOnSingleLine(SM, EdgeRange);
1752
1753 // If the statements are on different lines, continue.
1754 if (!ByteWidth)
1755 continue;
1756
1757 const size_t MAX_PUNY_EDGE_LENGTH = 2;
1758 if (*ByteWidth <= MAX_PUNY_EDGE_LENGTH) {
1759 // FIXME: There are enough /bytes/ between the endpoints of the edge, but
1760 // there might not be enough /columns/. A proper user-visible column count
1761 // is probably too expensive, though.
1762 I = path.erase(I);
1763 erased = true;
1764 continue;
1765 }
1766 }
1767}
1768
1770 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ++I) {
1771 const auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get());
1772
1773 if (!PieceI)
1774 continue;
1775
1776 PathPieces::iterator NextI = I; ++NextI;
1777 if (NextI == E)
1778 return;
1779
1780 const auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
1781
1782 if (!PieceNextI)
1783 continue;
1784
1785 // Erase the second piece if it has the same exact message text.
1786 if (PieceI->getString() == PieceNextI->getString()) {
1787 path.erase(NextI);
1788 }
1789 }
1790}
1791
1792static bool optimizeEdges(const PathDiagnosticConstruct &C, PathPieces &path,
1793 OptimizedCallsSet &OCS) {
1794 bool hasChanges = false;
1795 const LocationContext *LC = C.getLocationContextFor(&path);
1796 assert(LC);
1797 const ParentMap &PM = LC->getParentMap();
1798 const SourceManager &SM = C.getSourceManager();
1799
1800 for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) {
1801 // Optimize subpaths.
1802 if (auto *CallI = dyn_cast<PathDiagnosticCallPiece>(I->get())) {
1803 // Record the fact that a call has been optimized so we only do the
1804 // effort once.
1805 if (!OCS.count(CallI)) {
1806 while (optimizeEdges(C, CallI->path, OCS)) {
1807 }
1808 OCS.insert(CallI);
1809 }
1810 ++I;
1811 continue;
1812 }
1813
1814 // Pattern match the current piece and its successor.
1815 auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
1816
1817 if (!PieceI) {
1818 ++I;
1819 continue;
1820 }
1821
1822 const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
1823 const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
1824 const Stmt *level1 = getStmtParent(s1Start, PM);
1825 const Stmt *level2 = getStmtParent(s1End, PM);
1826
1827 PathPieces::iterator NextI = I; ++NextI;
1828 if (NextI == E)
1829 break;
1830
1831 const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
1832
1833 if (!PieceNextI) {
1834 ++I;
1835 continue;
1836 }
1837
1838 const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
1839 const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
1840 const Stmt *level3 = getStmtParent(s2Start, PM);
1841 const Stmt *level4 = getStmtParent(s2End, PM);
1842
1843 // Rule I.
1844 //
1845 // If we have two consecutive control edges whose end/begin locations
1846 // are at the same level (e.g. statements or top-level expressions within
1847 // a compound statement, or siblings share a single ancestor expression),
1848 // then merge them if they have no interesting intermediate event.
1849 //
1850 // For example:
1851 //
1852 // (1.1 -> 1.2) -> (1.2 -> 1.3) becomes (1.1 -> 1.3) because the common
1853 // parent is '1'. Here 'x.y.z' represents the hierarchy of statements.
1854 //
1855 // NOTE: this will be limited later in cases where we add barriers
1856 // to prevent this optimization.
1857 if (level1 && level1 == level2 && level1 == level3 && level1 == level4) {
1858 PieceI->setEndLocation(PieceNextI->getEndLocation());
1859 path.erase(NextI);
1860 hasChanges = true;
1861 continue;
1862 }
1863
1864 // Rule II.
1865 //
1866 // Eliminate edges between subexpressions and parent expressions
1867 // when the subexpression is consumed.
1868 //
1869 // NOTE: this will be limited later in cases where we add barriers
1870 // to prevent this optimization.
1871 if (s1End && s1End == s2Start && level2) {
1872 bool removeEdge = false;
1873 // Remove edges into the increment or initialization of a
1874 // loop that have no interleaving event. This means that
1875 // they aren't interesting.
1876 if (isIncrementOrInitInForLoop(s1End, level2))
1877 removeEdge = true;
1878 // Next only consider edges that are not anchored on
1879 // the condition of a terminator. This are intermediate edges
1880 // that we might want to trim.
1881 else if (!isConditionForTerminator(level2, s1End)) {
1882 // Trim edges on expressions that are consumed by
1883 // the parent expression.
1884 if (isa<Expr>(s1End) && PM.isConsumedExpr(cast<Expr>(s1End))) {
1885 removeEdge = true;
1886 }
1887 // Trim edges where a lexical containment doesn't exist.
1888 // For example:
1889 //
1890 // X -> Y -> Z
1891 //
1892 // If 'Z' lexically contains Y (it is an ancestor) and
1893 // 'X' does not lexically contain Y (it is a descendant OR
1894 // it has no lexical relationship at all) then trim.
1895 //
1896 // This can eliminate edges where we dive into a subexpression
1897 // and then pop back out, etc.
1898 else if (s1Start && s2End &&
1899 lexicalContains(PM, s2Start, s2End) &&
1900 !lexicalContains(PM, s1End, s1Start)) {
1901 removeEdge = true;
1902 }
1903 // Trim edges from a subexpression back to the top level if the
1904 // subexpression is on a different line.
1905 //
1906 // A.1 -> A -> B
1907 // becomes
1908 // A.1 -> B
1909 //
1910 // These edges just look ugly and don't usually add anything.
1911 else if (s1Start && s2End &&
1912 lexicalContains(PM, s1Start, s1End)) {
1913 SourceRange EdgeRange(PieceI->getEndLocation().asLocation(),
1914 PieceI->getStartLocation().asLocation());
1915 if (!getLengthOnSingleLine(SM, EdgeRange))
1916 removeEdge = true;
1917 }
1918 }
1919
1920 if (removeEdge) {
1921 PieceI->setEndLocation(PieceNextI->getEndLocation());
1922 path.erase(NextI);
1923 hasChanges = true;
1924 continue;
1925 }
1926 }
1927
1928 // Optimize edges for ObjC fast-enumeration loops.
1929 //
1930 // (X -> collection) -> (collection -> element)
1931 //
1932 // becomes:
1933 //
1934 // (X -> element)
1935 if (s1End == s2Start) {
1936 const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(level3);
1937 if (FS && FS->getCollection()->IgnoreParens() == s2Start &&
1938 s2End == FS->getElement()) {
1939 PieceI->setEndLocation(PieceNextI->getEndLocation());
1940 path.erase(NextI);
1941 hasChanges = true;
1942 continue;
1943 }
1944 }
1945
1946 // No changes at this index? Move to the next one.
1947 ++I;
1948 }
1949
1950 if (!hasChanges) {
1951 // Adjust edges into subexpressions to make them more uniform
1952 // and aesthetically pleasing.
1953 addContextEdges(path, LC);
1954 // Remove "cyclical" edges that include one or more context edges.
1955 removeContextCycles(path, SM);
1956 // Hoist edges originating from branch conditions to branches
1957 // for simple branches.
1959 // Remove any puny edges left over after primary optimization pass.
1960 removePunyEdges(path, SM, PM);
1961 // Remove identical events.
1963 }
1964
1965 return hasChanges;
1966}
1967
1968/// Drop the very first edge in a path, which should be a function entry edge.
1969///
1970/// If the first edge is not a function entry edge (say, because the first
1971/// statement had an invalid source location), this function does nothing.
1972// FIXME: We should just generate invalid edges anyway and have the optimizer
1973// deal with them.
1974static void dropFunctionEntryEdge(const PathDiagnosticConstruct &C,
1975 PathPieces &Path) {
1976 const auto *FirstEdge =
1977 dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get());
1978 if (!FirstEdge)
1979 return;
1980
1981 const Decl *D = C.getLocationContextFor(&Path)->getDecl();
1982 PathDiagnosticLocation EntryLoc =
1983 PathDiagnosticLocation::createBegin(D, C.getSourceManager());
1984 if (FirstEdge->getStartLocation() != EntryLoc)
1985 return;
1986
1987 Path.pop_front();
1988}
1989
1990/// Populate executes lines with lines containing at least one diagnostics.
1992
1993 PathPieces path = PD.path.flatten(/*ShouldFlattenMacros=*/true);
1994 FilesToLineNumsMap &ExecutedLines = PD.getExecutedLines();
1995
1996 for (const auto &P : path) {
1997 FullSourceLoc Loc = P->getLocation().asLocation().getExpansionLoc();
1998 FileID FID = Loc.getFileID();
1999 unsigned LineNo = Loc.getLineNumber();
2000 assert(FID.isValid());
2001 ExecutedLines[FID].insert(LineNo);
2002 }
2003}
2004
2005PathDiagnosticConstruct::PathDiagnosticConstruct(
2006 const PathDiagnosticConsumer *PDC, const ExplodedNode *ErrorNode,
2007 const PathSensitiveBugReport *R, const Decl *AnalysisEntryPoint)
2008 : Consumer(PDC), CurrentNode(ErrorNode),
2009 SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()),
2010 PD(generateEmptyDiagnosticForReport(R, getSourceManager(),
2011 AnalysisEntryPoint)) {
2012 LCM[&PD->getActivePath()] = ErrorNode->getLocationContext();
2013}
2014
2015PathDiagnosticBuilder::PathDiagnosticBuilder(
2016 BugReporterContext BRC, std::unique_ptr<ExplodedGraph> BugPath,
2017 PathSensitiveBugReport *r, const ExplodedNode *ErrorNode,
2018 std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics)
2019 : BugReporterContext(BRC), BugPath(std::move(BugPath)), R(r),
2020 ErrorNode(ErrorNode),
2021 VisitorsDiagnostics(std::move(VisitorsDiagnostics)) {}
2022
2023std::unique_ptr<PathDiagnostic>
2024PathDiagnosticBuilder::generate(const PathDiagnosticConsumer *PDC) const {
2025 const Decl *EntryPoint = getBugReporter().getAnalysisEntryPoint();
2026 PathDiagnosticConstruct Construct(PDC, ErrorNode, R, EntryPoint);
2027
2028 const SourceManager &SM = getSourceManager();
2029 const AnalyzerOptions &Opts = getAnalyzerOptions();
2030
2031 if (!PDC->shouldGenerateDiagnostics())
2032 return generateEmptyDiagnosticForReport(R, getSourceManager(), EntryPoint);
2033
2034 // Construct the final (warning) event for the bug report.
2035 auto EndNotes = VisitorsDiagnostics->find(ErrorNode);
2036 PathDiagnosticPieceRef LastPiece;
2037 if (EndNotes != VisitorsDiagnostics->end()) {
2038 assert(!EndNotes->second.empty());
2039 LastPiece = EndNotes->second[0];
2040 } else {
2041 LastPiece = BugReporterVisitor::getDefaultEndPath(*this, ErrorNode,
2042 *getBugReport());
2043 }
2044 Construct.PD->setEndOfPath(LastPiece);
2045
2046 PathDiagnosticLocation PrevLoc = Construct.PD->getLocation();
2047 // From the error node to the root, ascend the bug path and construct the bug
2048 // report.
2049 while (Construct.ascendToPrevNode()) {
2050 generatePathDiagnosticsForNode(Construct, PrevLoc);
2051
2052 auto VisitorNotes = VisitorsDiagnostics->find(Construct.getCurrentNode());
2053 if (VisitorNotes == VisitorsDiagnostics->end())
2054 continue;
2055
2056 // This is a workaround due to inability to put shared PathDiagnosticPiece
2057 // into a FoldingSet.
2058 std::set<llvm::FoldingSetNodeID> DeduplicationSet;
2059
2060 // Add pieces from custom visitors.
2061 for (const PathDiagnosticPieceRef &Note : VisitorNotes->second) {
2062 llvm::FoldingSetNodeID ID;
2063 Note->Profile(ID);
2064 if (!DeduplicationSet.insert(ID).second)
2065 continue;
2066
2067 if (PDC->shouldAddPathEdges())
2068 addEdgeToPath(Construct.getActivePath(), PrevLoc, Note->getLocation());
2069 updateStackPiecesWithMessage(Note, Construct.CallStack);
2070 Construct.getActivePath().push_front(Note);
2071 }
2072 }
2073
2074 if (PDC->shouldAddPathEdges()) {
2075 // Add an edge to the start of the function.
2076 // We'll prune it out later, but it helps make diagnostics more uniform.
2077 const StackFrameContext *CalleeLC =
2078 Construct.getLocationContextForActivePath()->getStackFrame();
2079 const Decl *D = CalleeLC->getDecl();
2080 addEdgeToPath(Construct.getActivePath(), PrevLoc,
2082 }
2083
2084
2085 // Finally, prune the diagnostic path of uninteresting stuff.
2086 if (!Construct.PD->path.empty()) {
2087 if (R->shouldPrunePath() && Opts.ShouldPrunePaths) {
2088 bool stillHasNotes =
2089 removeUnneededCalls(Construct, Construct.getMutablePieces(), R);
2090 assert(stillHasNotes);
2091 (void)stillHasNotes;
2092 }
2093
2094 // Remove pop-up notes if needed.
2095 if (!Opts.ShouldAddPopUpNotes)
2096 removePopUpNotes(Construct.getMutablePieces());
2097
2098 // Redirect all call pieces to have valid locations.
2099 adjustCallLocations(Construct.getMutablePieces());
2100 removePiecesWithInvalidLocations(Construct.getMutablePieces());
2101
2102 if (PDC->shouldAddPathEdges()) {
2103
2104 // Reduce the number of edges from a very conservative set
2105 // to an aesthetically pleasing subset that conveys the
2106 // necessary information.
2108 while (optimizeEdges(Construct, Construct.getMutablePieces(), OCS)) {
2109 }
2110
2111 // Drop the very first function-entry edge. It's not really necessary
2112 // for top-level functions.
2113 dropFunctionEntryEdge(Construct, Construct.getMutablePieces());
2114 }
2115
2116 // Remove messages that are basically the same, and edges that may not
2117 // make sense.
2118 // We have to do this after edge optimization in the Extensive mode.
2119 removeRedundantMsgs(Construct.getMutablePieces());
2120 removeEdgesToDefaultInitializers(Construct.getMutablePieces());
2121 }
2122
2123 if (Opts.ShouldDisplayMacroExpansions)
2124 CompactMacroExpandedPieces(Construct.getMutablePieces(), SM);
2125
2126 return std::move(Construct.PD);
2127}
2128
2129//===----------------------------------------------------------------------===//
2130// Methods for BugType and subclasses.
2131//===----------------------------------------------------------------------===//
2132
2133void BugType::anchor() {}
2134
2135//===----------------------------------------------------------------------===//
2136// Methods for BugReport and subclasses.
2137//===----------------------------------------------------------------------===//
2138
2139LLVM_ATTRIBUTE_USED static bool
2140isDependency(const CheckerRegistryData &Registry, StringRef CheckerName) {
2141 for (const std::pair<StringRef, StringRef> &Pair : Registry.Dependencies) {
2142 if (Pair.second == CheckerName)
2143 return true;
2144 }
2145 return false;
2146}
2147
2148LLVM_ATTRIBUTE_USED static bool isHidden(const CheckerRegistryData &Registry,
2149 StringRef CheckerName) {
2150 for (const CheckerInfo &Checker : Registry.Checkers) {
2151 if (Checker.FullName == CheckerName)
2152 return Checker.IsHidden;
2153 }
2154 llvm_unreachable(
2155 "Checker name not found in CheckerRegistry -- did you retrieve it "
2156 "correctly from CheckerManager::getCurrentCheckerName?");
2157}
2158
2160 const BugType &bt, StringRef shortDesc, StringRef desc,
2161 const ExplodedNode *errorNode, PathDiagnosticLocation LocationToUnique,
2162 const Decl *DeclToUnique)
2163 : BugReport(Kind::PathSensitive, bt, shortDesc, desc), ErrorNode(errorNode),
2164 ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() : SourceRange()),
2165 UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) {
2166 assert(ErrorNode && "The error node must be non-null!");
2167 assert(!isDependency(ErrorNode->getState()
2168 ->getAnalysisManager()
2169 .getCheckerManager()
2170 ->getCheckerRegistryData(),
2171 bt.getCheckerName()) &&
2172 "Some checkers depend on this one! We don't allow dependency "
2173 "checkers to emit warnings, because checkers should depend on "
2174 "*modeling*, not *diagnostics*.");
2175
2176 assert((bt.getCheckerName().starts_with("debug") ||
2177 !isHidden(ErrorNode->getState()
2178 ->getAnalysisManager()
2179 .getCheckerManager()
2180 ->getCheckerRegistryData(),
2181 bt.getCheckerName())) &&
2182 "Hidden checkers musn't emit diagnostics as they are by definition "
2183 "non-user facing!");
2184}
2185
2187 std::unique_ptr<BugReporterVisitor> visitor) {
2188 if (!visitor)
2189 return;
2190
2191 llvm::FoldingSetNodeID ID;
2192 visitor->Profile(ID);
2193
2194 void *InsertPos = nullptr;
2195 if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) {
2196 return;
2197 }
2198
2199 Callbacks.push_back(std::move(visitor));
2200}
2201
2205
2207 const ExplodedNode *N = getErrorNode();
2208 if (!N)
2209 return nullptr;
2210
2211 const LocationContext *LC = N->getLocationContext();
2212 return LC->getStackFrame()->getDecl();
2213}
2214
2215void BasicBugReport::Profile(llvm::FoldingSetNodeID& hash) const {
2216 hash.AddInteger(static_cast<int>(getKind()));
2217 hash.AddPointer(&BT);
2218 hash.AddString(getShortDescription());
2219 assert(Location.isValid());
2220 Location.Profile(hash);
2221
2222 for (SourceRange range : Ranges) {
2223 if (!range.isValid())
2224 continue;
2225 hash.Add(range.getBegin());
2226 hash.Add(range.getEnd());
2227 }
2228}
2229
2230void PathSensitiveBugReport::Profile(llvm::FoldingSetNodeID &hash) const {
2231 hash.AddInteger(static_cast<int>(getKind()));
2232 hash.AddPointer(&BT);
2233 hash.AddString(getShortDescription());
2235 if (UL.isValid()) {
2236 UL.Profile(hash);
2237 } else {
2238 // TODO: The statement may be null if the report was emitted before any
2239 // statements were executed. In particular, some checkers by design
2240 // occasionally emit their reports in empty functions (that have no
2241 // statements in their body). Do we profile correctly in this case?
2242 hash.AddPointer(ErrorNode->getCurrentOrPreviousStmtForDiagnostics());
2243 }
2244
2245 for (SourceRange range : Ranges) {
2246 if (!range.isValid())
2247 continue;
2248 hash.Add(range.getBegin());
2249 hash.Add(range.getEnd());
2250 }
2251}
2252
2253template <class T>
2255 llvm::DenseMap<T, bugreporter::TrackingKind> &InterestingnessMap, T Val,
2257 auto Result = InterestingnessMap.insert({Val, TKind});
2258
2259 if (Result.second)
2260 return;
2261
2262 // Even if this symbol/region was already marked as interesting as a
2263 // condition, if we later mark it as interesting again but with
2264 // thorough tracking, overwrite it. Entities marked with thorough
2265 // interestiness are the most important (or most interesting, if you will),
2266 // and we wouldn't like to downplay their importance.
2267
2268 switch (TKind) {
2270 Result.first->getSecond() = bugreporter::TrackingKind::Thorough;
2271 return;
2273 return;
2274 }
2275
2276 llvm_unreachable(
2277 "BugReport::markInteresting currently can only handle 2 different "
2278 "tracking kinds! Please define what tracking kind should this entitiy"
2279 "have, if it was already marked as interesting with a different kind!");
2280}
2281
2284 if (!sym)
2285 return;
2286
2288
2289 // FIXME: No tests exist for this code and it is questionable:
2290 // How to handle multiple metadata for the same region?
2291 if (const auto *meta = dyn_cast<SymbolMetadata>(sym))
2292 markInteresting(meta->getRegion(), TKind);
2293}
2294
2296 if (!sym)
2297 return;
2298 InterestingSymbols.erase(sym);
2299
2300 // The metadata part of markInteresting is not reversed here.
2301 // Just making the same region not interesting is incorrect
2302 // in specific cases.
2303 if (const auto *meta = dyn_cast<SymbolMetadata>(sym))
2304 markNotInteresting(meta->getRegion());
2305}
2306
2309 if (!R)
2310 return;
2311
2312 R = R->getBaseRegion();
2314
2315 if (const auto *SR = dyn_cast<SymbolicRegion>(R))
2316 markInteresting(SR->getSymbol(), TKind);
2317}
2318
2320 if (!R)
2321 return;
2322
2323 R = R->getBaseRegion();
2324 InterestingRegions.erase(R);
2325
2326 if (const auto *SR = dyn_cast<SymbolicRegion>(R))
2327 markNotInteresting(SR->getSymbol());
2328}
2329
2332 markInteresting(V.getAsRegion(), TKind);
2333 markInteresting(V.getAsSymbol(), TKind);
2334}
2335
2337 if (!LC)
2338 return;
2339 InterestingLocationContexts.insert(LC);
2340}
2341
2342std::optional<bugreporter::TrackingKind>
2344 auto RKind = getInterestingnessKind(V.getAsRegion());
2345 auto SKind = getInterestingnessKind(V.getAsSymbol());
2346 if (!RKind)
2347 return SKind;
2348 if (!SKind)
2349 return RKind;
2350
2351 // If either is marked with throrough tracking, return that, we wouldn't like
2352 // to downplay a note's importance by 'only' mentioning it as a condition.
2353 switch(*RKind) {
2355 return RKind;
2357 return SKind;
2358 }
2359
2360 llvm_unreachable(
2361 "BugReport::getInterestingnessKind currently can only handle 2 different "
2362 "tracking kinds! Please define what tracking kind should we return here "
2363 "when the kind of getAsRegion() and getAsSymbol() is different!");
2364 return std::nullopt;
2365}
2366
2367std::optional<bugreporter::TrackingKind>
2369 if (!sym)
2370 return std::nullopt;
2371 // We don't currently consider metadata symbols to be interesting
2372 // even if we know their region is interesting. Is that correct behavior?
2373 auto It = InterestingSymbols.find(sym);
2374 if (It == InterestingSymbols.end())
2375 return std::nullopt;
2376 return It->getSecond();
2377}
2378
2379std::optional<bugreporter::TrackingKind>
2381 if (!R)
2382 return std::nullopt;
2383
2384 R = R->getBaseRegion();
2385 auto It = InterestingRegions.find(R);
2386 if (It != InterestingRegions.end())
2387 return It->getSecond();
2388
2389 if (const auto *SR = dyn_cast<SymbolicRegion>(R))
2390 return getInterestingnessKind(SR->getSymbol());
2391 return std::nullopt;
2392}
2393
2395 return getInterestingnessKind(V).has_value();
2396}
2397
2399 return getInterestingnessKind(sym).has_value();
2400}
2401
2403 return getInterestingnessKind(R).has_value();
2404}
2405
2407 if (!LC)
2408 return false;
2409 return InterestingLocationContexts.count(LC);
2410}
2411
2413 if (!ErrorNode)
2414 return nullptr;
2415
2416 ProgramPoint ProgP = ErrorNode->getLocation();
2417 const Stmt *S = nullptr;
2418
2419 if (std::optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) {
2420 CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit();
2421 if (BE->getBlock() == &Exit)
2422 S = ErrorNode->getPreviousStmtForDiagnostics();
2423 }
2424 if (!S)
2425 S = ErrorNode->getStmtForDiagnostics();
2426
2427 return S;
2428}
2429
2432 // If no custom ranges, add the range of the statement corresponding to
2433 // the error node.
2434 if (Ranges.empty() && isa_and_nonnull<Expr>(getStmt()))
2435 return ErrorNodeRange;
2436
2437 return Ranges;
2438}
2439
2440static bool exitingDestructor(const ExplodedNode *N) {
2441 // Need to loop here, as some times the Error node is already outside of the
2442 // destructor context, and the previous node is an edge that is also outside.
2443 while (N && !N->getLocation().getAs<StmtPoint>()) {
2444 N = N->getFirstPred();
2445 }
2447}
2448
2449static const Stmt *
2451 if (exitingDestructor(N)) {
2452 // If we are exiting a destructor call, it is more useful to point to
2453 // the next stmt which is usually the temporary declaration.
2454 if (const Stmt *S = N->getNextStmtForDiagnostics())
2455 return S;
2456 // If next stmt is not found, it is likely the end of a top-level
2457 // function analysis. find the last execution statement then.
2458 }
2460}
2461
2464 assert(ErrorNode && "Cannot create a location with a null node.");
2465 const Stmt *S = ErrorNode->getStmtForDiagnostics();
2466 ProgramPoint P = ErrorNode->getLocation();
2467 const LocationContext *LC = P.getLocationContext();
2468 SourceManager &SM =
2469 ErrorNode->getState()->getStateManager().getContext().getSourceManager();
2470
2471 if (!S) {
2472 // If this is an implicit call, return the implicit call point location.
2473 if (std::optional<PreImplicitCall> PIE = P.getAs<PreImplicitCall>())
2474 return PathDiagnosticLocation(PIE->getLocation(), SM);
2475 if (auto FE = P.getAs<FunctionExitPoint>()) {
2476 if (const ReturnStmt *RS = FE->getStmt())
2478
2480 }
2481 if (!S)
2482 S = ErrorNode->getNextStmtForDiagnostics();
2483 }
2484
2485 if (S) {
2486 // Attributed statements usually have corrupted begin locations,
2487 // it's OK to ignore attributes for our purposes and deal with
2488 // the actual annotated statement.
2489 if (const auto *AS = dyn_cast<AttributedStmt>(S))
2490 S = AS->getSubStmt();
2491
2492 // For member expressions, return the location of the '.' or '->'.
2493 if (const auto *ME = dyn_cast<MemberExpr>(S))
2495
2496 // For binary operators, return the location of the operator.
2497 if (const auto *B = dyn_cast<BinaryOperator>(S))
2499
2502
2503 if (S->getBeginLoc().isValid())
2504 return PathDiagnosticLocation(S, SM, LC);
2505
2508 }
2509
2510 return PathDiagnosticLocation::createDeclEnd(ErrorNode->getLocationContext(),
2511 SM);
2512}
2513
2514//===----------------------------------------------------------------------===//
2515// Methods for BugReporter and subclasses.
2516//===----------------------------------------------------------------------===//
2517
2519 return Eng.getGraph();
2520}
2521
2523 return Eng.getStateManager();
2524}
2525
2527 : D(D), UserSuppressions(D.getASTContext()) {}
2528
2530 // Make sure reports are flushed.
2531 assert(StrBugTypes.empty() &&
2532 "Destroying BugReporter before diagnostics are emitted!");
2533
2534 // Free the bug reports we are tracking.
2535 for (const auto I : EQClassesVector)
2536 delete I;
2537}
2538
2540 // We need to flush reports in deterministic order to ensure the order
2541 // of the reports is consistent between runs.
2542 for (const auto EQ : EQClassesVector)
2543 FlushReport(*EQ);
2544
2545 // BugReporter owns and deletes only BugTypes created implicitly through
2546 // EmitBasicReport.
2547 // FIXME: There are leaks from checkers that assume that the BugTypes they
2548 // create will be destroyed by the BugReporter.
2549 StrBugTypes.clear();
2550}
2551
2552//===----------------------------------------------------------------------===//
2553// PathDiagnostics generation.
2554//===----------------------------------------------------------------------===//
2555
2556namespace {
2557
2558/// A wrapper around an ExplodedGraph that contains a single path from the root
2559/// to the error node.
2560class BugPathInfo {
2561public:
2562 std::unique_ptr<ExplodedGraph> BugPath;
2564 const ExplodedNode *ErrorNode;
2565};
2566
2567/// A wrapper around an ExplodedGraph whose leafs are all error nodes. Can
2568/// conveniently retrieve bug paths from a single error node to the root.
2569class BugPathGetter {
2570 std::unique_ptr<ExplodedGraph> TrimmedGraph;
2571
2572 using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>;
2573
2574 /// Assign each node with its distance from the root.
2575 PriorityMapTy PriorityMap;
2576
2577 /// Since the getErrorNode() or BugReport refers to the original ExplodedGraph,
2578 /// we need to pair it to the error node of the constructed trimmed graph.
2579 using ReportNewNodePair =
2580 std::pair<PathSensitiveBugReport *, const ExplodedNode *>;
2582
2583 BugPathInfo CurrentBugPath;
2584
2585 /// A helper class for sorting ExplodedNodes by priority.
2586 template <bool Descending>
2587 class PriorityCompare {
2588 const PriorityMapTy &PriorityMap;
2589
2590 public:
2591 PriorityCompare(const PriorityMapTy &M) : PriorityMap(M) {}
2592
2593 bool operator()(const ExplodedNode *LHS, const ExplodedNode *RHS) const {
2594 PriorityMapTy::const_iterator LI = PriorityMap.find(LHS);
2595 PriorityMapTy::const_iterator RI = PriorityMap.find(RHS);
2596 PriorityMapTy::const_iterator E = PriorityMap.end();
2597
2598 if (LI == E)
2599 return Descending;
2600 if (RI == E)
2601 return !Descending;
2602
2603 return Descending ? LI->second > RI->second
2604 : LI->second < RI->second;
2605 }
2606
2607 bool operator()(const ReportNewNodePair &LHS,
2608 const ReportNewNodePair &RHS) const {
2609 return (*this)(LHS.second, RHS.second);
2610 }
2611 };
2612
2613public:
2614 BugPathGetter(const ExplodedGraph *OriginalGraph,
2615 ArrayRef<PathSensitiveBugReport *> &bugReports);
2616
2617 BugPathInfo *getNextBugPath();
2618};
2619
2620} // namespace
2621
2622BugPathGetter::BugPathGetter(const ExplodedGraph *OriginalGraph,
2623 ArrayRef<PathSensitiveBugReport *> &bugReports) {
2624 SmallVector<const ExplodedNode *, 32> Nodes;
2625 for (const auto I : bugReports) {
2626 assert(I->isValid() &&
2627 "We only allow BugReporterVisitors and BugReporter itself to "
2628 "invalidate reports!");
2629 Nodes.emplace_back(I->getErrorNode());
2630 }
2631
2632 // The trimmed graph is created in the body of the constructor to ensure
2633 // that the DenseMaps have been initialized already.
2634 InterExplodedGraphMap ForwardMap;
2635 TrimmedGraph = OriginalGraph->trim(Nodes, &ForwardMap);
2636
2637 // Find the (first) error node in the trimmed graph. We just need to consult
2638 // the node map which maps from nodes in the original graph to nodes
2639 // in the new graph.
2640 llvm::SmallPtrSet<const ExplodedNode *, 32> RemainingNodes;
2641
2642 for (PathSensitiveBugReport *Report : bugReports) {
2643 const ExplodedNode *NewNode = ForwardMap.lookup(Report->getErrorNode());
2644 assert(NewNode &&
2645 "Failed to construct a trimmed graph that contains this error "
2646 "node!");
2647 ReportNodes.emplace_back(Report, NewNode);
2648 RemainingNodes.insert(NewNode);
2649 }
2650
2651 assert(!RemainingNodes.empty() && "No error node found in the trimmed graph");
2652
2653 // Perform a forward BFS to find all the shortest paths.
2654 std::queue<const ExplodedNode *> WS;
2655
2656 WS.push(TrimmedGraph->getRoot());
2657 unsigned Priority = 0;
2658
2659 while (!WS.empty()) {
2660 const ExplodedNode *Node = WS.front();
2661 WS.pop();
2662
2663 PriorityMapTy::iterator PriorityEntry;
2664 bool IsNew;
2665 std::tie(PriorityEntry, IsNew) = PriorityMap.insert({Node, Priority});
2666 ++Priority;
2667
2668 if (!IsNew) {
2669 assert(PriorityEntry->second <= Priority);
2670 continue;
2671 }
2672
2673 if (RemainingNodes.erase(Node))
2674 if (RemainingNodes.empty())
2675 break;
2676
2677 for (const ExplodedNode *Succ : Node->succs())
2678 WS.push(Succ);
2679 }
2680
2681 // Sort the error paths from longest to shortest.
2682 llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap));
2683}
2684
2685BugPathInfo *BugPathGetter::getNextBugPath() {
2686 if (ReportNodes.empty())
2687 return nullptr;
2688
2689 const ExplodedNode *OrigN;
2690 std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val();
2691 assert(PriorityMap.contains(OrigN) && "error node not accessible from root");
2692
2693 // Create a new graph with a single path. This is the graph that will be
2694 // returned to the caller.
2695 auto GNew = std::make_unique<ExplodedGraph>();
2696
2697 // Now walk from the error node up the BFS path, always taking the
2698 // predeccessor with the lowest number.
2699 ExplodedNode *Succ = nullptr;
2700 while (true) {
2701 // Create the equivalent node in the new graph with the same state
2702 // and location.
2703 ExplodedNode *NewN = GNew->createUncachedNode(
2704 OrigN->getLocation(), OrigN->getState(),
2705 OrigN->getID(), OrigN->isSink());
2706
2707 // Link up the new node with the previous node.
2708 if (Succ)
2709 Succ->addPredecessor(NewN, *GNew);
2710 else
2711 CurrentBugPath.ErrorNode = NewN;
2712
2713 Succ = NewN;
2714
2715 // Are we at the final node?
2716 if (OrigN->pred_empty()) {
2717 assert(OrigN == TrimmedGraph->getRoot() &&
2718 "There should be only one root!");
2719 GNew->designateAsRoot(NewN);
2720 break;
2721 }
2722
2723 // Find the next predeccessor node. We choose the node that is marked
2724 // with the lowest BFS number.
2725 OrigN = *std::min_element(OrigN->pred_begin(), OrigN->pred_end(),
2726 PriorityCompare<false>(PriorityMap));
2727 }
2728
2729 CurrentBugPath.BugPath = std::move(GNew);
2730
2731 return &CurrentBugPath;
2732}
2733
2734/// CompactMacroExpandedPieces - This function postprocesses a PathDiagnostic
2735/// object and collapses PathDiagosticPieces that are expanded by macros.
2737 const SourceManager& SM) {
2738 using MacroStackTy = std::vector<
2739 std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>;
2740
2741 using PiecesTy = std::vector<PathDiagnosticPieceRef>;
2742
2743 MacroStackTy MacroStack;
2744 PiecesTy Pieces;
2745
2746 for (PathPieces::const_iterator I = path.begin(), E = path.end();
2747 I != E; ++I) {
2748 const auto &piece = *I;
2749
2750 // Recursively compact calls.
2751 if (auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) {
2752 CompactMacroExpandedPieces(call->path, SM);
2753 }
2754
2755 // Get the location of the PathDiagnosticPiece.
2756 const FullSourceLoc Loc = piece->getLocation().asLocation();
2757
2758 // Determine the instantiation location, which is the location we group
2759 // related PathDiagnosticPieces.
2760 SourceLocation InstantiationLoc = Loc.isMacroID() ?
2761 SM.getExpansionLoc(Loc) :
2763
2764 if (Loc.isFileID()) {
2765 MacroStack.clear();
2766 Pieces.push_back(piece);
2767 continue;
2768 }
2769
2770 assert(Loc.isMacroID());
2771
2772 // Is the PathDiagnosticPiece within the same macro group?
2773 if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
2774 MacroStack.back().first->subPieces.push_back(piece);
2775 continue;
2776 }
2777
2778 // We aren't in the same group. Are we descending into a new macro
2779 // or are part of an old one?
2780 std::shared_ptr<PathDiagnosticMacroPiece> MacroGroup;
2781
2782 SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ?
2783 SM.getExpansionLoc(Loc) :
2785
2786 // Walk the entire macro stack.
2787 while (!MacroStack.empty()) {
2788 if (InstantiationLoc == MacroStack.back().second) {
2789 MacroGroup = MacroStack.back().first;
2790 break;
2791 }
2792
2793 if (ParentInstantiationLoc == MacroStack.back().second) {
2794 MacroGroup = MacroStack.back().first;
2795 break;
2796 }
2797
2798 MacroStack.pop_back();
2799 }
2800
2801 if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
2802 // Create a new macro group and add it to the stack.
2803 auto NewGroup = std::make_shared<PathDiagnosticMacroPiece>(
2804 PathDiagnosticLocation::createSingleLocation(piece->getLocation()));
2805
2806 if (MacroGroup)
2807 MacroGroup->subPieces.push_back(NewGroup);
2808 else {
2809 assert(InstantiationLoc.isFileID());
2810 Pieces.push_back(NewGroup);
2811 }
2812
2813 MacroGroup = NewGroup;
2814 MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
2815 }
2816
2817 // Finally, add the PathDiagnosticPiece to the group.
2818 MacroGroup->subPieces.push_back(piece);
2819 }
2820
2821 // Now take the pieces and construct a new PathDiagnostic.
2822 path.clear();
2823
2824 llvm::append_range(path, Pieces);
2825}
2826
2827/// Generate notes from all visitors.
2828/// Notes associated with @c ErrorNode are generated using
2829/// @c getEndPath, and the rest are generated with @c VisitNode.
2830static std::unique_ptr<VisitorsDiagnosticsTy>
2832 const ExplodedNode *ErrorNode,
2833 BugReporterContext &BRC) {
2834 std::unique_ptr<VisitorsDiagnosticsTy> Notes =
2835 std::make_unique<VisitorsDiagnosticsTy>();
2837
2838 // Run visitors on all nodes starting from the node *before* the last one.
2839 // The last node is reserved for notes generated with @c getEndPath.
2840 const ExplodedNode *NextNode = ErrorNode->getFirstPred();
2841 while (NextNode) {
2842
2843 // At each iteration, move all visitors from report to visitor list. This is
2844 // important, because the Profile() functions of the visitors make sure that
2845 // a visitor isn't added multiple times for the same node, but it's fine
2846 // to add the a visitor with Profile() for different nodes (e.g. tracking
2847 // a region at different points of the symbolic execution).
2848 for (std::unique_ptr<BugReporterVisitor> &Visitor : R->visitors())
2849 visitors.push_back(std::move(Visitor));
2850
2851 R->clearVisitors();
2852
2853 const ExplodedNode *Pred = NextNode->getFirstPred();
2854 if (!Pred) {
2855 PathDiagnosticPieceRef LastPiece;
2856 for (auto &V : visitors) {
2857 V->finalizeVisitor(BRC, ErrorNode, *R);
2858
2859 if (auto Piece = V->getEndPath(BRC, ErrorNode, *R)) {
2860 assert(!LastPiece &&
2861 "There can only be one final piece in a diagnostic.");
2862 assert(Piece->getKind() == PathDiagnosticPiece::Kind::Event &&
2863 "The final piece must contain a message!");
2864 LastPiece = std::move(Piece);
2865 (*Notes)[ErrorNode].push_back(LastPiece);
2866 }
2867 }
2868 break;
2869 }
2870
2871 for (auto &V : visitors) {
2872 auto P = V->VisitNode(NextNode, BRC, *R);
2873 if (P)
2874 (*Notes)[NextNode].push_back(std::move(P));
2875 }
2876
2877 if (!R->isValid())
2878 break;
2879
2880 NextNode = Pred;
2881 }
2882
2883 return Notes;
2884}
2885
2886std::optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
2887 ArrayRef<PathSensitiveBugReport *> &bugReports,
2888 PathSensitiveBugReporter &Reporter) {
2889 Z3CrosscheckOracle Z3Oracle(Reporter.getAnalyzerOptions());
2890
2891 BugPathGetter BugGraph(&Reporter.getGraph(), bugReports);
2892
2893 while (BugPathInfo *BugPath = BugGraph.getNextBugPath()) {
2894 // Find the BugReport with the original location.
2895 PathSensitiveBugReport *R = BugPath->Report;
2896 assert(R && "No original report found for sliced graph.");
2897 assert(R->isValid() && "Report selected by trimmed graph marked invalid.");
2898 const ExplodedNode *ErrorNode = BugPath->ErrorNode;
2899
2900 // Register refutation visitors first, if they mark the bug invalid no
2901 // further analysis is required
2903
2904 // Register additional node visitors.
2907 R->addVisitor<TagVisitor>();
2908
2909 BugReporterContext BRC(Reporter);
2910
2911 // Run all visitors on a given graph, once.
2912 std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes =
2913 generateVisitorsDiagnostics(R, ErrorNode, BRC);
2914
2915 if (R->isValid()) {
2916 if (Reporter.getAnalyzerOptions().ShouldCrosscheckWithZ3) {
2917 llvm::TimeTraceScope TCS{"Crosscheck with Z3"};
2918 // If crosscheck is enabled, remove all visitors, add the refutation
2919 // visitor and check again
2920 R->clearVisitors();
2921 Z3CrosscheckVisitor::Z3Result CrosscheckResult;
2922 R->addVisitor<Z3CrosscheckVisitor>(CrosscheckResult,
2923 Reporter.getAnalyzerOptions());
2924
2925 // We don't overwrite the notes inserted by other visitors because the
2926 // refutation manager does not add any new note to the path
2927 generateVisitorsDiagnostics(R, BugPath->ErrorNode, BRC);
2928 switch (Z3Oracle.interpretQueryResult(CrosscheckResult)) {
2930 ++NumTimesReportRefuted;
2931 R->markInvalid("Infeasible constraints", /*Data=*/nullptr);
2932 continue;
2934 ++NumTimesReportEQClassAborted;
2935 return {};
2937 ++NumTimesReportPassesZ3;
2938 break;
2939 }
2940 }
2941
2942 assert(R->isValid());
2943 return PathDiagnosticBuilder(std::move(BRC), std::move(BugPath->BugPath),
2944 BugPath->Report, BugPath->ErrorNode,
2945 std::move(visitorNotes));
2946 }
2947 }
2948
2949 ++NumTimesReportEQClassWasExhausted;
2950 return {};
2951}
2952
2953std::unique_ptr<DiagnosticForConsumerMapTy>
2955 ArrayRef<std::unique_ptr<PathDiagnosticConsumer>> consumers,
2957 assert(!bugReports.empty());
2958
2959 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
2960
2961 std::optional<PathDiagnosticBuilder> PDB =
2962 PathDiagnosticBuilder::findValidReport(bugReports, *this);
2963
2964 if (PDB) {
2965 for (const auto &PC : consumers) {
2966 if (std::unique_ptr<PathDiagnostic> PD = PDB->generate(PC.get())) {
2967 (*Out)[PC.get()] = std::move(PD);
2968 }
2969 }
2970 }
2971
2972 return Out;
2973}
2974
2975void BugReporter::emitReport(std::unique_ptr<BugReport> R) {
2976 bool ValidSourceLoc = R->getLocation().isValid();
2977 assert(ValidSourceLoc);
2978 // If we mess up in a release build, we'd still prefer to just drop the bug
2979 // instead of trying to go on.
2980 if (!ValidSourceLoc)
2981 return;
2982
2983 // If the user asked to suppress this report, we should skip it.
2984 if (UserSuppressions.isSuppressed(*R))
2985 return;
2986
2987 // Compute the bug report's hash to determine its equivalence class.
2988 llvm::FoldingSetNodeID ID;
2989 R->Profile(ID);
2990
2991 // Lookup the equivance class. If there isn't one, create it.
2992 void *InsertPos;
2993 BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos);
2994
2995 if (!EQ) {
2996 EQ = new BugReportEquivClass(std::move(R));
2997 EQClasses.InsertNode(EQ, InsertPos);
2998 EQClassesVector.push_back(EQ);
2999 } else
3000 EQ->AddReport(std::move(R));
3001}
3002
3003void PathSensitiveBugReporter::emitReport(std::unique_ptr<BugReport> R) {
3004 if (auto PR = dyn_cast<PathSensitiveBugReport>(R.get()))
3005 if (const ExplodedNode *E = PR->getErrorNode()) {
3006 // An error node must either be a sink or have a tag, otherwise
3007 // it could get reclaimed before the path diagnostic is created.
3008 assert((E->isSink() || E->getLocation().getTag()) &&
3009 "Error node must either be a sink or have a tag");
3010
3011 const AnalysisDeclContext *DeclCtx =
3012 E->getLocationContext()->getAnalysisDeclContext();
3013 // The source of autosynthesized body can be handcrafted AST or a model
3014 // file. The locations from handcrafted ASTs have no valid source
3015 // locations and have to be discarded. Locations from model files should
3016 // be preserved for processing and reporting.
3017 if (DeclCtx->isBodyAutosynthesized() &&
3019 return;
3020 }
3021
3022 BugReporter::emitReport(std::move(R));
3023}
3024
3025//===----------------------------------------------------------------------===//
3026// Emitting reports in equivalence classes.
3027//===----------------------------------------------------------------------===//
3028
3029namespace {
3030
3031struct FRIEC_WLItem {
3032 const ExplodedNode *N;
3034
3035 FRIEC_WLItem(const ExplodedNode *n)
3036 : N(n), I(N->succ_begin()), E(N->succ_end()) {}
3037};
3038
3039} // namespace
3040
3041BugReport *PathSensitiveBugReporter::findReportInEquivalenceClass(
3042 BugReportEquivClass &EQ, SmallVectorImpl<BugReport *> &bugReports) {
3043 // If we don't need to suppress any of the nodes because they are
3044 // post-dominated by a sink, simply add all the nodes in the equivalence class
3045 // to 'Nodes'. Any of the reports will serve as a "representative" report.
3046 assert(EQ.getReports().size() > 0);
3047 const BugType& BT = EQ.getReports()[0]->getBugType();
3048 if (!BT.isSuppressOnSink()) {
3049 BugReport *R = EQ.getReports()[0].get();
3050 for (auto &J : EQ.getReports()) {
3051 if (auto *PR = dyn_cast<PathSensitiveBugReport>(J.get())) {
3052 R = PR;
3053 bugReports.push_back(PR);
3054 }
3055 }
3056 return R;
3057 }
3058
3059 // For bug reports that should be suppressed when all paths are post-dominated
3060 // by a sink node, iterate through the reports in the equivalence class
3061 // until we find one that isn't post-dominated (if one exists). We use a
3062 // DFS traversal of the ExplodedGraph to find a non-sink node. We could write
3063 // this as a recursive function, but we don't want to risk blowing out the
3064 // stack for very long paths.
3065 BugReport *exampleReport = nullptr;
3066
3067 for (const auto &I: EQ.getReports()) {
3068 auto *R = dyn_cast<PathSensitiveBugReport>(I.get());
3069 if (!R)
3070 continue;
3071
3072 const ExplodedNode *errorNode = R->getErrorNode();
3073 if (errorNode->isSink()) {
3074 llvm_unreachable(
3075 "BugType::isSuppressSink() should not be 'true' for sink end nodes");
3076 }
3077 // No successors? By definition this nodes isn't post-dominated by a sink.
3078 if (errorNode->succ_empty()) {
3079 bugReports.push_back(R);
3080 if (!exampleReport)
3081 exampleReport = R;
3082 continue;
3083 }
3084
3085 // See if we are in a no-return CFG block. If so, treat this similarly
3086 // to being post-dominated by a sink. This works better when the analysis
3087 // is incomplete and we have never reached the no-return function call(s)
3088 // that we'd inevitably bump into on this path.
3089 if (const CFGBlock *ErrorB = errorNode->getCFGBlock())
3090 if (ErrorB->isInevitablySinking())
3091 continue;
3092
3093 // At this point we know that 'N' is not a sink and it has at least one
3094 // successor. Use a DFS worklist to find a non-sink end-of-path node.
3095 using WLItem = FRIEC_WLItem;
3096 using DFSWorkList = SmallVector<WLItem, 10>;
3097
3098 llvm::DenseMap<const ExplodedNode *, unsigned> Visited;
3099
3100 DFSWorkList WL;
3101 WL.push_back(errorNode);
3102 Visited[errorNode] = 1;
3103
3104 while (!WL.empty()) {
3105 WLItem &WI = WL.back();
3106 assert(!WI.N->succ_empty());
3107
3108 for (; WI.I != WI.E; ++WI.I) {
3109 const ExplodedNode *Succ = *WI.I;
3110 // End-of-path node?
3111 if (Succ->succ_empty()) {
3112 // If we found an end-of-path node that is not a sink.
3113 if (!Succ->isSink()) {
3114 bugReports.push_back(R);
3115 if (!exampleReport)
3116 exampleReport = R;
3117 WL.clear();
3118 break;
3119 }
3120 // Found a sink? Continue on to the next successor.
3121 continue;
3122 }
3123 // Mark the successor as visited. If it hasn't been explored,
3124 // enqueue it to the DFS worklist.
3125 unsigned &mark = Visited[Succ];
3126 if (!mark) {
3127 mark = 1;
3128 WL.push_back(Succ);
3129 break;
3130 }
3131 }
3132
3133 // The worklist may have been cleared at this point. First
3134 // check if it is empty before checking the last item.
3135 if (!WL.empty() && &WL.back() == &WI)
3136 WL.pop_back();
3137 }
3138 }
3139
3140 // ExampleReport will be NULL if all the nodes in the equivalence class
3141 // were post-dominated by sinks.
3142 return exampleReport;
3143}
3144
3145void BugReporter::FlushReport(BugReportEquivClass &EQ) {
3146 llvm::TimeTraceScope TCS{timeTraceName(EQ), [&]() {
3147 return timeTraceMetadata(EQ, getSourceManager());
3148 }};
3149 SmallVector<BugReport*, 10> bugReports;
3150 BugReport *report = findReportInEquivalenceClass(EQ, bugReports);
3151 if (!report)
3152 return;
3153
3154 // See whether we need to silence the checker/package.
3155 for (const std::string &CheckerOrPackage :
3156 getAnalyzerOptions().SilencedCheckersAndPackages) {
3157 if (report->getBugType().getCheckerName().starts_with(CheckerOrPackage))
3158 return;
3159 }
3160
3161 ArrayRef Consumers = getPathDiagnosticConsumers();
3162 std::unique_ptr<DiagnosticForConsumerMapTy> Diagnostics =
3163 generateDiagnosticForConsumerMap(report, Consumers, bugReports);
3164
3165 for (auto &P : *Diagnostics) {
3166 PathDiagnosticConsumer *Consumer = P.first;
3167 std::unique_ptr<PathDiagnostic> &PD = P.second;
3168
3169 // If the path is empty, generate a single step path with the location
3170 // of the issue.
3171 if (PD->path.empty()) {
3172 PathDiagnosticLocation L = report->getLocation();
3173 auto piece = std::make_unique<PathDiagnosticEventPiece>(
3174 L, report->getDescription());
3175 for (SourceRange Range : report->getRanges())
3176 piece->addRange(Range);
3177 PD->setEndOfPath(std::move(piece));
3178 }
3179
3180 PathPieces &Pieces = PD->getMutablePieces();
3181 if (getAnalyzerOptions().ShouldDisplayNotesAsEvents) {
3182 // For path diagnostic consumers that don't support extra notes,
3183 // we may optionally convert those to path notes.
3184 for (const auto &I : llvm::reverse(report->getNotes())) {
3185 PathDiagnosticNotePiece *Piece = I.get();
3186 auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
3187 Piece->getLocation(), Piece->getString());
3188 for (const auto &R: Piece->getRanges())
3189 ConvertedPiece->addRange(R);
3190
3191 Pieces.push_front(std::move(ConvertedPiece));
3192 }
3193 } else {
3194 for (const auto &I : llvm::reverse(report->getNotes()))
3195 Pieces.push_front(I);
3196 }
3197
3198 for (const auto &I : report->getFixits())
3199 Pieces.back()->addFixit(I);
3200
3202
3203 // If we are debugging, let's have the entry point as the first note.
3204 if (getAnalyzerOptions().AnalyzerDisplayProgress ||
3205 getAnalyzerOptions().AnalyzerNoteAnalysisEntryPoints) {
3206 const Decl *EntryPoint = getAnalysisEntryPoint();
3207 Pieces.push_front(std::make_shared<PathDiagnosticEventPiece>(
3208 PathDiagnosticLocation{EntryPoint->getLocation(), getSourceManager()},
3209 "[debug] analyzing from " +
3211 }
3212 Consumer->HandlePathDiagnostic(std::move(PD));
3213 }
3214}
3215
3216/// Insert all lines participating in the function signature \p Signature
3217/// into \p ExecutedLines.
3219 const Decl *Signature, const SourceManager &SM,
3220 FilesToLineNumsMap &ExecutedLines) {
3221 SourceRange SignatureSourceRange;
3222 const Stmt* Body = Signature->getBody();
3223 if (const auto FD = dyn_cast<FunctionDecl>(Signature)) {
3224 SignatureSourceRange = FD->getSourceRange();
3225 } else if (const auto OD = dyn_cast<ObjCMethodDecl>(Signature)) {
3226 SignatureSourceRange = OD->getSourceRange();
3227 } else {
3228 return;
3229 }
3230 SourceLocation Start = SignatureSourceRange.getBegin();
3231 SourceLocation End = Body ? Body->getSourceRange().getBegin()
3232 : SignatureSourceRange.getEnd();
3233 if (!Start.isValid() || !End.isValid())
3234 return;
3235 unsigned StartLine = SM.getExpansionLineNumber(Start);
3236 unsigned EndLine = SM.getExpansionLineNumber(End);
3237
3238 FileID FID = SM.getFileID(SM.getExpansionLoc(Start));
3239 for (unsigned Line = StartLine; Line <= EndLine; Line++)
3240 ExecutedLines[FID].insert(Line);
3241}
3242
3244 const Stmt *S, const SourceManager &SM,
3245 FilesToLineNumsMap &ExecutedLines) {
3247 if (!Loc.isValid())
3248 return;
3249 SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
3250 FileID FID = SM.getFileID(ExpansionLoc);
3251 unsigned LineNo = SM.getExpansionLineNumber(ExpansionLoc);
3252 ExecutedLines[FID].insert(LineNo);
3253}
3254
3255/// \return all executed lines including function signatures on the path
3256/// starting from \p N.
3257static std::unique_ptr<FilesToLineNumsMap>
3259 auto ExecutedLines = std::make_unique<FilesToLineNumsMap>();
3260
3261 while (N) {
3262 if (N->getFirstPred() == nullptr) {
3263 // First node: show signature of the entrance point.
3264 const Decl *D = N->getLocationContext()->getDecl();
3266 } else if (auto CE = N->getLocationAs<CallEnter>()) {
3267 // Inlined function: show signature.
3268 const Decl* D = CE->getCalleeContext()->getDecl();
3270 } else if (const Stmt *S = N->getStmtForDiagnostics()) {
3271 populateExecutedLinesWithStmt(S, SM, *ExecutedLines);
3272
3273 // Show extra context for some parent kinds.
3274 const Stmt *P = N->getParentMap().getParent(S);
3275
3276 // The path exploration can die before the node with the associated
3277 // return statement is generated, but we do want to show the whole
3278 // return.
3279 if (const auto *RS = dyn_cast_or_null<ReturnStmt>(P)) {
3280 populateExecutedLinesWithStmt(RS, SM, *ExecutedLines);
3281 P = N->getParentMap().getParent(RS);
3282 }
3283
3284 if (isa_and_nonnull<SwitchCase, LabelStmt>(P))
3285 populateExecutedLinesWithStmt(P, SM, *ExecutedLines);
3286 }
3287
3288 N = N->getFirstPred();
3289 }
3290 return ExecutedLines;
3291}
3292
3293std::unique_ptr<DiagnosticForConsumerMapTy>
3295 BugReport *exampleReport,
3296 ArrayRef<std::unique_ptr<PathDiagnosticConsumer>> consumers,
3297 ArrayRef<BugReport *> bugReports) {
3298 auto *basicReport = cast<BasicBugReport>(exampleReport);
3299 auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
3300 for (const auto &Consumer : consumers)
3301 (*Out)[Consumer.get()] =
3302 generateDiagnosticForBasicReport(basicReport, AnalysisEntryPoint);
3303 return Out;
3304}
3305
3308 const SourceManager &SMgr) {
3309 SourceLocation CallLoc = CP->callEnter.asLocation();
3310
3311 // If the call is within a macro, don't do anything (for now).
3312 if (CallLoc.isMacroID())
3313 return nullptr;
3314
3315 assert(AnalysisManager::isInCodeFile(CallLoc, SMgr) &&
3316 "The call piece should not be in a header file.");
3317
3318 // Check if CP represents a path through a function outside of the main file.
3320 return CP;
3321
3322 const PathPieces &Path = CP->path;
3323 if (Path.empty())
3324 return nullptr;
3325
3326 // Check if the last piece in the callee path is a call to a function outside
3327 // of the main file.
3328 if (auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get()))
3329 return getFirstStackedCallToHeaderFile(CPInner, SMgr);
3330
3331 // Otherwise, the last piece is in the main file.
3332 return nullptr;
3333}
3334
3336 if (PD.path.empty())
3337 return;
3338
3339 PathDiagnosticPiece *LastP = PD.path.back().get();
3340 assert(LastP);
3341 const SourceManager &SMgr = LastP->getLocation().getManager();
3342
3343 // We only need to check if the report ends inside headers, if the last piece
3344 // is a call piece.
3345 if (auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
3346 CP = getFirstStackedCallToHeaderFile(CP, SMgr);
3347 if (CP) {
3348 // Mark the piece.
3350
3351 // Update the path diagnostic message.
3352 const auto *ND = dyn_cast<NamedDecl>(CP->getCallee());
3353 if (ND) {
3354 SmallString<200> buf;
3355 llvm::raw_svector_ostream os(buf);
3356 os << " (within a call to '" << ND->getDeclName() << "')";
3357 PD.appendToDesc(os.str());
3358 }
3359
3360 // Reset the report containing declaration and location.
3361 PD.setDeclWithIssue(CP->getCaller());
3362 PD.setLocation(CP->getLocation());
3363
3364 return;
3365 }
3366 }
3367}
3368
3369std::unique_ptr<DiagnosticForConsumerMapTy>
3370PathSensitiveBugReporter::generateDiagnosticForConsumerMap(
3371 BugReport *exampleReport,
3372 ArrayRef<std::unique_ptr<PathDiagnosticConsumer>> consumers,
3373 ArrayRef<BugReport *> bugReports) {
3374 if (isa<BasicBugReport>(exampleReport))
3376 consumers, bugReports);
3377
3378 // Generate the full path sensitive diagnostic, using the generation scheme
3379 // specified by the PathDiagnosticConsumer. Note that we have to generate
3380 // path diagnostics even for consumers which do not support paths, because
3381 // the BugReporterVisitors may mark this bug as a false positive.
3382 assert(!bugReports.empty());
3383 MaxBugClassSize.updateMax(bugReports.size());
3384
3385 // Avoid copying the whole array because there may be a lot of reports.
3386 ArrayRef<PathSensitiveBugReport *> convertedArrayOfReports(
3387 reinterpret_cast<PathSensitiveBugReport *const *>(&*bugReports.begin()),
3388 reinterpret_cast<PathSensitiveBugReport *const *>(&*bugReports.end()));
3389 std::unique_ptr<DiagnosticForConsumerMapTy> Out = generatePathDiagnostics(
3390 consumers, convertedArrayOfReports);
3391
3392 if (Out->empty())
3393 return Out;
3394
3395 MaxValidBugClassSize.updateMax(bugReports.size());
3396
3397 // Examine the report and see if the last piece is in a header. Reset the
3398 // report location to the last piece in the main source file.
3399 const AnalyzerOptions &Opts = getAnalyzerOptions();
3400 for (auto const &P : *Out)
3401 if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.AnalyzeAll)
3403
3404 return Out;
3405}
3406
3407void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
3408 const CheckerFrontend *Checker,
3409 StringRef Name, StringRef Category,
3410 StringRef Str, PathDiagnosticLocation Loc,
3411 ArrayRef<SourceRange> Ranges,
3412 ArrayRef<FixItHint> Fixits) {
3413 EmitBasicReport(DeclWithIssue, Checker->getName(), Name, Category, Str, Loc,
3414 Ranges, Fixits);
3415}
3416
3417void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
3418 CheckerNameRef CheckName,
3419 StringRef name, StringRef category,
3420 StringRef str, PathDiagnosticLocation Loc,
3421 ArrayRef<SourceRange> Ranges,
3422 ArrayRef<FixItHint> Fixits) {
3423 // 'BT' is owned by BugReporter.
3424 BugType *BT = getBugTypeForName(CheckName, name, category);
3425 auto R = std::make_unique<BasicBugReport>(*BT, str, Loc);
3426 R->setDeclWithIssue(DeclWithIssue);
3427 for (const auto &SR : Ranges)
3428 R->addRange(SR);
3429 for (const auto &FH : Fixits)
3430 R->addFixItHint(FH);
3431 emitReport(std::move(R));
3432}
3433
3434BugType *BugReporter::getBugTypeForName(CheckerNameRef CheckName,
3435 StringRef name, StringRef category) {
3436 SmallString<136> fullDesc;
3437 llvm::raw_svector_ostream(fullDesc)
3438 << CheckName << ":" << name << ":" << category;
3439 std::unique_ptr<BugType> &BT = StrBugTypes[fullDesc];
3440 if (!BT)
3441 BT = std::make_unique<BugType>(CheckName, name, category);
3442 return BT.get();
3443}
#define V(N, I)
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 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.
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)
static llvm::TimeTraceMetadata timeTraceMetadata(const ExplodedNode *Pred, const ProgramPoint &Loc)
#define STAT_COUNTER(VARNAME, DESC)
#define STAT_MAX(VARNAME, DESC)
Defines the clang::Expr interface and subclasses for C++ expressions.
bool isLoop(const FormatStyle &Style) const
FormatToken * Next
The next token in the unwrapped line.
#define X(type, name)
Definition Value.h:97
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
#define SM(sm)
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Defines the Objective-C statement AST node classes.
SourceManager & getSourceManager()
Definition ASTContext.h:837
AnalysisDeclContext contains the context data for the function, method or block under analysis.
static std::string getFunctionName(const Decl *D)
const CFGBlock * getSrc() const
const CFGBlock * getDst() const
Represents a single basic block in a source-level CFG.
Definition CFG.h:605
Stmt * getLabel()
Definition CFG.h:1106
succ_iterator succ_begin()
Definition CFG.h:990
Stmt * getTerminatorStmt()
Definition CFG.h:1087
const Stmt * getLoopTarget() const
Definition CFG.h:1104
Stmt * getTerminatorCondition(bool StripParens=true)
Definition CFG.cpp:6399
unsigned succ_size() const
Definition CFG.h:1008
CFGBlock & getExit()
Definition CFG.h:1333
CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for statement, represented as 'for (ra...
Definition StmtCXX.h:135
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.
Definition DeclBase.h:86
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:546
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition DeclBase.h:593
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Definition DeclBase.h:1087
virtual bool hasBody() const
Returns true if this Decl represents a declaration for a body of code, such as a function or method d...
Definition DeclBase.h:1093
SourceLocation getLocation() const
Definition DeclBase.h:439
This represents one expression.
Definition Expr.h:112
llvm::APSInt EvaluateKnownConstInt(const ASTContext &Ctx) const
EvaluateKnownConstInt - Call EvaluateAsRValue and return the folded integer.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool isValid() const
ForStmt - This represents a 'for (init;cond;inc)' stmt.
Definition Stmt.h:2867
A SourceLocation and its associated SourceManager.
unsigned getExpansionLineNumber(bool *Invalid=nullptr) const
IfStmt - This represents an if/then/else.
Definition Stmt.h:2238
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.
Definition StmtObjC.h:23
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.
T castAs() const
Convert to the specified ProgramPoint type, asserting that this ProgramPoint is of the desired type.
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;.
Definition Stmt.h:3139
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.
Definition Stmt.h:85
StmtClass getStmtClass() const
Definition Stmt.h:1472
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:338
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Stmt.cpp:350
WhileStmt - This represents a 'while' stmt.
Definition Stmt.h:2676
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()
BugReport(Kind kind, const BugType &bt, StringRef desc)
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 & BT
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.
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.
ArrayRef< std::unique_ptr< PathDiagnosticConsumer > > getPathDiagnosticConsumers()
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerFrontend *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges={}, ArrayRef< FixItHint > Fixits={})
virtual std::unique_ptr< DiagnosticForConsumerMapTy > generateDiagnosticForConsumerMap(BugReport *exampleReport, ArrayRef< std::unique_ptr< PathDiagnosticConsumer > > consumers, ArrayRef< BugReport * > bugReports)
Generate the diagnostics for the given bug report.
const AnalyzerOptions & getAnalyzerOptions()
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
bool isSuppressOnSink() const
isSuppressOnSink - Returns true if bug reports associated with this bug type should be suppressed if ...
Definition BugType.h:70
StringRef getCategory() const
Definition BugType.h:59
StringRef getDescription() const
Definition BugType.h:58
StringRef getCheckerName() const
Definition BugType.h:60
A CheckerFrontend instance is what the user recognizes as "one checker": it has a public canonical na...
Definition Checker.h:514
CheckerNameRef getName() const
Definition Checker.h:524
This wrapper is used to ensure that only StringRefs originating from the CheckerRegistry are used as ...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:553
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
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 &
ExplodedNode * getFirstPred()
const ExplodedNode *const * const_succ_iterator
Suppress reports that might lead to known false positives.
MemRegion - The root abstract class for all memory regions.
Definition MemRegion.h:98
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
Prints path notes when a message is sent to a nil receiver.
PathDiagnosticLocation getLocation() const override
void setCallee(const CallEnter &CE, const SourceManager &SM)
static std::shared_ptr< PathDiagnosticCallPiece > construct(const CallExitEnd &CE, const SourceManager &SM)
PathDiagnosticLocation callEnterWithin
virtual bool supportsLogicalOpControlFlow() const
void HandlePathDiagnostic(std::unique_ptr< PathDiagnostic > D)
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 '.
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.
static PathDiagnosticLocation createDeclEnd(const LocationContext *LC, const SourceManager &SM)
Constructs a location for the end of the enclosing declaration body.
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
const void * getTag() const
Return the opaque tag (if any) on the PathDiagnosticPiece.
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
void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind=bugreporter::TrackingKind::Thorough)
Marks a symbol as interesting.
SmallVector< std::unique_ptr< BugReporterVisitor >, 8 > VisitorList
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.
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...
PathDiagnosticLocation UniqueingLocation
Reports with different uniqueing locations are considered to be different for the purposes of dedupli...
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
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.
llvm::SmallPtrSet< const LocationContext *, 2 > InterestingLocationContexts
A set of location contexts that correspoind to call sites which should be considered "interesting".
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.
void emitReport(std::unique_ptr< BugReport > R) override
Add the given report to the set of reports tracked by BugReporter.
std::unique_ptr< DiagnosticForConsumerMapTy > generatePathDiagnostics(ArrayRef< std::unique_ptr< PathDiagnosticConsumer > > consumers, ArrayRef< PathSensitiveBugReport * > &bugReports)
bugReports A set of bug reports within a single equivalence class
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.
Definition SVals.h:56
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition SVals.h:87
SymbolRef getAsLocSymbol(bool IncludeBaseRegions=false) const
If this SVal is a location and wraps a symbol, return that SymbolRef.
Definition SVals.cpp:67
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'.
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
const SymExpr * SymbolRef
Definition SymExpr.h:133
@ CF
Indicates that the tracked object is a CF object.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
std::map< FileID, std::set< unsigned > > FilesToLineNumsMap
File IDs mapped to sets of line numbers.
bool EQ(InterpState &S, CodePtr OpPC)
Definition Interp.h:1203
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
Expr * Cond
};
const FunctionProtoType * T
U cast(CodeGen::Address addr)
Definition Address.h:327
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30