/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Basic/Diagnostic.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- Diagnostic.cpp - C Language Family Diagnostic Handling -------------===// |
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 file implements the Diagnostic-related interfaces. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "clang/Basic/Diagnostic.h" |
14 | | #include "clang/Basic/CharInfo.h" |
15 | | #include "clang/Basic/DiagnosticError.h" |
16 | | #include "clang/Basic/DiagnosticIDs.h" |
17 | | #include "clang/Basic/DiagnosticOptions.h" |
18 | | #include "clang/Basic/IdentifierTable.h" |
19 | | #include "clang/Basic/PartialDiagnostic.h" |
20 | | #include "clang/Basic/SourceLocation.h" |
21 | | #include "clang/Basic/SourceManager.h" |
22 | | #include "clang/Basic/Specifiers.h" |
23 | | #include "clang/Basic/TokenKinds.h" |
24 | | #include "llvm/ADT/SmallString.h" |
25 | | #include "llvm/ADT/SmallVector.h" |
26 | | #include "llvm/ADT/StringExtras.h" |
27 | | #include "llvm/ADT/StringRef.h" |
28 | | #include "llvm/Support/CrashRecoveryContext.h" |
29 | | #include "llvm/Support/Locale.h" |
30 | | #include "llvm/Support/raw_ostream.h" |
31 | | #include <algorithm> |
32 | | #include <cassert> |
33 | | #include <cstddef> |
34 | | #include <cstdint> |
35 | | #include <cstring> |
36 | | #include <limits> |
37 | | #include <string> |
38 | | #include <utility> |
39 | | #include <vector> |
40 | | |
41 | | using namespace clang; |
42 | | |
43 | | const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB, |
44 | 144 | DiagNullabilityKind nullability) { |
45 | 144 | StringRef string; |
46 | 144 | switch (nullability.first) { |
47 | 87 | case NullabilityKind::NonNull: |
48 | 87 | string = nullability.second ? "'nonnull'"11 : "'_Nonnull'"76 ; |
49 | 87 | break; |
50 | | |
51 | 43 | case NullabilityKind::Nullable: |
52 | 43 | string = nullability.second ? "'nullable'"20 : "'_Nullable'"23 ; |
53 | 43 | break; |
54 | | |
55 | 7 | case NullabilityKind::Unspecified: |
56 | 7 | string = nullability.second ? "'null_unspecified'"4 : "'_Null_unspecified'"3 ; |
57 | 7 | break; |
58 | | |
59 | 7 | case NullabilityKind::NullableResult: |
60 | 7 | assert(!nullability.second && |
61 | 7 | "_Nullable_result isn't supported as context-sensitive keyword"); |
62 | 0 | string = "_Nullable_result"; |
63 | 7 | break; |
64 | 144 | } |
65 | | |
66 | 144 | DB.AddString(string); |
67 | 144 | return DB; |
68 | 144 | } |
69 | | |
70 | | const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB, |
71 | 6 | llvm::Error &&E) { |
72 | 6 | DB.AddString(toString(std::move(E))); |
73 | 6 | return DB; |
74 | 6 | } |
75 | | |
76 | | static void DummyArgToStringFn(DiagnosticsEngine::ArgumentKind AK, intptr_t QT, |
77 | | StringRef Modifier, StringRef Argument, |
78 | | ArrayRef<DiagnosticsEngine::ArgumentValue> PrevArgs, |
79 | | SmallVectorImpl<char> &Output, |
80 | | void *Cookie, |
81 | 0 | ArrayRef<intptr_t> QualTypeVals) { |
82 | 0 | StringRef Str = "<can't format argument>"; |
83 | 0 | Output.append(Str.begin(), Str.end()); |
84 | 0 | } |
85 | | |
86 | | DiagnosticsEngine::DiagnosticsEngine( |
87 | | IntrusiveRefCntPtr<DiagnosticIDs> diags, |
88 | | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts, DiagnosticConsumer *client, |
89 | | bool ShouldOwnClient) |
90 | 410k | : Diags(std::move(diags)), DiagOpts(std::move(DiagOpts)) { |
91 | 410k | setClient(client, ShouldOwnClient); |
92 | 410k | ArgToStringFn = DummyArgToStringFn; |
93 | | |
94 | 410k | Reset(); |
95 | 410k | } |
96 | | |
97 | 404k | DiagnosticsEngine::~DiagnosticsEngine() { |
98 | | // If we own the diagnostic client, destroy it first so that it can access the |
99 | | // engine from its destructor. |
100 | 404k | setClient(nullptr); |
101 | 404k | } |
102 | | |
103 | 0 | void DiagnosticsEngine::dump() const { |
104 | 0 | DiagStatesByLoc.dump(*SourceMgr); |
105 | 0 | } |
106 | | |
107 | 3 | void DiagnosticsEngine::dump(StringRef DiagName) const { |
108 | 3 | DiagStatesByLoc.dump(*SourceMgr, DiagName); |
109 | 3 | } |
110 | | |
111 | | void DiagnosticsEngine::setClient(DiagnosticConsumer *client, |
112 | 1.18M | bool ShouldOwnClient) { |
113 | 1.18M | Owner.reset(ShouldOwnClient ? client1.02M : nullptr155k ); |
114 | 1.18M | Client = client; |
115 | 1.18M | } |
116 | | |
117 | 61.8k | void DiagnosticsEngine::pushMappings(SourceLocation Loc) { |
118 | 61.8k | DiagStateOnPushStack.push_back(GetCurDiagState()); |
119 | 61.8k | } |
120 | | |
121 | 61.8k | bool DiagnosticsEngine::popMappings(SourceLocation Loc) { |
122 | 61.8k | if (DiagStateOnPushStack.empty()) |
123 | 2 | return false; |
124 | | |
125 | 61.8k | if (DiagStateOnPushStack.back() != GetCurDiagState()) { |
126 | | // State changed at some point between push/pop. |
127 | 61.7k | PushDiagStatePoint(DiagStateOnPushStack.back(), Loc); |
128 | 61.7k | } |
129 | 61.8k | DiagStateOnPushStack.pop_back(); |
130 | 61.8k | return true; |
131 | 61.8k | } |
132 | | |
133 | 419k | void DiagnosticsEngine::Reset() { |
134 | 419k | ErrorOccurred = false; |
135 | 419k | UncompilableErrorOccurred = false; |
136 | 419k | FatalErrorOccurred = false; |
137 | 419k | UnrecoverableErrorOccurred = false; |
138 | | |
139 | 419k | NumWarnings = 0; |
140 | 419k | NumErrors = 0; |
141 | 419k | TrapNumErrorsOccurred = 0; |
142 | 419k | TrapNumUnrecoverableErrorsOccurred = 0; |
143 | | |
144 | 419k | CurDiagID = std::numeric_limits<unsigned>::max(); |
145 | 419k | LastDiagLevel = DiagnosticIDs::Ignored; |
146 | 419k | DelayedDiagID = 0; |
147 | | |
148 | | // Clear state related to #pragma diagnostic. |
149 | 419k | DiagStates.clear(); |
150 | 419k | DiagStatesByLoc.clear(); |
151 | 419k | DiagStateOnPushStack.clear(); |
152 | | |
153 | | // Create a DiagState and DiagStatePoint representing diagnostic changes |
154 | | // through command-line. |
155 | 419k | DiagStates.emplace_back(); |
156 | 419k | DiagStatesByLoc.appendFirst(&DiagStates.back()); |
157 | 419k | } |
158 | | |
159 | | void DiagnosticsEngine::SetDelayedDiagnostic(unsigned DiagID, StringRef Arg1, |
160 | 11 | StringRef Arg2, StringRef Arg3) { |
161 | 11 | if (DelayedDiagID) |
162 | 0 | return; |
163 | | |
164 | 11 | DelayedDiagID = DiagID; |
165 | 11 | DelayedDiagArg1 = Arg1.str(); |
166 | 11 | DelayedDiagArg2 = Arg2.str(); |
167 | 11 | DelayedDiagArg3 = Arg3.str(); |
168 | 11 | } |
169 | | |
170 | 11 | void DiagnosticsEngine::ReportDelayed() { |
171 | 11 | unsigned ID = DelayedDiagID; |
172 | 11 | DelayedDiagID = 0; |
173 | 11 | Report(ID) << DelayedDiagArg1 << DelayedDiagArg2 << DelayedDiagArg3; |
174 | 11 | } |
175 | | |
176 | 419k | void DiagnosticsEngine::DiagStateMap::appendFirst(DiagState *State) { |
177 | 419k | assert(Files.empty() && "not first"); |
178 | 0 | FirstDiagState = CurDiagState = State; |
179 | 419k | CurDiagStateLoc = SourceLocation(); |
180 | 419k | } |
181 | | |
182 | | void DiagnosticsEngine::DiagStateMap::append(SourceManager &SrcMgr, |
183 | | SourceLocation Loc, |
184 | 150k | DiagState *State) { |
185 | 150k | CurDiagState = State; |
186 | 150k | CurDiagStateLoc = Loc; |
187 | | |
188 | 150k | std::pair<FileID, unsigned> Decomp = SrcMgr.getDecomposedLoc(Loc); |
189 | 150k | unsigned Offset = Decomp.second; |
190 | 1.57M | for (File *F = getFile(SrcMgr, Decomp.first); F; |
191 | 1.42M | Offset = F->ParentOffset, F = F->Parent) { |
192 | 1.42M | F->HasLocalTransitions = true; |
193 | 1.42M | auto &Last = F->StateTransitions.back(); |
194 | 1.42M | assert(Last.Offset <= Offset && "state transitions added out of order"); |
195 | | |
196 | 1.42M | if (Last.Offset == Offset) { |
197 | 1.19M | if (Last.State == State) |
198 | 0 | break; |
199 | 1.19M | Last.State = State; |
200 | 1.19M | continue; |
201 | 1.19M | } |
202 | | |
203 | 238k | F->StateTransitions.push_back({State, Offset}); |
204 | 238k | } |
205 | 150k | } |
206 | | |
207 | | DiagnosticsEngine::DiagState * |
208 | | DiagnosticsEngine::DiagStateMap::lookup(SourceManager &SrcMgr, |
209 | 179M | SourceLocation Loc) const { |
210 | | // Common case: we have not seen any diagnostic pragmas. |
211 | 179M | if (Files.empty()) |
212 | 128M | return FirstDiagState; |
213 | | |
214 | 51.2M | std::pair<FileID, unsigned> Decomp = SrcMgr.getDecomposedLoc(Loc); |
215 | 51.2M | const File *F = getFile(SrcMgr, Decomp.first); |
216 | 51.2M | return F->lookup(Decomp.second); |
217 | 179M | } |
218 | | |
219 | | DiagnosticsEngine::DiagState * |
220 | 54.0M | DiagnosticsEngine::DiagStateMap::File::lookup(unsigned Offset) const { |
221 | 54.0M | auto OnePastIt = |
222 | 66.7M | llvm::partition_point(StateTransitions, [=](const DiagStatePoint &P) { |
223 | 66.7M | return P.Offset <= Offset; |
224 | 66.7M | }); |
225 | 54.0M | assert(OnePastIt != StateTransitions.begin() && "missing initial state"); |
226 | 0 | return OnePastIt[-1].State; |
227 | 54.0M | } |
228 | | |
229 | | DiagnosticsEngine::DiagStateMap::File * |
230 | | DiagnosticsEngine::DiagStateMap::getFile(SourceManager &SrcMgr, |
231 | 54.1M | FileID ID) const { |
232 | | // Get or insert the File for this ID. |
233 | 54.1M | auto Range = Files.equal_range(ID); |
234 | 54.1M | if (Range.first != Range.second) |
235 | 51.3M | return &Range.first->second; |
236 | 2.78M | auto &F = Files.insert(Range.first, std::make_pair(ID, File()))->second; |
237 | | |
238 | | // We created a new File; look up the diagnostic state at the start of it and |
239 | | // initialize it. |
240 | 2.78M | if (ID.isValid()) { |
241 | 2.78M | std::pair<FileID, unsigned> Decomp = SrcMgr.getDecomposedIncludedLoc(ID); |
242 | 2.78M | F.Parent = getFile(SrcMgr, Decomp.first); |
243 | 2.78M | F.ParentOffset = Decomp.second; |
244 | 2.78M | F.StateTransitions.push_back({F.Parent->lookup(Decomp.second), 0}); |
245 | 2.78M | } else { |
246 | | // This is the (imaginary) root file into which we pretend all top-level |
247 | | // files are included; it descends from the initial state. |
248 | | // |
249 | | // FIXME: This doesn't guarantee that we use the same ordering as |
250 | | // isBeforeInTranslationUnit in the cases where someone invented another |
251 | | // top-level file and added diagnostic pragmas to it. See the code at the |
252 | | // end of isBeforeInTranslationUnit for the quirks it deals with. |
253 | 3.62k | F.StateTransitions.push_back({FirstDiagState, 0}); |
254 | 3.62k | } |
255 | 2.78M | return &F; |
256 | 54.1M | } |
257 | | |
258 | | void DiagnosticsEngine::DiagStateMap::dump(SourceManager &SrcMgr, |
259 | 3 | StringRef DiagName) const { |
260 | 3 | llvm::errs() << "diagnostic state at "; |
261 | 3 | CurDiagStateLoc.print(llvm::errs(), SrcMgr); |
262 | 3 | llvm::errs() << ": " << CurDiagState << "\n"; |
263 | | |
264 | 6 | for (auto &F : Files) { |
265 | 6 | FileID ID = F.first; |
266 | 6 | File &File = F.second; |
267 | | |
268 | 6 | bool PrintedOuterHeading = false; |
269 | 12 | auto PrintOuterHeading = [&] { |
270 | 12 | if (PrintedOuterHeading) return6 ; |
271 | 6 | PrintedOuterHeading = true; |
272 | | |
273 | 6 | llvm::errs() << "File " << &File << " <FileID " << ID.getHashValue() |
274 | 6 | << ">: " << SrcMgr.getBufferOrFake(ID).getBufferIdentifier(); |
275 | | |
276 | 6 | if (F.second.Parent) { |
277 | 3 | std::pair<FileID, unsigned> Decomp = |
278 | 3 | SrcMgr.getDecomposedIncludedLoc(ID); |
279 | 3 | assert(File.ParentOffset == Decomp.second); |
280 | 0 | llvm::errs() << " parent " << File.Parent << " <FileID " |
281 | 3 | << Decomp.first.getHashValue() << "> "; |
282 | 3 | SrcMgr.getLocForStartOfFile(Decomp.first) |
283 | 3 | .getLocWithOffset(Decomp.second) |
284 | 3 | .print(llvm::errs(), SrcMgr); |
285 | 3 | } |
286 | 6 | if (File.HasLocalTransitions) |
287 | 6 | llvm::errs() << " has_local_transitions"; |
288 | 6 | llvm::errs() << "\n"; |
289 | 6 | }; |
290 | | |
291 | 6 | if (DiagName.empty()) |
292 | 0 | PrintOuterHeading(); |
293 | | |
294 | 12 | for (DiagStatePoint &Transition : File.StateTransitions) { |
295 | 12 | bool PrintedInnerHeading = false; |
296 | 24 | auto PrintInnerHeading = [&] { |
297 | 24 | if (PrintedInnerHeading) return12 ; |
298 | 12 | PrintedInnerHeading = true; |
299 | | |
300 | 12 | PrintOuterHeading(); |
301 | 12 | llvm::errs() << " "; |
302 | 12 | SrcMgr.getLocForStartOfFile(ID) |
303 | 12 | .getLocWithOffset(Transition.Offset) |
304 | 12 | .print(llvm::errs(), SrcMgr); |
305 | 12 | llvm::errs() << ": state " << Transition.State << ":\n"; |
306 | 12 | }; |
307 | | |
308 | 12 | if (DiagName.empty()) |
309 | 0 | PrintInnerHeading(); |
310 | | |
311 | 72 | for (auto &Mapping : *Transition.State) { |
312 | 72 | StringRef Option = |
313 | 72 | DiagnosticIDs::getWarningOptionForDiag(Mapping.first); |
314 | 72 | if (!DiagName.empty() && DiagName != Option) |
315 | 48 | continue; |
316 | | |
317 | 24 | PrintInnerHeading(); |
318 | 24 | llvm::errs() << " "; |
319 | 24 | if (Option.empty()) |
320 | 0 | llvm::errs() << "<unknown " << Mapping.first << ">"; |
321 | 24 | else |
322 | 24 | llvm::errs() << Option; |
323 | 24 | llvm::errs() << ": "; |
324 | | |
325 | 24 | switch (Mapping.second.getSeverity()) { |
326 | 6 | case diag::Severity::Ignored: llvm::errs() << "ignored"; break; |
327 | 0 | case diag::Severity::Remark: llvm::errs() << "remark"; break; |
328 | 18 | case diag::Severity::Warning: llvm::errs() << "warning"; break; |
329 | 0 | case diag::Severity::Error: llvm::errs() << "error"; break; |
330 | 0 | case diag::Severity::Fatal: llvm::errs() << "fatal"; break; |
331 | 24 | } |
332 | | |
333 | 24 | if (!Mapping.second.isUser()) |
334 | 0 | llvm::errs() << " default"; |
335 | 24 | if (Mapping.second.isPragma()) |
336 | 6 | llvm::errs() << " pragma"; |
337 | 24 | if (Mapping.second.hasNoWarningAsError()) |
338 | 6 | llvm::errs() << " no-error"; |
339 | 24 | if (Mapping.second.hasNoErrorAsFatal()) |
340 | 6 | llvm::errs() << " no-fatal"; |
341 | 24 | if (Mapping.second.wasUpgradedFromWarning()) |
342 | 0 | llvm::errs() << " overruled"; |
343 | 24 | llvm::errs() << "\n"; |
344 | 24 | } |
345 | 12 | } |
346 | 6 | } |
347 | 3 | } |
348 | | |
349 | | void DiagnosticsEngine::PushDiagStatePoint(DiagState *State, |
350 | 150k | SourceLocation Loc) { |
351 | 150k | assert(Loc.isValid() && "Adding invalid loc point"); |
352 | 0 | DiagStatesByLoc.append(*SourceMgr, Loc, State); |
353 | 150k | } |
354 | | |
355 | | void DiagnosticsEngine::setSeverity(diag::kind Diag, diag::Severity Map, |
356 | 3.94M | SourceLocation L) { |
357 | 3.94M | assert(Diag < diag::DIAG_UPPER_LIMIT && |
358 | 3.94M | "Can only map builtin diagnostics"); |
359 | 0 | assert((Diags->isBuiltinWarningOrExtension(Diag) || |
360 | 3.94M | (Map == diag::Severity::Fatal || Map == diag::Severity::Error)) && |
361 | 3.94M | "Cannot map errors into warnings!"); |
362 | 0 | assert((L.isInvalid() || SourceMgr) && "No SourceMgr for valid location"); |
363 | | |
364 | | // Don't allow a mapping to a warning override an error/fatal mapping. |
365 | 0 | bool WasUpgradedFromWarning = false; |
366 | 3.94M | if (Map == diag::Severity::Warning) { |
367 | 558k | DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag); |
368 | 558k | if (Info.getSeverity() == diag::Severity::Error || |
369 | 558k | Info.getSeverity() == diag::Severity::Fatal534k ) { |
370 | 27.3k | Map = Info.getSeverity(); |
371 | 27.3k | WasUpgradedFromWarning = true; |
372 | 27.3k | } |
373 | 558k | } |
374 | 3.94M | DiagnosticMapping Mapping = makeUserMapping(Map, L); |
375 | 3.94M | Mapping.setUpgradedFromWarning(WasUpgradedFromWarning); |
376 | | |
377 | | // Make sure we propagate the NoWarningAsError flag from an existing |
378 | | // mapping (which may be the default mapping). |
379 | 3.94M | DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag); |
380 | 3.94M | Mapping.setNoWarningAsError(Info.hasNoWarningAsError() || |
381 | 3.94M | Mapping.hasNoWarningAsError()3.76M ); |
382 | | |
383 | | // Common case; setting all the diagnostics of a group in one place. |
384 | 3.94M | if ((L.isInvalid() || L == DiagStatesByLoc.getCurDiagStateLoc()1.31M ) && |
385 | 3.94M | DiagStatesByLoc.getCurDiagState()3.85M ) { |
386 | | // FIXME: This is theoretically wrong: if the current state is shared with |
387 | | // some other location (via push/pop) we will change the state for that |
388 | | // other location as well. This cannot currently happen, as we can't update |
389 | | // the diagnostic state at the same location at which we pop. |
390 | 3.85M | DiagStatesByLoc.getCurDiagState()->setMapping(Diag, Mapping); |
391 | 3.85M | return; |
392 | 3.85M | } |
393 | | |
394 | | // A diagnostic pragma occurred, create a new DiagState initialized with |
395 | | // the current one and a new DiagStatePoint to record at which location |
396 | | // the new state became active. |
397 | 88.3k | DiagStates.push_back(*GetCurDiagState()); |
398 | 88.3k | DiagStates.back().setMapping(Diag, Mapping); |
399 | 88.3k | PushDiagStatePoint(&DiagStates.back(), L); |
400 | 88.3k | } |
401 | | |
402 | | bool DiagnosticsEngine::setSeverityForGroup(diag::Flavor Flavor, |
403 | | StringRef Group, diag::Severity Map, |
404 | 342k | SourceLocation Loc) { |
405 | | // Get the diagnostics in this group. |
406 | 342k | SmallVector<diag::kind, 256> GroupDiags; |
407 | 342k | if (Diags->getDiagnosticsInGroup(Flavor, Group, GroupDiags)) |
408 | 41 | return true; |
409 | | |
410 | | // Set the mapping. |
411 | 342k | for (diag::kind Diag : GroupDiags) |
412 | 3.85M | setSeverity(Diag, Map, Loc); |
413 | | |
414 | 342k | return false; |
415 | 342k | } |
416 | | |
417 | | bool DiagnosticsEngine::setSeverityForGroup(diag::Flavor Flavor, |
418 | | diag::Group Group, |
419 | | diag::Severity Map, |
420 | 2 | SourceLocation Loc) { |
421 | 2 | return setSeverityForGroup(Flavor, Diags->getWarningOptionForGroup(Group), |
422 | 2 | Map, Loc); |
423 | 2 | } |
424 | | |
425 | | bool DiagnosticsEngine::setDiagnosticGroupWarningAsError(StringRef Group, |
426 | 142k | bool Enabled) { |
427 | | // If we are enabling this feature, just set the diagnostic mappings to map to |
428 | | // errors. |
429 | 142k | if (Enabled) |
430 | 142k | return setSeverityForGroup(diag::Flavor::WarningOrError, Group, |
431 | 142k | diag::Severity::Error); |
432 | | |
433 | | // Otherwise, we want to set the diagnostic mapping's "no Werror" bit, and |
434 | | // potentially downgrade anything already mapped to be a warning. |
435 | | |
436 | | // Get the diagnostics in this group. |
437 | 101 | SmallVector<diag::kind, 8> GroupDiags; |
438 | 101 | if (Diags->getDiagnosticsInGroup(diag::Flavor::WarningOrError, Group, |
439 | 101 | GroupDiags)) |
440 | 0 | return true; |
441 | | |
442 | | // Perform the mapping change. |
443 | 430 | for (diag::kind Diag : GroupDiags)101 { |
444 | 430 | DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag); |
445 | | |
446 | 430 | if (Info.getSeverity() == diag::Severity::Error || |
447 | 430 | Info.getSeverity() == diag::Severity::Fatal223 ) |
448 | 207 | Info.setSeverity(diag::Severity::Warning); |
449 | | |
450 | 430 | Info.setNoWarningAsError(true); |
451 | 430 | } |
452 | | |
453 | 101 | return false; |
454 | 101 | } |
455 | | |
456 | | bool DiagnosticsEngine::setDiagnosticGroupErrorAsFatal(StringRef Group, |
457 | 1 | bool Enabled) { |
458 | | // If we are enabling this feature, just set the diagnostic mappings to map to |
459 | | // fatal errors. |
460 | 1 | if (Enabled) |
461 | 1 | return setSeverityForGroup(diag::Flavor::WarningOrError, Group, |
462 | 1 | diag::Severity::Fatal); |
463 | | |
464 | | // Otherwise, we want to set the diagnostic mapping's "no Wfatal-errors" bit, |
465 | | // and potentially downgrade anything already mapped to be a fatal error. |
466 | | |
467 | | // Get the diagnostics in this group. |
468 | 0 | SmallVector<diag::kind, 8> GroupDiags; |
469 | 0 | if (Diags->getDiagnosticsInGroup(diag::Flavor::WarningOrError, Group, |
470 | 0 | GroupDiags)) |
471 | 0 | return true; |
472 | | |
473 | | // Perform the mapping change. |
474 | 0 | for (diag::kind Diag : GroupDiags) { |
475 | 0 | DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag); |
476 | |
|
477 | 0 | if (Info.getSeverity() == diag::Severity::Fatal) |
478 | 0 | Info.setSeverity(diag::Severity::Error); |
479 | |
|
480 | 0 | Info.setNoErrorAsFatal(true); |
481 | 0 | } |
482 | |
|
483 | 0 | return false; |
484 | 0 | } |
485 | | |
486 | | void DiagnosticsEngine::setSeverityForAll(diag::Flavor Flavor, |
487 | | diag::Severity Map, |
488 | 44 | SourceLocation Loc) { |
489 | | // Get all the diagnostics. |
490 | 44 | std::vector<diag::kind> AllDiags; |
491 | 44 | DiagnosticIDs::getAllDiagnostics(Flavor, AllDiags); |
492 | | |
493 | | // Set the mapping. |
494 | 44 | for (diag::kind Diag : AllDiags) |
495 | 207k | if (Diags->isBuiltinWarningOrExtension(Diag)) |
496 | 95.1k | setSeverity(Diag, Map, Loc); |
497 | 44 | } |
498 | | |
499 | 150 | void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) { |
500 | 150 | assert(CurDiagID == std::numeric_limits<unsigned>::max() && |
501 | 150 | "Multiple diagnostics in flight at once!"); |
502 | | |
503 | 0 | CurDiagLoc = storedDiag.getLocation(); |
504 | 150 | CurDiagID = storedDiag.getID(); |
505 | 150 | DiagStorage.NumDiagArgs = 0; |
506 | | |
507 | 150 | DiagStorage.DiagRanges.clear(); |
508 | 150 | DiagStorage.DiagRanges.append(storedDiag.range_begin(), |
509 | 150 | storedDiag.range_end()); |
510 | | |
511 | 150 | DiagStorage.FixItHints.clear(); |
512 | 150 | DiagStorage.FixItHints.append(storedDiag.fixit_begin(), |
513 | 150 | storedDiag.fixit_end()); |
514 | | |
515 | 150 | assert(Client && "DiagnosticConsumer not set!"); |
516 | 0 | Level DiagLevel = storedDiag.getLevel(); |
517 | 150 | Diagnostic Info(this, storedDiag.getMessage()); |
518 | 150 | Client->HandleDiagnostic(DiagLevel, Info); |
519 | 150 | if (Client->IncludeInDiagnosticCounts()) { |
520 | 150 | if (DiagLevel == DiagnosticsEngine::Warning) |
521 | 1 | ++NumWarnings; |
522 | 150 | } |
523 | | |
524 | 150 | CurDiagID = std::numeric_limits<unsigned>::max(); |
525 | 150 | } |
526 | | |
527 | 11.0M | bool DiagnosticsEngine::EmitCurrentDiagnostic(bool Force) { |
528 | 11.0M | assert(getClient() && "DiagnosticClient not set!"); |
529 | | |
530 | 0 | bool Emitted; |
531 | 11.0M | if (Force) { |
532 | 162 | Diagnostic Info(this); |
533 | | |
534 | | // Figure out the diagnostic level of this message. |
535 | 162 | DiagnosticIDs::Level DiagLevel |
536 | 162 | = Diags->getDiagnosticLevel(Info.getID(), Info.getLocation(), *this); |
537 | | |
538 | 162 | Emitted = (DiagLevel != DiagnosticIDs::Ignored); |
539 | 162 | if (Emitted) { |
540 | | // Emit the diagnostic regardless of suppression level. |
541 | 162 | Diags->EmitDiag(*this, DiagLevel); |
542 | 162 | } |
543 | 11.0M | } else { |
544 | | // Process the diagnostic, sending the accumulated information to the |
545 | | // DiagnosticConsumer. |
546 | 11.0M | Emitted = ProcessDiag(); |
547 | 11.0M | } |
548 | | |
549 | | // Clear out the current diagnostic object. |
550 | 11.0M | Clear(); |
551 | | |
552 | | // If there was a delayed diagnostic, emit it now. |
553 | 11.0M | if (!Force && DelayedDiagID11.0M ) |
554 | 11 | ReportDelayed(); |
555 | | |
556 | 11.0M | return Emitted; |
557 | 11.0M | } |
558 | | |
559 | 459k | DiagnosticConsumer::~DiagnosticConsumer() = default; |
560 | | |
561 | | void DiagnosticConsumer::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, |
562 | 413k | const Diagnostic &Info) { |
563 | 413k | if (!IncludeInDiagnosticCounts()) |
564 | 4 | return; |
565 | | |
566 | 413k | if (DiagLevel == DiagnosticsEngine::Warning) |
567 | 80.1k | ++NumWarnings; |
568 | 333k | else if (DiagLevel >= DiagnosticsEngine::Error) |
569 | 205k | ++NumErrors; |
570 | 413k | } |
571 | | |
572 | | /// ModifierIs - Return true if the specified modifier matches specified string. |
573 | | template <std::size_t StrLen> |
574 | | static bool ModifierIs(const char *Modifier, unsigned ModifierLen, |
575 | 788k | const char (&Str)[StrLen]) { |
576 | 788k | return StrLen-1 == ModifierLen && memcmp(Modifier, Str, StrLen-1) == 0196k ; |
577 | 788k | } Diagnostic.cpp:bool ModifierIs<5ul>(char const*, unsigned int, char const (&) [5ul]) Line | Count | Source | 575 | 531k | const char (&Str)[StrLen]) { | 576 | 531k | return StrLen-1 == ModifierLen && memcmp(Modifier, Str, StrLen-1) == 012.3k ; | 577 | 531k | } |
Diagnostic.cpp:bool ModifierIs<7ul>(char const*, unsigned int, char const (&) [7ul]) Line | Count | Source | 575 | 214k | const char (&Str)[StrLen]) { | 576 | 214k | return StrLen-1 == ModifierLen && memcmp(Modifier, Str, StrLen-1) == 0175k ; | 577 | 214k | } |
Diagnostic.cpp:bool ModifierIs<2ul>(char const*, unsigned int, char const (&) [2ul]) Line | Count | Source | 575 | 23.4k | const char (&Str)[StrLen]) { | 576 | 23.4k | return StrLen-1 == ModifierLen && memcmp(Modifier, Str, StrLen-1) == 01.75k ; | 577 | 23.4k | } |
Diagnostic.cpp:bool ModifierIs<8ul>(char const*, unsigned int, char const (&) [8ul]) Line | Count | Source | 575 | 18.6k | const char (&Str)[StrLen]) { | 576 | 18.6k | return StrLen-1 == ModifierLen && memcmp(Modifier, Str, StrLen-1) == 06.82k ; | 577 | 18.6k | } |
|
578 | | |
579 | | /// ScanForward - Scans forward, looking for the given character, skipping |
580 | | /// nested clauses and escaped characters. |
581 | 530k | static const char *ScanFormat(const char *I, const char *E, char Target) { |
582 | 530k | unsigned Depth = 0; |
583 | | |
584 | 23.0M | for ( ; I != E; ++I22.5M ) { |
585 | 23.0M | if (Depth == 0 && *I == Target20.2M ) return I480k ; |
586 | 22.5M | if (Depth != 0 && *I == '}'2.80M ) Depth--65.4k ; |
587 | | |
588 | 22.5M | if (*I == '%') { |
589 | 223k | I++; |
590 | 223k | if (I == E) break0 ; |
591 | | |
592 | | // Escaped characters get implicitly skipped here. |
593 | | |
594 | | // Format specifier. |
595 | 223k | if (!isDigit(*I) && !isPunctuation(*I)86.7k ) { |
596 | 458k | for (I++; I != E && !isDigit(*I) && *I != '{'438k ; I++373k ) ;373k |
597 | 85.6k | if (I == E) break0 ; |
598 | 85.6k | if (*I == '{') |
599 | 65.4k | Depth++; |
600 | 85.6k | } |
601 | 223k | } |
602 | 22.5M | } |
603 | 50.6k | return E; |
604 | 530k | } |
605 | | |
606 | | /// HandleSelectModifier - Handle the integer 'select' modifier. This is used |
607 | | /// like this: %select{foo|bar|baz}2. This means that the integer argument |
608 | | /// "%2" has a value from 0-2. If the value is 0, the diagnostic prints 'foo'. |
609 | | /// If the value is 1, it prints 'bar'. If it has the value 2, it prints 'baz'. |
610 | | /// This is very useful for certain classes of variant diagnostics. |
611 | | static void HandleSelectModifier(const Diagnostic &DInfo, unsigned ValNo, |
612 | | const char *Argument, unsigned ArgumentLen, |
613 | 169k | SmallVectorImpl<char> &OutStr) { |
614 | 169k | const char *ArgumentEnd = Argument+ArgumentLen; |
615 | | |
616 | | // Skip over 'ValNo' |'s. |
617 | 304k | while (ValNo) { |
618 | 134k | const char *NextVal = ScanFormat(Argument, ArgumentEnd, '|'); |
619 | 134k | assert(NextVal != ArgumentEnd && "Value for integer select modifier was" |
620 | 134k | " larger than the number of options in the diagnostic string!"); |
621 | 0 | Argument = NextVal+1; // Skip this string. |
622 | 134k | --ValNo; |
623 | 134k | } |
624 | | |
625 | | // Get the end of the value. This is either the } or the |. |
626 | 169k | const char *EndPtr = ScanFormat(Argument, ArgumentEnd, '|'); |
627 | | |
628 | | // Recursively format the result of the select clause into the output string. |
629 | 169k | DInfo.FormatDiagnostic(Argument, EndPtr, OutStr); |
630 | 169k | } |
631 | | |
632 | | /// HandleIntegerSModifier - Handle the integer 's' modifier. This adds the |
633 | | /// letter 's' to the string if the value is not 1. This is used in cases like |
634 | | /// this: "you idiot, you have %4 parameter%s4!". |
635 | | static void HandleIntegerSModifier(unsigned ValNo, |
636 | 1.75k | SmallVectorImpl<char> &OutStr) { |
637 | 1.75k | if (ValNo != 1) |
638 | 1.04k | OutStr.push_back('s'); |
639 | 1.75k | } |
640 | | |
641 | | /// HandleOrdinalModifier - Handle the integer 'ord' modifier. This |
642 | | /// prints the ordinal form of the given integer, with 1 corresponding |
643 | | /// to the first ordinal. Currently this is hard-coded to use the |
644 | | /// English form. |
645 | | static void HandleOrdinalModifier(unsigned ValNo, |
646 | 6.82k | SmallVectorImpl<char> &OutStr) { |
647 | 6.82k | assert(ValNo != 0 && "ValNo must be strictly positive!"); |
648 | | |
649 | 0 | llvm::raw_svector_ostream Out(OutStr); |
650 | | |
651 | | // We could use text forms for the first N ordinals, but the numeric |
652 | | // forms are actually nicer in diagnostics because they stand out. |
653 | 6.82k | Out << ValNo << llvm::getOrdinalSuffix(ValNo); |
654 | 6.82k | } |
655 | | |
656 | | /// PluralNumber - Parse an unsigned integer and advance Start. |
657 | 3.62k | static unsigned PluralNumber(const char *&Start, const char *End) { |
658 | | // Programming 101: Parse a decimal number :-) |
659 | 3.62k | unsigned Val = 0; |
660 | 7.25k | while (Start != End && *Start >= '0'3.64k && *Start <= '9'3.63k ) { |
661 | 3.62k | Val *= 10; |
662 | 3.62k | Val += *Start - '0'; |
663 | 3.62k | ++Start; |
664 | 3.62k | } |
665 | 3.62k | return Val; |
666 | 3.62k | } |
667 | | |
668 | | /// TestPluralRange - Test if Val is in the parsed range. Modifies Start. |
669 | 3.62k | static bool TestPluralRange(unsigned Val, const char *&Start, const char *End) { |
670 | 3.62k | if (*Start != '[') { |
671 | 3.61k | unsigned Ref = PluralNumber(Start, End); |
672 | 3.61k | return Ref == Val; |
673 | 3.61k | } |
674 | | |
675 | 7 | ++Start; |
676 | 7 | unsigned Low = PluralNumber(Start, End); |
677 | 7 | assert(*Start == ',' && "Bad plural expression syntax: expected ,"); |
678 | 0 | ++Start; |
679 | 7 | unsigned High = PluralNumber(Start, End); |
680 | 7 | assert(*Start == ']' && "Bad plural expression syntax: expected )"); |
681 | 0 | ++Start; |
682 | 7 | return Low <= Val && Val <= High; |
683 | 3.62k | } |
684 | | |
685 | | /// EvalPluralExpr - Actual expression evaluator for HandlePluralModifier. |
686 | 5.28k | static bool EvalPluralExpr(unsigned ValNo, const char *Start, const char *End) { |
687 | | // Empty condition? |
688 | 5.28k | if (*Start == ':') |
689 | 1.66k | return true; |
690 | | |
691 | 3.62k | while (true) { |
692 | 3.62k | char C = *Start; |
693 | 3.62k | if (C == '%') { |
694 | | // Modulo expression |
695 | 0 | ++Start; |
696 | 0 | unsigned Arg = PluralNumber(Start, End); |
697 | 0 | assert(*Start == '=' && "Bad plural expression syntax: expected ="); |
698 | 0 | ++Start; |
699 | 0 | unsigned ValMod = ValNo % Arg; |
700 | 0 | if (TestPluralRange(ValMod, Start, End)) |
701 | 0 | return true; |
702 | 3.62k | } else { |
703 | 3.62k | assert((C == '[' || (C >= '0' && C <= '9')) && |
704 | 3.62k | "Bad plural expression syntax: unexpected character"); |
705 | | // Range expression |
706 | 3.62k | if (TestPluralRange(ValNo, Start, End)) |
707 | 1.35k | return true; |
708 | 3.62k | } |
709 | | |
710 | | // Scan for next or-expr part. |
711 | 2.27k | Start = std::find(Start, End, ','); |
712 | 2.27k | if (Start == End) |
713 | 2.27k | break; |
714 | 0 | ++Start; |
715 | 0 | } |
716 | 2.27k | return false; |
717 | 3.62k | } |
718 | | |
719 | | /// HandlePluralModifier - Handle the integer 'plural' modifier. This is used |
720 | | /// for complex plural forms, or in languages where all plurals are complex. |
721 | | /// The syntax is: %plural{cond1:form1|cond2:form2|:form3}, where condn are |
722 | | /// conditions that are tested in order, the form corresponding to the first |
723 | | /// that applies being emitted. The empty condition is always true, making the |
724 | | /// last form a default case. |
725 | | /// Conditions are simple boolean expressions, where n is the number argument. |
726 | | /// Here are the rules. |
727 | | /// condition := expression | empty |
728 | | /// empty := -> always true |
729 | | /// expression := numeric [',' expression] -> logical or |
730 | | /// numeric := range -> true if n in range |
731 | | /// | '%' number '=' range -> true if n % number in range |
732 | | /// range := number |
733 | | /// | '[' number ',' number ']' -> ranges are inclusive both ends |
734 | | /// |
735 | | /// Here are some examples from the GNU gettext manual written in this form: |
736 | | /// English: |
737 | | /// {1:form0|:form1} |
738 | | /// Latvian: |
739 | | /// {0:form2|%100=11,%10=0,%10=[2,9]:form1|:form0} |
740 | | /// Gaeilge: |
741 | | /// {1:form0|2:form1|:form2} |
742 | | /// Romanian: |
743 | | /// {1:form0|0,%100=[1,19]:form1|:form2} |
744 | | /// Lithuanian: |
745 | | /// {%10=0,%100=[10,19]:form2|%10=1:form0|:form1} |
746 | | /// Russian (requires repeated form): |
747 | | /// {%100=[11,14]:form2|%10=1:form0|%10=[2,4]:form1|:form2} |
748 | | /// Slovak |
749 | | /// {1:form0|[2,4]:form1|:form2} |
750 | | /// Polish (requires repeated form): |
751 | | /// {1:form0|%100=[10,20]:form2|%10=[2,4]:form1|:form2} |
752 | | static void HandlePluralModifier(const Diagnostic &DInfo, unsigned ValNo, |
753 | | const char *Argument, unsigned ArgumentLen, |
754 | 3.01k | SmallVectorImpl<char> &OutStr) { |
755 | 3.01k | const char *ArgumentEnd = Argument + ArgumentLen; |
756 | 5.28k | while (true) { |
757 | 5.28k | assert(Argument < ArgumentEnd && "Plural expression didn't match."); |
758 | 0 | const char *ExprEnd = Argument; |
759 | 8.93k | while (*ExprEnd != ':') { |
760 | 3.64k | assert(ExprEnd != ArgumentEnd && "Plural missing expression end"); |
761 | 0 | ++ExprEnd; |
762 | 3.64k | } |
763 | 5.28k | if (EvalPluralExpr(ValNo, Argument, ExprEnd)) { |
764 | 3.01k | Argument = ExprEnd + 1; |
765 | 3.01k | ExprEnd = ScanFormat(Argument, ArgumentEnd, '|'); |
766 | | |
767 | | // Recursively format the result of the plural clause into the |
768 | | // output string. |
769 | 3.01k | DInfo.FormatDiagnostic(Argument, ExprEnd, OutStr); |
770 | 3.01k | return; |
771 | 3.01k | } |
772 | 2.27k | Argument = ScanFormat(Argument, ArgumentEnd - 1, '|') + 1; |
773 | 2.27k | } |
774 | 3.01k | } |
775 | | |
776 | | /// Returns the friendly description for a token kind that will appear |
777 | | /// without quotes in diagnostic messages. These strings may be translatable in |
778 | | /// future. |
779 | 345 | static const char *getTokenDescForDiagnostic(tok::TokenKind Kind) { |
780 | 345 | switch (Kind) { |
781 | 343 | case tok::identifier: |
782 | 343 | return "identifier"; |
783 | 2 | default: |
784 | 2 | return nullptr; |
785 | 345 | } |
786 | 345 | } |
787 | | |
788 | | /// FormatDiagnostic - Format this diagnostic into a string, substituting the |
789 | | /// formal arguments into the %0 slots. The result is appended onto the Str |
790 | | /// array. |
791 | | void Diagnostic:: |
792 | 415k | FormatDiagnostic(SmallVectorImpl<char> &OutStr) const { |
793 | 415k | if (!StoredDiagMessage.empty()) { |
794 | 153 | OutStr.append(StoredDiagMessage.begin(), StoredDiagMessage.end()); |
795 | 153 | return; |
796 | 153 | } |
797 | | |
798 | 414k | StringRef Diag = |
799 | 414k | getDiags()->getDiagnosticIDs()->getDescription(getID()); |
800 | | |
801 | 414k | FormatDiagnostic(Diag.begin(), Diag.end(), OutStr); |
802 | 414k | } |
803 | | |
804 | | void Diagnostic:: |
805 | | FormatDiagnostic(const char *DiagStr, const char *DiagEnd, |
806 | 624k | SmallVectorImpl<char> &OutStr) const { |
807 | | // When the diagnostic string is only "%0", the entire string is being given |
808 | | // by an outside source. Remove unprintable characters from this string |
809 | | // and skip all the other string processing. |
810 | 624k | if (DiagEnd - DiagStr == 2 && |
811 | 624k | StringRef(DiagStr, DiagEnd - DiagStr).equals("%0")29.1k && |
812 | 624k | getArgKind(0) == DiagnosticsEngine::ak_std_string26.0k ) { |
813 | 25.9k | const std::string &S = getArgStdStr(0); |
814 | 1.30M | for (char c : S) { |
815 | 1.30M | if (llvm::sys::locale::isPrint(c) || c == '\t'386 ) { |
816 | 1.30M | OutStr.push_back(c); |
817 | 1.30M | } |
818 | 1.30M | } |
819 | 25.9k | return; |
820 | 25.9k | } |
821 | | |
822 | | /// FormattedArgs - Keep track of all of the arguments formatted by |
823 | | /// ConvertArgToString and pass them into subsequent calls to |
824 | | /// ConvertArgToString, allowing the implementation to avoid redundancies in |
825 | | /// obvious cases. |
826 | 598k | SmallVector<DiagnosticsEngine::ArgumentValue, 8> FormattedArgs; |
827 | | |
828 | | /// QualTypeVals - Pass a vector of arrays so that QualType names can be |
829 | | /// compared to see if more information is needed to be printed. |
830 | 598k | SmallVector<intptr_t, 2> QualTypeVals; |
831 | 598k | SmallString<64> Tree; |
832 | | |
833 | 2.00M | for (unsigned i = 0, e = getNumArgs(); i < e; ++i1.41M ) |
834 | 1.41M | if (getArgKind(i) == DiagnosticsEngine::ak_qualtype) |
835 | 260k | QualTypeVals.push_back(getRawArg(i)); |
836 | | |
837 | 1.92M | while (DiagStr != DiagEnd) { |
838 | 1.32M | if (DiagStr[0] != '%') { |
839 | | // Append non-%0 substrings to Str if we have one. |
840 | 792k | const char *StrEnd = std::find(DiagStr, DiagEnd, '%'); |
841 | 792k | OutStr.append(DiagStr, StrEnd); |
842 | 792k | DiagStr = StrEnd; |
843 | 792k | continue; |
844 | 792k | } else if (531k isPunctuation(DiagStr[1])531k ) { |
845 | 208 | OutStr.push_back(DiagStr[1]); // %% -> %. |
846 | 208 | DiagStr += 2; |
847 | 208 | continue; |
848 | 208 | } |
849 | | |
850 | | // Skip the %. |
851 | 531k | ++DiagStr; |
852 | | |
853 | | // This must be a placeholder for a diagnostic argument. The format for a |
854 | | // placeholder is one of "%0", "%modifier0", or "%modifier{arguments}0". |
855 | | // The digit is a number from 0-9 indicating which argument this comes from. |
856 | | // The modifier is a string of digits from the set [-a-z]+, arguments is a |
857 | | // brace enclosed string. |
858 | 531k | const char *Modifier = nullptr, *Argument = nullptr; |
859 | 531k | unsigned ModifierLen = 0, ArgumentLen = 0; |
860 | | |
861 | | // Check to see if we have a modifier. If so eat it. |
862 | 531k | if (!isDigit(DiagStr[0])) { |
863 | 204k | Modifier = DiagStr; |
864 | 1.35M | while (DiagStr[0] == '-' || |
865 | 1.35M | (DiagStr[0] >= 'a' && DiagStr[0] <= 'z'1.33M )) |
866 | 1.15M | ++DiagStr; |
867 | 204k | ModifierLen = DiagStr-Modifier; |
868 | | |
869 | | // If we have an argument, get it next. |
870 | 204k | if (DiagStr[0] == '{') { |
871 | 184k | ++DiagStr; // Skip {. |
872 | 184k | Argument = DiagStr; |
873 | | |
874 | 184k | DiagStr = ScanFormat(DiagStr, DiagEnd, '}'); |
875 | 184k | assert(DiagStr != DiagEnd && "Mismatched {}'s in diagnostic string!"); |
876 | 0 | ArgumentLen = DiagStr-Argument; |
877 | 184k | ++DiagStr; // Skip }. |
878 | 184k | } |
879 | 204k | } |
880 | | |
881 | 0 | assert(isDigit(*DiagStr) && "Invalid format for argument in diagnostic"); |
882 | 0 | unsigned ArgNo = *DiagStr++ - '0'; |
883 | | |
884 | | // Only used for type diffing. |
885 | 531k | unsigned ArgNo2 = ArgNo; |
886 | | |
887 | 531k | DiagnosticsEngine::ArgumentKind Kind = getArgKind(ArgNo); |
888 | 531k | if (ModifierIs(Modifier, ModifierLen, "diff")) { |
889 | 12.3k | assert(*DiagStr == ',' && isDigit(*(DiagStr + 1)) && |
890 | 12.3k | "Invalid format for diff modifier"); |
891 | 0 | ++DiagStr; // Comma. |
892 | 12.3k | ArgNo2 = *DiagStr++ - '0'; |
893 | 12.3k | DiagnosticsEngine::ArgumentKind Kind2 = getArgKind(ArgNo2); |
894 | 12.3k | if (Kind == DiagnosticsEngine::ak_qualtype && |
895 | 12.3k | Kind2 == DiagnosticsEngine::ak_qualtype12.2k ) |
896 | 12.2k | Kind = DiagnosticsEngine::ak_qualtype_pair; |
897 | 65 | else { |
898 | | // %diff only supports QualTypes. For other kinds of arguments, |
899 | | // use the default printing. For example, if the modifier is: |
900 | | // "%diff{compare $ to $|other text}1,2" |
901 | | // treat it as: |
902 | | // "compare %1 to %2" |
903 | 65 | const char *ArgumentEnd = Argument + ArgumentLen; |
904 | 65 | const char *Pipe = ScanFormat(Argument, ArgumentEnd, '|'); |
905 | 65 | assert(ScanFormat(Pipe + 1, ArgumentEnd, '|') == ArgumentEnd && |
906 | 65 | "Found too many '|'s in a %diff modifier!"); |
907 | 0 | const char *FirstDollar = ScanFormat(Argument, Pipe, '$'); |
908 | 65 | const char *SecondDollar = ScanFormat(FirstDollar + 1, Pipe, '$'); |
909 | 65 | const char ArgStr1[] = { '%', static_cast<char>('0' + ArgNo) }; |
910 | 65 | const char ArgStr2[] = { '%', static_cast<char>('0' + ArgNo2) }; |
911 | 65 | FormatDiagnostic(Argument, FirstDollar, OutStr); |
912 | 65 | FormatDiagnostic(ArgStr1, ArgStr1 + 2, OutStr); |
913 | 65 | FormatDiagnostic(FirstDollar + 1, SecondDollar, OutStr); |
914 | 65 | FormatDiagnostic(ArgStr2, ArgStr2 + 2, OutStr); |
915 | 65 | FormatDiagnostic(SecondDollar + 1, Pipe, OutStr); |
916 | 65 | continue; |
917 | 65 | } |
918 | 12.3k | } |
919 | | |
920 | 531k | switch (Kind) { |
921 | | // ---- STRINGS ---- |
922 | 115k | case DiagnosticsEngine::ak_std_string: { |
923 | 115k | const std::string &S = getArgStdStr(ArgNo); |
924 | 115k | assert(ModifierLen == 0 && "No modifiers for strings yet"); |
925 | 0 | OutStr.append(S.begin(), S.end()); |
926 | 115k | break; |
927 | 0 | } |
928 | 17.0k | case DiagnosticsEngine::ak_c_string: { |
929 | 17.0k | const char *S = getArgCStr(ArgNo); |
930 | 17.0k | assert(ModifierLen == 0 && "No modifiers for strings yet"); |
931 | | |
932 | | // Don't crash if get passed a null pointer by accident. |
933 | 17.0k | if (!S) |
934 | 0 | S = "(null)"; |
935 | | |
936 | 17.0k | OutStr.append(S, S + strlen(S)); |
937 | 17.0k | break; |
938 | 0 | } |
939 | | // ---- INTEGERS ---- |
940 | 115k | case DiagnosticsEngine::ak_sint: { |
941 | 115k | int64_t Val = getArgSInt(ArgNo); |
942 | | |
943 | 115k | if (ModifierIs(Modifier, ModifierLen, "select")) { |
944 | 110k | HandleSelectModifier(*this, (unsigned)Val, Argument, ArgumentLen, |
945 | 110k | OutStr); |
946 | 110k | } else if (4.55k ModifierIs(Modifier, ModifierLen, "s")4.55k ) { |
947 | 41 | HandleIntegerSModifier(Val, OutStr); |
948 | 4.51k | } else if (ModifierIs(Modifier, ModifierLen, "plural")) { |
949 | 720 | HandlePluralModifier(*this, (unsigned)Val, Argument, ArgumentLen, |
950 | 720 | OutStr); |
951 | 3.79k | } else if (ModifierIs(Modifier, ModifierLen, "ordinal")) { |
952 | 84 | HandleOrdinalModifier((unsigned)Val, OutStr); |
953 | 3.71k | } else { |
954 | 3.71k | assert(ModifierLen == 0 && "Unknown integer modifier"); |
955 | 0 | llvm::raw_svector_ostream(OutStr) << Val; |
956 | 3.71k | } |
957 | 0 | break; |
958 | 0 | } |
959 | 77.7k | case DiagnosticsEngine::ak_uint: { |
960 | 77.7k | uint64_t Val = getArgUInt(ArgNo); |
961 | | |
962 | 77.7k | if (ModifierIs(Modifier, ModifierLen, "select")) { |
963 | 58.8k | HandleSelectModifier(*this, Val, Argument, ArgumentLen, OutStr); |
964 | 58.8k | } else if (18.8k ModifierIs(Modifier, ModifierLen, "s")18.8k ) { |
965 | 1.71k | HandleIntegerSModifier(Val, OutStr); |
966 | 17.1k | } else if (ModifierIs(Modifier, ModifierLen, "plural")) { |
967 | 2.29k | HandlePluralModifier(*this, (unsigned)Val, Argument, ArgumentLen, |
968 | 2.29k | OutStr); |
969 | 14.8k | } else if (ModifierIs(Modifier, ModifierLen, "ordinal")) { |
970 | 6.74k | HandleOrdinalModifier(Val, OutStr); |
971 | 8.09k | } else { |
972 | 8.09k | assert(ModifierLen == 0 && "Unknown integer modifier"); |
973 | 0 | llvm::raw_svector_ostream(OutStr) << Val; |
974 | 8.09k | } |
975 | 0 | break; |
976 | 0 | } |
977 | | // ---- TOKEN SPELLINGS ---- |
978 | 39.8k | case DiagnosticsEngine::ak_tokenkind: { |
979 | 39.8k | tok::TokenKind Kind = static_cast<tok::TokenKind>(getRawArg(ArgNo)); |
980 | 39.8k | assert(ModifierLen == 0 && "No modifiers for token kinds yet"); |
981 | | |
982 | 0 | llvm::raw_svector_ostream Out(OutStr); |
983 | 39.8k | if (const char *S = tok::getPunctuatorSpelling(Kind)) |
984 | | // Quoted token spelling for punctuators. |
985 | 39.4k | Out << '\'' << S << '\''; |
986 | 369 | else if ((S = tok::getKeywordSpelling(Kind))) |
987 | | // Unquoted token spelling for keywords. |
988 | 24 | Out << S; |
989 | 345 | else if ((S = getTokenDescForDiagnostic(Kind))) |
990 | | // Unquoted translatable token name. |
991 | 343 | Out << S; |
992 | 2 | else if ((S = tok::getTokenName(Kind))) |
993 | | // Debug name, shouldn't appear in user-facing diagnostics. |
994 | 2 | Out << '<' << S << '>'; |
995 | 0 | else |
996 | 0 | Out << "(null)"; |
997 | 39.8k | break; |
998 | 0 | } |
999 | | // ---- NAMES and TYPES ---- |
1000 | 9.38k | case DiagnosticsEngine::ak_identifierinfo: { |
1001 | 9.38k | const IdentifierInfo *II = getArgIdentifier(ArgNo); |
1002 | 9.38k | assert(ModifierLen == 0 && "No modifiers for strings yet"); |
1003 | | |
1004 | | // Don't crash if get passed a null pointer by accident. |
1005 | 9.38k | if (!II) { |
1006 | 10 | const char *S = "(null)"; |
1007 | 10 | OutStr.append(S, S + strlen(S)); |
1008 | 10 | continue; |
1009 | 10 | } |
1010 | | |
1011 | 9.37k | llvm::raw_svector_ostream(OutStr) << '\'' << II->getName() << '\''; |
1012 | 9.37k | break; |
1013 | 9.38k | } |
1014 | 112 | case DiagnosticsEngine::ak_addrspace: |
1015 | 253 | case DiagnosticsEngine::ak_qual: |
1016 | 57.4k | case DiagnosticsEngine::ak_qualtype: |
1017 | 82.4k | case DiagnosticsEngine::ak_declarationname: |
1018 | 139k | case DiagnosticsEngine::ak_nameddecl: |
1019 | 139k | case DiagnosticsEngine::ak_nestednamespec: |
1020 | 141k | case DiagnosticsEngine::ak_declcontext: |
1021 | 143k | case DiagnosticsEngine::ak_attr: |
1022 | 143k | getDiags()->ConvertArgToString(Kind, getRawArg(ArgNo), |
1023 | 143k | StringRef(Modifier, ModifierLen), |
1024 | 143k | StringRef(Argument, ArgumentLen), |
1025 | 143k | FormattedArgs, |
1026 | 143k | OutStr, QualTypeVals); |
1027 | 143k | break; |
1028 | 12.2k | case DiagnosticsEngine::ak_qualtype_pair: { |
1029 | | // Create a struct with all the info needed for printing. |
1030 | 12.2k | TemplateDiffTypes TDT; |
1031 | 12.2k | TDT.FromType = getRawArg(ArgNo); |
1032 | 12.2k | TDT.ToType = getRawArg(ArgNo2); |
1033 | 12.2k | TDT.ElideType = getDiags()->ElideType; |
1034 | 12.2k | TDT.ShowColors = getDiags()->ShowColors; |
1035 | 12.2k | TDT.TemplateDiffUsed = false; |
1036 | 12.2k | intptr_t val = reinterpret_cast<intptr_t>(&TDT); |
1037 | | |
1038 | 12.2k | const char *ArgumentEnd = Argument + ArgumentLen; |
1039 | 12.2k | const char *Pipe = ScanFormat(Argument, ArgumentEnd, '|'); |
1040 | | |
1041 | | // Print the tree. If this diagnostic already has a tree, skip the |
1042 | | // second tree. |
1043 | 12.2k | if (getDiags()->PrintTemplateTree && Tree.empty()583 ) { |
1044 | 583 | TDT.PrintFromType = true; |
1045 | 583 | TDT.PrintTree = true; |
1046 | 583 | getDiags()->ConvertArgToString(Kind, val, |
1047 | 583 | StringRef(Modifier, ModifierLen), |
1048 | 583 | StringRef(Argument, ArgumentLen), |
1049 | 583 | FormattedArgs, |
1050 | 583 | Tree, QualTypeVals); |
1051 | | // If there is no tree information, fall back to regular printing. |
1052 | 583 | if (!Tree.empty()) { |
1053 | 349 | FormatDiagnostic(Pipe + 1, ArgumentEnd, OutStr); |
1054 | 349 | break; |
1055 | 349 | } |
1056 | 583 | } |
1057 | | |
1058 | | // Non-tree printing, also the fall-back when tree printing fails. |
1059 | | // The fall-back is triggered when the types compared are not templates. |
1060 | 11.8k | const char *FirstDollar = ScanFormat(Argument, ArgumentEnd, '$'); |
1061 | 11.8k | const char *SecondDollar = ScanFormat(FirstDollar + 1, ArgumentEnd, '$'); |
1062 | | |
1063 | | // Append before text |
1064 | 11.8k | FormatDiagnostic(Argument, FirstDollar, OutStr); |
1065 | | |
1066 | | // Append first type |
1067 | 11.8k | TDT.PrintTree = false; |
1068 | 11.8k | TDT.PrintFromType = true; |
1069 | 11.8k | getDiags()->ConvertArgToString(Kind, val, |
1070 | 11.8k | StringRef(Modifier, ModifierLen), |
1071 | 11.8k | StringRef(Argument, ArgumentLen), |
1072 | 11.8k | FormattedArgs, |
1073 | 11.8k | OutStr, QualTypeVals); |
1074 | 11.8k | if (!TDT.TemplateDiffUsed) |
1075 | 11.4k | FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_qualtype, |
1076 | 11.4k | TDT.FromType)); |
1077 | | |
1078 | | // Append middle text |
1079 | 11.8k | FormatDiagnostic(FirstDollar + 1, SecondDollar, OutStr); |
1080 | | |
1081 | | // Append second type |
1082 | 11.8k | TDT.PrintFromType = false; |
1083 | 11.8k | getDiags()->ConvertArgToString(Kind, val, |
1084 | 11.8k | StringRef(Modifier, ModifierLen), |
1085 | 11.8k | StringRef(Argument, ArgumentLen), |
1086 | 11.8k | FormattedArgs, |
1087 | 11.8k | OutStr, QualTypeVals); |
1088 | 11.8k | if (!TDT.TemplateDiffUsed) |
1089 | 11.4k | FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_qualtype, |
1090 | 11.4k | TDT.ToType)); |
1091 | | |
1092 | | // Append end text |
1093 | 11.8k | FormatDiagnostic(SecondDollar + 1, Pipe, OutStr); |
1094 | 11.8k | break; |
1095 | 12.2k | } |
1096 | 531k | } |
1097 | | |
1098 | | // Remember this argument info for subsequent formatting operations. Turn |
1099 | | // std::strings into a null terminated string to make it be the same case as |
1100 | | // all the other ones. |
1101 | 531k | if (Kind == DiagnosticsEngine::ak_qualtype_pair) |
1102 | 12.2k | continue; |
1103 | 518k | else if (Kind != DiagnosticsEngine::ak_std_string) |
1104 | 403k | FormattedArgs.push_back(std::make_pair(Kind, getRawArg(ArgNo))); |
1105 | 115k | else |
1106 | 115k | FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_c_string, |
1107 | 115k | (intptr_t)getArgStdStr(ArgNo).c_str())); |
1108 | 531k | } |
1109 | | |
1110 | | // Append the type tree to the end of the diagnostics. |
1111 | 598k | OutStr.append(Tree.begin(), Tree.end()); |
1112 | 598k | } |
1113 | | |
1114 | | StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level, unsigned ID, |
1115 | | StringRef Message) |
1116 | 0 | : ID(ID), Level(Level), Message(Message) {} |
1117 | | |
1118 | | StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level, |
1119 | | const Diagnostic &Info) |
1120 | 10.9k | : ID(Info.getID()), Level(Level) { |
1121 | 10.9k | assert((Info.getLocation().isInvalid() || Info.hasSourceManager()) && |
1122 | 10.9k | "Valid source location without setting a source manager for diagnostic"); |
1123 | 10.9k | if (Info.getLocation().isValid()) |
1124 | 10.9k | Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager()); |
1125 | 10.9k | SmallString<64> Message; |
1126 | 10.9k | Info.FormatDiagnostic(Message); |
1127 | 10.9k | this->Message.assign(Message.begin(), Message.end()); |
1128 | 10.9k | this->Ranges.assign(Info.getRanges().begin(), Info.getRanges().end()); |
1129 | 10.9k | this->FixIts.assign(Info.getFixItHints().begin(), Info.getFixItHints().end()); |
1130 | 10.9k | } |
1131 | | |
1132 | | StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level, unsigned ID, |
1133 | | StringRef Message, FullSourceLoc Loc, |
1134 | | ArrayRef<CharSourceRange> Ranges, |
1135 | | ArrayRef<FixItHint> FixIts) |
1136 | | : ID(ID), Level(Level), Loc(Loc), Message(Message), |
1137 | | Ranges(Ranges.begin(), Ranges.end()), FixIts(FixIts.begin(), FixIts.end()) |
1138 | 85 | { |
1139 | 85 | } |
1140 | | |
1141 | | llvm::raw_ostream &clang::operator<<(llvm::raw_ostream &OS, |
1142 | 0 | const StoredDiagnostic &SD) { |
1143 | 0 | if (SD.getLocation().hasManager()) |
1144 | 0 | OS << SD.getLocation().printToString(SD.getLocation().getManager()) << ": "; |
1145 | 0 | OS << SD.getMessage(); |
1146 | 0 | return OS; |
1147 | 0 | } |
1148 | | |
1149 | | /// IncludeInDiagnosticCounts - This method (whose default implementation |
1150 | | /// returns true) indicates whether the diagnostics handled by this |
1151 | | /// DiagnosticConsumer should be included in the number of diagnostics |
1152 | | /// reported by DiagnosticsEngine. |
1153 | 4.14M | bool DiagnosticConsumer::IncludeInDiagnosticCounts() const { return true; } |
1154 | | |
1155 | 0 | void IgnoringDiagConsumer::anchor() {} |
1156 | | |
1157 | 1.85k | ForwardingDiagnosticConsumer::~ForwardingDiagnosticConsumer() = default; |
1158 | | |
1159 | | void ForwardingDiagnosticConsumer::HandleDiagnostic( |
1160 | | DiagnosticsEngine::Level DiagLevel, |
1161 | 642 | const Diagnostic &Info) { |
1162 | 642 | Target.HandleDiagnostic(DiagLevel, Info); |
1163 | 642 | } |
1164 | | |
1165 | 24 | void ForwardingDiagnosticConsumer::clear() { |
1166 | 24 | DiagnosticConsumer::clear(); |
1167 | 24 | Target.clear(); |
1168 | 24 | } |
1169 | | |
1170 | 814 | bool ForwardingDiagnosticConsumer::IncludeInDiagnosticCounts() const { |
1171 | 814 | return Target.IncludeInDiagnosticCounts(); |
1172 | 814 | } |
1173 | | |
1174 | 94.9k | PartialDiagnostic::DiagStorageAllocator::DiagStorageAllocator() { |
1175 | 1.61M | for (unsigned I = 0; I != NumCached; ++I1.51M ) |
1176 | 1.51M | FreeList[I] = Cached + I; |
1177 | 94.9k | NumFreeListEntries = NumCached; |
1178 | 94.9k | } |
1179 | | |
1180 | 90.0k | PartialDiagnostic::DiagStorageAllocator::~DiagStorageAllocator() { |
1181 | | // Don't assert if we are in a CrashRecovery context, as this invariant may |
1182 | | // be invalidated during a crash. |
1183 | 90.0k | assert((NumFreeListEntries == NumCached || |
1184 | 90.0k | llvm::CrashRecoveryContext::isRecoveringFromCrash()) && |
1185 | 90.0k | "A partial is on the lam"); |
1186 | 90.0k | } |
1187 | | |
1188 | | char DiagnosticError::ID; |