xref: /aosp_15_r20/external/clang/lib/Frontend/TextDiagnosticPrinter.cpp (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li //===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===//
2*67e74705SXin Li //
3*67e74705SXin Li //                     The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li //
10*67e74705SXin Li // This diagnostic client prints out their diagnostic messages.
11*67e74705SXin Li //
12*67e74705SXin Li //===----------------------------------------------------------------------===//
13*67e74705SXin Li 
14*67e74705SXin Li #include "clang/Frontend/TextDiagnosticPrinter.h"
15*67e74705SXin Li #include "clang/Basic/DiagnosticOptions.h"
16*67e74705SXin Li #include "clang/Basic/FileManager.h"
17*67e74705SXin Li #include "clang/Basic/SourceManager.h"
18*67e74705SXin Li #include "clang/Frontend/TextDiagnostic.h"
19*67e74705SXin Li #include "clang/Lex/Lexer.h"
20*67e74705SXin Li #include "llvm/ADT/SmallString.h"
21*67e74705SXin Li #include "llvm/Support/ErrorHandling.h"
22*67e74705SXin Li #include "llvm/Support/MemoryBuffer.h"
23*67e74705SXin Li #include "llvm/Support/raw_ostream.h"
24*67e74705SXin Li #include <algorithm>
25*67e74705SXin Li using namespace clang;
26*67e74705SXin Li 
TextDiagnosticPrinter(raw_ostream & os,DiagnosticOptions * diags,bool _OwnsOutputStream)27*67e74705SXin Li TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os,
28*67e74705SXin Li                                              DiagnosticOptions *diags,
29*67e74705SXin Li                                              bool _OwnsOutputStream)
30*67e74705SXin Li   : OS(os), DiagOpts(diags),
31*67e74705SXin Li     OwnsOutputStream(_OwnsOutputStream) {
32*67e74705SXin Li }
33*67e74705SXin Li 
~TextDiagnosticPrinter()34*67e74705SXin Li TextDiagnosticPrinter::~TextDiagnosticPrinter() {
35*67e74705SXin Li   if (OwnsOutputStream)
36*67e74705SXin Li     delete &OS;
37*67e74705SXin Li }
38*67e74705SXin Li 
BeginSourceFile(const LangOptions & LO,const Preprocessor * PP)39*67e74705SXin Li void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO,
40*67e74705SXin Li                                             const Preprocessor *PP) {
41*67e74705SXin Li   // Build the TextDiagnostic utility.
42*67e74705SXin Li   TextDiag.reset(new TextDiagnostic(OS, LO, &*DiagOpts));
43*67e74705SXin Li }
44*67e74705SXin Li 
EndSourceFile()45*67e74705SXin Li void TextDiagnosticPrinter::EndSourceFile() {
46*67e74705SXin Li   TextDiag.reset();
47*67e74705SXin Li }
48*67e74705SXin Li 
49*67e74705SXin Li /// \brief Print any diagnostic option information to a raw_ostream.
50*67e74705SXin Li ///
51*67e74705SXin Li /// This implements all of the logic for adding diagnostic options to a message
52*67e74705SXin Li /// (via OS). Each relevant option is comma separated and all are enclosed in
53*67e74705SXin Li /// the standard bracketing: " [...]".
printDiagnosticOptions(raw_ostream & OS,DiagnosticsEngine::Level Level,const Diagnostic & Info,const DiagnosticOptions & DiagOpts)54*67e74705SXin Li static void printDiagnosticOptions(raw_ostream &OS,
55*67e74705SXin Li                                    DiagnosticsEngine::Level Level,
56*67e74705SXin Li                                    const Diagnostic &Info,
57*67e74705SXin Li                                    const DiagnosticOptions &DiagOpts) {
58*67e74705SXin Li   bool Started = false;
59*67e74705SXin Li   if (DiagOpts.ShowOptionNames) {
60*67e74705SXin Li     // Handle special cases for non-warnings early.
61*67e74705SXin Li     if (Info.getID() == diag::fatal_too_many_errors) {
62*67e74705SXin Li       OS << " [-ferror-limit=]";
63*67e74705SXin Li       return;
64*67e74705SXin Li     }
65*67e74705SXin Li 
66*67e74705SXin Li     // The code below is somewhat fragile because we are essentially trying to
67*67e74705SXin Li     // report to the user what happened by inferring what the diagnostic engine
68*67e74705SXin Li     // did. Eventually it might make more sense to have the diagnostic engine
69*67e74705SXin Li     // include some "why" information in the diagnostic.
70*67e74705SXin Li 
71*67e74705SXin Li     // If this is a warning which has been mapped to an error by the user (as
72*67e74705SXin Li     // inferred by checking whether the default mapping is to an error) then
73*67e74705SXin Li     // flag it as such. Note that diagnostics could also have been mapped by a
74*67e74705SXin Li     // pragma, but we don't currently have a way to distinguish this.
75*67e74705SXin Li     if (Level == DiagnosticsEngine::Error &&
76*67e74705SXin Li         DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) &&
77*67e74705SXin Li         !DiagnosticIDs::isDefaultMappingAsError(Info.getID())) {
78*67e74705SXin Li       OS << " [-Werror";
79*67e74705SXin Li       Started = true;
80*67e74705SXin Li     }
81*67e74705SXin Li 
82*67e74705SXin Li     StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
83*67e74705SXin Li     if (!Opt.empty()) {
84*67e74705SXin Li       OS << (Started ? "," : " [")
85*67e74705SXin Li          << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt;
86*67e74705SXin Li       StringRef OptValue = Info.getDiags()->getFlagValue();
87*67e74705SXin Li       if (!OptValue.empty())
88*67e74705SXin Li         OS << "=" << OptValue;
89*67e74705SXin Li       Started = true;
90*67e74705SXin Li     }
91*67e74705SXin Li   }
92*67e74705SXin Li 
93*67e74705SXin Li   // If the user wants to see category information, include it too.
94*67e74705SXin Li   if (DiagOpts.ShowCategories) {
95*67e74705SXin Li     unsigned DiagCategory =
96*67e74705SXin Li       DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
97*67e74705SXin Li     if (DiagCategory) {
98*67e74705SXin Li       OS << (Started ? "," : " [");
99*67e74705SXin Li       Started = true;
100*67e74705SXin Li       if (DiagOpts.ShowCategories == 1)
101*67e74705SXin Li         OS << DiagCategory;
102*67e74705SXin Li       else {
103*67e74705SXin Li         assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value");
104*67e74705SXin Li         OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory);
105*67e74705SXin Li       }
106*67e74705SXin Li     }
107*67e74705SXin Li   }
108*67e74705SXin Li   if (Started)
109*67e74705SXin Li     OS << ']';
110*67e74705SXin Li }
111*67e74705SXin Li 
HandleDiagnostic(DiagnosticsEngine::Level Level,const Diagnostic & Info)112*67e74705SXin Li void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
113*67e74705SXin Li                                              const Diagnostic &Info) {
114*67e74705SXin Li   // Default implementation (Warnings/errors count).
115*67e74705SXin Li   DiagnosticConsumer::HandleDiagnostic(Level, Info);
116*67e74705SXin Li 
117*67e74705SXin Li   // Render the diagnostic message into a temporary buffer eagerly. We'll use
118*67e74705SXin Li   // this later as we print out the diagnostic to the terminal.
119*67e74705SXin Li   SmallString<100> OutStr;
120*67e74705SXin Li   Info.FormatDiagnostic(OutStr);
121*67e74705SXin Li 
122*67e74705SXin Li   llvm::raw_svector_ostream DiagMessageStream(OutStr);
123*67e74705SXin Li   printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts);
124*67e74705SXin Li 
125*67e74705SXin Li   // Keeps track of the starting position of the location
126*67e74705SXin Li   // information (e.g., "foo.c:10:4:") that precedes the error
127*67e74705SXin Li   // message. We use this information to determine how long the
128*67e74705SXin Li   // file+line+column number prefix is.
129*67e74705SXin Li   uint64_t StartOfLocationInfo = OS.tell();
130*67e74705SXin Li 
131*67e74705SXin Li   if (!Prefix.empty())
132*67e74705SXin Li     OS << Prefix << ": ";
133*67e74705SXin Li 
134*67e74705SXin Li   // Use a dedicated, simpler path for diagnostics without a valid location.
135*67e74705SXin Li   // This is important as if the location is missing, we may be emitting
136*67e74705SXin Li   // diagnostics in a context that lacks language options, a source manager, or
137*67e74705SXin Li   // other infrastructure necessary when emitting more rich diagnostics.
138*67e74705SXin Li   if (!Info.getLocation().isValid()) {
139*67e74705SXin Li     TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors,
140*67e74705SXin Li                                          DiagOpts->CLFallbackMode);
141*67e74705SXin Li     TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(),
142*67e74705SXin Li                                            OS.tell() - StartOfLocationInfo,
143*67e74705SXin Li                                            DiagOpts->MessageLength,
144*67e74705SXin Li                                            DiagOpts->ShowColors);
145*67e74705SXin Li     OS.flush();
146*67e74705SXin Li     return;
147*67e74705SXin Li   }
148*67e74705SXin Li 
149*67e74705SXin Li   // Assert that the rest of our infrastructure is setup properly.
150*67e74705SXin Li   assert(DiagOpts && "Unexpected diagnostic without options set");
151*67e74705SXin Li   assert(Info.hasSourceManager() &&
152*67e74705SXin Li          "Unexpected diagnostic with no source manager");
153*67e74705SXin Li   assert(TextDiag && "Unexpected diagnostic outside source file processing");
154*67e74705SXin Li 
155*67e74705SXin Li   TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(),
156*67e74705SXin Li                            Info.getRanges(),
157*67e74705SXin Li                            Info.getFixItHints(),
158*67e74705SXin Li                            &Info.getSourceManager());
159*67e74705SXin Li 
160*67e74705SXin Li   OS.flush();
161*67e74705SXin Li }
162