//===--- 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 "HTMLDiagnostics.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" #include "clang/AST/ASTContext.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/Rewrite/Rewriter.h" #include "clang/Rewrite/HTMLRewrite.h" #include "clang/Lex/Lexer.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Streams.h" #include "llvm/System/Path.h" #include #include using namespace clang; //===----------------------------------------------------------------------===// // Boilerplate. //===----------------------------------------------------------------------===// namespace { class VISIBILITY_HIDDEN HTMLDiagnostics : public PathDiagnosticClient { llvm::sys::Path Directory, FilePrefix; bool createdDir, noDir; public: HTMLDiagnostics(const std::string& prefix); virtual ~HTMLDiagnostics() {} virtual void HandlePathDiagnostic(const PathDiagnostic& D); void HandlePiece(Rewriter& R, const PathDiagnosticPiece& P, unsigned num, unsigned max); void HighlightRange(Rewriter& R, SourceRange Range, unsigned MainFileID); }; } // end anonymous namespace HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix) : Directory(prefix), FilePrefix(prefix), createdDir(false), noDir(false) { // All html files begin with "report" FilePrefix.appendComponent("report"); } PathDiagnosticClient* clang::CreateHTMLDiagnosticClient(const std::string& prefix) { return new HTMLDiagnostics(prefix); } //===----------------------------------------------------------------------===// // Report processing. //===----------------------------------------------------------------------===// void HTMLDiagnostics::HandlePathDiagnostic(const PathDiagnostic& D) { if (D.empty()) return; // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; Directory.createDirectoryOnDisk(true, NULL); if (!Directory.isDirectory()) { llvm::cerr << "warning: could not create directory '" << FilePrefix.toString() << "'\n"; noDir = true; return; } } if (noDir) return; // Create a new rewriter to generate HTML. SourceManager& SMgr = D.begin()->getLocation().getManager(); Rewriter R(SMgr); // Process the path. unsigned n = D.size(); unsigned max = n; for (PathDiagnostic::const_reverse_iterator I=D.rbegin(), E=D.rend(); I!=E; ++I, --n) { HandlePiece(R, *I, n, max); } // Add line numbers, header, footer, etc. unsigned FileID = R.getSourceMgr().getMainFileID(); html::EscapeText(R, FileID); html::AddLineNumbers(R, FileID); // Get the full directory name of the analyzed file. const FileEntry* Entry = SMgr.getFileEntryForID(FileID); std::string DirName(Entry->getDir()->getName()); if (DirName == ".") DirName = llvm::sys::Path::GetCurrentDirectory().toString(); // Add the name of the file as an

tag. { std::ostringstream os; os << "

" << html::EscapeText(DirName) << "/" << html::EscapeText(Entry->getName()) << "

\n"; R.InsertStrBefore(SourceLocation::getFileLoc(FileID, 0), os.str()); } // Embed meta-data tags. const std::string& BugDesc = D.getDescription(); if (!BugDesc.empty()) { std::ostringstream os; os << "\n\n"; R.InsertStrBefore(SourceLocation::getFileLoc(FileID, 0), os.str()); } { std::ostringstream os; os << "\n\n"; R.InsertStrBefore(SourceLocation::getFileLoc(FileID, 0), os.str()); } { std::ostringstream os; os << "\n\n"; R.InsertStrBefore(SourceLocation::getFileLoc(FileID, 0), os.str()); } { std::ostringstream os; os << "\n\n"; R.InsertStrBefore(SourceLocation::getFileLoc(FileID, 0), os.str()); } // Add CSS, header, and footer. html::AddHeaderFooterInternalBuiltinCSS(R, FileID); // Get the rewrite buffer. const RewriteBuffer *Buf = R.getRewriteBufferFor(FileID); if (!Buf) { llvm::cerr << "warning: no diagnostics generated for main file.\n"; return; } // Create the stream to write out the HTML. std::ofstream os; { // 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); os.open(H.toString().c_str()); if (!os) { llvm::cerr << "warning: could not create file '" << F.toString() << "'\n"; return; } } // Emit the HTML to disk. for (RewriteBuffer::iterator I = Buf->begin(), E = Buf->end(); I!=E; ++I) os << *I; } void HTMLDiagnostics::HandlePiece(Rewriter& R, 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(); if (!Pos.isValid()) return; SourceManager& SM = R.getSourceMgr(); FullSourceLoc LPos = Pos.getLogicalLoc(); unsigned FileID = LPos.getLocation().getFileID(); assert (&LPos.getManager() == &SM && "SourceManagers are different!"); unsigned MainFileID = SM.getMainFileID(); if (FileID != MainFileID) return; // Compute the column number. Rewind from the current position to the start // of the line. unsigned ColNo = LPos.getColumnNumber(); const char *TokLogicalPtr = LPos.getCharacterData(); const char *LineStart = TokLogicalPtr-ColNo; // Compute the margin offset by counting tabs and non-tabs. unsigned PosNo = 0; for (const char* c = LineStart; c != TokLogicalPtr; ++c) PosNo += *c == '\t' ? 4 : 1; // Create the html for the message. std::ostringstream os; os << "\n" << "
"; os << html::EscapeText(P.getString()) << "
"; // Insert the new html. const llvm::MemoryBuffer *Buf = SM.getBuffer(FileID); const char* FileStart = Buf->getBufferStart(); R.InsertStrBefore(SourceLocation::getFileLoc(FileID, LineStart - FileStart), os.str()); // Now highlight the ranges. for (const SourceRange *I = P.ranges_begin(), *E = P.ranges_end(); I != E; ++I) HighlightRange(R, *I, MainFileID); } void HTMLDiagnostics::HighlightRange(Rewriter& R, SourceRange Range, unsigned MainFileID) { SourceManager& SourceMgr = R.getSourceMgr(); SourceLocation LogicalStart = SourceMgr.getLogicalLoc(Range.getBegin()); unsigned StartLineNo = SourceMgr.getLineNumber(LogicalStart); SourceLocation LogicalEnd = SourceMgr.getLogicalLoc(Range.getEnd()); unsigned EndLineNo = SourceMgr.getLineNumber(LogicalEnd); if (EndLineNo < StartLineNo) return; if (LogicalStart.getFileID() != MainFileID || LogicalEnd.getFileID() != MainFileID) return; // Compute the column number of the end. unsigned EndColNo = SourceMgr.getColumnNumber(LogicalEnd); unsigned OldEndColNo = EndColNo; if (EndColNo) { // Add in the length of the token, so that we cover multi-char tokens. EndColNo += Lexer::MeasureTokenLength(Range.getEnd(), SourceMgr); } // Highlight the range. Make the span tag the outermost tag for the // selected range. SourceLocation E = LogicalEnd.getFileLocWithOffset(OldEndColNo > EndColNo ? -(OldEndColNo - EndColNo) : EndColNo - OldEndColNo); R.InsertCStrBefore(LogicalStart, ""); R.InsertCStrAfter(E, ""); }