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