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