LogDiagnosticPrinter.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. //===--- LogDiagnosticPrinter.cpp - Log 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. #include "clang/Frontend/LogDiagnosticPrinter.h"
  10. #include "clang/Basic/FileManager.h"
  11. #include "clang/Basic/SourceManager.h"
  12. #include "llvm/ADT/SmallString.h"
  13. #include "llvm/Support/raw_ostream.h"
  14. #include "llvm/Support/ErrorHandling.h"
  15. using namespace clang;
  16. LogDiagnosticPrinter::LogDiagnosticPrinter(raw_ostream &os,
  17. const DiagnosticOptions &diags,
  18. bool _OwnsOutputStream)
  19. : OS(os), LangOpts(0), DiagOpts(&diags),
  20. OwnsOutputStream(_OwnsOutputStream) {
  21. }
  22. LogDiagnosticPrinter::~LogDiagnosticPrinter() {
  23. if (OwnsOutputStream)
  24. delete &OS;
  25. }
  26. static StringRef getLevelName(DiagnosticsEngine::Level Level) {
  27. switch (Level) {
  28. case DiagnosticsEngine::Ignored: return "ignored";
  29. case DiagnosticsEngine::Note: return "note";
  30. case DiagnosticsEngine::Warning: return "warning";
  31. case DiagnosticsEngine::Error: return "error";
  32. case DiagnosticsEngine::Fatal: return "fatal error";
  33. }
  34. llvm_unreachable("Invalid DiagnosticsEngine level!");
  35. }
  36. // Escape XML characters inside the raw string.
  37. static void emitString(llvm::raw_svector_ostream &OS, const StringRef Raw) {
  38. for (StringRef::iterator I = Raw.begin(), E = Raw.end(); I != E; ++I) {
  39. char c = *I;
  40. switch (c) {
  41. default: OS << c; break;
  42. case '&': OS << "&amp;"; break;
  43. case '<': OS << "&lt;"; break;
  44. case '>': OS << "&gt;"; break;
  45. case '\'': OS << "&apos;"; break;
  46. case '\"': OS << "&quot;"; break;
  47. }
  48. }
  49. }
  50. void LogDiagnosticPrinter::EndSourceFile() {
  51. // We emit all the diagnostics in EndSourceFile. However, we don't emit any
  52. // entry if no diagnostics were present.
  53. //
  54. // Note that DiagnosticConsumer has no "end-of-compilation" callback, so we
  55. // will miss any diagnostics which are emitted after and outside the
  56. // translation unit processing.
  57. if (Entries.empty())
  58. return;
  59. // Write to a temporary string to ensure atomic write of diagnostic object.
  60. SmallString<512> Msg;
  61. llvm::raw_svector_ostream OS(Msg);
  62. OS << "<dict>\n";
  63. if (!MainFilename.empty()) {
  64. OS << " <key>main-file</key>\n"
  65. << " <string>";
  66. emitString(OS, MainFilename);
  67. OS << "</string>\n";
  68. }
  69. if (!DwarfDebugFlags.empty()) {
  70. OS << " <key>dwarf-debug-flags</key>\n"
  71. << " <string>";
  72. emitString(OS, DwarfDebugFlags);
  73. OS << "</string>\n";
  74. }
  75. OS << " <key>diagnostics</key>\n";
  76. OS << " <array>\n";
  77. for (unsigned i = 0, e = Entries.size(); i != e; ++i) {
  78. DiagEntry &DE = Entries[i];
  79. OS << " <dict>\n";
  80. OS << " <key>level</key>\n"
  81. << " <string>";
  82. emitString(OS, getLevelName(DE.DiagnosticLevel));
  83. OS << "</string>\n";
  84. if (!DE.Filename.empty()) {
  85. OS << " <key>filename</key>\n"
  86. << " <string>";
  87. emitString(OS, DE.Filename);
  88. OS << "</string>\n";
  89. }
  90. if (DE.Line != 0) {
  91. OS << " <key>line</key>\n"
  92. << " <integer>" << DE.Line << "</integer>\n";
  93. }
  94. if (DE.Column != 0) {
  95. OS << " <key>column</key>\n"
  96. << " <integer>" << DE.Column << "</integer>\n";
  97. }
  98. if (!DE.Message.empty()) {
  99. OS << " <key>message</key>\n"
  100. << " <string>";
  101. emitString(OS, DE.Message);
  102. OS << "</string>\n";
  103. }
  104. OS << " </dict>\n";
  105. }
  106. OS << " </array>\n";
  107. OS << "</dict>\n";
  108. this->OS << OS.str();
  109. }
  110. void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
  111. const Diagnostic &Info) {
  112. // Default implementation (Warnings/errors count).
  113. DiagnosticConsumer::HandleDiagnostic(Level, Info);
  114. // Initialize the main file name, if we haven't already fetched it.
  115. if (MainFilename.empty() && Info.hasSourceManager()) {
  116. const SourceManager &SM = Info.getSourceManager();
  117. FileID FID = SM.getMainFileID();
  118. if (!FID.isInvalid()) {
  119. const FileEntry *FE = SM.getFileEntryForID(FID);
  120. if (FE && FE->getName())
  121. MainFilename = FE->getName();
  122. }
  123. }
  124. // Create the diag entry.
  125. DiagEntry DE;
  126. DE.DiagnosticID = Info.getID();
  127. DE.DiagnosticLevel = Level;
  128. // Format the message.
  129. SmallString<100> MessageStr;
  130. Info.FormatDiagnostic(MessageStr);
  131. DE.Message = MessageStr.str();
  132. // Set the location information.
  133. DE.Filename = "";
  134. DE.Line = DE.Column = 0;
  135. if (Info.getLocation().isValid() && Info.hasSourceManager()) {
  136. const SourceManager &SM = Info.getSourceManager();
  137. PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation());
  138. if (PLoc.isInvalid()) {
  139. // At least print the file name if available:
  140. FileID FID = SM.getFileID(Info.getLocation());
  141. if (!FID.isInvalid()) {
  142. const FileEntry *FE = SM.getFileEntryForID(FID);
  143. if (FE && FE->getName())
  144. DE.Filename = FE->getName();
  145. }
  146. } else {
  147. DE.Filename = PLoc.getFilename();
  148. DE.Line = PLoc.getLine();
  149. DE.Column = PLoc.getColumn();
  150. }
  151. }
  152. // Record the diagnostic entry.
  153. Entries.push_back(DE);
  154. }
  155. DiagnosticConsumer *
  156. LogDiagnosticPrinter::clone(DiagnosticsEngine &Diags) const {
  157. return new LogDiagnosticPrinter(OS, *DiagOpts, /*OwnsOutputStream=*/false);
  158. }