void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info) { // Default implementation (Warnings/errors count). DiagnosticConsumer::HandleDiagnostic(Level, Info); // Initialize the main file name, if we haven't already fetched it. if (MainFilename.empty() && Info.hasSourceManager()) { const SourceManager &SM = Info.getSourceManager(); FileID FID = SM.getMainFileID(); if (!FID.isInvalid()) { const FileEntry *FE = SM.getFileEntryForID(FID); if (FE && FE->getName()) MainFilename = FE->getName(); } } // Create the diag entry. DiagEntry DE; DE.DiagnosticID = Info.getID(); DE.DiagnosticLevel = Level; // Format the message. SmallString<100> MessageStr; Info.FormatDiagnostic(MessageStr); DE.Message = MessageStr.str(); // Set the location information. DE.Filename = ""; DE.Line = DE.Column = 0; if (Info.getLocation().isValid() && Info.hasSourceManager()) { const SourceManager &SM = Info.getSourceManager(); PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation()); if (PLoc.isInvalid()) { // At least print the file name if available: FileID FID = SM.getFileID(Info.getLocation()); if (!FID.isInvalid()) { const FileEntry *FE = SM.getFileEntryForID(FID); if (FE && FE->getName()) DE.Filename = FE->getName(); } } else { DE.Filename = PLoc.getFilename(); DE.Line = PLoc.getLine(); DE.Column = PLoc.getColumn(); } } // Record the diagnostic entry. Entries.push_back(DE); }
/// FindExpectedDiags - Lex the main source file to find all of the // expected errors and warnings. static void FindExpectedDiags(const Preprocessor &PP, ExpectedData &ED, FileID FID) { // Create a raw lexer to pull all the comments out of FID. if (FID.isInvalid()) return; SourceManager& SM = PP.getSourceManager(); // Create a lexer to lex all the tokens of the main file in raw mode. const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); Lexer RawLex(FID, FromFile, SM, PP.getLangOpts()); // Return comments as tokens, this is how we find expected diagnostics. RawLex.SetCommentRetentionState(true); Token Tok; Tok.setKind(tok::comment); while (Tok.isNot(tok::eof)) { RawLex.Lex(Tok); if (!Tok.is(tok::comment)) continue; std::string Comment = PP.getSpelling(Tok); if (Comment.empty()) continue; // Find all expected errors/warnings/notes. ParseDirective(Comment, ED, SM, Tok.getLocation(), PP.getDiagnostics()); }; }
void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS) { // Check SourceManager hasn't changed. setSourceManager(SM); #ifndef NDEBUG if (FID.isInvalid()) return; const FileEntry *FE = SM.getFileEntryForID(FID); if (PS == IsParsed) { // Move the FileID from the unparsed set to the parsed set. UnparsedFiles.erase(FID); ParsedFiles.insert(std::make_pair(FID, FE)); } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) { // Add the FileID to the unparsed set if we haven't seen it before. // Check for directives. bool FoundDirectives; if (PS == IsUnparsedNoDirectives) FoundDirectives = false; else FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts); // Add the FileID to the unparsed set. UnparsedFiles.insert(std::make_pair(FID, UnparsedFileStatus(FE, FoundDirectives))); } #endif }
void clang_getFileLocation(CXSourceLocation location, CXFile *file, unsigned *line, unsigned *column, unsigned *offset) { if (!isASTUnitSourceLocation(location)) { CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset); return; } SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); if (!location.ptr_data[0] || Loc.isInvalid()) return createNullLocation(file, line, column, offset); const SourceManager &SM = *static_cast<const SourceManager*>(location.ptr_data[0]); SourceLocation FileLoc = SM.getFileLoc(Loc); std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(FileLoc); FileID FID = LocInfo.first; unsigned FileOffset = LocInfo.second; if (FID.isInvalid()) return createNullLocation(file, line, column, offset); if (file) *file = const_cast<FileEntry *>(SM.getFileEntryForID(FID)); if (line) *line = SM.getLineNumber(FID, FileOffset); if (column) *column = SM.getColumnNumber(FID, FileOffset); if (offset) *offset = FileOffset; }
/// \brief Lex the specified source file to determine whether it contains /// any expected-* directives. As a Lexer is used rather than a full-blown /// Preprocessor, directives inside skipped #if blocks will still be found. /// /// \return true if any directives were found. static bool findDirectives(SourceManager &SM, FileID FID, const LangOptions &LangOpts) { // Create a raw lexer to pull all the comments out of FID. if (FID.isInvalid()) return false; // Create a lexer to lex all the tokens of the main file in raw mode. const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); Lexer RawLex(FID, FromFile, SM, LangOpts); // Return comments as tokens, this is how we find expected diagnostics. RawLex.SetCommentRetentionState(true); Token Tok; Tok.setKind(tok::comment); VerifyDiagnosticConsumer::DirectiveStatus Status = VerifyDiagnosticConsumer::HasNoDirectives; while (Tok.isNot(tok::eof)) { RawLex.Lex(Tok); if (!Tok.is(tok::comment)) continue; std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts); if (Comment.empty()) continue; // Find first directive. if (ParseDirective(Comment, 0, SM, Tok.getLocation(), SM.getDiagnostics(), Status)) return true; } return false; }
/// 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, 0, 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. llvm::MemoryBuffer *SB = llvm::MemoryBuffer::getMemBufferCopy(Predefines, "<built-in>"); assert(SB && "Cannot create predefined source buffer"); FileID FID = SourceMgr.createFileIDForMemBuffer(SB); assert(!FID.isInvalid() && "Could not create FileID for predefines?"); // Start parsing the predefines. EnterSourceFile(FID, 0, SourceLocation()); }
void CXIndexDataConsumer::translateLoc(SourceLocation Loc, CXIdxClientFile *indexFile, CXFile *file, unsigned *line, unsigned *column, unsigned *offset) { if (Loc.isInvalid()) return; SourceManager &SM = Ctx->getSourceManager(); Loc = SM.getFileLoc(Loc); std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); FileID FID = LocInfo.first; unsigned FileOffset = LocInfo.second; if (FID.isInvalid()) return; const FileEntry *FE = SM.getFileEntryForID(FID); if (indexFile) *indexFile = getIndexFile(FE); if (file) *file = const_cast<FileEntry *>(FE); if (line) *line = SM.getLineNumber(FID, FileOffset); if (column) *column = SM.getColumnNumber(FID, FileOffset); if (offset) *offset = FileOffset; }
Replacement IncludeDirectives::addAngledInclude(const clang::FileEntry *File, llvm::StringRef Include) { FileID FID = Sources.translateFile(File); assert(!FID.isInvalid() && "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); }
/// \brief Returns true if the preprocessed entity that \arg PPEI iterator /// points to is coming from the file \arg FID. /// /// Can be used to avoid implicit deserializations of preallocated /// preprocessed entities if we only care about entities of a specific file /// and not from files #included in the range given at /// \see getPreprocessedEntitiesInRange. bool PreprocessingRecord::isEntityInFileID(iterator PPEI, FileID FID) { if (FID.isInvalid()) return false; PPEntityID PPID = PPEI.Position; if (PPID < 0) { assert(unsigned(-PPID-1) < LoadedPreprocessedEntities.size() && "Out-of bounds loaded preprocessed entity"); assert(ExternalSource && "No external source to load from"); unsigned LoadedIndex = LoadedPreprocessedEntities.size()+PPID; if (PreprocessedEntity *PPE = LoadedPreprocessedEntities[LoadedIndex]) return isPreprocessedEntityIfInFileID(PPE, FID, SourceMgr); // See if the external source can see if the entity is in the file without // deserializing it. llvm::Optional<bool> IsInFile = ExternalSource->isPreprocessedEntityInFileID(LoadedIndex, FID); if (IsInFile.hasValue()) return IsInFile.getValue(); // The external source did not provide a definite answer, go and deserialize // the entity to check it. return isPreprocessedEntityIfInFileID( getLoadedPreprocessedEntity(LoadedIndex), FID, SourceMgr); } assert(unsigned(PPID) < PreprocessedEntities.size() && "Out-of bounds local preprocessed entity"); return isPreprocessedEntityIfInFileID(PreprocessedEntities[PPID], FID, SourceMgr); }
/// EnterMainSourceFile - Enter the specified FileID as the main source file, /// which implicitly adds the builtin defines etc. bool 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(); // Enter the main file source buffer. std::string ErrorStr; if (EnterSourceFile(MainFileID, 0, ErrorStr)) return true; // 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. llvm::MemoryBuffer *SB = llvm::MemoryBuffer::getMemBufferCopy(Predefines, "<built-in>"); assert(SB && "Cannot fail to create predefined source buffer"); FileID FID = SourceMgr.createFileIDForMemBuffer(SB); assert(!FID.isInvalid() && "Could not create FileID for predefines?"); // Start parsing the predefines. return EnterSourceFile(FID, 0, ErrorStr); }
static bool isPreprocessedEntityIfInFileID(PreprocessedEntity *PPE, FileID FID, SourceManager &SM) { assert(!FID.isInvalid()); if (!PPE) return false; SourceLocation Loc = PPE->getSourceRange().getBegin(); if (Loc.isInvalid()) return false; if (SM.isInFileID(SM.getFileLoc(Loc), FID)) return true; else return false; }
bool IndexingContext::importedModule(const ImportDecl *ImportD) { if (ImportD->isInvalidDecl()) return true; SourceLocation Loc; auto IdLocs = ImportD->getIdentifierLocs(); if (!IdLocs.empty()) Loc = IdLocs.back(); else Loc = ImportD->getLocation(); SourceManager &SM = Ctx->getSourceManager(); FileID FID = SM.getFileID(SM.getFileLoc(Loc)); if (FID.isInvalid()) return true; bool Invalid = false; const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); if (Invalid || !SEntry.isFile()) return true; if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { switch (IndexOpts.SystemSymbolFilter) { case IndexingOptions::SystemSymbolFilterKind::None: return true; case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly: case IndexingOptions::SystemSymbolFilterKind::All: break; } } const Module *Mod = ImportD->getImportedModule(); if (!ImportD->isImplicit() && Mod->Parent && !IdLocs.empty()) { reportModuleReferences(Mod->Parent, IdLocs.drop_back(), ImportD, DataConsumer); } SymbolRoleSet Roles = (unsigned)SymbolRole::Declaration; if (ImportD->isImplicit()) Roles |= (unsigned)SymbolRole::Implicit; return DataConsumer.handleModuleOccurence(ImportD, Mod, Roles, Loc); }
bool CXIndexDataConsumer::markEntityOccurrenceInFile(const NamedDecl *D, SourceLocation Loc) { if (!D || Loc.isInvalid()) return true; SourceManager &SM = Ctx->getSourceManager(); D = getEntityDecl(D); std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(SM.getFileLoc(Loc)); FileID FID = LocInfo.first; if (FID.isInvalid()) return true; const FileEntry *FE = SM.getFileEntryForID(FID); if (!FE) return true; RefFileOccurrence RefOccur(FE, D); std::pair<llvm::DenseSet<RefFileOccurrence>::iterator, bool> res = RefFileOccurrences.insert(RefOccur); return !res.second; // already in map }
/// \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 \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 (!tooling::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.isInvalid() && "Expected a valid FileID"); const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID); Result = std::string(Buffer->begin(), Buffer->end()); return true; }
bool IndexingContext::importedModule(const ImportDecl *ImportD) { SourceLocation Loc; auto IdLocs = ImportD->getIdentifierLocs(); if (!IdLocs.empty()) Loc = IdLocs.front(); else Loc = ImportD->getLocation(); SourceManager &SM = Ctx->getSourceManager(); Loc = SM.getFileLoc(Loc); if (Loc.isInvalid()) return true; FileID FID; unsigned Offset; std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); if (FID.isInvalid()) return true; bool Invalid = false; const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); if (Invalid || !SEntry.isFile()) return true; if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { switch (IndexOpts.SystemSymbolFilter) { case IndexingOptions::SystemSymbolFilterKind::None: return true; case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly: case IndexingOptions::SystemSymbolFilterKind::All: break; } } SymbolRoleSet Roles = (unsigned)SymbolRole::Declaration; if (ImportD->isImplicit()) Roles |= (unsigned)SymbolRole::Implicit; return DataConsumer.handleModuleOccurence(ImportD, Roles, FID, Offset); }
/// 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(); // Enter the main file source buffer. EnterSourceFile(MainFileID, 0); // 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); std::vector<char> PrologFile; PrologFile.reserve(4080); // FIXME: Don't make a copy. PrologFile.insert(PrologFile.end(), Predefines.begin(), Predefines.end()); // Memory buffer must end with a null byte! PrologFile.push_back(0); // Now that we have emitted the predefined macros, #includes, etc into // PrologFile, preprocess it to populate the initial preprocessor state. llvm::MemoryBuffer *SB = llvm::MemoryBuffer::getMemBufferCopy(&PrologFile.front(),&PrologFile.back(), "<built-in>"); assert(SB && "Cannot fail to create predefined source buffer"); FileID FID = SourceMgr.createFileIDForMemBuffer(SB); assert(!FID.isInvalid() && "Could not create FileID for predefines?"); // Start parsing the predefines. EnterSourceFile(FID, 0); }
/// Returns true if the preprocessed entity that \arg PPEI iterator /// points to is coming from the file \arg FID. /// /// Can be used to avoid implicit deserializations of preallocated /// preprocessed entities if we only care about entities of a specific file /// and not from files \#included in the range given at /// \see getPreprocessedEntitiesInRange. bool PreprocessingRecord::isEntityInFileID(iterator PPEI, FileID FID) { if (FID.isInvalid()) return false; int Pos = std::distance(iterator(this, 0), PPEI); if (Pos < 0) { if (unsigned(-Pos-1) >= LoadedPreprocessedEntities.size()) { assert(0 && "Out-of bounds loaded preprocessed entity"); return false; } assert(ExternalSource && "No external source to load from"); unsigned LoadedIndex = LoadedPreprocessedEntities.size()+Pos; if (PreprocessedEntity *PPE = LoadedPreprocessedEntities[LoadedIndex]) return isPreprocessedEntityIfInFileID(PPE, FID, SourceMgr); // See if the external source can see if the entity is in the file without // deserializing it. Optional<bool> IsInFile = ExternalSource->isPreprocessedEntityInFileID(LoadedIndex, FID); if (IsInFile.hasValue()) return IsInFile.getValue(); // The external source did not provide a definite answer, go and deserialize // the entity to check it. return isPreprocessedEntityIfInFileID( getLoadedPreprocessedEntity(LoadedIndex), FID, SourceMgr); } if (unsigned(Pos) >= PreprocessedEntities.size()) { assert(0 && "Out-of bounds local preprocessed entity"); return false; } return isPreprocessedEntityIfInFileID(PreprocessedEntities[Pos], FID, SourceMgr); }
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 PathDiagnosticConsumer::HandlePathDiagnostic( std::unique_ptr<PathDiagnostic> D) { if (!D || D->path.empty()) return; // We need to flatten the locations (convert Stmt* to locations) because // the referenced statements may be freed by the time the diagnostics // are emitted. D->flattenLocations(); // If the PathDiagnosticConsumer does not support diagnostics that // cross file boundaries, prune out such diagnostics now. if (!supportsCrossFileDiagnostics()) { // Verify that the entire path is from the same FileID. FileID FID; const SourceManager &SMgr = D->path.front()->getLocation().getManager(); SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); SmallString<128> buf; llvm::raw_svector_ostream warning(buf); warning << "warning: Path diagnostic report is not generated. Current " << "output format does not support diagnostics that cross file " << "boundaries. Refer to --analyzer-output for valid output " << "formats\n"; while (!WorkList.empty()) { const PathPieces &path = *WorkList.pop_back_val(); for (const auto &I : path) { const PathDiagnosticPiece *piece = I.get(); FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); if (FID.isInvalid()) { FID = SMgr.getFileID(L); } else if (SMgr.getFileID(L) != FID) { llvm::errs() << warning.str(); return; } // Check the source ranges. ArrayRef<SourceRange> Ranges = piece->getRanges(); for (const auto &I : Ranges) { SourceLocation L = SMgr.getExpansionLoc(I.getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) { llvm::errs() << warning.str(); return; } L = SMgr.getExpansionLoc(I.getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) { llvm::errs() << warning.str(); return; } } if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece)) WorkList.push_back(&call->path); else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) WorkList.push_back(¯o->subPieces); } } if (FID.isInvalid()) return; // FIXME: Emit a warning? } // Profile the node to see if we already have something matching it llvm::FoldingSetNodeID profile; D->Profile(profile); void *InsertPos = nullptr; if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { // Keep the PathDiagnostic with the shorter path. // Note, the enclosing routine is called in deterministic order, so the // results will be consistent between runs (no reason to break ties if the // size is the same). const unsigned orig_size = orig->full_size(); const unsigned new_size = D->full_size(); if (orig_size <= new_size) return; assert(orig != D.get()); Diags.RemoveNode(orig); delete orig; } Diags.InsertNode(D.release()); }
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.isInvalid()) { 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; cxcursor::getDeclCursorUSR(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>"; } { // Pretty-print the declaration. Result << "<Declaration>"; SmallString<128> Declaration; getSourceTextOfDeclaration(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.Returns) { Result << "<ResultDiscussion>"; visit(Parts.Returns); 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; Result.flush(); }
void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { FullCommentParts Parts(C); 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->ThisDecl->getLocation(); std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); FileID FID = LocInfo.first; unsigned FileOffset = LocInfo.second; if (!FID.isInvalid()) { 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->ThisDecl)) { 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; cxcursor::getDeclCursorUSR(DI->ThisDecl, 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>"; } 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.Returns) { Result << "<ResultDiscussion>"; visit(Parts.Returns); Result << "</ResultDiscussion>"; } { 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; Result.flush(); }
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; }
void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, const DiagnosticInfo &Info) { // Default implementation (Warnings/errors count). DiagnosticClient::HandleDiagnostic(Level, Info); // Keeps track of the the starting position of the location // information (e.g., "foo.c:10:4:") that precedes the error // message. We use this information to determine how long the // file+line+column number prefix is. uint64_t StartOfLocationInfo = OS.tell(); if (!Prefix.empty()) OS << Prefix << ": "; // If the location is specified, print out a file/line/col and include trace // if enabled. if (Info.getLocation().isValid()) { const SourceManager &SM = Info.getSourceManager(); PresumedLoc PLoc = getDiagnosticPresumedLoc(SM, Info.getLocation()); if (PLoc.isInvalid()) { // At least print the file name if available: FileID FID = SM.getFileID(Info.getLocation()); if (!FID.isInvalid()) { const FileEntry* FE = SM.getFileEntryForID(FID); if (FE && FE->getName()) { OS << FE->getName(); if (FE->getDevice() == 0 && FE->getInode() == 0 && FE->getFileMode() == 0) { // in PCH is a guess, but a good one: OS << " (in PCH)"; } OS << ": "; } } } else { unsigned LineNo = PLoc.getLine(); // First, if this diagnostic is not in the main file, print out the // "included from" lines. if (LastWarningLoc != PLoc.getIncludeLoc()) { LastWarningLoc = PLoc.getIncludeLoc(); PrintIncludeStack(Level, LastWarningLoc, SM); StartOfLocationInfo = OS.tell(); } // Compute the column number. if (DiagOpts->ShowLocation) { if (DiagOpts->ShowColors) OS.changeColor(savedColor, true); OS << PLoc.getFilename(); switch (DiagOpts->Format) { case DiagnosticOptions::Clang: OS << ':' << LineNo; break; case DiagnosticOptions::Msvc: OS << '(' << LineNo; break; case DiagnosticOptions::Vi: OS << " +" << LineNo; break; } if (DiagOpts->ShowColumn) if (unsigned ColNo = PLoc.getColumn()) { if (DiagOpts->Format == DiagnosticOptions::Msvc) { OS << ','; ColNo--; } else OS << ':'; OS << ColNo; } switch (DiagOpts->Format) { case DiagnosticOptions::Clang: case DiagnosticOptions::Vi: OS << ':'; break; case DiagnosticOptions::Msvc: OS << ") : "; break; } if (DiagOpts->ShowSourceRanges && Info.getNumRanges()) { FileID CaretFileID = SM.getFileID(SM.getInstantiationLoc(Info.getLocation())); bool PrintedRange = false; for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) { // Ignore invalid ranges. if (!Info.getRange(i).isValid()) continue; SourceLocation B = Info.getRange(i).getBegin(); SourceLocation E = Info.getRange(i).getEnd(); B = SM.getInstantiationLoc(B); E = SM.getInstantiationLoc(E); // 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 && Info.getRange(i).getEnd().isMacroID()) E = SM.getInstantiationRange(Info.getRange(i).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 (Info.getRange(i).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 << ' '; if (DiagOpts->ShowColors) OS.resetColor(); } } if (DiagOpts->ShowColors) { // Print diagnostic category in bold and color switch (Level) { case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); case Diagnostic::Note: OS.changeColor(noteColor, true); break; case Diagnostic::Warning: OS.changeColor(warningColor, true); break; case Diagnostic::Error: OS.changeColor(errorColor, true); break; case Diagnostic::Fatal: OS.changeColor(fatalColor, true); break; } } switch (Level) { case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); case Diagnostic::Note: OS << "note: "; break; case Diagnostic::Warning: OS << "warning: "; break; case Diagnostic::Error: OS << "error: "; break; case Diagnostic::Fatal: OS << "fatal error: "; break; } if (DiagOpts->ShowColors) OS.resetColor(); llvm::SmallString<100> OutStr; Info.FormatDiagnostic(OutStr); if (DiagOpts->ShowNames && !DiagnosticIDs::isBuiltinNote(Info.getID())) { OutStr += " ["; OutStr += DiagnosticIDs::getName(Info.getID()); OutStr += "]"; } std::string OptionName; if (DiagOpts->ShowOptionNames) { // Was this a warning mapped to an error using -Werror or pragma? if (Level == Diagnostic::Error && DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID())) { diag::Mapping mapping = diag::MAP_IGNORE; Info.getDiags()->getDiagnosticLevel(Info.getID(), Info.getLocation(), &mapping); if (mapping == diag::MAP_WARNING) OptionName += "-Werror"; } llvm::StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); if (!Opt.empty()) { if (!OptionName.empty()) OptionName += ','; OptionName += "-W"; OptionName += Opt; } else if (Info.getID() == diag::fatal_too_many_errors) { OptionName = "-ferror-limit="; } else { // If the diagnostic is an extension diagnostic and not enabled by default // then it must have been turned on with -pedantic. bool EnabledByDefault; if (DiagnosticIDs::isBuiltinExtensionDiag(Info.getID(), EnabledByDefault) && !EnabledByDefault) OptionName = "-pedantic"; } } // If the user wants to see category information, include it too. unsigned DiagCategory = 0; if (DiagOpts->ShowCategories) DiagCategory = DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); // If there is any categorization information, include it. if (!OptionName.empty() || DiagCategory != 0) { bool NeedsComma = false; OutStr += " ["; if (!OptionName.empty()) { OutStr += OptionName; NeedsComma = true; } if (DiagCategory) { if (NeedsComma) OutStr += ','; if (DiagOpts->ShowCategories == 1) OutStr += llvm::utostr(DiagCategory); else { assert(DiagOpts->ShowCategories == 2 && "Invalid ShowCategories value"); OutStr += DiagnosticIDs::getCategoryNameFromID(DiagCategory); } } OutStr += "]"; } if (DiagOpts->ShowColors) { // Print warnings, errors and fatal errors in bold, no color switch (Level) { case Diagnostic::Warning: OS.changeColor(savedColor, true); break; case Diagnostic::Error: OS.changeColor(savedColor, true); break; case Diagnostic::Fatal: OS.changeColor(savedColor, true); break; default: break; //don't bold notes } } if (DiagOpts->MessageLength) { // We will be word-wrapping the error message, so compute the // column number where we currently are (after printing the // location information). unsigned Column = OS.tell() - StartOfLocationInfo; PrintWordWrapped(OS, OutStr, DiagOpts->MessageLength, Column); } else { OS.write(OutStr.begin(), OutStr.size()); } OS << '\n'; if (DiagOpts->ShowColors) OS.resetColor(); // If caret diagnostics are enabled and we have location, we want to // emit the caret. However, we only do this if the location moved // from the last diagnostic, if the last diagnostic was a note that // was part of a different warning or error diagnostic, or if the // diagnostic has ranges. We don't want to emit the same caret // multiple times if one loc has multiple diagnostics. if (DiagOpts->ShowCarets && Info.getLocation().isValid() && ((LastLoc != Info.getLocation()) || Info.getNumRanges() || (LastCaretDiagnosticWasNote && Level != Diagnostic::Note) || Info.getNumFixItHints())) { // Cache the LastLoc, it allows us to omit duplicate source/caret spewage. LastLoc = FullSourceLoc(Info.getLocation(), Info.getSourceManager()); LastCaretDiagnosticWasNote = (Level == Diagnostic::Note); // Get the ranges into a local array we can hack on. CharSourceRange Ranges[20]; unsigned NumRanges = Info.getNumRanges(); assert(NumRanges < 20 && "Out of space"); for (unsigned i = 0; i != NumRanges; ++i) Ranges[i] = Info.getRange(i); unsigned NumHints = Info.getNumFixItHints(); for (unsigned i = 0; i != NumHints; ++i) { const FixItHint &Hint = Info.getFixItHint(i); if (Hint.RemoveRange.isValid()) { assert(NumRanges < 20 && "Out of space"); Ranges[NumRanges++] = Hint.RemoveRange; } } const SourceManager &SM = LastLoc.getManager(); unsigned MacroInstSkipStart = 0, MacroInstSkipEnd = 0; if (DiagOpts && DiagOpts->MacroBacktraceLimit && !LastLoc.isFileID()) { // Compute the length of the macro-expansion backtrace, so that we // can establish which steps in the macro backtrace we'll skip. SourceLocation Loc = LastLoc; unsigned Depth = 0; do { ++Depth; Loc = skipToMacroArgExpansion(SM, Loc); Loc = getImmediateMacroCallerLoc(SM, Loc); } while (!Loc.isFileID()); if (Depth > DiagOpts->MacroBacktraceLimit) { MacroInstSkipStart = DiagOpts->MacroBacktraceLimit / 2 + DiagOpts->MacroBacktraceLimit % 2; MacroInstSkipEnd = Depth - DiagOpts->MacroBacktraceLimit / 2; } } EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(), Info.getFixItHints(), Info.getNumFixItHints(), DiagOpts->MessageLength, 0, MacroInstSkipStart, MacroInstSkipEnd); } OS.flush(); }
bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, bool IsRef, const Decl *Parent, SymbolRoleSet Roles, ArrayRef<SymbolRelation> Relations, const Expr *OrigE, const Decl *OrigD, const DeclContext *ContainerDC) { if (D->isImplicit() && !isa<ObjCMethodDecl>(D)) return true; if (!isa<NamedDecl>(D) || shouldSkipNamelessDecl(cast<NamedDecl>(D))) return true; SourceManager &SM = Ctx->getSourceManager(); FileID FID = SM.getFileID(SM.getFileLoc(Loc)); if (FID.isInvalid()) return true; bool Invalid = false; const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); if (Invalid || !SEntry.isFile()) return true; if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { switch (IndexOpts.SystemSymbolFilter) { case IndexingOptions::SystemSymbolFilterKind::None: return true; case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly: if (!shouldReportOccurrenceForSystemDeclOnlyMode(IsRef, Roles, Relations)) return true; break; case IndexingOptions::SystemSymbolFilterKind::All: break; } } if (!OrigD) OrigD = D; if (isTemplateImplicitInstantiation(D)) { if (!IsRef) return true; D = adjustTemplateImplicitInstantiation(D); if (!D) return true; assert(!isTemplateImplicitInstantiation(D)); } if (IsRef) Roles |= (unsigned)SymbolRole::Reference; else if (isDeclADefinition(OrigD, ContainerDC, *Ctx)) Roles |= (unsigned)SymbolRole::Definition; else Roles |= (unsigned)SymbolRole::Declaration; D = getCanonicalDecl(D); Parent = adjustParent(Parent); if (Parent) Parent = getCanonicalDecl(Parent); SmallVector<SymbolRelation, 6> FinalRelations; FinalRelations.reserve(Relations.size()+1); auto addRelation = [&](SymbolRelation Rel) { auto It = llvm::find_if(FinalRelations, [&](SymbolRelation Elem) -> bool { return Elem.RelatedSymbol == Rel.RelatedSymbol; }); if (It != FinalRelations.end()) { It->Roles |= Rel.Roles; } else { FinalRelations.push_back(Rel); } Roles |= Rel.Roles; }; if (Parent) { if (IsRef || (!isa<ParmVarDecl>(D) && isFunctionLocalSymbol(D))) { addRelation(SymbolRelation{ (unsigned)SymbolRole::RelationContainedBy, Parent }); } else { addRelation(SymbolRelation{ (unsigned)SymbolRole::RelationChildOf, Parent }); } } for (auto &Rel : Relations) { addRelation(SymbolRelation(Rel.Roles, Rel.RelatedSymbol->getCanonicalDecl())); } IndexDataConsumer::ASTNodeInfo Node{OrigE, OrigD, Parent, ContainerDC}; return DataConsumer.handleDeclOccurence(D, Roles, FinalRelations, Loc, Node); }
/// \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.isInvalid()) { const FileEntry* FE = SM.getFileEntryForID(FID); if (FE && FE->getName()) { OS << FE->getName(); if (FE->getDevice() == 0 && FE->getInode() == 0 && FE->getFileMode() == 0) { // in PCH is a guess, but a good one: OS << " (in PCH)"; } OS << ": "; } } return; } unsigned LineNo = PLoc.getLine(); if (!DiagOpts->ShowLocation) return; if (DiagOpts->ShowColors) OS.changeColor(savedColor, true); OS << PLoc.getFilename(); switch (DiagOpts->getFormat()) { case DiagnosticOptions::LFort: 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 << ','; ColNo--; } else OS << ':'; OS << ColNo; } switch (DiagOpts->getFormat()) { case DiagnosticOptions::LFort: case DiagnosticOptions::Vi: OS << ':'; break; case DiagnosticOptions::Msvc: 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 << ' '; }
void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { llvm::OwningPtr<PathDiagnostic> OwningD(D); if (!D || D->path.empty()) return; // We need to flatten the locations (convert Stmt* to locations) because // the referenced statements may be freed by the time the diagnostics // are emitted. D->flattenLocations(); // If the PathDiagnosticConsumer does not support diagnostics that // cross file boundaries, prune out such diagnostics now. if (!supportsCrossFileDiagnostics()) { // Verify that the entire path is from the same FileID. FileID FID; const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager(); llvm::SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.back(); WorkList.pop_back(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) { const PathDiagnosticPiece *piece = I->getPtr(); FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); if (FID.isInvalid()) { FID = SMgr.getFileID(L); } else if (SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? // Check the source ranges. ArrayRef<SourceRange> Ranges = piece->getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { SourceLocation L = SMgr.getExpansionLoc(I->getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? L = SMgr.getExpansionLoc(I->getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } if (FID.isInvalid()) return; // FIXME: Emit a warning? } // Profile the node to see if we already have something matching it llvm::FoldingSetNodeID profile; D->Profile(profile); void *InsertPos = 0; if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { // Keep the PathDiagnostic with the shorter path. // Note, the enclosing routine is called in deterministic order, so the // results will be consistent between runs (no reason to break ties if the // size is the same). const unsigned orig_size = orig->full_size(); const unsigned new_size = D->full_size(); if (orig_size <= new_size) return; assert(orig != D); Diags.RemoveNode(orig); delete orig; } Diags.InsertNode(OwningD.take()); }
void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { llvm::OwningPtr<PathDiagnostic> OwningD(D); if (!D || D->path.empty()) return; // We need to flatten the locations (convert Stmt* to locations) because // the referenced statements may be freed by the time the diagnostics // are emitted. D->flattenLocations(); // If the PathDiagnosticConsumer does not support diagnostics that // cross file boundaries, prune out such diagnostics now. if (!supportsCrossFileDiagnostics()) { // Verify that the entire path is from the same FileID. FileID FID; const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager(); llvm::SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.back(); WorkList.pop_back(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) { const PathDiagnosticPiece *piece = I->getPtr(); FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); if (FID.isInvalid()) { FID = SMgr.getFileID(L); } else if (SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? // Check the source ranges. for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), RE = piece->ranges_end(); RI != RE; ++RI) { SourceLocation L = SMgr.getExpansionLoc(RI->getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? L = SMgr.getExpansionLoc(RI->getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } if (FID.isInvalid()) return; // FIXME: Emit a warning? } // Profile the node to see if we already have something matching it llvm::FoldingSetNodeID profile; D->Profile(profile); void *InsertPos = 0; if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { // Keep the PathDiagnostic with the shorter path. const unsigned orig_size = orig->full_size(); const unsigned new_size = D->full_size(); if (orig_size <= new_size) { bool shouldKeepOriginal = true; if (orig_size == new_size) { // Here we break ties in a fairly arbitrary, but deterministic, way. llvm::FoldingSetNodeID fullProfile, fullProfileOrig; D->FullProfile(fullProfile); orig->FullProfile(fullProfileOrig); if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash()) shouldKeepOriginal = false; } if (shouldKeepOriginal) return; } Diags.RemoveNode(orig); delete orig; } Diags.InsertNode(OwningD.take()); }