diff --git a/clang/lib/Analysis/BugReporter.cpp b/clang/lib/Analysis/BugReporter.cpp index 12e50d346016..86ee484f36cf 100644 --- a/clang/lib/Analysis/BugReporter.cpp +++ b/clang/lib/Analysis/BugReporter.cpp @@ -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 > + MacroStackTy; + + typedef std::vector + 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(*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()); } diff --git a/clang/lib/Analysis/PathDiagnostic.cpp b/clang/lib/Analysis/PathDiagnostic.cpp index ae53ed9dbf07..a504e80b5961 100644 --- a/clang/lib/Analysis/PathDiagnostic.cpp +++ b/clang/lib/Analysis/PathDiagnostic.cpp @@ -13,8 +13,24 @@ #include "clang/Analysis/PathDiagnostic.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/Casting.h" #include 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(*I)) + return true; + + if (PathDiagnosticMacroPiece *MP = dyn_cast(*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) diff --git a/clang/lib/Frontend/HTMLDiagnostics.cpp b/clang/lib/Frontend/HTMLDiagnostics.cpp index 02b327db2ad1..0b266cda0f0c 100644 --- a/clang/lib/Frontend/HTMLDiagnostics.cpp +++ b/clang/lib/Frontend/HTMLDiagnostics.cpp @@ -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
(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; @@ -394,7 +425,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, else { unsigned characters = max_line; unsigned lines = len / max_line; - + if (lines > 0) { for (; characters > max_token; --characters) if (len / characters > lines) { @@ -402,60 +433,71 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, break; } } - + em = characters / 2; } + + if (em < max_line/2) + os << "; max-width:" << em << "em"; + } + else + os << "; max-width:100em"; + + os << "\">"; + + if (max > 1) { + os << ""; - - // Insert the new html. - unsigned DisplayPos = LineEnd - FileStart; - SourceLocation Loc = - SM.getLocForStartOfFile(LPosInfo.first).getFileLocWithOffset(DisplayPos); - - R.InsertStrBefore(Loc, os.str()); + if (max > 1) + os << "
"; + os << "
" << num << "
"; + os << "
"; + } + + if (const PathDiagnosticMacroPiece *MP = + dyn_cast(&P)) { + + os << "Within the expansion of the macro '"; - // 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; + // Get the name of the macro by relexing it. + { + FullSourceLoc L = MP->getLocation().getInstantiationLoc(); + assert(L.isFileID()); + std::pair 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"; - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "\n
"; - - if (max > 1) { - os << "
"; - os << "
" << num << "
"; - os << "
"; - } - - os << html::EscapeText(Msg); - - if (max > 1) { + if (max > 1) os << "
"; - } + + // Within a macro piece. Write out each event. + ProcessMacroPiece(os, *MP, 0); + } + else { + os << html::EscapeText(P.getString()); - os << "
"; } - // Now highlight the ranges. - + os << "
"; + + // Insert the new html. + unsigned DisplayPos = LineEnd - FileStart; + SourceLocation Loc = + 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 buf; + + do { + unsigned x = n % ('z' - 'a'); + buf.push_back('a' + x); + n = n / ('z' - 'a'); + } while (n); + + assert(!buf.empty()); + + for (llvm::SmallVectorImpl::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(*I)) { + num = ProcessMacroPiece(os, *MP, num); + continue; + } + + if (PathDiagnosticEventPiece *EP = dyn_cast(*I)) { + os << "
" + "" + "
"; + EmitAlphaCounter(os, num++); + os << "
" + << html::EscapeText(EP->getString()) + << "
\n"; + } + } + + return num; +} + void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range, const char *HighlightStart,