forked from OSchip/llvm-project
[analyzer] Add a prunable note for skipping vbase inits in subclasses.
When initialization of virtual base classes is skipped, we now tell the user about it, because this aspect of C++ isn't very well-known. The implementation is based on the new "note tags" feature (r358781). In order to make use of it, allow note tags to produce prunable notes, and move the note tag factory to CoreEngine. Differential Revision: https://reviews.llvm.org/D61817 llvm-svn: 361682
This commit is contained in:
parent
192a7474d6
commit
ef0aab3138
|
@ -604,8 +604,10 @@ private:
|
|||
static int Kind;
|
||||
|
||||
const Callback Cb;
|
||||
const bool IsPrunable;
|
||||
|
||||
NoteTag(Callback &&Cb) : ProgramPointTag(&Kind), Cb(std::move(Cb)) {}
|
||||
NoteTag(Callback &&Cb, bool IsPrunable)
|
||||
: ProgramPointTag(&Kind), Cb(std::move(Cb)), IsPrunable(IsPrunable) {}
|
||||
|
||||
public:
|
||||
static bool classof(const ProgramPointTag *T) {
|
||||
|
@ -628,15 +630,17 @@ public:
|
|||
return "Note Tag";
|
||||
}
|
||||
|
||||
bool isPrunable() const { return IsPrunable; }
|
||||
|
||||
// Manage memory for NoteTag objects.
|
||||
class Factory {
|
||||
std::vector<std::unique_ptr<NoteTag>> Tags;
|
||||
|
||||
public:
|
||||
const NoteTag *makeNoteTag(Callback &&Cb) {
|
||||
const NoteTag *makeNoteTag(Callback &&Cb, bool IsPrunable = false) {
|
||||
// We cannot use make_unique because we cannot access the private
|
||||
// constructor from inside it.
|
||||
std::unique_ptr<NoteTag> T(new NoteTag(std::move(Cb)));
|
||||
std::unique_ptr<NoteTag> T(new NoteTag(std::move(Cb), IsPrunable));
|
||||
Tags.push_back(std::move(T));
|
||||
return Tags.back().get();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "clang/Analysis/CFG.h"
|
||||
#include "clang/Analysis/ProgramPoint.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
|
||||
|
@ -95,6 +96,10 @@ private:
|
|||
/// (This data is owned by AnalysisConsumer.)
|
||||
FunctionSummariesTy *FunctionSummaries;
|
||||
|
||||
/// Add path note tags along the path when we see that something interesting
|
||||
/// is happening. This field is the allocator for such tags.
|
||||
NoteTag::Factory NoteTags;
|
||||
|
||||
void generateNode(const ProgramPoint &Loc,
|
||||
ProgramStateRef State,
|
||||
ExplodedNode *Pred);
|
||||
|
@ -194,6 +199,8 @@ public:
|
|||
|
||||
/// Enqueue a single node created as a result of statement processing.
|
||||
void enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx);
|
||||
|
||||
NoteTag::Factory &getNoteTags() { return NoteTags; }
|
||||
};
|
||||
|
||||
// TODO: Turn into a class.
|
||||
|
|
|
@ -156,8 +156,6 @@ private:
|
|||
/// The flag, which specifies the mode of inlining for the engine.
|
||||
InliningModes HowToInline;
|
||||
|
||||
NoteTag::Factory NoteTags;
|
||||
|
||||
public:
|
||||
ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, AnalysisManager &mgr,
|
||||
SetOfConstDecls *VisitedCalleesIn,
|
||||
|
@ -399,7 +397,7 @@ public:
|
|||
SymbolManager &getSymbolManager() { return SymMgr; }
|
||||
MemRegionManager &getRegionManager() { return MRMgr; }
|
||||
|
||||
NoteTag::Factory &getNoteTags() { return NoteTags; }
|
||||
NoteTag::Factory &getNoteTags() { return Engine.getNoteTags(); }
|
||||
|
||||
|
||||
// Functions for external checking of whether we have unfinished work
|
||||
|
|
|
@ -2501,7 +2501,9 @@ TagVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
|
|||
if (Optional<std::string> Msg = T->generateMessage(BRC, R)) {
|
||||
PathDiagnosticLocation Loc =
|
||||
PathDiagnosticLocation::create(PP, BRC.getSourceManager());
|
||||
return std::make_shared<PathDiagnosticEventPiece>(Loc, *Msg);
|
||||
auto Piece = std::make_shared<PathDiagnosticEventPiece>(Loc, *Msg);
|
||||
Piece->setPrunable(T->isPrunable());
|
||||
return Piece;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
|
|
@ -216,6 +216,25 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
|
|||
LC->getDecl(),
|
||||
LC->getCFG()->getNumBlockIDs());
|
||||
|
||||
// Display a prunable path note to the user if it's a virtual bases branch
|
||||
// and we're taking the path that skips virtual base constructors.
|
||||
if (L.getSrc()->getTerminator().isVirtualBaseBranch() &&
|
||||
L.getDst() == *L.getSrc()->succ_begin()) {
|
||||
ProgramPoint P = L.withTag(getNoteTags().makeNoteTag(
|
||||
[](BugReporterContext &, BugReport &) -> std::string {
|
||||
// TODO: Just call out the name of the most derived class
|
||||
// when we know it.
|
||||
return "Virtual base initialization skipped because "
|
||||
"it has already been handled by the most derived class";
|
||||
}, /*IsPrunable=*/true));
|
||||
// Perform the transition.
|
||||
ExplodedNodeSet Dst;
|
||||
NodeBuilder Bldr(Pred, Dst, BuilderCtx);
|
||||
Pred = Bldr.generateNode(P, Pred->getState(), Pred);
|
||||
if (!Pred)
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we are entering the EXIT block.
|
||||
if (Blk == &(L.getLocationContext()->getCFG()->getExit())) {
|
||||
assert(L.getLocationContext()->getCFG()->getExit().empty() &&
|
||||
|
|
|
@ -724,7 +724,15 @@ PathDiagnosticLocation::create(const ProgramPoint& P,
|
|||
const Stmt* S = nullptr;
|
||||
if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
|
||||
const CFGBlock *BSrc = BE->getSrc();
|
||||
S = BSrc->getTerminatorCondition();
|
||||
if (BSrc->getTerminator().isVirtualBaseBranch()) {
|
||||
// TODO: VirtualBaseBranches should also appear for destructors.
|
||||
// In this case we should put the diagnostic at the end of decl.
|
||||
return PathDiagnosticLocation::createBegin(
|
||||
P.getLocationContext()->getDecl(), SMng);
|
||||
|
||||
} else {
|
||||
S = BSrc->getTerminatorCondition();
|
||||
}
|
||||
} else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) {
|
||||
S = SP->getStmt();
|
||||
if (P.getAs<PostStmtPurgeDeadSymbols>())
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
// RUN: %clang_analyze_cc1 -w -analyzer-checker=core -analyzer-output=text \
|
||||
// RUN: -verify %s
|
||||
|
||||
namespace note_on_skipped_vbases {
|
||||
struct A {
|
||||
int x;
|
||||
A() : x(0) {} // expected-note{{The value 0 is assigned to 'c.x'}}
|
||||
A(int x) : x(x) {}
|
||||
};
|
||||
|
||||
struct B : virtual A {
|
||||
int y;
|
||||
// This note appears only once, when this constructor is called from C.
|
||||
// When this constructor is called from D, this note is still correct but
|
||||
// it doesn't appear because it's pruned out because it's irrelevant to the
|
||||
// bug report.
|
||||
B(): // expected-note{{Virtual base initialization skipped because it has already been handled by the most derived class}}
|
||||
A(1),
|
||||
y(1 / x) // expected-warning{{Division by zero}}
|
||||
// expected-note@-1{{Division by zero}}
|
||||
{}
|
||||
};
|
||||
|
||||
struct C : B {
|
||||
C(): // expected-note{{Calling default constructor for 'A'}}
|
||||
// expected-note@-1{{Returning from default constructor for 'A'}}
|
||||
B() // expected-note{{Calling default constructor for 'B'}}
|
||||
{}
|
||||
};
|
||||
|
||||
void test_note() {
|
||||
C c; // expected-note{{Calling default constructor for 'C'}}
|
||||
}
|
||||
|
||||
struct D: B {
|
||||
D() : A(1), B() {}
|
||||
};
|
||||
|
||||
void test_prunability() {
|
||||
D d;
|
||||
1 / 0; // expected-warning{{Division by zero}}
|
||||
// expected-note@-1{{Division by zero}}
|
||||
}
|
||||
} // namespace note_on_skipped_vbases
|
Loading…
Reference in New Issue