xref: /freebsd/contrib/llvm-project/clang/lib/Frontend/LogDiagnosticPrinter.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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-exception
6 //
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"
15 using namespace clang;
16 using namespace markup;
17 
18 LogDiagnosticPrinter::LogDiagnosticPrinter(
19     raw_ostream &os, DiagnosticOptions &,
20     std::unique_ptr<raw_ostream> StreamOwner)
21     : OS(os), StreamOwner(std::move(StreamOwner)), LangOpts(nullptr) {}
22 
23 static 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 
35 void
36 LogDiagnosticPrinter::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 
73 void LogDiagnosticPrinter::EndSourceFile() {
74   // We emit all the diagnostics in EndSourceFile. However, we don't emit any
75   // entry if no diagnostics were present.
76   //
77   // Note that DiagnosticConsumer has no "end-of-compilation" callback, so we
78   // will miss any diagnostics which are emitted after and outside the
79   // 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 
108 void 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