/// \brief Print any diagnostic option information to a raw_ostream. /// /// This implements all of the logic for adding diagnostic options to a message /// (via OS). Each relevant option is comma separated and all are enclosed in /// the standard bracketing: " [...]". static void printDiagnosticOptions(raw_ostream &OS, DiagnosticsEngine::Level Level, const Diagnostic &Info, const DiagnosticOptions &DiagOpts) { bool Started = false; if (DiagOpts.ShowOptionNames) { // Handle special cases for non-warnings early. if (Info.getID() == diag::fatal_too_many_errors) { OS << " [-ferror-limit=]"; return; } // The code below is somewhat fragile because we are essentially trying to // report to the user what happened by inferring what the diagnostic engine // did. Eventually it might make more sense to have the diagnostic engine // include some "why" information in the diagnostic. // If this is a warning which has been mapped to an error by the user (as // inferred by checking whether the default mapping is to an error) then // flag it as such. Note that diagnostics could also have been mapped by a // pragma, but we don't currently have a way to distinguish this. if (Level == DiagnosticsEngine::Error && DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) && !DiagnosticIDs::isDefaultMappingAsError(Info.getID())) { OS << " [-Werror"; Started = true; } StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); if (!Opt.empty()) { OS << (Started ? "," : " [") << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt; StringRef OptValue = Info.getDiags()->getFlagValue(); if (!OptValue.empty()) OS << "=" << OptValue; Started = true; } } // If the user wants to see category information, include it too. if (DiagOpts.ShowCategories) { unsigned DiagCategory = DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); if (DiagCategory) { OS << (Started ? "," : " ["); Started = true; if (DiagOpts.ShowCategories == 1) OS << DiagCategory; else { assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value"); OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory); } } } if (Started) OS << ']'; }
void ClangTidyDiagnosticConsumer::HandleDiagnostic( DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { if (DiagLevel == DiagnosticsEngine::Note) { assert(!Errors.empty() && "A diagnostic note can only be appended to a message."); } else { finalizeLastError(); StringRef WarningOption = Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag( Info.getID()); std::string CheckName = !WarningOption.empty() ? ("clang-diagnostic-" + WarningOption).str() : Context.getCheckName(Info.getID()).str(); if (CheckName.empty()) { // This is a compiler diagnostic without a warning option. Assign check // name based on its level. switch (DiagLevel) { case DiagnosticsEngine::Error: case DiagnosticsEngine::Fatal: CheckName = "clang-diagnostic-error"; break; case DiagnosticsEngine::Warning: CheckName = "clang-diagnostic-warning"; break; default: CheckName = "clang-diagnostic-unknown"; break; } } ClangTidyError::Level Level = ClangTidyError::Warning; if (DiagLevel == DiagnosticsEngine::Error || DiagLevel == DiagnosticsEngine::Fatal) { // Force reporting of Clang errors regardless of filters and non-user // code. Level = ClangTidyError::Error; LastErrorRelatesToUserCode = true; LastErrorPassesLineFilter = true; } Errors.push_back(ClangTidyError(CheckName, Level)); } // FIXME: Provide correct LangOptions for each file. LangOptions LangOpts; ClangTidyDiagnosticRenderer Converter( LangOpts, &Context.DiagEngine->getDiagnosticOptions(), Errors.back()); SmallString<100> Message; Info.FormatDiagnostic(Message); SourceManager *Sources = nullptr; if (Info.hasSourceManager()) Sources = &Info.getSourceManager(); Converter.emitDiagnostic(Info.getLocation(), DiagLevel, Message, Info.getRanges(), Info.getFixItHints(), Sources); checkFilters(Info.getLocation()); }
unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { if (DiagLevel == DiagnosticsEngine::Note) return 0; // No flag for notes. StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); if (FlagName.empty()) return 0; // Here we assume that FlagName points to static data whose pointer // value is fixed. This allows us to unique by diagnostic groups. const void *data = FlagName.data(); std::pair<unsigned, StringRef> &entry = DiagFlags[data]; if (entry.first == 0) { entry.first = DiagFlags.size(); entry.second = FlagName; // Lazily emit the string in a separate record. RecordData Record; Record.push_back(RECORD_DIAG_FLAG); Record.push_back(entry.first); Record.push_back(FlagName.size()); Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG), Record, FlagName); } return entry.first; }
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) override { if (Info.getID() == clang::diag::warn_drv_input_file_unused) { // Arg 1 for this diagnostic is the option that didn't get used. UnusedInputs.push_back(Info.getArgStdStr(0)); } if (Other) Other->HandleDiagnostic(DiagLevel, Info); }
void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { if (DiagLevel != DiagnosticsEngine::Note) { if (inNonNoteDiagnostic) { // We have encountered a non-note diagnostic. Finish up the previous // diagnostic block before starting a new one. Stream.ExitBlock(); } inNonNoteDiagnostic = true; } Stream.EnterSubblock(BLOCK_DIAG, 4); // Emit the RECORD_DIAG record. Record.clear(); Record.push_back(RECORD_DIAG); Record.push_back(DiagLevel); AddLocToRecord(Info.getLocation(), Record); // Emit the category string lazily and get the category ID. Record.push_back(getEmitCategory(Info.getID())); // Emit the diagnostic flag string lazily and get the mapped ID. Record.push_back(getEmitDiagnosticFlag(DiagLevel, Info)); // Emit the diagnostic text. diagBuf.clear(); Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text. Record.push_back(diagBuf.str().size()); Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str()); // Emit Source Ranges. ArrayRef<CharSourceRange> Ranges = Info.getRanges(); for (ArrayRef<CharSourceRange>::iterator it=Ranges.begin(), ei=Ranges.end(); it != ei; ++it) { EmitCharSourceRange(*it); } // Emit FixIts. for (unsigned i = 0, n = Info.getNumFixItHints(); i != n; ++i) { const FixItHint &fix = Info.getFixItHint(i); if (fix.isNull()) continue; Record.clear(); Record.push_back(RECORD_FIXIT); AddCharSourceRangeToRecord(fix.RemoveRange, Record); Record.push_back(fix.CodeToInsert.size()); Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FIXIT), Record, fix.CodeToInsert); } if (DiagLevel == DiagnosticsEngine::Note) { // Notes currently cannot have child diagnostics. Complete the // diagnostic now. Stream.ExitBlock(); } }
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); }
StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info) : ID(Info.getID()), Level(Level) { assert((Info.getLocation().isInvalid() || Info.hasSourceManager()) && "Valid source location without setting a source manager for diagnostic"); if (Info.getLocation().isValid()) Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager()); SmallString<64> Message; Info.FormatDiagnostic(Message); this->Message.assign(Message.begin(), Message.end()); this->Ranges.assign(Info.getRanges().begin(), Info.getRanges().end()); this->FixIts.assign(Info.getFixItHints().begin(), Info.getFixItHints().end()); }
void ClangTidyDiagnosticConsumer::HandleDiagnostic( DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { // FIXME: Demultiplex diagnostics. // FIXME: Ensure that we don't get notes from user code related to errors // from non-user code. if (Diags->getSourceManager().isInSystemHeader(Info.getLocation())) return; if (DiagLevel != DiagnosticsEngine::Note) { Errors.push_back( ClangTidyError(Context.getCheckName(Info.getID()), getMessage(Info))); } else { assert(!Errors.empty() && "A diagnostic note can only be appended to a message."); Errors.back().Notes.push_back(getMessage(Info)); } addFixes(Info, Errors.back()); }
StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info) : ID(Info.getID()), Level(Level) { assert((Info.getLocation().isInvalid() || Info.hasSourceManager()) && "Valid source location without setting a source manager for diagnostic"); if (Info.getLocation().isValid()) Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager()); SmallString<64> Message; Info.FormatDiagnostic(Message); this->Message.assign(Message.begin(), Message.end()); Ranges.reserve(Info.getNumRanges()); for (unsigned I = 0, N = Info.getNumRanges(); I != N; ++I) Ranges.push_back(Info.getRange(I)); FixIts.reserve(Info.getNumFixItHints()); for (unsigned I = 0, N = Info.getNumFixItHints(); I != N; ++I) FixIts.push_back(Info.getFixItHint(I)); }
void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) { auto behavior = state.determineBehavior(diagnostic.getID()); if (behavior == DiagnosticState::Behavior::Ignore) return; // Figure out the source location. SourceLoc loc = diagnostic.getLoc(); if (loc.isInvalid() && diagnostic.getDecl()) { const Decl *decl = diagnostic.getDecl(); // If a declaration was provided instead of a location, and that declaration // has a location we can point to, use that location. loc = decl->getLoc(); if (loc.isInvalid()) { // There is no location we can point to. Pretty-print the declaration // so we can point to it. SourceLoc ppLoc = PrettyPrintedDeclarations[decl]; if (ppLoc.isInvalid()) { class TrackingPrinter : public StreamPrinter { SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries; public: TrackingPrinter( SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries, raw_ostream &OS) : StreamPrinter(OS), Entries(Entries) {} void printDeclLoc(const Decl *D) override { Entries.push_back({ D, OS.tell() }); } }; SmallVector<std::pair<const Decl *, uint64_t>, 8> entries; llvm::SmallString<128> buffer; llvm::SmallString<128> bufferName; { // Figure out which declaration to print. It's the top-most // declaration (not a module). const Decl *ppDecl = decl; auto dc = decl->getDeclContext(); // FIXME: Horrible, horrible hackaround. We're not getting a // DeclContext everywhere we should. if (!dc) { return; } while (!dc->isModuleContext()) { switch (dc->getContextKind()) { case DeclContextKind::Module: llvm_unreachable("Not in a module context!"); break; case DeclContextKind::FileUnit: case DeclContextKind::TopLevelCodeDecl: break; case DeclContextKind::ExtensionDecl: ppDecl = cast<ExtensionDecl>(dc); break; case DeclContextKind::GenericTypeDecl: ppDecl = cast<GenericTypeDecl>(dc); break; case DeclContextKind::SerializedLocal: case DeclContextKind::Initializer: case DeclContextKind::AbstractClosureExpr: case DeclContextKind::AbstractFunctionDecl: case DeclContextKind::SubscriptDecl: break; } dc = dc->getParent(); } // Build the module name path (in reverse), which we use to // build the name of the buffer. SmallVector<StringRef, 4> nameComponents; while (dc) { nameComponents.push_back(cast<Module>(dc)->getName().str()); dc = dc->getParent(); } for (unsigned i = nameComponents.size(); i; --i) { bufferName += nameComponents[i-1]; bufferName += '.'; } if (auto value = dyn_cast<ValueDecl>(ppDecl)) { bufferName += value->getNameStr(); } else if (auto ext = dyn_cast<ExtensionDecl>(ppDecl)) { bufferName += ext->getExtendedType().getString(); } // Pretty-print the declaration we've picked. llvm::raw_svector_ostream out(buffer); TrackingPrinter printer(entries, out); ppDecl->print(printer, PrintOptions::printForDiagnostics()); } // Build a buffer with the pretty-printed declaration. auto bufferID = SourceMgr.addMemBufferCopy(buffer, bufferName); auto memBufferStartLoc = SourceMgr.getLocForBufferStart(bufferID); // Go through all of the pretty-printed entries and record their // locations. for (auto entry : entries) { PrettyPrintedDeclarations[entry.first] = memBufferStartLoc.getAdvancedLoc(entry.second); } // Grab the pretty-printed location. ppLoc = PrettyPrintedDeclarations[decl]; } loc = ppLoc; } } // Actually substitute the diagnostic arguments into the diagnostic text. llvm::SmallString<256> Text; { llvm::raw_svector_ostream Out(Text); formatDiagnosticText(diagnosticStrings[(unsigned)diagnostic.getID()], diagnostic.getArgs(), Out); } // Pass the diagnostic off to the consumer. DiagnosticInfo Info; Info.ID = diagnostic.getID(); Info.Ranges = diagnostic.getRanges(); Info.FixIts = diagnostic.getFixIts(); for (auto &Consumer : Consumers) { Consumer->handleDiagnostic(SourceMgr, loc, toDiagnosticKind(behavior), Text, Info); } }
/// \brief Print the diagnostic name to a raw_ostream. /// /// This prints the diagnostic name to a raw_ostream if it has one. It formats /// the name according to the expected diagnostic message formatting: /// " [diagnostic_name_here]" static void printDiagnosticName(raw_ostream &OS, const Diagnostic &Info) { if (!DiagnosticIDs::isBuiltinNote(Info.getID())) OS << " [" << DiagnosticIDs::getName(Info.getID()) << "]"; }
void ClangTidyDiagnosticConsumer::HandleDiagnostic( DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note) return; if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error && DiagLevel != DiagnosticsEngine::Fatal && LineIsMarkedWithNOLINTinMacro(Diags->getSourceManager(), Info.getLocation())) { ++Context.Stats.ErrorsIgnoredNOLINT; // Ignored a warning, should ignore related notes as well LastErrorWasIgnored = true; return; } LastErrorWasIgnored = false; // Count warnings/errors. DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); if (DiagLevel == DiagnosticsEngine::Note) { assert(!Errors.empty() && "A diagnostic note can only be appended to a message."); } else { finalizeLastError(); StringRef WarningOption = Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag( Info.getID()); std::string CheckName = !WarningOption.empty() ? ("clang-diagnostic-" + WarningOption).str() : Context.getCheckName(Info.getID()).str(); if (CheckName.empty()) { // This is a compiler diagnostic without a warning option. Assign check // name based on its level. switch (DiagLevel) { case DiagnosticsEngine::Error: case DiagnosticsEngine::Fatal: CheckName = "clang-diagnostic-error"; break; case DiagnosticsEngine::Warning: CheckName = "clang-diagnostic-warning"; break; default: CheckName = "clang-diagnostic-unknown"; break; } } ClangTidyError::Level Level = ClangTidyError::Warning; if (DiagLevel == DiagnosticsEngine::Error || DiagLevel == DiagnosticsEngine::Fatal) { // Force reporting of Clang errors regardless of filters and non-user // code. Level = ClangTidyError::Error; LastErrorRelatesToUserCode = true; LastErrorPassesLineFilter = true; } bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning && Context.getWarningAsErrorFilter().contains(CheckName); Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(), IsWarningAsError); } ClangTidyDiagnosticRenderer Converter( Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(), Errors.back()); SmallString<100> Message; Info.FormatDiagnostic(Message); SourceManager *Sources = nullptr; if (Info.hasSourceManager()) Sources = &Info.getSourceManager(); Converter.emitDiagnostic(Info.getLocation(), DiagLevel, Message, Info.getRanges(), Info.getFixItHints(), Sources); checkFilters(Info.getLocation()); }