forked from llvm/llvm-project
/
BugReporterVisitors.h
740 lines (627 loc) · 29.4 KB
/
BugReporterVisitors.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
//===- BugReporterVisitors.h - Generate PathDiagnostics ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file declares BugReporterVisitors, which are used to generate enhanced
// diagnostic traces.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H
#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H
#include "clang/Analysis/ProgramPoint.h"
#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringRef.h"
#include <list>
#include <memory>
#include <utility>
namespace clang {
class BinaryOperator;
class CFGBlock;
class DeclRefExpr;
class Expr;
class Stmt;
namespace ento {
class PathSensitiveBugReport;
class BugReporterContext;
class ExplodedNode;
class MemRegion;
class PathDiagnosticPiece;
using PathDiagnosticPieceRef = std::shared_ptr<PathDiagnosticPiece>;
/// BugReporterVisitors are used to add custom diagnostics along a path.
class BugReporterVisitor : public llvm::FoldingSetNode {
public:
BugReporterVisitor() = default;
BugReporterVisitor(const BugReporterVisitor &) = default;
BugReporterVisitor(BugReporterVisitor &&) {}
virtual ~BugReporterVisitor();
/// Return a diagnostic piece which should be associated with the
/// given node.
/// Note that this function does *not* get run on the very last node
/// of the report, as the PathDiagnosticPiece associated with the
/// last node should be unique.
/// Use \ref getEndPath to customize the note associated with the report
/// end instead.
///
/// The last parameter can be used to register a new visitor with the given
/// BugReport while processing a node.
virtual PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) = 0;
/// Last function called on the visitor, no further calls to VisitNode
/// would follow.
virtual void finalizeVisitor(BugReporterContext &BRC,
const ExplodedNode *EndPathNode,
PathSensitiveBugReport &BR);
/// Provide custom definition for the final diagnostic piece on the
/// path - the piece, which is displayed before the path is expanded.
///
/// NOTE that this function can be implemented on at most one used visitor,
/// and otherwise it crahes at runtime.
virtual PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
const ExplodedNode *N,
PathSensitiveBugReport &BR);
virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0;
/// Generates the default final diagnostic piece.
static PathDiagnosticPieceRef
getDefaultEndPath(const BugReporterContext &BRC, const ExplodedNode *N,
const PathSensitiveBugReport &BR);
};
namespace bugreporter {
/// Specifies the type of tracking for an expression.
enum class TrackingKind {
/// Default tracking kind -- specifies that as much information should be
/// gathered about the tracked expression value as possible.
Thorough,
/// Specifies that a more moderate tracking should be used for the expression
/// value. This will essentially make sure that functions relevant to it
/// aren't pruned, but otherwise relies on the user reading the code or
/// following the arrows.
Condition
};
/// Defines a set of options altering tracking behavior.
struct TrackingOptions {
/// Specifies the kind of tracking.
TrackingKind Kind = TrackingKind::Thorough;
/// Specifies whether we should employ false positive suppression
/// (inlined defensive checks, returned null).
bool EnableNullFPSuppression = true;
};
/// Describes an event when the value got stored into a memory region.
///
/// As opposed to checker checkBind API, it reacts also to binds
/// generated by the checker as well. It can be useful when the binding
/// happened as a result of evalCall, for example.
struct StoreInfo {
enum Kind {
/// The value got stored into the region during initialization:
/// int x = 42;
Initialization,
/// The value got stored into the region during assignment:
/// int x;
/// x = 42;
Assignment,
/// The value got stored into the parameter region as the result
/// of a call.
CallArgument,
/// The value got stored into the region as block capture.
/// Block data is modeled as a separate region, thus whenever
/// the analyzer sees a captured variable, its value is copied
/// into a special block region.
BlockCapture
};
/// The type of store operation.
Kind StoreKind;
/// The node where the store happened.
const ExplodedNode *StoreSite;
/// The expression where the value comes from.
/// NOTE: might be null.
const Expr *SourceOfTheValue;
/// Symbolic value that is being stored.
SVal Value;
/// Memory regions involved in the store operation.
/// Dest <- Origin
/// NOTE: Origin might be null, when the stored value doesn't come
/// from another region.
const MemRegion *Dest, *Origin;
};
class Tracker;
using TrackerRef = llvm::IntrusiveRefCntPtr<Tracker>;
class ExpressionHandler;
class StoreHandler;
/// A generalized component for tracking expressions, values, and stores.
///
/// Tracker aimes at providing a sensible set of default behaviors that can be
/// used by any checker, while providing mechanisms to hook into any part of the
/// tracking process and insert checker-specific logic.
class Tracker : public llvm::RefCountedBase<Tracker> {
private:
using ExpressionHandlerPtr = std::unique_ptr<ExpressionHandler>;
using StoreHandlerPtr = std::unique_ptr<StoreHandler>;
PathSensitiveBugReport &Report;
std::list<ExpressionHandlerPtr> ExpressionHandlers;
std::list<StoreHandlerPtr> StoreHandlers;
protected:
/// \param Report The bug report to which visitors should be attached.
Tracker(PathSensitiveBugReport &Report);
public:
virtual ~Tracker() = default;
static TrackerRef create(PathSensitiveBugReport &Report) {
return new Tracker(Report);
}
PathSensitiveBugReport &getReport() { return Report; }
/// Describes a tracking result with the most basic information of what was
/// actually done (or not done).
struct Result {
/// Usually it means that the tracker added visitors.
bool FoundSomethingToTrack = false;
/// Signifies that the tracking was interrupted at some point.
/// Usually this information is important only for sub-trackers.
bool WasInterrupted = false;
/// Combines the current result with the given result.
void combineWith(const Result &Other) {
// If we found something in one of the cases, we can
// say we found something overall.
FoundSomethingToTrack |= Other.FoundSomethingToTrack;
// The same goes to the interruption.
WasInterrupted |= Other.WasInterrupted;
}
};
/// Track expression value back to its point of origin.
///
/// \param E The expression value which we are tracking
/// \param N A node "downstream" from the evaluation of the statement.
/// \param Opts Tracking options specifying how we want to track the value.
virtual Result track(const Expr *E, const ExplodedNode *N,
TrackingOptions Opts = {});
/// Track how the value got stored into the given region and where it came
/// from.
///
/// \param V We're searching for the store where \c R received this value.
/// \param R The region we're tracking.
/// \param Opts Tracking options specifying how we want to track the value.
/// \param Origin Only adds notes when the last store happened in a
/// different stackframe to this one. Disregarded if the tracking kind
/// is thorough.
/// This is useful, because for non-tracked regions, notes about
/// changes to its value in a nested stackframe could be pruned, and
/// this visitor can prevent that without polluting the bugpath too
/// much.
virtual Result track(SVal V, const MemRegion *R, TrackingOptions Opts = {},
const StackFrameContext *Origin = nullptr);
/// Handle the store operation and produce the note.
///
/// \param SI The information fully describing the store.
/// \param Opts Tracking options specifying how we got to it.
///
/// NOTE: this method is designed for sub-trackers and visitors.
virtual PathDiagnosticPieceRef handle(StoreInfo SI, BugReporterContext &BRC,
TrackingOptions Opts);
/// Add custom expression handler with the highest priority.
///
/// It means that it will be asked for handling first, and can prevent
/// other handlers from running if decides to interrupt.
void addHighPriorityHandler(ExpressionHandlerPtr SH) {
ExpressionHandlers.push_front(std::move(SH));
}
/// Add custom expression handler with the lowest priority.
///
/// It means that it will be asked for handling last, and other handlers can
/// prevent it from running if any of them decides to interrupt.
void addLowPriorityHandler(ExpressionHandlerPtr SH) {
ExpressionHandlers.push_back(std::move(SH));
}
/// Add custom store handler with the highest priority.
///
/// It means that it will be asked for handling first, and will prevent
/// other handlers from running if it produces non-null note.
void addHighPriorityHandler(StoreHandlerPtr SH) {
StoreHandlers.push_front(std::move(SH));
}
/// Add custom store handler with the lowest priority.
///
/// It means that it will be asked for handling last, only
/// if all other handlers failed to produce the note.
void addLowPriorityHandler(StoreHandlerPtr SH) {
StoreHandlers.push_back(std::move(SH));
}
/// Add custom expression/store handler with the highest priority
///
/// See other overloads for explanation.
template <class HandlerType, class... Args>
void addHighPriorityHandler(Args &&... ConstructorArgs) {
addHighPriorityHandler(std::make_unique<HandlerType>(
*this, std::forward<Args>(ConstructorArgs)...));
}
/// Add custom expression/store handler with the lowest priority
///
/// See other overloads for explanation.
template <class HandlerType, class... Args>
void addLowPriorityHandler(Args &&... ConstructorArgs) {
addLowPriorityHandler(std::make_unique<HandlerType>(
*this, std::forward<Args>(ConstructorArgs)...));
}
};
/// Handles expressions during the tracking.
class ExpressionHandler {
private:
Tracker &ParentTracker;
public:
ExpressionHandler(Tracker &ParentTracker) : ParentTracker(ParentTracker) {}
virtual ~ExpressionHandler() {}
/// Handle the given expression from the given node.
///
/// \param E The expression value which we are tracking
/// \param Original A node "downstream" where the tracking started.
/// \param ExprNode A node where the evaluation of \c E actually happens.
/// \param Opts Tracking options specifying how we are tracking the value.
virtual Tracker::Result handle(const Expr *E, const ExplodedNode *Original,
const ExplodedNode *ExprNode,
TrackingOptions Opts) = 0;
/// \Return the tracker that initiated the process.
Tracker &getParentTracker() { return ParentTracker; }
};
/// Handles stores during the tracking.
class StoreHandler {
private:
Tracker &ParentTracker;
public:
StoreHandler(Tracker &ParentTracker) : ParentTracker(ParentTracker) {}
virtual ~StoreHandler() {}
/// Handle the given store and produce the node.
///
/// \param SI The information fully describing the store.
/// \param Opts Tracking options specifying how we are tracking the value.
///
/// \return the produced note, null if the handler doesn't support this kind
/// of stores.
virtual PathDiagnosticPieceRef handle(StoreInfo SI, BugReporterContext &BRC,
TrackingOptions Opts) = 0;
Tracker &getParentTracker() { return ParentTracker; }
protected:
PathDiagnosticPieceRef constructNote(StoreInfo SI, BugReporterContext &BRC,
StringRef NodeText);
};
/// Visitor that tracks expressions and values.
class TrackingBugReporterVisitor : public BugReporterVisitor {
private:
TrackerRef ParentTracker;
public:
TrackingBugReporterVisitor(TrackerRef ParentTracker)
: ParentTracker(ParentTracker) {}
Tracker &getParentTracker() { return *ParentTracker; }
};
/// Attempts to add visitors to track expression value back to its point of
/// origin.
///
/// \param N A node "downstream" from the evaluation of the statement.
/// \param E The expression value which we are tracking
/// \param R The bug report to which visitors should be attached.
/// \param Opts Tracking options specifying how we are tracking the value.
///
/// \return Whether or not the function was able to add visitors for this
/// statement. Note that returning \c true does not actually imply
/// that any visitors were added.
bool trackExpressionValue(const ExplodedNode *N, const Expr *E,
PathSensitiveBugReport &R, TrackingOptions Opts = {});
/// Track how the value got stored into the given region and where it came
/// from.
///
/// \param V We're searching for the store where \c R received this value.
/// \param R The region we're tracking.
/// \param Opts Tracking options specifying how we want to track the value.
/// \param Origin Only adds notes when the last store happened in a
/// different stackframe to this one. Disregarded if the tracking kind
/// is thorough.
/// This is useful, because for non-tracked regions, notes about
/// changes to its value in a nested stackframe could be pruned, and
/// this visitor can prevent that without polluting the bugpath too
/// much.
void trackStoredValue(KnownSVal V, const MemRegion *R,
PathSensitiveBugReport &Report, TrackingOptions Opts = {},
const StackFrameContext *Origin = nullptr);
const Expr *getDerefExpr(const Stmt *S);
} // namespace bugreporter
class TrackConstraintBRVisitor final : public BugReporterVisitor {
DefinedSVal Constraint;
bool Assumption;
bool IsSatisfied = false;
bool IsZeroCheck;
/// We should start tracking from the last node along the path in which the
/// value is constrained.
bool IsTrackingTurnedOn = false;
public:
TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption)
: Constraint(constraint), Assumption(assumption),
IsZeroCheck(!Assumption && isa<Loc>(Constraint)) {}
void Profile(llvm::FoldingSetNodeID &ID) const override;
/// Return the tag associated with this visitor. This tag will be used
/// to make all PathDiagnosticPieces created by this visitor.
static const char *getTag();
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
private:
/// Checks if the constraint is valid in the current state.
bool isUnderconstrained(const ExplodedNode *N) const;
};
/// \class NilReceiverBRVisitor
/// Prints path notes when a message is sent to a nil receiver.
class NilReceiverBRVisitor final : public BugReporterVisitor {
public:
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int x = 0;
ID.AddPointer(&x);
}
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
/// If the statement is a message send expression with nil receiver, returns
/// the receiver expression. Returns NULL otherwise.
static const Expr *getNilReceiver(const Stmt *S, const ExplodedNode *N);
};
/// Visitor that tries to report interesting diagnostics from conditions.
class ConditionBRVisitor final : public BugReporterVisitor {
// FIXME: constexpr initialization isn't supported by MSVC2013.
constexpr static llvm::StringLiteral GenericTrueMessage =
"Assuming the condition is true";
constexpr static llvm::StringLiteral GenericFalseMessage =
"Assuming the condition is false";
public:
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int x = 0;
ID.AddPointer(&x);
}
/// Return the tag associated with this visitor. This tag will be used
/// to make all PathDiagnosticPieces created by this visitor.
static const char *getTag();
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
PathDiagnosticPieceRef VisitNodeImpl(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR);
PathDiagnosticPieceRef
VisitTerminator(const Stmt *Term, const ExplodedNode *N,
const CFGBlock *SrcBlk, const CFGBlock *DstBlk,
PathSensitiveBugReport &R, BugReporterContext &BRC);
PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond,
BugReporterContext &BRC,
PathSensitiveBugReport &R,
const ExplodedNode *N, bool TookTrue);
PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR,
BugReporterContext &BRC,
PathSensitiveBugReport &R,
const ExplodedNode *N, bool TookTrue,
bool IsAssuming);
PathDiagnosticPieceRef
VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr,
BugReporterContext &BRC, PathSensitiveBugReport &R,
const ExplodedNode *N, bool TookTrue, bool IsAssuming);
PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const MemberExpr *ME,
BugReporterContext &BRC,
PathSensitiveBugReport &R,
const ExplodedNode *N, bool TookTrue,
bool IsAssuming);
PathDiagnosticPieceRef
VisitConditionVariable(StringRef LhsString, const Expr *CondVarExpr,
BugReporterContext &BRC, PathSensitiveBugReport &R,
const ExplodedNode *N, bool TookTrue);
/// Tries to print the value of the given expression.
///
/// \param CondVarExpr The expression to print its value.
/// \param Out The stream to print.
/// \param N The node where we encountered the condition.
/// \param TookTrue Whether we took the \c true branch of the condition.
///
/// \return Whether the print was successful. (The printing is successful if
/// we model the value and we could obtain it.)
bool printValue(const Expr *CondVarExpr, raw_ostream &Out,
const ExplodedNode *N, bool TookTrue, bool IsAssuming);
bool patternMatch(const Expr *Ex,
const Expr *ParentEx,
raw_ostream &Out,
BugReporterContext &BRC,
PathSensitiveBugReport &R,
const ExplodedNode *N,
Optional<bool> &prunable,
bool IsSameFieldName);
static bool isPieceMessageGeneric(const PathDiagnosticPiece *Piece);
};
/// Suppress reports that might lead to known false positives.
///
/// Currently this suppresses reports based on locations of bugs.
class LikelyFalsePositiveSuppressionBRVisitor final
: public BugReporterVisitor {
public:
static void *getTag() {
static int Tag = 0;
return static_cast<void *>(&Tag);
}
void Profile(llvm::FoldingSetNodeID &ID) const override {
ID.AddPointer(getTag());
}
PathDiagnosticPieceRef VisitNode(const ExplodedNode *, BugReporterContext &,
PathSensitiveBugReport &) override {
return nullptr;
}
void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N,
PathSensitiveBugReport &BR) override;
};
/// When a region containing undefined value or '0' value is passed
/// as an argument in a call, marks the call as interesting.
///
/// As a result, BugReporter will not prune the path through the function even
/// if the region's contents are not modified/accessed by the call.
class UndefOrNullArgVisitor final : public BugReporterVisitor {
/// The interesting memory region this visitor is tracking.
const MemRegion *R;
public:
UndefOrNullArgVisitor(const MemRegion *InR) : R(InR) {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int Tag = 0;
ID.AddPointer(&Tag);
ID.AddPointer(R);
}
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
};
class SuppressInlineDefensiveChecksVisitor final : public BugReporterVisitor {
/// The symbolic value for which we are tracking constraints.
/// This value is constrained to null in the end of path.
DefinedSVal V;
/// Track if we found the node where the constraint was first added.
bool IsSatisfied = false;
/// Since the visitors can be registered on nodes previous to the last
/// node in the BugReport, but the path traversal always starts with the last
/// node, the visitor invariant (that we start with a node in which V is null)
/// might not hold when node visitation starts. We are going to start tracking
/// from the last node in which the value is null.
bool IsTrackingTurnedOn = false;
public:
SuppressInlineDefensiveChecksVisitor(DefinedSVal Val, const ExplodedNode *N);
void Profile(llvm::FoldingSetNodeID &ID) const override;
/// Return the tag associated with this visitor. This tag will be used
/// to make all PathDiagnosticPieces created by this visitor.
static const char *getTag();
PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
};
/// The bug visitor will walk all the nodes in a path and collect all the
/// constraints. When it reaches the root node, will create a refutation
/// manager and check if the constraints are satisfiable
class FalsePositiveRefutationBRVisitor final : public BugReporterVisitor {
private:
/// Holds the constraints in a given path
ConstraintMap Constraints;
public:
FalsePositiveRefutationBRVisitor();
void Profile(llvm::FoldingSetNodeID &ID) const override;
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
PathSensitiveBugReport &BR) override;
void addConstraints(const ExplodedNode *N,
bool OverwriteConstraintsOnExistingSyms);
};
/// The visitor detects NoteTags and displays the event notes they contain.
class TagVisitor : public BugReporterVisitor {
public:
void Profile(llvm::FoldingSetNodeID &ID) const override;
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &R) override;
};
class ObjCMethodCall;
class CXXConstructorCall;
/// Put a diagnostic on return statement (or on } in its absence) of all inlined
/// functions for which some property remained unchanged.
/// Resulting diagnostics may read such as "Returning without writing to X".
///
/// Descendants can define what a "state change is", like a change of value
/// to a memory region, liveness, etc. For function calls where the state did
/// not change as defined, a custom note may be constructed.
///
/// For a minimal example, check out
/// clang/unittests/StaticAnalyzer/NoStateChangeFuncVisitorTest.cpp.
class NoStateChangeFuncVisitor : public BugReporterVisitor {
private:
/// Frames modifying the state as defined in \c wasModifiedBeforeCallExit.
/// This visitor generates a note only if a function does *not* change the
/// state that way. This information is not immediately available
/// by looking at the node associated with the exit from the function
/// (usually the return statement). To avoid recomputing the same information
/// many times (going up the path for each node and checking whether the
/// region was written into) we instead lazily compute the stack frames
/// along the path.
// TODO: Can't we just use a map instead? This is likely not as cheap as it
// makes the code difficult to read.
llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifying;
llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated;
/// Check and lazily calculate whether the state is modified in the stack
/// frame to which \p CallExitBeginN belongs.
/// The calculation is cached in FramesModifying.
bool isModifiedInFrame(const ExplodedNode *CallExitBeginN);
void markFrameAsModifying(const StackFrameContext *SCtx);
/// Write to \c FramesModifying all stack frames along the path in the current
/// stack frame which modifies the state.
void findModifyingFrames(const ExplodedNode *const CallExitBeginN);
protected:
bugreporter::TrackingKind TKind;
/// \return Whether the state was modified from the current node, \p CurrN, to
/// the end of the stack frame, at \p CallExitBeginN. \p CurrN and
/// \p CallExitBeginN are always in the same stack frame.
/// Clients should override this callback when a state change is important
/// not only on the entire function call, but inside of it as well.
/// Example: we may want to leave a note about the lack of locking/unlocking
/// on a particular mutex, but not if inside the function its state was
/// changed, but also restored. wasModifiedInFunction() wouldn't know of this
/// change.
virtual bool wasModifiedBeforeCallExit(const ExplodedNode *CurrN,
const ExplodedNode *CallExitBeginN) {
return false;
}
/// \return Whether the state was modified in the inlined function call in
/// between \p CallEnterN and \p CallExitEndN. Mind that the stack frame
/// retrieved from a CallEnterN and CallExitEndN is the *caller's* stack
/// frame! The inlined function's stack should be retrieved from either the
/// immediate successor to \p CallEnterN or immediate predecessor to
/// \p CallExitEndN.
/// Clients should override this function if a state changes local to the
/// inlined function are not interesting, only the change occuring as a
/// result of it.
/// Example: we want to leave a not about a leaked resource object not being
/// deallocated / its ownership changed inside a function, and we don't care
/// if it was assigned to a local variable (its change in ownership is
/// inconsequential).
virtual bool wasModifiedInFunction(const ExplodedNode *CallEnterN,
const ExplodedNode *CallExitEndN) {
return false;
}
/// Consume the information on the non-modifying stack frame in order to
/// either emit a note or not. May suppress the report entirely.
/// \return Diagnostics piece for the unmodified state in the current
/// function, if it decides to emit one. A good description might start with
/// "Returning without...".
virtual PathDiagnosticPieceRef
maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
const ObjCMethodCall &Call,
const ExplodedNode *N) = 0;
/// Consume the information on the non-modifying stack frame in order to
/// either emit a note or not. May suppress the report entirely.
/// \return Diagnostics piece for the unmodified state in the current
/// function, if it decides to emit one. A good description might start with
/// "Returning without...".
virtual PathDiagnosticPieceRef
maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
const CXXConstructorCall &Call,
const ExplodedNode *N) = 0;
/// Consume the information on the non-modifying stack frame in order to
/// either emit a note or not. May suppress the report entirely.
/// \return Diagnostics piece for the unmodified state in the current
/// function, if it decides to emit one. A good description might start with
/// "Returning without...".
virtual PathDiagnosticPieceRef
maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
const ExplodedNode *N) = 0;
public:
NoStateChangeFuncVisitor(bugreporter::TrackingKind TKind) : TKind(TKind) {}
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BR,
PathSensitiveBugReport &R) final;
};
} // namespace ento
} // namespace clang
#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H