LogDiagnosticPrinter.cpp revision f7ccbad5d9949e7ddd1cbef43d482553b811e026
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" 16using namespace clang; 17 18LogDiagnosticPrinter::LogDiagnosticPrinter(raw_ostream &os, 19 const DiagnosticOptions &diags, 20 bool _OwnsOutputStream) 21 : OS(os), LangOpts(0), DiagOpts(&diags), 22 OwnsOutputStream(_OwnsOutputStream) { 23} 24 25LogDiagnosticPrinter::~LogDiagnosticPrinter() { 26 if (OwnsOutputStream) 27 delete &OS; 28} 29 30static 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. 42static 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 << "&"; break; 48 case '<': OS << "<"; break; 49 case '>': OS << ">"; break; 50 case '\'': OS << "'"; break; 51 case '\"': OS << """; break; 52 } 53 } 54} 55 56void 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 121void 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 173DiagnosticConsumer * 174LogDiagnosticPrinter::clone(DiagnosticsEngine &Diags) const { 175 return new LogDiagnosticPrinter(OS, *DiagOpts, /*OwnsOutputStream=*/false); 176} 177 178