161 lines · cpp
1//===--- LogDiagnosticPrinter.cpp - Log Diagnostic Printer ----------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Frontend/LogDiagnosticPrinter.h"10#include "clang/Basic/DiagnosticOptions.h"11#include "clang/Basic/PlistSupport.h"12#include "clang/Basic/SourceManager.h"13#include "llvm/Support/ErrorHandling.h"14#include "llvm/Support/raw_ostream.h"15using namespace clang;16using namespace markup;17 18LogDiagnosticPrinter::LogDiagnosticPrinter(19 raw_ostream &os, DiagnosticOptions &,20 std::unique_ptr<raw_ostream> StreamOwner)21 : OS(os), StreamOwner(std::move(StreamOwner)), LangOpts(nullptr) {}22 23static StringRef getLevelName(DiagnosticsEngine::Level Level) {24 switch (Level) {25 case DiagnosticsEngine::Ignored: return "ignored";26 case DiagnosticsEngine::Remark: return "remark";27 case DiagnosticsEngine::Note: return "note";28 case DiagnosticsEngine::Warning: return "warning";29 case DiagnosticsEngine::Error: return "error";30 case DiagnosticsEngine::Fatal: return "fatal error";31 }32 llvm_unreachable("Invalid DiagnosticsEngine level!");33}34 35void36LogDiagnosticPrinter::EmitDiagEntry(llvm::raw_ostream &OS,37 const LogDiagnosticPrinter::DiagEntry &DE) {38 OS << " <dict>\n";39 OS << " <key>level</key>\n"40 << " ";41 EmitString(OS, getLevelName(DE.DiagnosticLevel)) << '\n';42 if (!DE.Filename.empty()) {43 OS << " <key>filename</key>\n"44 << " ";45 EmitString(OS, DE.Filename) << '\n';46 }47 if (DE.Line != 0) {48 OS << " <key>line</key>\n"49 << " ";50 EmitInteger(OS, DE.Line) << '\n';51 }52 if (DE.Column != 0) {53 OS << " <key>column</key>\n"54 << " ";55 EmitInteger(OS, DE.Column) << '\n';56 }57 if (!DE.Message.empty()) {58 OS << " <key>message</key>\n"59 << " ";60 EmitString(OS, DE.Message) << '\n';61 }62 OS << " <key>ID</key>\n"63 << " ";64 EmitInteger(OS, DE.DiagnosticID) << '\n';65 if (!DE.WarningOption.empty()) {66 OS << " <key>WarningOption</key>\n"67 << " ";68 EmitString(OS, DE.WarningOption) << '\n';69 }70 OS << " </dict>\n";71}72 73void LogDiagnosticPrinter::EndSourceFile() {74 // We emit all the diagnostics in EndSourceFile. However, we don't emit any75 // entry if no diagnostics were present.76 //77 // Note that DiagnosticConsumer has no "end-of-compilation" callback, so we78 // will miss any diagnostics which are emitted after and outside the79 // translation unit processing.80 if (Entries.empty())81 return;82 83 // Write to a temporary string to ensure atomic write of diagnostic object.84 SmallString<512> Msg;85 llvm::raw_svector_ostream OS(Msg);86 87 OS << "<dict>\n";88 if (!MainFilename.empty()) {89 OS << " <key>main-file</key>\n"90 << " ";91 EmitString(OS, MainFilename) << '\n';92 }93 if (!DwarfDebugFlags.empty()) {94 OS << " <key>dwarf-debug-flags</key>\n"95 << " ";96 EmitString(OS, DwarfDebugFlags) << '\n';97 }98 OS << " <key>diagnostics</key>\n";99 OS << " <array>\n";100 for (auto &DE : Entries)101 EmitDiagEntry(OS, DE);102 OS << " </array>\n";103 OS << "</dict>\n";104 105 this->OS << OS.str();106}107 108void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,109 const Diagnostic &Info) {110 // Default implementation (Warnings/errors count).111 DiagnosticConsumer::HandleDiagnostic(Level, Info);112 113 // Initialize the main file name, if we haven't already fetched it.114 if (MainFilename.empty() && Info.hasSourceManager()) {115 const SourceManager &SM = Info.getSourceManager();116 FileID FID = SM.getMainFileID();117 if (FID.isValid()) {118 if (OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID))119 MainFilename = std::string(FE->getName());120 }121 }122 123 // Create the diag entry.124 DiagEntry DE;125 DE.DiagnosticID = Info.getID();126 DE.DiagnosticLevel = Level;127 128 DE.WarningOption =129 std::string(Info.getDiags()->getDiagnosticIDs()->getWarningOptionForDiag(130 DE.DiagnosticID));131 132 // Format the message.133 SmallString<100> MessageStr;134 Info.FormatDiagnostic(MessageStr);135 DE.Message = std::string(MessageStr);136 137 // Set the location information.138 DE.Filename = "";139 DE.Line = DE.Column = 0;140 if (Info.getLocation().isValid() && Info.hasSourceManager()) {141 const SourceManager &SM = Info.getSourceManager();142 PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation());143 144 if (PLoc.isInvalid()) {145 // At least print the file name if available:146 FileID FID = SM.getFileID(Info.getLocation());147 if (FID.isValid()) {148 if (OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID))149 DE.Filename = std::string(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