TextDiagnosticPrinter.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. //===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. //
  10. // This diagnostic client prints out their diagnostic messages.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/Frontend/TextDiagnosticPrinter.h"
  14. #include "clang/Basic/DiagnosticOptions.h"
  15. #include "clang/Basic/FileManager.h"
  16. #include "clang/Basic/SourceManager.h"
  17. #include "clang/Frontend/TextDiagnostic.h"
  18. #include "clang/Lex/Lexer.h"
  19. #include "llvm/ADT/SmallString.h"
  20. #include "llvm/Support/ErrorHandling.h"
  21. #include "llvm/Support/MemoryBuffer.h"
  22. #include "llvm/Support/raw_ostream.h"
  23. #include <algorithm>
  24. using namespace clang;
  25. TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os,
  26. DiagnosticOptions *diags,
  27. bool _OwnsOutputStream)
  28. : OS(os), DiagOpts(diags),
  29. OwnsOutputStream(_OwnsOutputStream) {
  30. }
  31. TextDiagnosticPrinter::~TextDiagnosticPrinter() {
  32. if (OwnsOutputStream)
  33. delete &OS;
  34. }
  35. void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO,
  36. const Preprocessor *PP) {
  37. // Build the TextDiagnostic utility.
  38. TextDiag.reset(new TextDiagnostic(OS, LO, &*DiagOpts));
  39. }
  40. void TextDiagnosticPrinter::EndSourceFile() {
  41. TextDiag.reset();
  42. }
  43. /// \brief Print any diagnostic option information to a raw_ostream.
  44. ///
  45. /// This implements all of the logic for adding diagnostic options to a message
  46. /// (via OS). Each relevant option is comma separated and all are enclosed in
  47. /// the standard bracketing: " [...]".
  48. static void printDiagnosticOptions(raw_ostream &OS,
  49. DiagnosticsEngine::Level Level,
  50. const Diagnostic &Info,
  51. const DiagnosticOptions &DiagOpts) {
  52. bool Started = false;
  53. if (DiagOpts.ShowOptionNames) {
  54. // Handle special cases for non-warnings early.
  55. if (Info.getID() == diag::fatal_too_many_errors) {
  56. OS << " [-ferror-limit=]";
  57. return;
  58. }
  59. // The code below is somewhat fragile because we are essentially trying to
  60. // report to the user what happened by inferring what the diagnostic engine
  61. // did. Eventually it might make more sense to have the diagnostic engine
  62. // include some "why" information in the diagnostic.
  63. // If this is a warning which has been mapped to an error by the user (as
  64. // inferred by checking whether the default mapping is to an error) then
  65. // flag it as such. Note that diagnostics could also have been mapped by a
  66. // pragma, but we don't currently have a way to distinguish this.
  67. if (Level == DiagnosticsEngine::Error &&
  68. DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) &&
  69. !DiagnosticIDs::isDefaultMappingAsError(Info.getID())) {
  70. OS << " [-Werror";
  71. Started = true;
  72. }
  73. StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
  74. if (!Opt.empty()) {
  75. OS << (Started ? "," : " [")
  76. << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt;
  77. StringRef OptValue = Info.getDiags()->getFlagValue();
  78. if (!OptValue.empty())
  79. OS << "=" << OptValue;
  80. Started = true;
  81. }
  82. }
  83. // If the user wants to see category information, include it too.
  84. if (DiagOpts.ShowCategories) {
  85. unsigned DiagCategory =
  86. DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
  87. if (DiagCategory) {
  88. OS << (Started ? "," : " [");
  89. Started = true;
  90. if (DiagOpts.ShowCategories == 1)
  91. OS << DiagCategory;
  92. else {
  93. assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value");
  94. OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory);
  95. }
  96. }
  97. }
  98. if (Started)
  99. OS << ']';
  100. }
  101. void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
  102. const Diagnostic &Info) {
  103. // Default implementation (Warnings/errors count).
  104. DiagnosticConsumer::HandleDiagnostic(Level, Info);
  105. // Render the diagnostic message into a temporary buffer eagerly. We'll use
  106. // this later as we print out the diagnostic to the terminal.
  107. SmallString<100> OutStr;
  108. Info.FormatDiagnostic(OutStr);
  109. llvm::raw_svector_ostream DiagMessageStream(OutStr);
  110. printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts);
  111. // Keeps track of the starting position of the location
  112. // information (e.g., "foo.c:10:4:") that precedes the error
  113. // message. We use this information to determine how long the
  114. // file+line+column number prefix is.
  115. uint64_t StartOfLocationInfo = OS.tell();
  116. if (!Prefix.empty())
  117. OS << Prefix << ": ";
  118. // Use a dedicated, simpler path for diagnostics without a valid location.
  119. // This is important as if the location is missing, we may be emitting
  120. // diagnostics in a context that lacks language options, a source manager, or
  121. // other infrastructure necessary when emitting more rich diagnostics.
  122. if (!Info.getLocation().isValid()) {
  123. TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors,
  124. DiagOpts->CLFallbackMode);
  125. TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(),
  126. OS.tell() - StartOfLocationInfo,
  127. DiagOpts->MessageLength,
  128. DiagOpts->ShowColors);
  129. OS.flush();
  130. return;
  131. }
  132. // Assert that the rest of our infrastructure is setup properly.
  133. assert(DiagOpts && "Unexpected diagnostic without options set");
  134. assert(Info.hasSourceManager() &&
  135. "Unexpected diagnostic with no source manager");
  136. assert(TextDiag && "Unexpected diagnostic outside source file processing");
  137. TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(),
  138. Info.getRanges(),
  139. Info.getFixItHints(),
  140. &Info.getSourceManager());
  141. OS.flush();
  142. }