clang API Documentation
00001 //===--- LogDiagnosticPrinter.cpp - Log Diagnostic Printer ----------------===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 00010 #include "clang/Frontend/LogDiagnosticPrinter.h" 00011 #include "clang/Basic/FileManager.h" 00012 #include "clang/Basic/SourceManager.h" 00013 #include "llvm/ADT/SmallString.h" 00014 #include "llvm/Support/raw_ostream.h" 00015 #include "llvm/Support/ErrorHandling.h" 00016 using namespace clang; 00017 00018 LogDiagnosticPrinter::LogDiagnosticPrinter(raw_ostream &os, 00019 const DiagnosticOptions &diags, 00020 bool _OwnsOutputStream) 00021 : OS(os), LangOpts(0), DiagOpts(&diags), 00022 OwnsOutputStream(_OwnsOutputStream) { 00023 } 00024 00025 LogDiagnosticPrinter::~LogDiagnosticPrinter() { 00026 if (OwnsOutputStream) 00027 delete &OS; 00028 } 00029 00030 static StringRef getLevelName(DiagnosticsEngine::Level Level) { 00031 switch (Level) { 00032 case DiagnosticsEngine::Ignored: return "ignored"; 00033 case DiagnosticsEngine::Note: return "note"; 00034 case DiagnosticsEngine::Warning: return "warning"; 00035 case DiagnosticsEngine::Error: return "error"; 00036 case DiagnosticsEngine::Fatal: return "fatal error"; 00037 } 00038 llvm_unreachable("Invalid DiagnosticsEngine level!"); 00039 } 00040 00041 // Escape XML characters inside the raw string. 00042 static void emitString(llvm::raw_svector_ostream &OS, const StringRef Raw) { 00043 for (StringRef::iterator I = Raw.begin(), E = Raw.end(); I != E; ++I) { 00044 char c = *I; 00045 switch (c) { 00046 default: OS << c; break; 00047 case '&': OS << "&"; break; 00048 case '<': OS << "<"; break; 00049 case '>': OS << ">"; break; 00050 case '\'': OS << "'"; break; 00051 case '\"': OS << """; break; 00052 } 00053 } 00054 } 00055 00056 void LogDiagnosticPrinter::EndSourceFile() { 00057 // We emit all the diagnostics in EndSourceFile. However, we don't emit any 00058 // entry if no diagnostics were present. 00059 // 00060 // Note that DiagnosticConsumer has no "end-of-compilation" callback, so we 00061 // will miss any diagnostics which are emitted after and outside the 00062 // translation unit processing. 00063 if (Entries.empty()) 00064 return; 00065 00066 // Write to a temporary string to ensure atomic write of diagnostic object. 00067 SmallString<512> Msg; 00068 llvm::raw_svector_ostream OS(Msg); 00069 00070 OS << "<dict>\n"; 00071 if (!MainFilename.empty()) { 00072 OS << " <key>main-file</key>\n" 00073 << " <string>"; 00074 emitString(OS, MainFilename); 00075 OS << "</string>\n"; 00076 } 00077 if (!DwarfDebugFlags.empty()) { 00078 OS << " <key>dwarf-debug-flags</key>\n" 00079 << " <string>"; 00080 emitString(OS, DwarfDebugFlags); 00081 OS << "</string>\n"; 00082 } 00083 OS << " <key>diagnostics</key>\n"; 00084 OS << " <array>\n"; 00085 for (unsigned i = 0, e = Entries.size(); i != e; ++i) { 00086 DiagEntry &DE = Entries[i]; 00087 00088 OS << " <dict>\n"; 00089 OS << " <key>level</key>\n" 00090 << " <string>"; 00091 emitString(OS, getLevelName(DE.DiagnosticLevel)); 00092 OS << "</string>\n"; 00093 if (!DE.Filename.empty()) { 00094 OS << " <key>filename</key>\n" 00095 << " <string>"; 00096 emitString(OS, DE.Filename); 00097 OS << "</string>\n"; 00098 } 00099 if (DE.Line != 0) { 00100 OS << " <key>line</key>\n" 00101 << " <integer>" << DE.Line << "</integer>\n"; 00102 } 00103 if (DE.Column != 0) { 00104 OS << " <key>column</key>\n" 00105 << " <integer>" << DE.Column << "</integer>\n"; 00106 } 00107 if (!DE.Message.empty()) { 00108 OS << " <key>message</key>\n" 00109 << " <string>"; 00110 emitString(OS, DE.Message); 00111 OS << "</string>\n"; 00112 } 00113 OS << " </dict>\n"; 00114 } 00115 OS << " </array>\n"; 00116 OS << "</dict>\n"; 00117 00118 this->OS << OS.str(); 00119 } 00120 00121 void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, 00122 const Diagnostic &Info) { 00123 // Default implementation (Warnings/errors count). 00124 DiagnosticConsumer::HandleDiagnostic(Level, Info); 00125 00126 // Initialize the main file name, if we haven't already fetched it. 00127 if (MainFilename.empty() && Info.hasSourceManager()) { 00128 const SourceManager &SM = Info.getSourceManager(); 00129 FileID FID = SM.getMainFileID(); 00130 if (!FID.isInvalid()) { 00131 const FileEntry *FE = SM.getFileEntryForID(FID); 00132 if (FE && FE->getName()) 00133 MainFilename = FE->getName(); 00134 } 00135 } 00136 00137 // Create the diag entry. 00138 DiagEntry DE; 00139 DE.DiagnosticID = Info.getID(); 00140 DE.DiagnosticLevel = Level; 00141 00142 // Format the message. 00143 SmallString<100> MessageStr; 00144 Info.FormatDiagnostic(MessageStr); 00145 DE.Message = MessageStr.str(); 00146 00147 // Set the location information. 00148 DE.Filename = ""; 00149 DE.Line = DE.Column = 0; 00150 if (Info.getLocation().isValid() && Info.hasSourceManager()) { 00151 const SourceManager &SM = Info.getSourceManager(); 00152 PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation()); 00153 00154 if (PLoc.isInvalid()) { 00155 // At least print the file name if available: 00156 FileID FID = SM.getFileID(Info.getLocation()); 00157 if (!FID.isInvalid()) { 00158 const FileEntry *FE = SM.getFileEntryForID(FID); 00159 if (FE && FE->getName()) 00160 DE.Filename = FE->getName(); 00161 } 00162 } else { 00163 DE.Filename = PLoc.getFilename(); 00164 DE.Line = PLoc.getLine(); 00165 DE.Column = PLoc.getColumn(); 00166 } 00167 } 00168 00169 // Record the diagnostic entry. 00170 Entries.push_back(DE); 00171 } 00172 00173 DiagnosticConsumer * 00174 LogDiagnosticPrinter::clone(DiagnosticsEngine &Diags) const { 00175 return new LogDiagnosticPrinter(OS, *DiagOpts, /*OwnsOutputStream=*/false); 00176 } 00177