19df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar//===--- LogDiagnosticPrinter.cpp - Log Diagnostic Printer ----------------===//
29df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar//
39df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar//                     The LLVM Compiler Infrastructure
49df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar//
59df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar// This file is distributed under the University of Illinois Open Source
69df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar// License. See LICENSE.TXT for details.
79df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar//
89df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar//===----------------------------------------------------------------------===//
99df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar
109df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar#include "clang/Frontend/LogDiagnosticPrinter.h"
1164bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar#include "clang/Basic/FileManager.h"
1264bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar#include "clang/Basic/SourceManager.h"
139df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar#include "llvm/ADT/SmallString.h"
149df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar#include "llvm/Support/raw_ostream.h"
15561d3abc881033776ece385a01a510e1cbc1fa92David Blaikie#include "llvm/Support/ErrorHandling.h"
169df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbarusing namespace clang;
179df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar
185f9e272e632e951b1efe824cd16acb4d96077930Chris LattnerLogDiagnosticPrinter::LogDiagnosticPrinter(raw_ostream &os,
199df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar                                           const DiagnosticOptions &diags,
209df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar                                           bool _OwnsOutputStream)
219df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar  : OS(os), LangOpts(0), DiagOpts(&diags),
229df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar    OwnsOutputStream(_OwnsOutputStream) {
239df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar}
249df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar
259df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel DunbarLogDiagnosticPrinter::~LogDiagnosticPrinter() {
269df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar  if (OwnsOutputStream)
279df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar    delete &OS;
289df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar}
299df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar
30d6471f7c1921c7802804ce3ff6fe9768310f72b9David Blaikiestatic StringRef getLevelName(DiagnosticsEngine::Level Level) {
3164bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  switch (Level) {
32d6471f7c1921c7802804ce3ff6fe9768310f72b9David Blaikie  case DiagnosticsEngine::Ignored: return "ignored";
33d6471f7c1921c7802804ce3ff6fe9768310f72b9David Blaikie  case DiagnosticsEngine::Note:    return "note";
34d6471f7c1921c7802804ce3ff6fe9768310f72b9David Blaikie  case DiagnosticsEngine::Warning: return "warning";
35d6471f7c1921c7802804ce3ff6fe9768310f72b9David Blaikie  case DiagnosticsEngine::Error:   return "error";
36d6471f7c1921c7802804ce3ff6fe9768310f72b9David Blaikie  case DiagnosticsEngine::Fatal:   return "fatal error";
3764bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  }
38561d3abc881033776ece385a01a510e1cbc1fa92David Blaikie  llvm_unreachable("Invalid DiagnosticsEngine level!");
3964bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar}
4064bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar
41cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier// Escape XML characters inside the raw string.
42cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosierstatic void emitString(llvm::raw_svector_ostream &OS, const StringRef Raw) {
43cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier  for (StringRef::iterator I = Raw.begin(), E = Raw.end(); I != E; ++I) {
44cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    char c = *I;
45cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    switch (c) {
46cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    default:   OS << c; break;
47cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    case '&':  OS << "&amp;"; break;
48cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    case '<':  OS << "&lt;"; break;
49cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    case '>':  OS << "&gt;"; break;
50cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    case '\'': OS << "&apos;"; break;
51cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    case '\"': OS << "&quot;"; break;
52cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    }
53cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier  }
54cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier}
55cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier
5664bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbarvoid LogDiagnosticPrinter::EndSourceFile() {
5764bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  // We emit all the diagnostics in EndSourceFile. However, we don't emit any
5864bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  // entry if no diagnostics were present.
5964bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  //
6078ad0b98848c17a0a11847fa1d456e2dfec8aa2fDavid Blaikie  // Note that DiagnosticConsumer has no "end-of-compilation" callback, so we
6178ad0b98848c17a0a11847fa1d456e2dfec8aa2fDavid Blaikie  // will miss any diagnostics which are emitted after and outside the
6278ad0b98848c17a0a11847fa1d456e2dfec8aa2fDavid Blaikie  // translation unit processing.
6364bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  if (Entries.empty())
6464bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar    return;
659df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar
669df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar  // Write to a temporary string to ensure atomic write of diagnostic object.
67f7ccbad5d9949e7ddd1cbef43d482553b811e026Dylan Noblesmith  SmallString<512> Msg;
689df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar  llvm::raw_svector_ostream OS(Msg);
699df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar
705dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar  OS << "<dict>\n";
7128f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar  if (!MainFilename.empty()) {
7228f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar    OS << "  <key>main-file</key>\n"
73cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier       << "  <string>";
74cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    emitString(OS, MainFilename);
75cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    OS << "</string>\n";
7628f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar  }
7728f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar  if (!DwarfDebugFlags.empty()) {
7828f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar    OS << "  <key>dwarf-debug-flags</key>\n"
79cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier       << "  <string>";
80cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    emitString(OS, DwarfDebugFlags);
81cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    OS << "</string>\n";
8228f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar  }
835dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar  OS << "  <key>diagnostics</key>\n";
845dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar  OS << "  <array>\n";
8564bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  for (unsigned i = 0, e = Entries.size(); i != e; ++i) {
8664bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar    DiagEntry &DE = Entries[i];
8764bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar
885dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar    OS << "    <dict>\n";
895dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar    OS << "      <key>level</key>\n"
90cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier       << "      <string>";
91cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    emitString(OS, getLevelName(DE.DiagnosticLevel));
92cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier    OS << "</string>\n";
935dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar    if (!DE.Filename.empty()) {
945dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar      OS << "      <key>filename</key>\n"
95cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier         << "      <string>";
96cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier      emitString(OS, DE.Filename);
97cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier      OS << "</string>\n";
985dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar    }
995dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar    if (DE.Line != 0) {
1005dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar      OS << "      <key>line</key>\n"
1015dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar         << "      <integer>" << DE.Line << "</integer>\n";
1025dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar    }
1035dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar    if (DE.Column != 0) {
1045dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar      OS << "      <key>column</key>\n"
1055dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar         << "      <integer>" << DE.Column << "</integer>\n";
1065dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar    }
1075dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar    if (!DE.Message.empty()) {
1085dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar      OS << "      <key>message</key>\n"
109cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier         << "      <string>";
110cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier      emitString(OS, DE.Message);
111cc78c6c6152b6c30b069769b45716e0ae6519211Chad Rosier      OS << "</string>\n";
1125dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar    }
1135dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar    OS << "    </dict>\n";
11464bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  }
1155dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar  OS << "  </array>\n";
1165dccf575ad0b45a268d4026047234a6872440c95Daniel Dunbar  OS << "</dict>\n";
1179df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar
1189df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar  this->OS << OS.str();
1199df23493f5b8a223dfbc491e4b7de3850797c2e7Daniel Dunbar}
12064bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar
121d6471f7c1921c7802804ce3ff6fe9768310f72b9David Blaikievoid LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
12240847cfb58acc3cac7d68727df9455ac45f2e118David Blaikie                                            const Diagnostic &Info) {
12364bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  // Default implementation (Warnings/errors count).
12478ad0b98848c17a0a11847fa1d456e2dfec8aa2fDavid Blaikie  DiagnosticConsumer::HandleDiagnostic(Level, Info);
12564bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar
12628f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar  // Initialize the main file name, if we haven't already fetched it.
1277665ad83d8eff7b8b2c5f3b893b6b7ece38f847cDaniel Dunbar  if (MainFilename.empty() && Info.hasSourceManager()) {
12828f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar    const SourceManager &SM = Info.getSourceManager();
12928f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar    FileID FID = SM.getMainFileID();
13028f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar    if (!FID.isInvalid()) {
13128f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar      const FileEntry *FE = SM.getFileEntryForID(FID);
13228f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar      if (FE && FE->getName())
13328f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar        MainFilename = FE->getName();
13428f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar    }
13528f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar  }
13628f14933edc863821e4f2ffa3663835c62440dcbDaniel Dunbar
13764bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  // Create the diag entry.
13864bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  DiagEntry DE;
13964bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  DE.DiagnosticID = Info.getID();
14064bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  DE.DiagnosticLevel = Level;
14164bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar
14264bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  // Format the message.
143f7ccbad5d9949e7ddd1cbef43d482553b811e026Dylan Noblesmith  SmallString<100> MessageStr;
14464bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  Info.FormatDiagnostic(MessageStr);
14564bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  DE.Message = MessageStr.str();
14664bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar
14764bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  // Set the location information.
14864bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  DE.Filename = "";
14964bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  DE.Line = DE.Column = 0;
1507665ad83d8eff7b8b2c5f3b893b6b7ece38f847cDaniel Dunbar  if (Info.getLocation().isValid() && Info.hasSourceManager()) {
15164bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar    const SourceManager &SM = Info.getSourceManager();
15264bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar    PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation());
15364bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar
15464bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar    if (PLoc.isInvalid()) {
15564bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar      // At least print the file name if available:
15664bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar      FileID FID = SM.getFileID(Info.getLocation());
15764bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar      if (!FID.isInvalid()) {
15864bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar        const FileEntry *FE = SM.getFileEntryForID(FID);
15964bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar        if (FE && FE->getName())
16064bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar          DE.Filename = FE->getName();
16164bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar      }
16264bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar    } else {
16364bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar      DE.Filename = PLoc.getFilename();
16464bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar      DE.Line = PLoc.getLine();
16564bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar      DE.Column = PLoc.getColumn();
16664bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar    }
16764bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  }
16864bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar
16964bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  // Record the diagnostic entry.
17064bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar  Entries.push_back(DE);
17164bfbf573e625c04abc3d40faa9b695fe21ebdbbDaniel Dunbar}
172aee526e77657afd1600276450e9c346953ad51d7Douglas Gregor
173aee526e77657afd1600276450e9c346953ad51d7Douglas GregorDiagnosticConsumer *
174aee526e77657afd1600276450e9c346953ad51d7Douglas GregorLogDiagnosticPrinter::clone(DiagnosticsEngine &Diags) const {
175aee526e77657afd1600276450e9c346953ad51d7Douglas Gregor  return new LogDiagnosticPrinter(OS, *DiagOpts, /*OwnsOutputStream=*/false);
176aee526e77657afd1600276450e9c346953ad51d7Douglas Gregor}
177aee526e77657afd1600276450e9c346953ad51d7Douglas Gregor
178