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) {
96 for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(),
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();
156 unsigned TotalPieces = path.size();
157 unsigned TotalNotePieces =
158 std::count_if(path.begin(), path.end(),
159 [](
const std::shared_ptr<PathDiagnosticPiece> &p) {
160 return isa<PathDiagnosticNotePiece>(*p);
163 unsigned TotalRegularPieces = TotalPieces - TotalNotePieces;
164 unsigned NumRegularPieces = TotalRegularPieces;
165 unsigned NumNotePieces = TotalNotePieces;
167 for (
auto I = path.rbegin(),
E = path.rend();
I !=
E; ++
I) {
168 if (isa<PathDiagnosticNotePiece>(
I->get())) {
172 HandlePiece(R, FID, **
I, NumNotePieces, TotalNotePieces);
175 HandlePiece(R, FID, **
I, NumRegularPieces, TotalRegularPieces);
203 if (llvm::sys::path::is_relative(Entry->
getName())) {
204 llvm::sys::fs::current_path(DirName);
208 int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
209 int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();
214 llvm::raw_string_ostream os(s);
216 os <<
"<!-- REPORTHEADER -->\n"
217 <<
"<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
218 "<tr><td class=\"rowname\">File:</td><td>"
221 <<
"</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>"
222 "<a href=\"#EndPath\">line "
230 unsigned NumExtraPieces = 0;
231 for (
const auto &Piece : path) {
232 if (
const auto *
P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) {
234 P->getLocation().asLocation().getExpansionLineNumber();
236 P->getLocation().asLocation().getExpansionColumnNumber();
237 os <<
"<tr><td class=\"rowname\">Note:</td><td>"
238 <<
"<a href=\"#Note" << NumExtraPieces <<
"\">line "
239 << LineNumber <<
", column " << ColumnNumber <<
"</a><br />"
240 <<
P->getString() <<
"</td></tr>";
252 os <<
"</table>\n<!-- REPORTSUMMARYEXTRA -->\n"
253 "<h3>Annotated Source Code</h3>\n";
261 llvm::raw_string_ostream os(s);
264 if (!BugDesc.empty())
265 os <<
"\n<!-- BUGDESC " << BugDesc <<
" -->\n";
268 if (!BugType.empty())
269 os <<
"\n<!-- BUGTYPE " << BugType <<
" -->\n";
279 if (!BugCategory.empty())
280 os <<
"\n<!-- BUGCATEGORY " << BugCategory <<
" -->\n";
282 os <<
"\n<!-- BUGFILE " << DirName << Entry->
getName() <<
" -->\n";
284 os <<
"\n<!-- FILENAME " << llvm::sys::path::filename(Entry->
getName()) <<
" -->\n";
286 os <<
"\n<!-- FUNCTIONNAME " << declName <<
" -->\n";
288 os <<
"\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT "
292 os <<
"\n<!-- BUGLINE "
296 os <<
"\n<!-- BUGCOLUMN "
300 os <<
"\n<!-- BUGPATHLENGTH " << path.size() <<
" -->\n";
303 os <<
"\n<!-- BUGMETAEND -->\n";
317 llvm::errs() <<
"warning: no diagnostics generated for main file.\n";
326 llvm::sys::path::append(Model, Directory,
"report-%%%%%%.html");
327 if (std::error_code EC =
328 llvm::sys::fs::make_absolute(Model)) {
329 llvm::errs() <<
"warning: could not make '" << Model
330 <<
"' absolute: " << EC.message() <<
'\n';
333 if (std::error_code EC =
334 llvm::sys::fs::createUniqueFile(Model, FD, ResultPath)) {
335 llvm::errs() <<
"warning: could not create file in '" << Directory
336 <<
"': " << EC.message() <<
'\n';
345 std::stringstream filename;
347 filename <<
"report-"
348 << llvm::sys::path::filename(Entry->
getName()).str()
349 <<
"-" << declName.c_str()
351 <<
"-" << i <<
".html";
352 llvm::sys::path::append(Model, Directory,
354 EC = llvm::sys::fs::openFileForWrite(Model,
356 llvm::sys::fs::F_RW |
357 llvm::sys::fs::F_Excl);
358 if (EC && EC != llvm::errc::file_exists) {
359 llvm::errs() <<
"warning: could not create file '" << Model
360 <<
"': " << EC.message() <<
'\n';
367 llvm::raw_fd_ostream os(FD,
true);
370 filesMade->addDiagnostic(D, getName(),
371 llvm::sys::path::filename(ResultPath));
380 unsigned num,
unsigned max) {
390 assert(&Pos.
getManager() == &SM &&
"SourceManagers are different!");
393 if (LPosInfo.first != BugFileID)
396 const llvm::MemoryBuffer *Buf = SM.
getBuffer(LPosInfo.first);
397 const char* FileStart = Buf->getBufferStart();
403 const char *LineStart = TokInstantiationPtr-ColNo;
406 const char *LineEnd = TokInstantiationPtr;
407 const char* FileEnd = Buf->getBufferEnd();
408 while (*LineEnd !=
'\n' && LineEnd != FileEnd)
413 for (
const char* c = LineStart; c != TokInstantiationPtr; ++c)
414 PosNo += *c ==
'\t' ? 8 : 1;
418 const char *
Kind =
nullptr;
420 bool SuppressIndex = (max == 1);
423 llvm_unreachable(
"Calls and extra notes should already be handled");
431 SuppressIndex =
true;
436 llvm::raw_string_ostream os(sbuf);
438 os <<
"\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
447 os <<
"\" class=\"msg";
449 os <<
" msg" <<
Kind;
450 os <<
"\" style=\"margin-left:" << PosNo <<
"ex";
453 if (!isa<PathDiagnosticMacroPiece>(P)) {
456 unsigned max_token = 0;
458 unsigned len = Msg.size();
468 if (cnt > max_token) max_token = cnt;
477 const unsigned max_line = 120;
479 if (max_token >= max_line)
482 unsigned characters = max_line;
483 unsigned lines = len / max_line;
486 for (; characters > max_token; --characters)
487 if (len / characters > lines) {
497 os <<
"; max-width:" << em <<
"em";
500 os <<
"; max-width:100em";
504 if (!SuppressIndex) {
505 os <<
"<table class=\"msgT\"><tr><td valign=\"top\">";
506 os <<
"<div class=\"PathIndex";
507 if (Kind) os <<
" PathIndex" <<
Kind;
508 os <<
"\">" << num <<
"</div>";
511 os <<
"</td><td><div class=\"PathNav\"><a href=\"#Path"
513 <<
"\" title=\"Previous event ("
515 <<
")\">←</a></div></td>";
522 dyn_cast<PathDiagnosticMacroPiece>(&P)) {
524 os <<
"Within the expansion of the macro '";
532 const char* MacroName = LocInfo.second + BufferInfo.data();
534 BufferInfo.begin(), MacroName, BufferInfo.end());
537 rawLexer.LexFromRawLexer(TheTok);
538 for (
unsigned i = 0, n = TheTok.getLength(); i < n; ++i)
544 if (!SuppressIndex) {
547 os <<
"<td><div class=\"PathNav\"><a href=\"#";
551 os <<
"Path" << (num + 1);
552 os <<
"\" title=\"Next event ("
554 <<
")\">→</a></div></td>";
557 os <<
"</tr></table>";
561 ProcessMacroPiece(os, *MP, 0);
566 if (!SuppressIndex) {
569 os <<
"<td><div class=\"PathNav\"><a href=\"#";
573 os <<
"Path" << (num + 1);
574 os <<
"\" title=\"Next event ("
576 <<
")\">→</a></div></td>";
579 os <<
"</tr></table>";
583 os <<
"</div></td></tr>";
586 unsigned DisplayPos = LineEnd - FileStart;
595 E = Ranges.end();
I !=
E; ++
I) {
601 unsigned x = n % (
'z' -
'a');
610 unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
618 dyn_cast<PathDiagnosticMacroPiece>(
I->get())) {
619 num = ProcessMacroPiece(os, *MP, num);
624 dyn_cast<PathDiagnosticEventPiece>(
I->get())) {
625 os <<
"<div class=\"msg msgEvent\" style=\"width:94%; "
627 "<table class=\"msgT\"><tr>"
628 "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
630 os <<
"</div></td><td valign=\"top\">"
632 <<
"</td></tr></table></div>\n";
641 const char *HighlightStart,
642 const char *HighlightEnd) {
652 if (EndLineNo < StartLineNo)
655 if (SM.
getFileID(InstantiationStart) != BugFileID ||
656 SM.
getFileID(InstantiationEnd) != BugFileID)
661 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
Stmt - This represents one statement.
Defines the SourceManager interface.
Decl - This represents one declaration (or definition), e.g.
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.
StringRef getName() const
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.
meta_iterator meta_end() const
Encodes a location in the source.
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
void AddHeaderFooterInternalBuiltinCSS(Rewriter &R, FileID FID, StringRef title)
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...