TextDiagnosticPrinter.cpp 6.0 KB

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