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 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; } // Compute the diagnostic text. diagBuf.clear(); Info.FormatDiagnostic(diagBuf); const SourceManager * SM = Info.hasSourceManager() ? &Info.getSourceManager() : 0; SDiagsRenderer Renderer(*this, Record, *LangOpts, DiagOpts); Renderer.emitDiagnostic(Info.getLocation(), DiagLevel, diagBuf.str(), Info.getRanges(), llvm::makeArrayRef(Info.getFixItHints(), Info.getNumFixItHints()), SM, &Info); }
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) override { if (Info.hasSourceManager() && LangOpts) { SourceManager &SM = Info.getSourceManager(); if (Info.getLocation().isValid()) { Info.getLocation().print(llvm::errs(), SM); llvm::errs() << ": "; } SmallString<16> DiagText; Info.FormatDiagnostic(DiagText); llvm::errs() << DiagText << '\n'; if (Info.getLocation().isValid()) { PrintSourceForLocation(Info.getLocation(), SM); } for (const CharSourceRange &Range : Info.getRanges()) { bool Invalid = true; StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid); if (!Invalid) { llvm::errs() << Ref.str() << '\n'; } } } DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); }
void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info) { // Default implementation (Warnings/errors count). DiagnosticConsumer::HandleDiagnostic(Level, Info); // Render the diagnostic message into a temporary buffer eagerly. We'll use // this later as we print out the diagnostic to the terminal. llvm::SmallString<100> OutStr; Info.FormatDiagnostic(OutStr); llvm::raw_svector_ostream DiagMessageStream(OutStr); if (DiagOpts->ShowNames) printDiagnosticName(DiagMessageStream, Info); printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts); // 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 << ": "; // Use a dedicated, simpler path for diagnostics without a valid location. // This is important as if the location is missing, we may be emitting // diagnostics in a context that lacks language options, a source manager, or // other infrastructure necessary when emitting more rich diagnostics. if (!Info.getLocation().isValid()) { TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors); TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(), OS.tell() - StartOfLocationInfo, DiagOpts->MessageLength, DiagOpts->ShowColors); OS.flush(); return; } // Assert that the rest of our infrastructure is setup properly. assert(LangOpts && "Unexpected diagnostic outside source file processing"); assert(DiagOpts && "Unexpected diagnostic without options set"); assert(Info.hasSourceManager() && "Unexpected diagnostic with no source manager"); // Rebuild the TextDiagnostic utility if missing or the source manager has // changed. if (!TextDiag || SM != &Info.getSourceManager()) { SM = &Info.getSourceManager(); TextDiag.reset(new TextDiagnostic(OS, *SM, *LangOpts, *DiagOpts)); } TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(), Info.getRanges(), llvm::makeArrayRef(Info.getFixItHints(), Info.getNumFixItHints())); OS.flush(); }
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()); }
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 SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { // Enter the block for a non-note diagnostic immediately, rather than waiting // for beginDiagnostic, in case associated notes are emitted before we get // there. if (DiagLevel != DiagnosticsEngine::Note) { if (State->EmittedAnyDiagBlocks) ExitDiagBlock(); EnterDiagBlock(); State->EmittedAnyDiagBlocks = true; } // Compute the diagnostic text. State->diagBuf.clear(); Info.FormatDiagnostic(State->diagBuf); if (Info.getLocation().isInvalid()) { // Special-case diagnostics with no location. We may not have entered a // source file in this case, so we can't use the normal DiagnosticsRenderer // machinery. // Make sure we bracket all notes as "sub-diagnostics". This matches // the behavior in SDiagsRenderer::emitDiagnostic(). if (DiagLevel == DiagnosticsEngine::Note) EnterDiagBlock(); EmitDiagnosticMessage(SourceLocation(), PresumedLoc(), DiagLevel, State->diagBuf, 0, &Info); if (DiagLevel == DiagnosticsEngine::Note) ExitDiagBlock(); return; } assert(Info.hasSourceManager() && LangOpts && "Unexpected diagnostic with valid location outside of a source file"); SDiagsRenderer Renderer(*this, *LangOpts, &*State->DiagOpts); Renderer.emitDiagnostic(Info.getLocation(), DiagLevel, State->diagBuf.str(), Info.getRanges(), llvm::makeArrayRef(Info.getFixItHints(), Info.getNumFixItHints()), &Info.getSourceManager(), &Info); }
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); } }
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()); }