void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, bool ShouldFlattenMacros) const { for (auto &Piece : *this) { switch (Piece->getKind()) { case PathDiagnosticPiece::Call: { auto &Call = cast<PathDiagnosticCallPiece>(*Piece); if (auto CallEnter = Call.getCallEnterEvent()) Current.push_back(std::move(CallEnter)); Call.path.flattenTo(Primary, Primary, ShouldFlattenMacros); if (auto callExit = Call.getCallExitEvent()) Current.push_back(std::move(callExit)); break; } case PathDiagnosticPiece::Macro: { auto &Macro = cast<PathDiagnosticMacroPiece>(*Piece); if (ShouldFlattenMacros) { Macro.subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros); } else { Current.push_back(Piece); PathPieces NewPath; Macro.subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros); // FIXME: This probably shouldn't mutate the original path piece. Macro.subPieces = NewPath; } break; } case PathDiagnosticPiece::Event: case PathDiagnosticPiece::ControlFlow: case PathDiagnosticPiece::Note: Current.push_back(Piece); break; } } }
static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) { if (X.size() != Y.size()) return X.size() < Y.size(); for (unsigned i = 0, n = X.size(); i != n; ++i) { Optional<bool> b = comparePiece(*X[i], *Y[i]); if (b.hasValue()) return b.getValue(); } return None; }
static void compute_path_size(const PathPieces &pieces, unsigned &size) { for (PathPieces::const_iterator it = pieces.begin(), et = pieces.end(); it != et; ++it) { const PathDiagnosticPiece *piece = it->getPtr(); if (const PathDiagnosticCallPiece *cp = dyn_cast<PathDiagnosticCallPiece>(piece)) { compute_path_size(cp->path, size); } else ++size; } }
void TextPathDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, FilesMade *) { for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), et = Diags.end(); it != et; ++it) { const PathDiagnostic *D = *it; PathPieces FlatPath = D->path.flatten(/*ShouldFlattenMacros=*/true); for (PathPieces::const_iterator I = FlatPath.begin(), E = FlatPath.end(); I != E; ++I) { unsigned diagID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note, (*I)->getString()); Diag.Report((*I)->getLocation().asLocation(), diagID); } } }
void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, bool ShouldFlattenMacros) const { for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { PathDiagnosticPiece *Piece = I->getPtr(); switch (Piece->getKind()) { case PathDiagnosticPiece::Call: { PathDiagnosticCallPiece *Call = cast<PathDiagnosticCallPiece>(Piece); IntrusiveRefCntPtr<PathDiagnosticEventPiece> CallEnter = Call->getCallEnterEvent(); if (CallEnter) Current.push_back(CallEnter); Call->path.flattenTo(Primary, Primary, ShouldFlattenMacros); IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = Call->getCallExitEvent(); if (callExit) Current.push_back(callExit); break; } case PathDiagnosticPiece::Macro: { PathDiagnosticMacroPiece *Macro = cast<PathDiagnosticMacroPiece>(Piece); if (ShouldFlattenMacros) { Macro->subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros); } else { Current.push_back(Piece); PathPieces NewPath; Macro->subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros); // FIXME: This probably shouldn't mutate the original path piece. Macro->subPieces = NewPath; } break; } case PathDiagnosticPiece::Event: case PathDiagnosticPiece::ControlFlow: Current.push_back(Piece); break; } } }
static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) { if (X.size() != Y.size()) return X.size() < Y.size(); PathPieces::const_iterator X_I = X.begin(), X_end = X.end(); PathPieces::const_iterator Y_I = Y.begin(), Y_end = Y.end(); for ( ; X_I != X_end && Y_I != Y_end; ++X_I, ++Y_I) { Optional<bool> b = comparePiece(**X_I, **Y_I); if (b.hasValue()) return b.getValue(); } return None; }
static json::Object createThreadFlow(const PathPieces &Pieces, json::Array &Files) { const SourceManager &SMgr = Pieces.front()->getLocation().getManager(); json::Array Locations; for (const auto &Piece : Pieces) { const PathDiagnosticLocation &P = Piece->getLocation(); Locations.push_back(createThreadFlowLocation( createLocation(createPhysicalLocation(P.asRange(), *P.asLocation().getFileEntry(), SMgr, Files), Piece->getString()), calculateImportance(*Piece))); } return json::Object{{"locations", std::move(Locations)}}; }
void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, FilesMade *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 = D.path.flatten(/*ShouldFlattenMacros=*/false); // 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.getVerboseDescription() << "</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); StringRef BugDesc = D.getVerboseDescription(); if (!BugDesc.empty()) os << "\n<!-- BUGDESC " << BugDesc << " -->\n"; StringRef BugType = D.getBugType(); if (!BugType.empty()) os << "\n<!-- BUGTYPE " << BugType << " -->\n"; StringRef 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->addDiagnostic(D, getName(), 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::ReportDiag(const PathDiagnostic& D, FilesMade *filesMade) { // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) { llvm::errs() << "warning: could not create directory '" << Directory << "': " << ec.message() << '\n'; noDir = true; return; } } if (noDir) return; // First flatten out the entire path to make it easier to use. PathPieces path = D.path.flatten(/*ShouldFlattenMacros=*/false); // 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()); // Get the function/method name SmallString<128> declName("unknown"); int offsetDecl = 0; if (const Decl *DeclWithIssue = D.getDeclWithIssue()) { if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { declName = ND->getDeclName().getAsString(); } if (const Stmt *Body = DeclWithIssue->getBody()) { // Retrieve the relative position of the declaration which will be used // for the file name FullSourceLoc L( SMgr.getExpansionLoc((*path.rbegin())->getLocation().asLocation()), SMgr); FullSourceLoc FunL(SMgr.getExpansionLoc(Body->getLocStart()), SMgr); offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber(); } } // 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. llvm::SmallString<0> DirName; if (llvm::sys::path::is_relative(Entry->getName())) { llvm::sys::fs::current_path(DirName); DirName += '/'; } int LineNumber = (*path.rbegin())->getLocation().asLocation().getExpansionLineNumber(); int ColumnNumber = (*path.rbegin())->getLocation().asLocation().getExpansionColumnNumber(); // 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 " << LineNumber << ", column " << ColumnNumber << "</a></td></tr>\n" "<tr><td class=\"rowname\">Description:</td><td>" << D.getVerboseDescription() << "</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); StringRef BugDesc = D.getVerboseDescription(); if (!BugDesc.empty()) os << "\n<!-- BUGDESC " << BugDesc << " -->\n"; StringRef BugType = D.getBugType(); if (!BugType.empty()) os << "\n<!-- BUGTYPE " << BugType << " -->\n"; StringRef BugCategory = D.getCategory(); if (!BugCategory.empty()) os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n"; os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n"; os << "\n<!-- FILENAME " << llvm::sys::path::filename(Entry->getName()) << " -->\n"; os << "\n<!-- FUNCTIONNAME " << declName << " -->\n"; os << "\n<!-- BUGLINE " << LineNumber << " -->\n"; os << "\n<!-- BUGCOLUMN " << ColumnNumber << " -->\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. int FD; SmallString<128> Model, ResultPath; if (!AnalyzerOpts.shouldWriteStableReportFilename()) { llvm::sys::path::append(Model, Directory, "report-%%%%%%.html"); if (std::error_code EC = llvm::sys::fs::createUniqueFile(Model, FD, ResultPath)) { llvm::errs() << "warning: could not create file in '" << Directory << "': " << EC.message() << '\n'; return; } } else { int i = 1; std::error_code EC; do { // Find a filename which is not already used std::stringstream filename; Model = ""; filename << "report-" << llvm::sys::path::filename(Entry->getName()).str() << "-" << declName.c_str() << "-" << offsetDecl << "-" << i << ".html"; llvm::sys::path::append(Model, Directory, filename.str()); EC = llvm::sys::fs::openFileForWrite(Model, FD, llvm::sys::fs::F_RW | llvm::sys::fs::F_Excl); if (EC && EC != std::errc::file_exists) { llvm::errs() << "warning: could not create file '" << Model << "': " << EC.message() << '\n'; return; } i++; } while (EC); } llvm::raw_fd_ostream os(FD, true); if (filesMade) filesMade->addDiagnostic(D, getName(), llvm::sys::path::filename(ResultPath)); // Emit the HTML to disk. for (RewriteBuffer::iterator I = Buf->begin(), E = Buf->end(); I!=E; ++I) os << *I; }