26 #include "llvm/Support/Errc.h"
27 #include "llvm/Support/FileSystem.h"
28 #include "llvm/Support/MemoryBuffer.h"
29 #include "llvm/Support/Path.h"
30 #include "llvm/Support/raw_ostream.h"
33 using namespace clang;
43 std::string Directory;
44 bool createdDir, noDir;
50 ~HTMLDiagnostics()
override { FlushDiagnostics(
nullptr); }
52 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
53 FilesMade *filesMade)
override;
55 StringRef getName()
const override {
56 return "HTMLDiagnostics";
59 unsigned ProcessMacroPiece(raw_ostream &os,
67 const char *HighlightStart =
"<span class=\"mrange\">",
68 const char *HighlightEnd =
"</span>");
71 FilesMade *filesMade);
77 const std::string& prefix,
79 : Directory(prefix), createdDir(
false), noDir(
false), PP(pp), AnalyzerOpts(AnalyzerOpts) {
84 const std::string& prefix,
86 C.push_back(
new HTMLDiagnostics(AnalyzerOpts, prefix, PP));
93 void HTMLDiagnostics::FlushDiagnosticsImpl(
94 std::vector<const PathDiagnostic *> &Diags,
95 FilesMade *filesMade) {
97 et = Diags.end(); it != et; ++it) {
103 FilesMade *filesMade) {
108 if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) {
109 llvm::errs() <<
"warning: could not create directory '"
110 << Directory <<
"': " << ec.message() <<
'\n';
126 const SourceManager &SMgr = path.front()->getLocation().getManager();
127 assert(!path.empty());
129 path.front()->getLocation().asLocation().getExpansionLoc().getFileID();
130 assert(FID.isValid());
139 if (
const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
140 declName = ND->getDeclName().getAsString();
143 if (
const Stmt *Body = DeclWithIssue->getBody()) {
150 offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber();
155 unsigned n = path.size();
158 for (PathPieces::const_reverse_iterator
I = path.rbegin(),
161 HandlePiece(R, FID, **
I, n, max);
186 if (llvm::sys::path::is_relative(Entry->
getName())) {
187 llvm::sys::fs::current_path(DirName);
191 int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
192 int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();
198 llvm::raw_string_ostream os(s);
200 os <<
"<!-- REPORTHEADER -->\n"
201 <<
"<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
202 "<tr><td class=\"rowname\">File:</td><td>"
205 <<
"</td></tr>\n<tr><td class=\"rowname\">Location:</td><td>"
206 "<a href=\"#EndPath\">line "
210 <<
"</a></td></tr>\n"
211 "<tr><td class=\"rowname\">Description:</td><td>"
221 os <<
"</table>\n<!-- REPORTSUMMARYEXTRA -->\n"
222 "<h3>Annotated Source Code</h3>\n";
230 llvm::raw_string_ostream os(s);
233 if (!BugDesc.empty())
234 os <<
"\n<!-- BUGDESC " << BugDesc <<
" -->\n";
237 if (!BugType.empty())
238 os <<
"\n<!-- BUGTYPE " << BugType <<
" -->\n";
248 if (!BugCategory.empty())
249 os <<
"\n<!-- BUGCATEGORY " << BugCategory <<
" -->\n";
251 os <<
"\n<!-- BUGFILE " << DirName << Entry->
getName() <<
" -->\n";
253 os <<
"\n<!-- FILENAME " << llvm::sys::path::filename(Entry->
getName()) <<
" -->\n";
255 os <<
"\n<!-- FUNCTIONNAME " << declName <<
" -->\n";
257 os <<
"\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT "
261 os <<
"\n<!-- BUGLINE "
265 os <<
"\n<!-- BUGCOLUMN "
269 os <<
"\n<!-- BUGPATHLENGTH " << path.size() <<
" -->\n";
272 os <<
"\n<!-- BUGMETAEND -->\n";
286 llvm::errs() <<
"warning: no diagnostics generated for main file.\n";
295 llvm::sys::path::append(Model, Directory,
"report-%%%%%%.html");
296 if (std::error_code EC =
297 llvm::sys::fs::make_absolute(Model)) {
298 llvm::errs() <<
"warning: could not make '" << Model
299 <<
"' absolute: " << EC.message() <<
'\n';
302 if (std::error_code EC =
303 llvm::sys::fs::createUniqueFile(Model, FD, ResultPath)) {
304 llvm::errs() <<
"warning: could not create file in '" << Directory
305 <<
"': " << EC.message() <<
'\n';
314 std::stringstream filename;
316 filename <<
"report-"
317 << llvm::sys::path::filename(Entry->
getName()).str()
318 <<
"-" << declName.c_str()
320 <<
"-" << i <<
".html";
321 llvm::sys::path::append(Model, Directory,
323 EC = llvm::sys::fs::openFileForWrite(Model,
325 llvm::sys::fs::F_RW |
326 llvm::sys::fs::F_Excl);
327 if (EC && EC != llvm::errc::file_exists) {
328 llvm::errs() <<
"warning: could not create file '" << Model
329 <<
"': " << EC.message() <<
'\n';
336 llvm::raw_fd_ostream os(FD,
true);
339 filesMade->addDiagnostic(D, getName(),
340 llvm::sys::path::filename(ResultPath));
349 unsigned num,
unsigned max) {
359 assert(&Pos.
getManager() == &SM &&
"SourceManagers are different!");
362 if (LPosInfo.first != BugFileID)
365 const llvm::MemoryBuffer *Buf = SM.
getBuffer(LPosInfo.first);
366 const char* FileStart = Buf->getBufferStart();
372 const char *LineStart = TokInstantiationPtr-ColNo;
375 const char *LineEnd = TokInstantiationPtr;
376 const char* FileEnd = Buf->getBufferEnd();
377 while (*LineEnd !=
'\n' && LineEnd != FileEnd)
382 for (
const char* c = LineStart; c != TokInstantiationPtr; ++c)
383 PosNo += *c ==
'\t' ? 8 : 1;
387 const char *
Kind =
nullptr;
389 case PathDiagnosticPiece::Call:
390 llvm_unreachable(
"Calls should already be handled");
391 case PathDiagnosticPiece::Event: Kind =
"Event";
break;
392 case PathDiagnosticPiece::ControlFlow: Kind =
"Control";
break;
394 case PathDiagnosticPiece::Macro: Kind =
"Control";
break;
398 llvm::raw_string_ostream os(sbuf);
400 os <<
"\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
407 os <<
"\" class=\"msg";
409 os <<
" msg" <<
Kind;
410 os <<
"\" style=\"margin-left:" << PosNo <<
"ex";
413 if (!isa<PathDiagnosticMacroPiece>(P)) {
416 unsigned max_token = 0;
418 unsigned len = Msg.size();
428 if (cnt > max_token) max_token = cnt;
437 const unsigned max_line = 120;
439 if (max_token >= max_line)
442 unsigned characters = max_line;
443 unsigned lines = len / max_line;
446 for (; characters > max_token; --characters)
447 if (len / characters > lines) {
457 os <<
"; max-width:" << em <<
"em";
460 os <<
"; max-width:100em";
465 os <<
"<table class=\"msgT\"><tr><td valign=\"top\">";
466 os <<
"<div class=\"PathIndex";
467 if (Kind) os <<
" PathIndex" <<
Kind;
468 os <<
"\">" << num <<
"</div>";
471 os <<
"</td><td><div class=\"PathNav\"><a href=\"#Path"
473 <<
"\" title=\"Previous event ("
475 <<
")\">←</a></div></td>";
482 dyn_cast<PathDiagnosticMacroPiece>(&P)) {
484 os <<
"Within the expansion of the macro '";
492 const char* MacroName = LocInfo.second + BufferInfo.data();
494 BufferInfo.begin(), MacroName, BufferInfo.end());
497 rawLexer.LexFromRawLexer(TheTok);
498 for (
unsigned i = 0, n = TheTok.getLength(); i < n; ++i)
507 os <<
"<td><div class=\"PathNav\"><a href=\"#";
511 os <<
"Path" << (num + 1);
512 os <<
"\" title=\"Next event ("
514 <<
")\">→</a></div></td>";
517 os <<
"</tr></table>";
521 ProcessMacroPiece(os, *MP, 0);
529 os <<
"<td><div class=\"PathNav\"><a href=\"#";
533 os <<
"Path" << (num + 1);
534 os <<
"\" title=\"Next event ("
536 <<
")\">→</a></div></td>";
539 os <<
"</tr></table>";
543 os <<
"</div></td></tr>";
546 unsigned DisplayPos = LineEnd - FileStart;
555 E = Ranges.end();
I !=
E; ++
I) {
561 unsigned x = n % (
'z' -
'a');
570 unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
578 dyn_cast<PathDiagnosticMacroPiece>(*
I)) {
579 num = ProcessMacroPiece(os, *MP, num);
584 os <<
"<div class=\"msg msgEvent\" style=\"width:94%; "
586 "<table class=\"msgT\"><tr>"
587 "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
589 os <<
"</div></td><td valign=\"top\">"
591 <<
"</td></tr></table></div>\n";
600 const char *HighlightStart,
601 const char *HighlightEnd) {
611 if (EndLineNo < StartLineNo)
614 if (SM.
getFileID(InstantiationStart) != BugFileID ||
615 SM.
getFileID(InstantiationEnd) != BugFileID)
620 unsigned OldEndColNo = EndColNo;
PathDiagnosticLocation getUniqueingLoc() const
Get the location on which the report should be uniqued.
Defines the clang::ASTContext interface.
SourceLocation getEnd() const
Lexer - This provides a simple interface that turns a text buffer into a stream of tokens...
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
llvm::SmallString< 32 > GetIssueHash(const SourceManager &SM, FullSourceLoc &IssueLoc, llvm::StringRef CheckerName, llvm::StringRef BugType, const Decl *D, const LangOptions &LangOpts)
Get an MD5 hash to help identify bugs.
ArrayRef< SourceRange > getRanges() const
Return the SourceRanges associated with this PathDiagnosticPiece.
PathDiagnosticLocation getLocation() const
RewriteBuffer - As code is rewritten, SourceBuffer's from the original input with modifications get a...
const Decl * getDeclWithIssue() const
Return the semantic context where an issue occurred.
FullSourceLoc asLocation() const
PathDiagnostic - PathDiagnostic objects represent a single path-sensitive diagnostic.
void HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP)
HighlightMacros - This uses the macro table state from the end of the file, to reexpand macros and in...
void AddLineNumbers(Rewriter &R, FileID FID)
StringRef getString() const
const LangOptions & getLangOpts() const
Token - This structure provides full information about a lexed token.
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)
SyntaxHighlight - Relex the specified FileID and annotate the HTML with information about keywords...
meta_iterator meta_begin() const
detail::InMemoryDirectory::const_iterator I
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()
Returns whether or not the report filename should be random or not.
static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...
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.
const TemplateArgument * iterator
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)
EscapeText - HTMLize a specified file so that special characters are are translated so that they are ...
Cached information about one file (either on disk or in the virtual file system). ...
SourceLocation getBegin() const
bool InsertTextBefore(SourceLocation Loc, StringRef Str)
InsertText - Insert the specified string at the specified location in the original buffer...
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
RopePieceBTreeIterator - This class provides read-only forward iteration over bytes that are in a Rop...
detail::InMemoryDirectory::const_iterator E
const LangOptions & getLangOpts() const
char __ovld __cnfn max(char x, char y)
Returns y if x < y, otherwise it returns x.
StringRef getBufferData(bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
Rewriter - This is the main interface to the rewrite buffers.
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.
NamedDecl - This represents a decl with a name.
SourceLocation getExpansionLoc(SourceLocation Loc) const
Given a SourceLocation object Loc, return the expansion location referenced by the ID...
StringRef getCheckName() const
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)
HighlightRange - Highlight a range in the source code with the specified start/end tags...