BugReporter:

- Group control flow and event PathDiagnosticPieces into PathDiagnosticMacroPieces.
- Afterwards, eliminate any PathDiagnosticMacroPieces from a PathDiagnostic that
  contain no informative events.

HTMLDiagnostics:
- Use new information about PathDiagnosticMacroPieces to specially format
  message bubbles for macro expansions containing interesting events.

llvm-svn: 66524
This commit is contained in:
Ted Kremenek 2009-03-10 05:16:17 +00:00
parent a039a62299
commit b4947e4378
3 changed files with 257 additions and 49 deletions

View File

@ -563,6 +563,98 @@ public:
};
}
/// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object
/// and collapses PathDiagosticPieces that are expanded by macros.
static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
typedef std::vector<std::pair<PathDiagnosticMacroPiece*, SourceLocation> >
MacroStackTy;
typedef std::vector<PathDiagnosticPiece*>
PiecesTy;
MacroStackTy MacroStack;
PiecesTy Pieces;
for (PathDiagnostic::iterator I = PD.begin(), E = PD.end(); I!=E; ++I) {
// Get the location of the PathDiagnosticPiece.
const FullSourceLoc Loc = I->getLocation();
// Determine the instantiation location, which is the location we group
// related PathDiagnosticPieces.
SourceLocation InstantiationLoc = Loc.isMacroID() ?
SM.getInstantiationLoc(Loc) :
SourceLocation();
if (Loc.isFileID()) {
MacroStack.clear();
Pieces.push_back(&*I);
continue;
}
assert(Loc.isMacroID());
// Is the PathDiagnosticPiece within the same macro group?
if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
MacroStack.back().first->push_back(&*I);
continue;
}
// We aren't in the same group. Are we descending into a new macro
// or are part of an old one?
PathDiagnosticMacroPiece *MacroGroup = 0;
SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ?
SM.getInstantiationLoc(Loc) :
SourceLocation();
// Walk the entire macro stack.
while (!MacroStack.empty()) {
if (InstantiationLoc == MacroStack.back().second) {
MacroGroup = MacroStack.back().first;
break;
}
if (ParentInstantiationLoc == MacroStack.back().second) {
MacroGroup = MacroStack.back().first;
break;
}
MacroStack.pop_back();
}
if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
// Create a new macro group and add it to the stack.
PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece(Loc);
if (MacroGroup)
MacroGroup->push_back(NewGroup);
else {
assert(InstantiationLoc.isFileID());
Pieces.push_back(NewGroup);
}
MacroGroup = NewGroup;
MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
}
// Finally, add the PathDiagnosticPiece to the group.
MacroGroup->push_back(&*I);
}
// Now take the pieces and construct a new PathDiagnostic.
PD.resetPath(false);
for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I) {
if (PathDiagnosticMacroPiece *MP=dyn_cast<PathDiagnosticMacroPiece>(*I))
if (!MP->containsEvent()) {
delete MP;
continue;
}
PD.push_back(*I);
}
}
void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
BugReportEquivClass& EQ) {
@ -799,6 +891,10 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
getStateManager().iterBindings(N->getState(), SNS);
}
}
// After constructing the full PathDiagnostic, do a pass over it to compact
// PathDiagnosticPieces that occur within a macro.
CompactPathDiagnostic(PD, getSourceManager());
}

View File

@ -13,8 +13,24 @@
#include "clang/Analysis/PathDiagnostic.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Casting.h"
#include <sstream>
using namespace clang;
using llvm::dyn_cast;
using llvm::isa;
bool PathDiagnosticMacroPiece::containsEvent() const {
for (const_iterator I = begin(), E = end(); I!=E; ++I) {
if (isa<PathDiagnosticEventPiece>(*I))
return true;
if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I))
if (MP->containsEvent())
return true;
}
return false;
}
static size_t GetNumCharsToLastNonPeriod(const char *s) {
const char *start = s;
@ -63,6 +79,16 @@ PathDiagnostic::~PathDiagnostic() {
for (iterator I = begin(), E = end(); I != E; ++I) delete &*I;
}
void PathDiagnostic::resetPath(bool deletePieces) {
Size = 0;
if (deletePieces)
for (iterator I=begin(), E=end(); I!=E; ++I)
delete &*I;
path.clear();
}
PathDiagnostic::PathDiagnostic(const char* bugtype, const char* desc,
const char* category)

View File

@ -20,6 +20,7 @@
#include "clang/Rewrite/Rewriter.h"
#include "clang/Rewrite/HTMLRewrite.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Streams.h"
@ -48,6 +49,10 @@ public:
virtual void HandlePathDiagnostic(const PathDiagnostic* D);
unsigned ProcessMacroPiece(llvm::raw_ostream& os,
const PathDiagnosticMacroPiece& P,
unsigned num);
void HandlePiece(Rewriter& R, FileID BugFileID,
const PathDiagnosticPiece& P, unsigned num, unsigned max);
@ -364,7 +369,32 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
PosNo += *c == '\t' ? 8 : 1;
// Create the html for the message.
{
const char *Kind = 0;
switch (P.getKind()) {
case PathDiagnosticPiece::Event: Kind = "Event"; break;
case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break;
// Setting Kind to "Control" is intentional.
case PathDiagnosticPiece::Macro: Kind = "Control"; break;
}
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
os << "\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
if (num == max)
os << "EndPath";
else
os << "Path" << num;
os << "\" class=\"msg";
if (Kind)
os << " msg" << Kind;
os << "\" style=\"margin-left:" << PosNo << "ex";
// Output a maximum size.
if (!isa<PathDiagnosticMacroPiece>(P)) {
// Get the string and determining its maximum substring.
const std::string& Msg = P.getString();
unsigned max_token = 0;
@ -383,9 +413,10 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
cnt = 0;
}
if (cnt > max_token) max_token = cnt;
if (cnt > max_token)
max_token = cnt;
// Next, determine the approximate size of the message bubble in em.
// Determine the approximate size of the message bubble in em.
unsigned em;
const unsigned max_line = 120;
@ -406,28 +437,12 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
em = characters / 2;
}
// Now generate the message bubble.
const char *Kind = 0;
switch (P.getKind()) {
case PathDiagnosticPiece::Event: Kind = "Event"; break;
case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break;
case PathDiagnosticPiece::Macro: Kind = "Macro"; break;
if (em < max_line/2)
os << "; max-width:" << em << "em";
}
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
os << "\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
if (num == max)
os << "EndPath";
else
os << "Path" << num;
os << "; max-width:100em";
os << "\" class=\"msg";
if (Kind) os << " msg" << Kind;
os << "\" style=\"margin-left:" << PosNo << "ex";
if (em < max_line/2) os << "; max-width:" << em << "em";
os << "\">";
if (max > 1) {
@ -438,9 +453,38 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
os << "</td><td>";
}
os << html::EscapeText(Msg);
if (const PathDiagnosticMacroPiece *MP =
dyn_cast<PathDiagnosticMacroPiece>(&P)) {
if (max > 1) {
os << "Within the expansion of the macro '";
// Get the name of the macro by relexing it.
{
FullSourceLoc L = MP->getLocation().getInstantiationLoc();
assert(L.isFileID());
std::pair<const char*, const char*> BufferInfo = L.getBufferData();
const char* MacroName = L.getDecomposedLoc().second + BufferInfo.first;
Lexer rawLexer(L, PP->getLangOptions(), BufferInfo.first,
MacroName, BufferInfo.second);
Token TheTok;
rawLexer.LexFromRawLexer(TheTok);
for (unsigned i = 0, n = TheTok.getLength(); i < n; ++i)
os << MacroName[i];
}
os << "':\n";
if (max > 1)
os << "</td></tr></table>";
// Within a macro piece. Write out each event.
ProcessMacroPiece(os, *MP, 0);
}
else {
os << html::EscapeText(P.getString());
if (max > 1)
os << "</td></tr></table>";
}
@ -452,10 +496,8 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
SM.getLocForStartOfFile(LPosInfo.first).getFileLocWithOffset(DisplayPos);
R.InsertStrBefore(Loc, os.str());
}
// Now highlight the ranges.
for (const SourceRange *I = P.ranges_begin(), *E = P.ranges_end();
I != E; ++I)
HighlightRange(R, LPosInfo.first, *I);
@ -482,6 +524,50 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
#endif
}
static void EmitAlphaCounter(llvm::raw_ostream& os, unsigned n) {
llvm::SmallVector<char, 10> buf;
do {
unsigned x = n % ('z' - 'a');
buf.push_back('a' + x);
n = n / ('z' - 'a');
} while (n);
assert(!buf.empty());
for (llvm::SmallVectorImpl<char>::reverse_iterator I=buf.rbegin(),
E=buf.rend(); I!=E; ++I)
os << *I;
}
unsigned HTMLDiagnostics::ProcessMacroPiece(llvm::raw_ostream& os,
const PathDiagnosticMacroPiece& P,
unsigned num) {
for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end();
I!=E; ++I) {
if (const PathDiagnosticMacroPiece *MP =
dyn_cast<PathDiagnosticMacroPiece>(*I)) {
num = ProcessMacroPiece(os, *MP, num);
continue;
}
if (PathDiagnosticEventPiece *EP = dyn_cast<PathDiagnosticEventPiece>(*I)) {
os << "<div class=\"msg msgEvent\" style=\"width:94%; "
"margin-left:5px\">"
"<table class=\"msgT\"><tr>"
"<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
EmitAlphaCounter(os, num++);
os << "</div></td><td valign=\"top\">"
<< html::EscapeText(EP->getString())
<< "</td></tr></table></div>\n";
}
}
return num;
}
void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
SourceRange Range,
const char *HighlightStart,