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