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