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