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