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