StoredDiagnostic::StoredDiagnostic(Diagnostic::Level Level, const DiagnosticInfo &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()); llvm::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 TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, const DiagnosticInfo &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.getLocation().getManager(); PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation()); 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(LastWarningLoc, SM); StartOfLocationInfo = OS.tell(); } // Compute the column number. if (DiagOpts->ShowLocation) { if (DiagOpts->ShowColors) OS.changeColor(savedColor, true); // Emit a Visual Studio compatible line number syntax. if (LangOpts && LangOpts->Microsoft) { OS << PLoc.getFilename() << '(' << LineNo << ')'; OS << " : "; } else { OS << PLoc.getFilename() << ':' << LineNo << ':'; if (DiagOpts->ShowColumn) if (unsigned ColNo = PLoc.getColumn()) OS << ColNo << ':'; } 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 = 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->ShowOptionNames) { if (const char *Opt = Diagnostic::getWarningOptionForDiag(Info.getID())) { OutStr += " [-W"; OutStr += Opt; OutStr += ']'; } else if (Diagnostic::isBuiltinExtensionDiag(Info.getID())) { OutStr += " [-pedantic]"; } } 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.getNumCodeModificationHints())) { // Cache the LastLoc, it allows us to omit duplicate source/caret spewage. LastLoc = Info.getLocation(); LastCaretDiagnosticWasNote = (Level == Diagnostic::Note); // Get the ranges into a local array we can hack on. SourceRange 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.getNumCodeModificationHints(); for (unsigned idx = 0; idx < NumHints; ++idx) { const CodeModificationHint &Hint = Info.getCodeModificationHint(idx); if (Hint.RemoveRange.isValid()) { assert(NumRanges < 20 && "Out of space"); Ranges[NumRanges++] = Hint.RemoveRange; } } EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(), Info.getCodeModificationHints(), Info.getNumCodeModificationHints(), DiagOpts->MessageLength); } OS.flush(); }
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(); }