Replacement IncludeDirectives::addAngledInclude(const clang::FileEntry *File, llvm::StringRef Include) { FileID FID = Sources.translateFile(File); assert(FID.isValid() && "Invalid file entry given!"); if (hasInclude(File, Include)) return Replacement(); unsigned Offset, NLFlags; std::tie(Offset, NLFlags) = angledIncludeInsertionOffset(FID); StringRef EOL = guessEOL(Sources, FID); llvm::SmallString<32> InsertionText; if (NLFlags & NL_Prepend) InsertionText += EOL; if (NLFlags & NL_PrependAnother) InsertionText += EOL; InsertionText += "#include <"; InsertionText += Include; InsertionText += ">"; if (NLFlags & NL_AppendTwice) { InsertionText += EOL; InsertionText += EOL; } return Replacement(File->getName(), Offset, 0, InsertionText); }
/// EnterMainSourceFile - Enter the specified FileID as the main source file, /// which implicitly adds the builtin defines etc. void Preprocessor::EnterMainSourceFile() { // We do not allow the preprocessor to reenter the main file. Doing so will // cause FileID's to accumulate information from both runs (e.g. #line // information) and predefined macros aren't guaranteed to be set properly. assert(NumEnteredSourceFiles == 0 && "Cannot reenter the main file!"); FileID MainFileID = SourceMgr.getMainFileID(); // If MainFileID is loaded it means we loaded an AST file, no need to enter // a main file. if (!SourceMgr.isLoadedFileID(MainFileID)) { // Enter the main file source buffer. EnterSourceFile(MainFileID, nullptr, SourceLocation()); // If we've been asked to skip bytes in the main file (e.g., as part of a // precompiled preamble), do so now. if (SkipMainFilePreamble.first > 0) CurLexer->SetByteOffset(SkipMainFilePreamble.first, SkipMainFilePreamble.second); // Tell the header info that the main file was entered. If the file is later // #imported, it won't be re-entered. if (const FileEntry *FE = SourceMgr.getFileEntryForID(MainFileID)) HeaderInfo.IncrementIncludeCount(FE); } // Preprocess Predefines to populate the initial preprocessor state. std::unique_ptr<llvm::MemoryBuffer> SB = llvm::MemoryBuffer::getMemBufferCopy(Predefines, "<built-in>"); assert(SB && "Cannot create predefined source buffer"); FileID FID = SourceMgr.createFileID(std::move(SB)); assert(FID.isValid() && "Could not create FileID for predefines?"); setPredefinesFileID(FID); // Start parsing the predefines. EnterSourceFile(FID, nullptr, SourceLocation()); if (!PPOpts->PCHThroughHeader.empty()) { // Lookup and save the FileID for the through header. If it isn't found // in the search path, it's a fatal error. const DirectoryLookup *CurDir; const FileEntry *File = LookupFile( SourceLocation(), PPOpts->PCHThroughHeader, /*isAngled=*/false, /*FromDir=*/nullptr, /*FromFile=*/nullptr, CurDir, /*SearchPath=*/nullptr, /*RelativePath=*/nullptr, /*SuggestedModule=*/nullptr, /*IsMapped=*/nullptr, /*IsFrameworkFound=*/nullptr); if (!File) { Diag(SourceLocation(), diag::err_pp_through_header_not_found) << PPOpts->PCHThroughHeader; return; } setPCHThroughHeaderFileID( SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User)); } // Skip tokens from the Predefines and if needed the main file. if ((usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) || (usingPCHWithPragmaHdrStop() && SkippingUntilPragmaHdrStop)) SkipTokensWhileUsingPCH(); }
static bool isPreprocessedEntityIfInFileID(PreprocessedEntity *PPE, FileID FID, SourceManager &SM) { assert(FID.isValid()); if (!PPE) return false; SourceLocation Loc = PPE->getSourceRange().getBegin(); if (Loc.isInvalid()) return false; return SM.isInFileID(SM.getFileLoc(Loc), FID); }
/// \brief Convenience function to get rewritten content for \c Filename from /// \c Rewrites. /// /// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath(). /// \post Replacements.empty() -> Result.empty() /// /// \param[in] Replacements Replacements to apply /// \param[in] Rewrites Rewriter to use to apply replacements. /// \param[out] Result Contents of the file after applying replacements if /// replacements were provided. /// /// \returns \parblock /// \li true if all replacements were applied successfully. /// \li false if at least one replacement failed to apply. static bool getRewrittenData(const std::vector<tooling::Replacement> &Replacements, Rewriter &Rewrites, std::string &Result) { if (Replacements.empty()) return true; if (!applyAllReplacements(Replacements, Rewrites)) return false; SourceManager &SM = Rewrites.getSourceMgr(); FileManager &Files = SM.getFileManager(); StringRef FileName = Replacements.begin()->getFilePath(); const clang::FileEntry *Entry = Files.getFile(FileName); assert(Entry && "Expected an existing file"); FileID ID = SM.translateFile(Entry); assert(ID.isValid() && "Expected a valid FileID"); const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID); Result = std::string(Buffer->begin(), Buffer->end()); return true; }
/// EnterMainSourceFile - Enter the specified FileID as the main source file, /// which implicitly adds the builtin defines etc. void Preprocessor::EnterMainSourceFile() { // We do not allow the preprocessor to reenter the main file. Doing so will // cause FileID's to accumulate information from both runs (e.g. #line // information) and predefined macros aren't guaranteed to be set properly. assert(NumEnteredSourceFiles == 0 && "Cannot reenter the main file!"); FileID MainFileID = SourceMgr.getMainFileID(); // If MainFileID is loaded it means we loaded an AST file, no need to enter // a main file. if (!SourceMgr.isLoadedFileID(MainFileID)) { // Enter the main file source buffer. EnterSourceFile(MainFileID, nullptr, SourceLocation()); // If we've been asked to skip bytes in the main file (e.g., as part of a // precompiled preamble), do so now. if (SkipMainFilePreamble.first > 0) CurLexer->SkipBytes(SkipMainFilePreamble.first, SkipMainFilePreamble.second); // Tell the header info that the main file was entered. If the file is later // #imported, it won't be re-entered. if (const FileEntry *FE = SourceMgr.getFileEntryForID(MainFileID)) HeaderInfo.IncrementIncludeCount(FE); } // Preprocess Predefines to populate the initial preprocessor state. std::unique_ptr<llvm::MemoryBuffer> SB = llvm::MemoryBuffer::getMemBufferCopy(Predefines, "<built-in>"); assert(SB && "Cannot create predefined source buffer"); FileID FID = SourceMgr.createFileID(std::move(SB)); assert(FID.isValid() && "Could not create FileID for predefines?"); setPredefinesFileID(FID); // Start parsing the predefines. EnterSourceFile(FID, nullptr, SourceLocation()); }
void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { FullCommentParts Parts(C, Traits); const DeclInfo *DI = C->getDeclInfo(); StringRef RootEndTag; if (DI) { switch (DI->getKind()) { case DeclInfo::OtherKind: RootEndTag = "</Other>"; Result << "<Other"; break; case DeclInfo::FunctionKind: RootEndTag = "</Function>"; Result << "<Function"; switch (DI->TemplateKind) { case DeclInfo::NotTemplate: break; case DeclInfo::Template: Result << " templateKind=\"template\""; break; case DeclInfo::TemplateSpecialization: Result << " templateKind=\"specialization\""; break; case DeclInfo::TemplatePartialSpecialization: llvm_unreachable("partial specializations of functions " "are not allowed in C++"); } if (DI->IsInstanceMethod) Result << " isInstanceMethod=\"1\""; if (DI->IsClassMethod) Result << " isClassMethod=\"1\""; break; case DeclInfo::ClassKind: RootEndTag = "</Class>"; Result << "<Class"; switch (DI->TemplateKind) { case DeclInfo::NotTemplate: break; case DeclInfo::Template: Result << " templateKind=\"template\""; break; case DeclInfo::TemplateSpecialization: Result << " templateKind=\"specialization\""; break; case DeclInfo::TemplatePartialSpecialization: Result << " templateKind=\"partialSpecialization\""; break; } break; case DeclInfo::VariableKind: RootEndTag = "</Variable>"; Result << "<Variable"; break; case DeclInfo::NamespaceKind: RootEndTag = "</Namespace>"; Result << "<Namespace"; break; case DeclInfo::TypedefKind: RootEndTag = "</Typedef>"; Result << "<Typedef"; break; case DeclInfo::EnumKind: RootEndTag = "</Enum>"; Result << "<Enum"; break; } { // Print line and column number. SourceLocation Loc = DI->CurrentDecl->getLocation(); std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); FileID FID = LocInfo.first; unsigned FileOffset = LocInfo.second; if (FID.isValid()) { if (const FileEntry *FE = SM.getFileEntryForID(FID)) { Result << " file=\""; appendToResultWithXMLEscaping(FE->getName()); Result << "\""; } Result << " line=\"" << SM.getLineNumber(FID, FileOffset) << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) << "\""; } } // Finish the root tag. Result << ">"; bool FoundName = false; if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { if (DeclarationName DeclName = ND->getDeclName()) { Result << "<Name>"; std::string Name = DeclName.getAsString(); appendToResultWithXMLEscaping(Name); FoundName = true; Result << "</Name>"; } } if (!FoundName) Result << "<Name><anonymous></Name>"; { // Print USR. SmallString<128> USR; generateUSRForDecl(DI->CommentDecl, USR); if (!USR.empty()) { Result << "<USR>"; appendToResultWithXMLEscaping(USR); Result << "</USR>"; } } } else { // No DeclInfo -- just emit some root tag and name tag. RootEndTag = "</Other>"; Result << "<Other><Name>unknown</Name>"; } if (Parts.Headerfile) { Result << "<Headerfile>"; visit(Parts.Headerfile); Result << "</Headerfile>"; } { // Pretty-print the declaration. Result << "<Declaration>"; SmallString<128> Declaration; getSourceTextOfDeclaration(DI, Declaration); formatTextOfDeclaration(DI, Declaration); appendToResultWithXMLEscaping(Declaration); Result << "</Declaration>"; } bool FirstParagraphIsBrief = false; if (Parts.Brief) { Result << "<Abstract>"; visit(Parts.Brief); Result << "</Abstract>"; } else if (Parts.FirstParagraph) { Result << "<Abstract>"; visit(Parts.FirstParagraph); Result << "</Abstract>"; FirstParagraphIsBrief = true; } if (Parts.TParams.size() != 0) { Result << "<TemplateParameters>"; for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) visit(Parts.TParams[i]); Result << "</TemplateParameters>"; } if (Parts.Params.size() != 0) { Result << "<Parameters>"; for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) visit(Parts.Params[i]); Result << "</Parameters>"; } if (Parts.Exceptions.size() != 0) { Result << "<Exceptions>"; for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i) visit(Parts.Exceptions[i]); Result << "</Exceptions>"; } if (Parts.Returns.size() != 0) { Result << "<ResultDiscussion>"; for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) visit(Parts.Returns[i]); Result << "</ResultDiscussion>"; } if (DI->CommentDecl->hasAttrs()) { const AttrVec &Attrs = DI->CommentDecl->getAttrs(); for (unsigned i = 0, e = Attrs.size(); i != e; i++) { const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); if (!AA) { if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { if (DA->getMessage().empty()) Result << "<Deprecated/>"; else { Result << "<Deprecated>"; appendToResultWithXMLEscaping(DA->getMessage()); Result << "</Deprecated>"; } } else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { if (UA->getMessage().empty()) Result << "<Unavailable/>"; else { Result << "<Unavailable>"; appendToResultWithXMLEscaping(UA->getMessage()); Result << "</Unavailable>"; } } continue; } // 'availability' attribute. Result << "<Availability"; StringRef Distribution; if (AA->getPlatform()) { Distribution = AvailabilityAttr::getPrettyPlatformName( AA->getPlatform()->getName()); if (Distribution.empty()) Distribution = AA->getPlatform()->getName(); } Result << " distribution=\"" << Distribution << "\">"; VersionTuple IntroducedInVersion = AA->getIntroduced(); if (!IntroducedInVersion.empty()) { Result << "<IntroducedInVersion>" << IntroducedInVersion.getAsString() << "</IntroducedInVersion>"; } VersionTuple DeprecatedInVersion = AA->getDeprecated(); if (!DeprecatedInVersion.empty()) { Result << "<DeprecatedInVersion>" << DeprecatedInVersion.getAsString() << "</DeprecatedInVersion>"; } VersionTuple RemovedAfterVersion = AA->getObsoleted(); if (!RemovedAfterVersion.empty()) { Result << "<RemovedAfterVersion>" << RemovedAfterVersion.getAsString() << "</RemovedAfterVersion>"; } StringRef DeprecationSummary = AA->getMessage(); if (!DeprecationSummary.empty()) { Result << "<DeprecationSummary>"; appendToResultWithXMLEscaping(DeprecationSummary); Result << "</DeprecationSummary>"; } if (AA->getUnavailable()) Result << "<Unavailable/>"; Result << "</Availability>"; } } { bool StartTagEmitted = false; for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { const Comment *C = Parts.MiscBlocks[i]; if (FirstParagraphIsBrief && C == Parts.FirstParagraph) continue; if (!StartTagEmitted) { Result << "<Discussion>"; StartTagEmitted = true; } visit(C); } if (StartTagEmitted) Result << "</Discussion>"; } Result << RootEndTag; }
/// \brief Print out the file/line/column information and include trace. /// /// This method handlen the emission of the diagnostic location information. /// This includes extracting as much location information as is present for /// the diagnostic and printing it, as well as any include stack or source /// ranges necessary. void TextDiagnostic::emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, ArrayRef<CharSourceRange> Ranges, const SourceManager &SM) { if (PLoc.isInvalid()) { // At least print the file name if available: FileID FID = SM.getFileID(Loc); if (FID.isValid()) { const FileEntry* FE = SM.getFileEntryForID(FID); if (FE && FE->isValid()) { emitFilename(FE->getName(), SM); if (FE->isInPCH()) OS << " (in PCH)"; OS << ": "; } } return; } unsigned LineNo = PLoc.getLine(); if (!DiagOpts->ShowLocation) return; if (DiagOpts->ShowColors) OS.changeColor(savedColor, true); emitFilename(PLoc.getFilename(), SM); switch (DiagOpts->getFormat()) { case DiagnosticOptions::Clang: OS << ':' << LineNo; break; case DiagnosticOptions::MSVC: OS << '(' << LineNo; break; case DiagnosticOptions::Vi: OS << " +" << LineNo; break; } if (DiagOpts->ShowColumn) // Compute the column number. if (unsigned ColNo = PLoc.getColumn()) { if (DiagOpts->getFormat() == DiagnosticOptions::MSVC) { OS << ','; // Visual Studio 2010 or earlier expects column number to be off by one if (LangOpts.MSCompatibilityVersion && !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012)) ColNo--; } else OS << ':'; OS << ColNo; } switch (DiagOpts->getFormat()) { case DiagnosticOptions::Clang: case DiagnosticOptions::Vi: OS << ':'; break; case DiagnosticOptions::MSVC: // MSVC2013 and before print 'file(4) : error'. MSVC2015 gets rid of the // space and prints 'file(4): error'. OS << ')'; if (LangOpts.MSCompatibilityVersion && !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2015)) OS << ' '; OS << ": "; break; } if (DiagOpts->ShowSourceRanges && !Ranges.empty()) { FileID CaretFileID = SM.getFileID(SM.getExpansionLoc(Loc)); bool PrintedRange = false; for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(), RE = Ranges.end(); RI != RE; ++RI) { // Ignore invalid ranges. if (!RI->isValid()) continue; SourceLocation B = SM.getExpansionLoc(RI->getBegin()); SourceLocation E = SM.getExpansionLoc(RI->getEnd()); // If the End location and the start location are the same and are a // macro location, then the range was something that came from a // macro expansion or _Pragma. If this is an object-like macro, the // best we can do is to highlight the range. If this is a // function-like macro, we'd also like to highlight the arguments. if (B == E && RI->getEnd().isMacroID()) E = SM.getExpansionRange(RI->getEnd()).second; std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); // If the start or end of the range is in another file, just discard // it. if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) continue; // Add in the length of the token, so that we cover multi-char // tokens. unsigned TokSize = 0; if (RI->isTokenRange()) TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts); OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':' << SM.getColumnNumber(BInfo.first, BInfo.second) << '-' << SM.getLineNumber(EInfo.first, EInfo.second) << ':' << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) << '}'; PrintedRange = true; } if (PrintedRange) OS << ':'; } OS << ' '; }
/// Print out the file/line/column information and include trace. /// /// This method handlen the emission of the diagnostic location information. /// This includes extracting as much location information as is present for /// the diagnostic and printing it, as well as any include stack or source /// ranges necessary. void TextDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, ArrayRef<CharSourceRange> Ranges) { if (PLoc.isInvalid()) { // At least print the file name if available: FileID FID = Loc.getFileID(); if (FID.isValid()) { const FileEntry *FE = Loc.getFileEntry(); if (FE && FE->isValid()) { emitFilename(FE->getName(), Loc.getManager()); if (FE->isInPCH()) OS << " (in PCH)"; OS << ": "; } } return; } unsigned LineNo = PLoc.getLine(); if (!DiagOpts->ShowLocation) return; if (DiagOpts->ShowColors) OS.changeColor(savedColor, true); emitFilename(PLoc.getFilename(), Loc.getManager()); switch (DiagOpts->getFormat()) { case DiagnosticOptions::Clang: OS << ':' << LineNo; break; case DiagnosticOptions::MSVC: OS << '(' << LineNo; break; case DiagnosticOptions::Vi: OS << " +" << LineNo; break; } if (DiagOpts->ShowColumn) // Compute the column number. if (unsigned ColNo = PLoc.getColumn()) { if (DiagOpts->getFormat() == DiagnosticOptions::MSVC) { OS << ','; // Visual Studio 2010 or earlier expects column number to be off by one if (LangOpts.MSCompatibilityVersion && !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012)) ColNo--; } else OS << ':'; OS << ColNo; } switch (DiagOpts->getFormat()) { case DiagnosticOptions::Clang: case DiagnosticOptions::Vi: OS << ':'; break; case DiagnosticOptions::MSVC: // MSVC2013 and before print 'file(4) : error'. MSVC2015 gets rid of the // space and prints 'file(4): error'. OS << ')'; if (LangOpts.MSCompatibilityVersion && !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2015)) OS << ' '; OS << ": "; break; } if (DiagOpts->ShowSourceRanges && !Ranges.empty()) { FileID CaretFileID = Loc.getExpansionLoc().getFileID(); bool PrintedRange = false; for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(), RE = Ranges.end(); RI != RE; ++RI) { // Ignore invalid ranges. if (!RI->isValid()) continue; auto &SM = Loc.getManager(); SourceLocation B = SM.getExpansionLoc(RI->getBegin()); CharSourceRange ERange = SM.getExpansionRange(RI->getEnd()); SourceLocation E = ERange.getEnd(); bool IsTokenRange = ERange.isTokenRange(); std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); // If the start or end of the range is in another file, just discard // it. if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) continue; // Add in the length of the token, so that we cover multi-char // tokens. unsigned TokSize = 0; if (IsTokenRange) TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts); FullSourceLoc BF(B, SM), EF(E, SM); OS << '{' << BF.getLineNumber() << ':' << BF.getColumnNumber() << '-' << EF.getLineNumber() << ':' << (EF.getColumnNumber() + TokSize) << '}'; PrintedRange = true; } if (PrintedRange) OS << ':'; } OS << ' '; }
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.front()->getLocation().getManager(); assert(!path.empty()); FileID FID = path.front()->getLocation().asLocation().getExpansionLoc().getFileID(); assert(FID.isValid()); // 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.back()->getLocation().asLocation()), SMgr); FullSourceLoc FunL(SMgr.getExpansionLoc(Body->getLocStart()), SMgr); offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber(); } } // Process the path. // Maintain the counts of extra note pieces separately. unsigned TotalPieces = path.size(); unsigned TotalNotePieces = std::count_if(path.begin(), path.end(), [](const std::shared_ptr<PathDiagnosticPiece> &p) { return isa<PathDiagnosticNotePiece>(*p); }); unsigned TotalRegularPieces = TotalPieces - TotalNotePieces; unsigned NumRegularPieces = TotalRegularPieces; unsigned NumNotePieces = TotalNotePieces; for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) { if (isa<PathDiagnosticNotePiece>(I->get())) { // This adds diagnostic bubbles, but not navigation. // Navigation through note pieces would be added later, // as a separate pass through the piece list. HandlePiece(R, FID, **I, NumNotePieces, TotalNotePieces); --NumNotePieces; } else { HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces); --NumRegularPieces; } } // 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.back()->getLocation().asLocation().getExpansionLineNumber(); int ColumnNumber = path.back()->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\">Warning:</td><td>" "<a href=\"#EndPath\">line " << LineNumber << ", column " << ColumnNumber << "</a><br />" << D.getVerboseDescription() << "</td></tr>\n"; // The navigation across the extra notes pieces. unsigned NumExtraPieces = 0; for (const auto &Piece : path) { if (const auto *P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) { int LineNumber = P->getLocation().asLocation().getExpansionLineNumber(); int ColumnNumber = P->getLocation().asLocation().getExpansionColumnNumber(); os << "<tr><td class=\"rowname\">Note:</td><td>" << "<a href=\"#Note" << NumExtraPieces << "\">line " << LineNumber << ", column " << ColumnNumber << "</a><br />" << P->getString() << "</td></tr>"; ++NumExtraPieces; } } // 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"; PathDiagnosticLocation UPDLoc = D.getUniqueingLoc(); FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid() ? UPDLoc.asLocation() : D.getLocation().asLocation()), SMgr); const Decl *DeclWithIssue = D.getDeclWithIssue(); 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<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " << GetIssueHash(SMgr, L, D.getCheckName(), D.getBugType(), DeclWithIssue, PP.getLangOpts()) << " -->\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::make_absolute(Model)) { llvm::errs() << "warning: could not make '" << Model << "' absolute: " << EC.message() << '\n'; return; } 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 != llvm::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; }