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); }
/// HandleDiagnostic - Store the errors, warnings, and notes that are /// reported. /// void TextDiagnosticBuffer::HandleDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info) { // Default implementation (Warnings/errors count). DiagnosticConsumer::HandleDiagnostic(Level, Info); SmallString<100> Buf; Info.FormatDiagnostic(Buf); switch (Level) { default: llvm_unreachable( "Diagnostic not handled during diagnostic buffering!"); case DiagnosticsEngine::Note: Notes.emplace_back(Info.getLocation(), Buf.str()); break; case DiagnosticsEngine::Warning: Warnings.emplace_back(Info.getLocation(), Buf.str()); break; case DiagnosticsEngine::Remark: Remarks.emplace_back(Info.getLocation(), Buf.str()); break; case DiagnosticsEngine::Error: case DiagnosticsEngine::Fatal: Errors.emplace_back(Info.getLocation(), Buf.str()); break; } }
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()); }
ClangTidyMessage ClangTidyDiagnosticConsumer::getMessage(const Diagnostic &Info) const { SmallString<100> Buf; Info.FormatDiagnostic(Buf); if (!Info.hasSourceManager()) { return ClangTidyMessage(Buf.str()); } return ClangTidyMessage(Buf.str(), Info.getSourceManager(), 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 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 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); }
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)); }
/// HandleSelectModifier - Handle the integer 'select' modifier. This is used /// like this: %select{foo|bar|baz}2. This means that the integer argument /// "%2" has a value from 0-2. If the value is 0, the diagnostic prints 'foo'. /// If the value is 1, it prints 'bar'. If it has the value 2, it prints 'baz'. /// This is very useful for certain classes of variant diagnostics. static void HandleSelectModifier(const Diagnostic &DInfo, unsigned ValNo, const char *Argument, unsigned ArgumentLen, SmallVectorImpl<char> &OutStr) { const char *ArgumentEnd = Argument+ArgumentLen; // Skip over 'ValNo' |'s. while (ValNo) { const char *NextVal = ScanFormat(Argument, ArgumentEnd, '|'); assert(NextVal != ArgumentEnd && "Value for integer select modifier was" " larger than the number of options in the diagnostic string!"); Argument = NextVal+1; // Skip this string. --ValNo; } // Get the end of the value. This is either the } or the |. const char *EndPtr = ScanFormat(Argument, ArgumentEnd, '|'); // Recursively format the result of the select clause into the output string. DInfo.FormatDiagnostic(Argument, EndPtr, OutStr); }
/// HandlePluralModifier - Handle the integer 'plural' modifier. This is used /// for complex plural forms, or in languages where all plurals are complex. /// The syntax is: %plural{cond1:form1|cond2:form2|:form3}, where condn are /// conditions that are tested in order, the form corresponding to the first /// that applies being emitted. The empty condition is always true, making the /// last form a default case. /// Conditions are simple boolean expressions, where n is the number argument. /// Here are the rules. /// condition := expression | empty /// empty := -> always true /// expression := numeric [',' expression] -> logical or /// numeric := range -> true if n in range /// | '%' number '=' range -> true if n % number in range /// range := number /// | '[' number ',' number ']' -> ranges are inclusive both ends /// /// Here are some examples from the GNU gettext manual written in this form: /// English: /// {1:form0|:form1} /// Latvian: /// {0:form2|%100=11,%10=0,%10=[2,9]:form1|:form0} /// Gaeilge: /// {1:form0|2:form1|:form2} /// Romanian: /// {1:form0|0,%100=[1,19]:form1|:form2} /// Lithuanian: /// {%10=0,%100=[10,19]:form2|%10=1:form0|:form1} /// Russian (requires repeated form): /// {%100=[11,14]:form2|%10=1:form0|%10=[2,4]:form1|:form2} /// Slovak /// {1:form0|[2,4]:form1|:form2} /// Polish (requires repeated form): /// {1:form0|%100=[10,20]:form2|%10=[2,4]:form1|:form2} static void HandlePluralModifier(const Diagnostic &DInfo, unsigned ValNo, const char *Argument, unsigned ArgumentLen, SmallVectorImpl<char> &OutStr) { const char *ArgumentEnd = Argument + ArgumentLen; while (1) { assert(Argument < ArgumentEnd && "Plural expression didn't match."); const char *ExprEnd = Argument; while (*ExprEnd != ':') { assert(ExprEnd != ArgumentEnd && "Plural missing expression end"); ++ExprEnd; } if (EvalPluralExpr(ValNo, Argument, ExprEnd)) { Argument = ExprEnd + 1; ExprEnd = ScanFormat(Argument, ArgumentEnd, '|'); // Recursively format the result of the plural clause into the // output string. DInfo.FormatDiagnostic(Argument, ExprEnd, OutStr); return; } Argument = ScanFormat(Argument, ArgumentEnd - 1, '|') + 1; } }
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()); }