Current Path : /compat/linux/proc/68247/root/usr/src/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ |
FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64 |
Current File : //compat/linux/proc/68247/root/usr/src/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp |
//===--- HTMLDiagnostics.cpp - HTML Diagnostics for Paths ----*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines the HTMLDiagnostics object. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" #include "clang/Rewrite/Rewriter.h" #include "clang/Rewrite/HTMLRewrite.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Path.h" using namespace clang; using namespace ento; //===----------------------------------------------------------------------===// // Boilerplate. //===----------------------------------------------------------------------===// namespace { class HTMLDiagnostics : public PathDiagnosticConsumer { llvm::sys::Path Directory, FilePrefix; bool createdDir, noDir; const Preprocessor &PP; public: HTMLDiagnostics(const std::string& prefix, const Preprocessor &pp); virtual ~HTMLDiagnostics() { FlushDiagnostics(NULL); } virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, SmallVectorImpl<std::string> *FilesMade); virtual StringRef getName() const { return "HTMLDiagnostics"; } unsigned ProcessMacroPiece(raw_ostream &os, const PathDiagnosticMacroPiece& P, unsigned num); void HandlePiece(Rewriter& R, FileID BugFileID, const PathDiagnosticPiece& P, unsigned num, unsigned max); void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range, const char *HighlightStart = "<span class=\"mrange\">", const char *HighlightEnd = "</span>"); void ReportDiag(const PathDiagnostic& D, SmallVectorImpl<std::string> *FilesMade); }; } // end anonymous namespace HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix, const Preprocessor &pp) : Directory(prefix), FilePrefix(prefix), createdDir(false), noDir(false), PP(pp) { // All html files begin with "report" FilePrefix.appendComponent("report"); } PathDiagnosticConsumer* ento::createHTMLDiagnosticConsumer(const std::string& prefix, const Preprocessor &PP) { return new HTMLDiagnostics(prefix, PP); } //===----------------------------------------------------------------------===// // Report processing. //===----------------------------------------------------------------------===// void HTMLDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, SmallVectorImpl<std::string> *FilesMade) { for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), et = Diags.end(); it != et; ++it) { ReportDiag(**it, FilesMade); } } static void flattenPath(PathPieces &primaryPath, PathPieces ¤tPath, const PathPieces &oldPath) { for (PathPieces::const_iterator it = oldPath.begin(), et = oldPath.end(); it != et; ++it ) { PathDiagnosticPiece *piece = it->getPtr(); if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = call->getCallEnterEvent(); if (callEnter) currentPath.push_back(callEnter); flattenPath(primaryPath, primaryPath, call->path); IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = call->getCallExitEvent(); if (callExit) currentPath.push_back(callExit); continue; } if (PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { currentPath.push_back(piece); PathPieces newPath; flattenPath(primaryPath, newPath, macro->subPieces); macro->subPieces = newPath; continue; } currentPath.push_back(piece); } } void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, SmallVectorImpl<std::string> *FilesMade) { // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; std::string ErrorMsg; Directory.createDirectoryOnDisk(true, &ErrorMsg); bool IsDirectory; if (llvm::sys::fs::is_directory(Directory.str(), IsDirectory) || !IsDirectory) { llvm::errs() << "warning: could not create directory '" << Directory.str() << "'\n" << "reason: " << ErrorMsg << '\n'; noDir = true; return; } } if (noDir) return; // First flatten out the entire path to make it easier to use. PathPieces path; flattenPath(path, path, D.path); // The path as already been prechecked that all parts of the path are // from the same file and that it is non-empty. const SourceManager &SMgr = (*path.begin())->getLocation().getManager(); assert(!path.empty()); FileID FID = (*path.begin())->getLocation().asLocation().getExpansionLoc().getFileID(); assert(!FID.isInvalid()); // Create a new rewriter to generate HTML. Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts()); // Process the path. unsigned n = path.size(); unsigned max = n; for (PathPieces::const_reverse_iterator I = path.rbegin(), E = path.rend(); I != E; ++I, --n) HandlePiece(R, FID, **I, n, max); // Add line numbers, header, footer, etc. // unsigned FID = R.getSourceMgr().getMainFileID(); html::EscapeText(R, FID); html::AddLineNumbers(R, FID); // If we have a preprocessor, relex the file and syntax highlight. // We might not have a preprocessor if we come from a deserialized AST file, // for example. html::SyntaxHighlight(R, FID, PP); html::HighlightMacros(R, FID, PP); // Get the full directory name of the analyzed file. const FileEntry* Entry = SMgr.getFileEntryForID(FID); // This is a cludge; basically we want to append either the full // working directory if we have no directory information. This is // a work in progress. std::string DirName = ""; if (llvm::sys::path::is_relative(Entry->getName())) { llvm::sys::Path P = llvm::sys::Path::GetCurrentDirectory(); DirName = P.str() + "/"; } // Add the name of the file as an <h1> tag. { std::string s; llvm::raw_string_ostream os(s); os << "<!-- REPORTHEADER -->\n" << "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n" "<tr><td class=\"rowname\">File:</td><td>" << html::EscapeText(DirName) << html::EscapeText(Entry->getName()) << "</td></tr>\n<tr><td class=\"rowname\">Location:</td><td>" "<a href=\"#EndPath\">line " << (*path.rbegin())->getLocation().asLocation().getExpansionLineNumber() << ", column " << (*path.rbegin())->getLocation().asLocation().getExpansionColumnNumber() << "</a></td></tr>\n" "<tr><td class=\"rowname\">Description:</td><td>" << D.getDescription() << "</td></tr>\n"; // Output any other meta data. for (PathDiagnostic::meta_iterator I=D.meta_begin(), E=D.meta_end(); I!=E; ++I) { os << "<tr><td></td><td>" << html::EscapeText(*I) << "</td></tr>\n"; } os << "</table>\n<!-- REPORTSUMMARYEXTRA -->\n" "<h3>Annotated Source Code</h3>\n"; R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str()); } // Embed meta-data tags. { std::string s; llvm::raw_string_ostream os(s); const std::string& BugDesc = D.getDescription(); if (!BugDesc.empty()) os << "\n<!-- BUGDESC " << BugDesc << " -->\n"; const std::string& BugType = D.getBugType(); if (!BugType.empty()) os << "\n<!-- BUGTYPE " << BugType << " -->\n"; const std::string& BugCategory = D.getCategory(); if (!BugCategory.empty()) os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n"; os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n"; os << "\n<!-- BUGLINE " << path.back()->getLocation().asLocation().getExpansionLineNumber() << " -->\n"; os << "\n<!-- BUGPATHLENGTH " << path.size() << " -->\n"; // Mark the end of the tags. os << "\n<!-- BUGMETAEND -->\n"; // Insert the text. R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str()); } // Add CSS, header, and footer. html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName()); // Get the rewrite buffer. const RewriteBuffer *Buf = R.getRewriteBufferFor(FID); if (!Buf) { llvm::errs() << "warning: no diagnostics generated for main file.\n"; return; } // Create a path for the target HTML file. llvm::sys::Path F(FilePrefix); F.makeUnique(false, NULL); // Rename the file with an HTML extension. llvm::sys::Path H(F); H.appendSuffix("html"); F.renamePathOnDisk(H, NULL); std::string ErrorMsg; llvm::raw_fd_ostream os(H.c_str(), ErrorMsg); if (!ErrorMsg.empty()) { llvm::errs() << "warning: could not create file '" << F.str() << "'\n"; return; } if (FilesMade) FilesMade->push_back(llvm::sys::path::filename(H.str())); // Emit the HTML to disk. for (RewriteBuffer::iterator I = Buf->begin(), E = Buf->end(); I!=E; ++I) os << *I; } void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, const PathDiagnosticPiece& P, unsigned num, unsigned max) { // For now, just draw a box above the line in question, and emit the // warning. FullSourceLoc Pos = P.getLocation().asLocation(); if (!Pos.isValid()) return; SourceManager &SM = R.getSourceMgr(); assert(&Pos.getManager() == &SM && "SourceManagers are different!"); std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedExpansionLoc(Pos); if (LPosInfo.first != BugFileID) return; const llvm::MemoryBuffer *Buf = SM.getBuffer(LPosInfo.first); const char* FileStart = Buf->getBufferStart(); // Compute the column number. Rewind from the current position to the start // of the line. unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second); const char *TokInstantiationPtr =Pos.getExpansionLoc().getCharacterData(); const char *LineStart = TokInstantiationPtr-ColNo; // Compute LineEnd. const char *LineEnd = TokInstantiationPtr; const char* FileEnd = Buf->getBufferEnd(); while (*LineEnd != '\n' && LineEnd != FileEnd) ++LineEnd; // Compute the margin offset by counting tabs and non-tabs. unsigned PosNo = 0; for (const char* c = LineStart; c != TokInstantiationPtr; ++c) PosNo += *c == '\t' ? 8 : 1; // Create the html for the message. const char *Kind = 0; switch (P.getKind()) { case PathDiagnosticPiece::Call: llvm_unreachable("Calls should already be handled"); 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; unsigned cnt = 0; unsigned len = Msg.size(); for (std::string::const_iterator I=Msg.begin(), E=Msg.end(); I!=E; ++I) switch (*I) { default: ++cnt; continue; case ' ': case '\t': case '\n': if (cnt > max_token) max_token = cnt; cnt = 0; } if (cnt > max_token) max_token = cnt; // Determine the approximate size of the message bubble in em. unsigned em; const unsigned max_line = 120; if (max_token >= max_line) em = max_token / 2; else { unsigned characters = max_line; unsigned lines = len / max_line; if (lines > 0) { for (; characters > max_token; --characters) if (len / characters > lines) { ++characters; break; } } em = characters / 2; } if (em < max_line/2) os << "; max-width:" << em << "em"; } else os << "; max-width:100em"; os << "\">"; if (max > 1) { os << "<table class=\"msgT\"><tr><td valign=\"top\">"; os << "<div class=\"PathIndex"; if (Kind) os << " PathIndex" << Kind; os << "\">" << num << "</div>"; os << "</td><td>"; } if (const PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(&P)) { os << "Within the expansion of the macro '"; // Get the name of the macro by relexing it. { FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc(); assert(L.isFileID()); StringRef BufferInfo = L.getBufferData(); const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data(); Lexer rawLexer(L, PP.getLangOpts(), BufferInfo.begin(), MacroName, BufferInfo.end()); 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>"; } os << "</div></td></tr>"; // Insert the new html. unsigned DisplayPos = LineEnd - FileStart; SourceLocation Loc = SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos); R.InsertTextBefore(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); #if 0 // If there is a code insertion hint, insert that code. // FIXME: This code is disabled because it seems to mangle the HTML // output. I'm leaving it here because it's generally the right idea, // but needs some help from someone more familiar with the rewriter. for (const FixItHint *Hint = P.fixit_begin(), *HintEnd = P.fixit_end(); Hint != HintEnd; ++Hint) { if (Hint->RemoveRange.isValid()) { HighlightRange(R, LPosInfo.first, Hint->RemoveRange, "<span class=\"CodeRemovalHint\">", "</span>"); } if (Hint->InsertionLoc.isValid()) { std::string EscapedCode = html::EscapeText(Hint->CodeToInsert, true); EscapedCode = "<span class=\"CodeInsertionHint\">" + EscapedCode + "</span>"; R.InsertTextBefore(Hint->InsertionLoc, EscapedCode); } } #endif } static void EmitAlphaCounter(raw_ostream &os, unsigned n) { unsigned x = n % ('z' - 'a'); n /= 'z' - 'a'; if (n > 0) EmitAlphaCounter(os, n); os << char('a' + x); } unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os, const PathDiagnosticMacroPiece& P, unsigned num) { for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.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, const char *HighlightEnd) { SourceManager &SM = R.getSourceMgr(); const LangOptions &LangOpts = R.getLangOpts(); SourceLocation InstantiationStart = SM.getExpansionLoc(Range.getBegin()); unsigned StartLineNo = SM.getExpansionLineNumber(InstantiationStart); SourceLocation InstantiationEnd = SM.getExpansionLoc(Range.getEnd()); unsigned EndLineNo = SM.getExpansionLineNumber(InstantiationEnd); if (EndLineNo < StartLineNo) return; if (SM.getFileID(InstantiationStart) != BugFileID || SM.getFileID(InstantiationEnd) != BugFileID) return; // Compute the column number of the end. unsigned EndColNo = SM.getExpansionColumnNumber(InstantiationEnd); unsigned OldEndColNo = EndColNo; if (EndColNo) { // Add in the length of the token, so that we cover multi-char tokens. EndColNo += Lexer::MeasureTokenLength(Range.getEnd(), SM, LangOpts)-1; } // Highlight the range. Make the span tag the outermost tag for the // selected range. SourceLocation E = InstantiationEnd.getLocWithOffset(EndColNo - OldEndColNo); html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd); }