25 #include "llvm/Support/Errc.h"
26 #include "llvm/Support/FileSystem.h"
27 #include "llvm/Support/MemoryBuffer.h"
28 #include "llvm/Support/Path.h"
29 #include "llvm/Support/raw_ostream.h"
32 using namespace clang;
42 std::string Directory;
43 bool createdDir, noDir;
49 ~HTMLDiagnostics()
override { FlushDiagnostics(
nullptr); }
51 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
52 FilesMade *filesMade)
override;
54 StringRef getName()
const override {
55 return "HTMLDiagnostics";
58 unsigned ProcessMacroPiece(raw_ostream &os,
66 const char *HighlightStart =
"<span class=\"mrange\">",
67 const char *HighlightEnd =
"</span>");
70 FilesMade *filesMade);
76 const std::string& prefix,
78 : Directory(prefix), createdDir(
false), noDir(
false), PP(pp), AnalyzerOpts(AnalyzerOpts) {
83 const std::string& prefix,
85 C.push_back(
new HTMLDiagnostics(AnalyzerOpts, prefix, PP));
92 void HTMLDiagnostics::FlushDiagnosticsImpl(
93 std::vector<const PathDiagnostic *> &Diags,
94 FilesMade *filesMade) {
95 for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(),
96 et = Diags.end(); it != et; ++it) {
102 FilesMade *filesMade) {
107 if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) {
108 llvm::errs() <<
"warning: could not create directory '"
109 << Directory <<
"': " << ec.message() <<
'\n';
125 const SourceManager &SMgr = (*path.begin())->getLocation().getManager();
126 assert(!path.empty());
128 (*path.begin())->getLocation().asLocation().getExpansionLoc().getFileID();
129 assert(!FID.isInvalid());
138 if (
const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
139 declName = ND->getDeclName().getAsString();
142 if (
const Stmt *Body = DeclWithIssue->getBody()) {
149 offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber();
154 unsigned n = path.size();
157 for (PathPieces::const_reverse_iterator I = path.rbegin(),
160 HandlePiece(R, FID, **I, n, max);
185 if (llvm::sys::path::is_relative(Entry->
getName())) {
186 llvm::sys::fs::current_path(DirName);
190 int LineNumber = (*path.rbegin())->getLocation().asLocation().getExpansionLineNumber();
191 int ColumnNumber = (*path.rbegin())->getLocation().asLocation().getExpansionColumnNumber();
197 llvm::raw_string_ostream os(s);
199 os <<
"<!-- REPORTHEADER -->\n"
200 <<
"<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
201 "<tr><td class=\"rowname\">File:</td><td>"
204 <<
"</td></tr>\n<tr><td class=\"rowname\">Location:</td><td>"
205 "<a href=\"#EndPath\">line "
209 <<
"</a></td></tr>\n"
210 "<tr><td class=\"rowname\">Description:</td><td>"
220 os <<
"</table>\n<!-- REPORTSUMMARYEXTRA -->\n"
221 "<h3>Annotated Source Code</h3>\n";
229 llvm::raw_string_ostream os(s);
232 if (!BugDesc.empty())
233 os <<
"\n<!-- BUGDESC " << BugDesc <<
" -->\n";
236 if (!BugType.empty())
237 os <<
"\n<!-- BUGTYPE " << BugType <<
" -->\n";
240 if (!BugCategory.empty())
241 os <<
"\n<!-- BUGCATEGORY " << BugCategory <<
" -->\n";
243 os <<
"\n<!-- BUGFILE " << DirName << Entry->
getName() <<
" -->\n";
245 os <<
"\n<!-- FILENAME " << llvm::sys::path::filename(Entry->
getName()) <<
" -->\n";
247 os <<
"\n<!-- FUNCTIONNAME " << declName <<
" -->\n";
249 os <<
"\n<!-- BUGLINE "
253 os <<
"\n<!-- BUGCOLUMN "
257 os <<
"\n<!-- BUGPATHLENGTH " << path.size() <<
" -->\n";
260 os <<
"\n<!-- BUGMETAEND -->\n";
274 llvm::errs() <<
"warning: no diagnostics generated for main file.\n";
283 llvm::sys::path::append(Model, Directory,
"report-%%%%%%.html");
285 if (std::error_code EC =
286 llvm::sys::fs::createUniqueFile(Model, FD, ResultPath)) {
287 llvm::errs() <<
"warning: could not create file in '" << Directory
288 <<
"': " << EC.message() <<
'\n';
297 std::stringstream filename;
299 filename <<
"report-"
300 << llvm::sys::path::filename(Entry->
getName()).str()
301 <<
"-" << declName.c_str()
303 <<
"-" << i <<
".html";
304 llvm::sys::path::append(Model, Directory,
306 EC = llvm::sys::fs::openFileForWrite(Model,
308 llvm::sys::fs::F_RW |
309 llvm::sys::fs::F_Excl);
310 if (EC && EC != llvm::errc::file_exists) {
311 llvm::errs() <<
"warning: could not create file '" << Model
312 <<
"': " << EC.message() <<
'\n';
319 llvm::raw_fd_ostream os(FD,
true);
322 filesMade->addDiagnostic(D, getName(),
323 llvm::sys::path::filename(ResultPath));
332 unsigned num,
unsigned max) {
342 assert(&Pos.
getManager() == &SM &&
"SourceManagers are different!");
345 if (LPosInfo.first != BugFileID)
348 const llvm::MemoryBuffer *Buf = SM.
getBuffer(LPosInfo.first);
349 const char* FileStart = Buf->getBufferStart();
355 const char *LineStart = TokInstantiationPtr-ColNo;
358 const char *LineEnd = TokInstantiationPtr;
359 const char* FileEnd = Buf->getBufferEnd();
360 while (*LineEnd !=
'\n' && LineEnd != FileEnd)
365 for (
const char* c = LineStart; c != TokInstantiationPtr; ++c)
366 PosNo += *c ==
'\t' ? 8 : 1;
370 const char *
Kind =
nullptr;
372 case PathDiagnosticPiece::Call:
373 llvm_unreachable(
"Calls should already be handled");
374 case PathDiagnosticPiece::Event: Kind =
"Event";
break;
375 case PathDiagnosticPiece::ControlFlow: Kind =
"Control";
break;
377 case PathDiagnosticPiece::Macro: Kind =
"Control";
break;
381 llvm::raw_string_ostream os(sbuf);
383 os <<
"\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
390 os <<
"\" class=\"msg";
392 os <<
" msg" <<
Kind;
393 os <<
"\" style=\"margin-left:" << PosNo <<
"ex";
396 if (!isa<PathDiagnosticMacroPiece>(P)) {
399 unsigned max_token = 0;
401 unsigned len = Msg.size();
403 for (std::string::const_iterator I=Msg.begin(), E=Msg.end(); I!=E; ++I)
411 if (cnt > max_token) max_token = cnt;
420 const unsigned max_line = 120;
422 if (max_token >= max_line)
425 unsigned characters = max_line;
426 unsigned lines = len / max_line;
429 for (; characters > max_token; --characters)
430 if (len / characters > lines) {
440 os <<
"; max-width:" << em <<
"em";
443 os <<
"; max-width:100em";
448 os <<
"<table class=\"msgT\"><tr><td valign=\"top\">";
449 os <<
"<div class=\"PathIndex";
450 if (Kind) os <<
" PathIndex" <<
Kind;
451 os <<
"\">" << num <<
"</div>";
454 os <<
"</td><td><div class=\"PathNav\"><a href=\"#Path"
456 <<
"\" title=\"Previous event ("
458 <<
")\">←</a></div></td>";
465 dyn_cast<PathDiagnosticMacroPiece>(&P)) {
467 os <<
"Within the expansion of the macro '";
475 const char* MacroName = LocInfo.second + BufferInfo.data();
477 BufferInfo.begin(), MacroName, BufferInfo.end());
480 rawLexer.LexFromRawLexer(TheTok);
481 for (
unsigned i = 0, n = TheTok.getLength(); i < n; ++i)
490 os <<
"<td><div class=\"PathNav\"><a href=\"#";
494 os <<
"Path" << (num + 1);
495 os <<
"\" title=\"Next event ("
497 <<
")\">→</a></div></td>";
500 os <<
"</tr></table>";
504 ProcessMacroPiece(os, *MP, 0);
512 os <<
"<td><div class=\"PathNav\"><a href=\"#";
516 os <<
"Path" << (num + 1);
517 os <<
"\" title=\"Next event ("
519 <<
")\">→</a></div></td>";
522 os <<
"</tr></table>";
526 os <<
"</div></td></tr>";
529 unsigned DisplayPos = LineEnd - FileStart;
538 E = Ranges.end(); I != E; ++I) {
544 unsigned x = n % (
'z' -
'a');
553 unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
561 dyn_cast<PathDiagnosticMacroPiece>(*I)) {
562 num = ProcessMacroPiece(os, *MP, num);
567 os <<
"<div class=\"msg msgEvent\" style=\"width:94%; "
569 "<table class=\"msgT\"><tr>"
570 "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
572 os <<
"</div></td><td valign=\"top\">"
574 <<
"</td></tr></table></div>\n";
583 const char *HighlightStart,
584 const char *HighlightEnd) {
594 if (EndLineNo < StartLineNo)
597 if (SM.
getFileID(InstantiationStart) != BugFileID ||
598 SM.
getFileID(InstantiationEnd) != BugFileID)
603 unsigned OldEndColNo = EndColNo;
Defines the clang::ASTContext interface.
SourceLocation getEnd() const
std::pair< FileID, unsigned > getDecomposedExpansionLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
SourceManager & getSourceMgr() const
Defines the clang::FileManager interface and associated types.
std::deque< std::string >::const_iterator meta_iterator
Defines the SourceManager interface.
PathPieces flatten(bool ShouldFlattenMacros) const
StringRef getCategory() const
llvm::MemoryBuffer * getBuffer(FileID FID, SourceLocation Loc, bool *Invalid=nullptr) const
Return the buffer for the specified FileID.
const SourceManager & getManager() const
ArrayRef< SourceRange > getRanges() const
Return the SourceRanges associated with this PathDiagnosticPiece.
const Decl * getDeclWithIssue() const
FullSourceLoc asLocation() const
void HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP)
void AddLineNumbers(Rewriter &R, FileID FID)
StringRef getString() const
const LangOptions & getLangOpts() const
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
unsigned getExpansionColumnNumber(SourceLocation Loc, bool *Invalid=nullptr) const
std::pair< FileID, unsigned > getDecomposedLoc() const
Decompose the specified location into a raw FileID + Offset pair.
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
void SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP)
meta_iterator meta_begin() const
virtual PathDiagnosticLocation getLocation() const =0
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
bool shouldWriteStableReportFilename()
static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
static void EmitAlphaCounter(raw_ostream &os, unsigned n)
Defines the clang::Preprocessor interface.
unsigned getExpansionLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece &P, const FIDMap &FM, const SourceManager &SM, const LangOptions &LangOpts)
unsigned getColumnNumber(FileID FID, unsigned FilePos, bool *Invalid=nullptr) const
Return the column # for the specified file position.
void AddHeaderFooterInternalBuiltinCSS(Rewriter &R, FileID FID, const char *title=nullptr)
meta_iterator meta_end() const
const char * getName() const
Encodes a location in the source. The SourceManager can decode this to get at the full include stack...
std::vector< PathDiagnosticConsumer * > PathDiagnosticConsumers
const char * getCharacterData(bool *Invalid=nullptr) const
bool isValid() const
Return true if this is a valid SourceLocation object.
StringRef getVerboseDescription() const
void EscapeText(Rewriter &R, FileID FID, bool EscapeSpaces=false, bool ReplaceTabs=false)
Cached information about one file (either on disk or in the virtual file system). ...
SourceLocation getBegin() const
bool InsertTextBefore(SourceLocation Loc, StringRef Str)
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
const LangOptions & getLangOpts() const
StringRef getBufferData(bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
A SourceLocation and its associated SourceManager.
StringRef getBugType() const
FullSourceLoc getExpansionLoc() const
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file. ...
A trivial tuple used to represent a source range.
SourceLocation getExpansionLoc(SourceLocation Loc) const
Given a SourceLocation object Loc, return the expansion location referenced by the ID...
This class handles loading and caching of source files into memory.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
void HighlightRange(Rewriter &R, SourceLocation B, SourceLocation E, const char *StartTag, const char *EndTag)