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