diff options
Diffstat (limited to 'clang/lib/Frontend/LogDiagnosticPrinter.cpp')
-rw-r--r-- | clang/lib/Frontend/LogDiagnosticPrinter.cpp | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/clang/lib/Frontend/LogDiagnosticPrinter.cpp b/clang/lib/Frontend/LogDiagnosticPrinter.cpp new file mode 100644 index 0000000..3fee957 --- /dev/null +++ b/clang/lib/Frontend/LogDiagnosticPrinter.cpp @@ -0,0 +1,177 @@ +//===--- LogDiagnosticPrinter.cpp - Log Diagnostic Printer ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/LogDiagnosticPrinter.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/ErrorHandling.h" +using namespace clang; + +LogDiagnosticPrinter::LogDiagnosticPrinter(raw_ostream &os, + const DiagnosticOptions &diags, + bool _OwnsOutputStream) + : OS(os), LangOpts(0), DiagOpts(&diags), + OwnsOutputStream(_OwnsOutputStream) { +} + +LogDiagnosticPrinter::~LogDiagnosticPrinter() { + if (OwnsOutputStream) + delete &OS; +} + +static StringRef getLevelName(DiagnosticsEngine::Level Level) { + switch (Level) { + case DiagnosticsEngine::Ignored: return "ignored"; + case DiagnosticsEngine::Note: return "note"; + case DiagnosticsEngine::Warning: return "warning"; + case DiagnosticsEngine::Error: return "error"; + case DiagnosticsEngine::Fatal: return "fatal error"; + } + llvm_unreachable("Invalid DiagnosticsEngine level!"); +} + +// Escape XML characters inside the raw string. +static void emitString(llvm::raw_svector_ostream &OS, const StringRef Raw) { + for (StringRef::iterator I = Raw.begin(), E = Raw.end(); I != E; ++I) { + char c = *I; + switch (c) { + default: OS << c; break; + case '&': OS << "&"; break; + case '<': OS << "<"; break; + case '>': OS << ">"; break; + case '\'': OS << "'"; break; + case '\"': OS << """; break; + } + } +} + +void LogDiagnosticPrinter::EndSourceFile() { + // We emit all the diagnostics in EndSourceFile. However, we don't emit any + // entry if no diagnostics were present. + // + // Note that DiagnosticConsumer has no "end-of-compilation" callback, so we + // will miss any diagnostics which are emitted after and outside the + // translation unit processing. + if (Entries.empty()) + return; + + // Write to a temporary string to ensure atomic write of diagnostic object. + SmallString<512> Msg; + llvm::raw_svector_ostream OS(Msg); + + OS << "<dict>\n"; + if (!MainFilename.empty()) { + OS << " <key>main-file</key>\n" + << " <string>"; + emitString(OS, MainFilename); + OS << "</string>\n"; + } + if (!DwarfDebugFlags.empty()) { + OS << " <key>dwarf-debug-flags</key>\n" + << " <string>"; + emitString(OS, DwarfDebugFlags); + OS << "</string>\n"; + } + OS << " <key>diagnostics</key>\n"; + OS << " <array>\n"; + for (unsigned i = 0, e = Entries.size(); i != e; ++i) { + DiagEntry &DE = Entries[i]; + + OS << " <dict>\n"; + OS << " <key>level</key>\n" + << " <string>"; + emitString(OS, getLevelName(DE.DiagnosticLevel)); + OS << "</string>\n"; + if (!DE.Filename.empty()) { + OS << " <key>filename</key>\n" + << " <string>"; + emitString(OS, DE.Filename); + OS << "</string>\n"; + } + if (DE.Line != 0) { + OS << " <key>line</key>\n" + << " <integer>" << DE.Line << "</integer>\n"; + } + if (DE.Column != 0) { + OS << " <key>column</key>\n" + << " <integer>" << DE.Column << "</integer>\n"; + } + if (!DE.Message.empty()) { + OS << " <key>message</key>\n" + << " <string>"; + emitString(OS, DE.Message); + OS << "</string>\n"; + } + OS << " </dict>\n"; + } + OS << " </array>\n"; + OS << "</dict>\n"; + + this->OS << OS.str(); +} + +void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(Level, Info); + + // Initialize the main file name, if we haven't already fetched it. + if (MainFilename.empty() && Info.hasSourceManager()) { + const SourceManager &SM = Info.getSourceManager(); + FileID FID = SM.getMainFileID(); + if (!FID.isInvalid()) { + const FileEntry *FE = SM.getFileEntryForID(FID); + if (FE && FE->getName()) + MainFilename = FE->getName(); + } + } + + // Create the diag entry. + DiagEntry DE; + DE.DiagnosticID = Info.getID(); + DE.DiagnosticLevel = Level; + + // Format the message. + SmallString<100> MessageStr; + Info.FormatDiagnostic(MessageStr); + DE.Message = MessageStr.str(); + + // Set the location information. + DE.Filename = ""; + DE.Line = DE.Column = 0; + if (Info.getLocation().isValid() && Info.hasSourceManager()) { + const SourceManager &SM = Info.getSourceManager(); + PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation()); + + if (PLoc.isInvalid()) { + // At least print the file name if available: + FileID FID = SM.getFileID(Info.getLocation()); + if (!FID.isInvalid()) { + const FileEntry *FE = SM.getFileEntryForID(FID); + if (FE && FE->getName()) + DE.Filename = FE->getName(); + } + } else { + DE.Filename = PLoc.getFilename(); + DE.Line = PLoc.getLine(); + DE.Column = PLoc.getColumn(); + } + } + + // Record the diagnostic entry. + Entries.push_back(DE); +} + +DiagnosticConsumer * +LogDiagnosticPrinter::clone(DiagnosticsEngine &Diags) const { + return new LogDiagnosticPrinter(OS, *DiagOpts, /*OwnsOutputStream=*/false); +} + |