clang 22.0.0git
Diagnostic.cpp
Go to the documentation of this file.
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
25#include "llvm/ADT/IntrusiveRefCntPtr.h"
26#include "llvm/ADT/SmallVector.h"
27#include "llvm/ADT/StringExtras.h"
28#include "llvm/ADT/StringMap.h"
29#include "llvm/ADT/StringRef.h"
30#include "llvm/Support/ConvertUTF.h"
31#include "llvm/Support/CrashRecoveryContext.h"
32#include "llvm/Support/Error.h"
33#include "llvm/Support/MemoryBuffer.h"
34#include "llvm/Support/SpecialCaseList.h"
35#include "llvm/Support/Unicode.h"
36#include "llvm/Support/VirtualFileSystem.h"
37#include "llvm/Support/raw_ostream.h"
38#include <algorithm>
39#include <cassert>
40#include <cstddef>
41#include <cstdint>
42#include <cstring>
43#include <memory>
44#include <string>
45#include <utility>
46#include <vector>
47
48using namespace clang;
49
51 DiagNullabilityKind nullability) {
52 DB.AddString(
53 ("'" +
54 getNullabilitySpelling(nullability.first,
55 /*isContextSensitive=*/nullability.second) +
56 "'")
57 .str());
58 return DB;
59}
60
62 llvm::Error &&E) {
63 DB.AddString(toString(std::move(E)));
64 return DB;
65}
66
67static void
69 StringRef Modifier, StringRef Argument,
71 SmallVectorImpl<char> &Output, void *Cookie,
72 ArrayRef<intptr_t> QualTypeVals) {
73 StringRef Str = "<can't format argument>";
74 Output.append(Str.begin(), Str.end());
75}
76
78 DiagnosticOptions &DiagOpts,
79 DiagnosticConsumer *client,
80 bool ShouldOwnClient)
81 : Diags(std::move(diags)), DiagOpts(DiagOpts) {
82 setClient(client, ShouldOwnClient);
83 ArgToStringFn = DummyArgToStringFn;
84
85 Reset();
86}
87
89 // If we own the diagnostic client, destroy it first so that it can access the
90 // engine from its destructor.
91 setClient(nullptr);
92}
93
94void DiagnosticsEngine::dump() const { DiagStatesByLoc.dump(*SourceMgr); }
95
96void DiagnosticsEngine::dump(StringRef DiagName) const {
97 DiagStatesByLoc.dump(*SourceMgr, DiagName);
98}
99
101 bool ShouldOwnClient) {
102 Owner.reset(ShouldOwnClient ? client : nullptr);
103 Client = client;
104}
105
107 DiagStateOnPushStack.push_back(GetCurDiagState());
108}
109
111 if (DiagStateOnPushStack.empty())
112 return false;
113
114 if (DiagStateOnPushStack.back() != GetCurDiagState()) {
115 // State changed at some point between push/pop.
116 PushDiagStatePoint(DiagStateOnPushStack.back(), Loc);
117 }
118 DiagStateOnPushStack.pop_back();
119 return true;
120}
121
122void DiagnosticsEngine::ResetPragmas() { DiagStatesByLoc.clear(/*Soft=*/true); }
123
124void DiagnosticsEngine::Reset(bool soft /*=false*/) {
125 ErrorOccurred = false;
126 UncompilableErrorOccurred = false;
127 FatalErrorOccurred = false;
128 UnrecoverableErrorOccurred = false;
129
130 NumWarnings = 0;
131 NumErrors = 0;
132 TrapNumErrorsOccurred = 0;
133 TrapNumUnrecoverableErrorsOccurred = 0;
134
135 LastDiagLevel = Ignored;
136
137 if (!soft) {
138 // Clear state related to #pragma diagnostic.
139 DiagStates.clear();
140 DiagStatesByLoc.clear(false);
141 DiagStateOnPushStack.clear();
142
143 // Create a DiagState and DiagStatePoint representing diagnostic changes
144 // through command-line.
145 DiagStates.emplace_back(*Diags);
146 DiagStatesByLoc.appendFirst(&DiagStates.back());
147 }
148}
149
151DiagnosticsEngine::DiagState::getOrAddMapping(diag::kind Diag) {
152 std::pair<iterator, bool> Result = DiagMap.try_emplace(Diag);
153
154 // Initialize the entry if we added it.
155 if (Result.second) {
156 Result.first->second = DiagIDs.getDefaultMapping(Diag);
158 DiagIDs.initCustomDiagMapping(Result.first->second, Diag);
159 }
160
161 return Result.first->second;
162}
163
164void DiagnosticsEngine::DiagStateMap::appendFirst(DiagState *State) {
165 assert(Files.empty() && "not first");
166 FirstDiagState = CurDiagState = State;
167 CurDiagStateLoc = SourceLocation();
168}
169
170void DiagnosticsEngine::DiagStateMap::append(SourceManager &SrcMgr,
171 SourceLocation Loc,
172 DiagState *State) {
173 CurDiagState = State;
174 CurDiagStateLoc = Loc;
175
176 FileIDAndOffset Decomp = SrcMgr.getDecomposedLoc(Loc);
177 unsigned Offset = Decomp.second;
178 for (File *F = getFile(SrcMgr, Decomp.first); F;
179 Offset = F->ParentOffset, F = F->Parent) {
180 F->HasLocalTransitions = true;
181 auto &Last = F->StateTransitions.back();
182 assert(Last.Offset <= Offset && "state transitions added out of order");
183
184 if (Last.Offset == Offset) {
185 if (Last.State == State)
186 break;
187 Last.State = State;
188 continue;
189 }
190
191 F->StateTransitions.push_back({State, Offset});
192 }
193}
194
195DiagnosticsEngine::DiagState *
196DiagnosticsEngine::DiagStateMap::lookup(SourceManager &SrcMgr,
197 SourceLocation Loc) const {
198 // Common case: we have not seen any diagnostic pragmas.
199 if (Files.empty())
200 return FirstDiagState;
201
202 FileIDAndOffset Decomp = SrcMgr.getDecomposedLoc(Loc);
203 const File *F = getFile(SrcMgr, Decomp.first);
204 return F->lookup(Decomp.second);
205}
206
207DiagnosticsEngine::DiagState *
208DiagnosticsEngine::DiagStateMap::File::lookup(unsigned Offset) const {
209 auto OnePastIt =
210 llvm::partition_point(StateTransitions, [=](const DiagStatePoint &P) {
211 return P.Offset <= Offset;
212 });
213 assert(OnePastIt != StateTransitions.begin() && "missing initial state");
214 return OnePastIt[-1].State;
215}
216
217DiagnosticsEngine::DiagStateMap::File *
218DiagnosticsEngine::DiagStateMap::getFile(SourceManager &SrcMgr,
219 FileID ID) const {
220 // Get or insert the File for this ID.
221 auto Range = Files.equal_range(ID);
222 if (Range.first != Range.second)
223 return &Range.first->second;
224 auto &F = Files.insert(Range.first, std::make_pair(ID, File()))->second;
225
226 // We created a new File; look up the diagnostic state at the start of it and
227 // initialize it.
228 if (ID.isValid()) {
229 FileIDAndOffset Decomp = SrcMgr.getDecomposedIncludedLoc(ID);
230 F.Parent = getFile(SrcMgr, Decomp.first);
231 F.ParentOffset = Decomp.second;
232 F.StateTransitions.push_back({F.Parent->lookup(Decomp.second), 0});
233 } else {
234 // This is the (imaginary) root file into which we pretend all top-level
235 // files are included; it descends from the initial state.
236 //
237 // FIXME: This doesn't guarantee that we use the same ordering as
238 // isBeforeInTranslationUnit in the cases where someone invented another
239 // top-level file and added diagnostic pragmas to it. See the code at the
240 // end of isBeforeInTranslationUnit for the quirks it deals with.
241 F.StateTransitions.push_back({FirstDiagState, 0});
242 }
243 return &F;
244}
245
246void DiagnosticsEngine::DiagStateMap::dump(SourceManager &SrcMgr,
247 StringRef DiagName) const {
248 llvm::errs() << "diagnostic state at ";
249 CurDiagStateLoc.print(llvm::errs(), SrcMgr);
250 llvm::errs() << ": " << CurDiagState << "\n";
251
252 for (auto &F : Files) {
253 FileID ID = F.first;
254 File &File = F.second;
255
256 bool PrintedOuterHeading = false;
257 auto PrintOuterHeading = [&] {
258 if (PrintedOuterHeading)
259 return;
260 PrintedOuterHeading = true;
261
262 llvm::errs() << "File " << &File << " <FileID " << ID.getHashValue()
263 << ">: " << SrcMgr.getBufferOrFake(ID).getBufferIdentifier();
264
265 if (F.second.Parent) {
266 FileIDAndOffset Decomp = SrcMgr.getDecomposedIncludedLoc(ID);
267 assert(File.ParentOffset == Decomp.second);
268 llvm::errs() << " parent " << File.Parent << " <FileID "
269 << Decomp.first.getHashValue() << "> ";
270 SrcMgr.getLocForStartOfFile(Decomp.first)
271 .getLocWithOffset(Decomp.second)
272 .print(llvm::errs(), SrcMgr);
273 }
274 if (File.HasLocalTransitions)
275 llvm::errs() << " has_local_transitions";
276 llvm::errs() << "\n";
277 };
278
279 if (DiagName.empty())
280 PrintOuterHeading();
281
282 for (DiagStatePoint &Transition : File.StateTransitions) {
283 bool PrintedInnerHeading = false;
284 auto PrintInnerHeading = [&] {
285 if (PrintedInnerHeading)
286 return;
287 PrintedInnerHeading = true;
288
289 PrintOuterHeading();
290 llvm::errs() << " ";
291 SrcMgr.getLocForStartOfFile(ID)
292 .getLocWithOffset(Transition.Offset)
293 .print(llvm::errs(), SrcMgr);
294 llvm::errs() << ": state " << Transition.State << ":\n";
295 };
296
297 if (DiagName.empty())
298 PrintInnerHeading();
299
300 for (auto &Mapping : *Transition.State) {
301 StringRef Option =
302 SrcMgr.getDiagnostics().Diags->getWarningOptionForDiag(
303 Mapping.first);
304 if (!DiagName.empty() && DiagName != Option)
305 continue;
306
307 PrintInnerHeading();
308 llvm::errs() << " ";
309 if (Option.empty())
310 llvm::errs() << "<unknown " << Mapping.first << ">";
311 else
312 llvm::errs() << Option;
313 llvm::errs() << ": ";
314
315 switch (Mapping.second.getSeverity()) {
317 llvm::errs() << "ignored";
318 break;
320 llvm::errs() << "remark";
321 break;
323 llvm::errs() << "warning";
324 break;
326 llvm::errs() << "error";
327 break;
329 llvm::errs() << "fatal";
330 break;
331 }
332
333 if (!Mapping.second.isUser())
334 llvm::errs() << " default";
335 if (Mapping.second.isPragma())
336 llvm::errs() << " pragma";
337 if (Mapping.second.hasNoWarningAsError())
338 llvm::errs() << " no-error";
339 if (Mapping.second.hasNoErrorAsFatal())
340 llvm::errs() << " no-fatal";
341 if (Mapping.second.wasUpgradedFromWarning())
342 llvm::errs() << " overruled";
343 llvm::errs() << "\n";
344 }
345 }
346 }
347}
348
349void DiagnosticsEngine::PushDiagStatePoint(DiagState *State,
350 SourceLocation Loc) {
351 assert(Loc.isValid() && "Adding invalid loc point");
352 DiagStatesByLoc.append(*SourceMgr, Loc, State);
353}
354
356 SourceLocation L) {
357 assert((Diags->isWarningOrExtension(Diag) ||
358 (Map == diag::Severity::Fatal || Map == diag::Severity::Error)) &&
359 "Cannot map errors into warnings!");
360 assert((L.isInvalid() || SourceMgr) && "No SourceMgr for valid location");
361
362 // A command line -Wfoo has an invalid L and cannot override error/fatal
363 // mapping, while a warning pragma can.
364 bool WasUpgradedFromWarning = false;
365 if (Map == diag::Severity::Warning && L.isInvalid()) {
366 DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag);
367 if (Info.getSeverity() == diag::Severity::Error ||
369 Map = Info.getSeverity();
370 WasUpgradedFromWarning = true;
371 }
372 }
373 DiagnosticMapping Mapping = makeUserMapping(Map, L);
374 Mapping.setUpgradedFromWarning(WasUpgradedFromWarning);
375
376 // Make sure we propagate the NoWarningAsError flag from an existing
377 // mapping (which may be the default mapping).
378 DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag);
380 Mapping.hasNoWarningAsError());
381
382 // Common case; setting all the diagnostics of a group in one place.
383 if ((L.isInvalid() || L == DiagStatesByLoc.getCurDiagStateLoc()) &&
384 DiagStatesByLoc.getCurDiagState()) {
385 // FIXME: This is theoretically wrong: if the current state is shared with
386 // some other location (via push/pop) we will change the state for that
387 // other location as well. This cannot currently happen, as we can't update
388 // the diagnostic state at the same location at which we pop.
389 DiagStatesByLoc.getCurDiagState()->setMapping(Diag, Mapping);
390 return;
391 }
392
393 // A diagnostic pragma occurred, create a new DiagState initialized with
394 // the current one and a new DiagStatePoint to record at which location
395 // the new state became active.
396 DiagStates.push_back(*GetCurDiagState());
397 DiagStates.back().setMapping(Diag, Mapping);
398 PushDiagStatePoint(&DiagStates.back(), L);
399}
400
402 StringRef Group, diag::Severity Map,
403 SourceLocation Loc) {
404 // Get the diagnostics in this group.
406 if (Diags->getDiagnosticsInGroup(Flavor, Group, GroupDiags))
407 return true;
408
409 Diags->setGroupSeverity(Group, Map);
410
411 // Set the mapping.
412 for (diag::kind Diag : GroupDiags)
413 setSeverity(Diag, Map, Loc);
414
415 return false;
416}
417
419 diag::Group Group,
420 diag::Severity Map,
421 SourceLocation Loc) {
422 return setSeverityForGroup(Flavor, Diags->getWarningOptionForGroup(Group),
423 Map, Loc);
424}
425
427 bool Enabled) {
428 // If we are enabling this feature, just set the diagnostic mappings to map to
429 // errors.
430 if (Enabled)
433 Diags->setGroupSeverity(Group, diag::Severity::Warning);
434
435 // Otherwise, we want to set the diagnostic mapping's "no Werror" bit, and
436 // potentially downgrade anything already mapped to be a warning.
437
438 // Get the diagnostics in this group.
440 if (Diags->getDiagnosticsInGroup(diag::Flavor::WarningOrError, Group,
441 GroupDiags))
442 return true;
443
444 // Perform the mapping change.
445 for (diag::kind Diag : GroupDiags) {
446 DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag);
447
448 if (Info.getSeverity() == diag::Severity::Error ||
451
452 Info.setNoWarningAsError(true);
453 }
454
455 return false;
456}
457
459 bool Enabled) {
460 // If we are enabling this feature, just set the diagnostic mappings to map to
461 // fatal errors.
462 if (Enabled)
465 Diags->setGroupSeverity(Group, diag::Severity::Error);
466
467 // Otherwise, we want to set the diagnostic mapping's "no Wfatal-errors" bit,
468 // and potentially downgrade anything already mapped to be a fatal error.
469
470 // Get the diagnostics in this group.
472 if (Diags->getDiagnosticsInGroup(diag::Flavor::WarningOrError, Group,
473 GroupDiags))
474 return true;
475
476 // Perform the mapping change.
477 for (diag::kind Diag : GroupDiags) {
478 DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag);
479
482
483 Info.setNoErrorAsFatal(true);
484 }
485
486 return false;
487}
488
490 diag::Severity Map,
491 SourceLocation Loc) {
492 // Get all the diagnostics.
493 std::vector<diag::kind> AllDiags;
494 DiagnosticIDs::getAllDiagnostics(Flavor, AllDiags);
495
496 // Set the mapping.
497 for (diag::kind Diag : AllDiags)
498 if (Diags->isWarningOrExtension(Diag))
499 setSeverity(Diag, Map, Loc);
500}
501
502namespace {
503// FIXME: We should isolate the parser from SpecialCaseList and just use it
504// here.
505class WarningsSpecialCaseList : public llvm::SpecialCaseList {
506public:
507 static std::unique_ptr<WarningsSpecialCaseList>
508 create(const llvm::MemoryBuffer &Input, std::string &Err);
509
510 // Section names refer to diagnostic groups, which cover multiple individual
511 // diagnostics. Expand diagnostic groups here to individual diagnostics.
512 // A diagnostic can have multiple diagnostic groups associated with it, we let
513 // the last section take precedence in such cases.
514 void processSections(DiagnosticsEngine &Diags);
515
516 bool isDiagSuppressed(diag::kind DiagId, SourceLocation DiagLoc,
517 const SourceManager &SM) const;
518
519private:
520 llvm::DenseMap<diag::kind, const Section *> DiagToSection;
521};
522} // namespace
523
524std::unique_ptr<WarningsSpecialCaseList>
525WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
526 std::string &Err) {
527 auto WarningSuppressionList = std::make_unique<WarningsSpecialCaseList>();
528 if (!WarningSuppressionList->createInternal(&Input, Err))
529 return nullptr;
530 return WarningSuppressionList;
531}
532
533void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
534 static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
535 for (const auto &SectionEntry : sections()) {
536 StringRef DiagGroup = SectionEntry.name();
537 if (DiagGroup == "*") {
538 // Drop the default section introduced by special case list, we only
539 // support exact diagnostic group names.
540 // FIXME: We should make this configurable in the parser instead.
541 continue;
542 }
543 SmallVector<diag::kind> GroupDiags;
544 if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
545 WarningFlavor, DiagGroup, GroupDiags)) {
546 StringRef Suggestion =
547 DiagnosticIDs::getNearestOption(WarningFlavor, DiagGroup);
548 Diags.Report(diag::warn_unknown_diag_option)
549 << static_cast<unsigned>(WarningFlavor) << DiagGroup
550 << !Suggestion.empty() << Suggestion;
551 continue;
552 }
553 for (diag::kind Diag : GroupDiags)
554 // We're intentionally overwriting any previous mappings here to make sure
555 // latest one takes precedence.
556 DiagToSection[Diag] = &SectionEntry;
557 }
558}
559
560void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &Input) {
561 std::string Error;
562 auto WarningSuppressionList = WarningsSpecialCaseList::create(Input, Error);
563 if (!WarningSuppressionList) {
564 // FIXME: Use a `%select` statement instead of printing `Error` as-is. This
565 // should help localization.
566 Report(diag::err_drv_malformed_warning_suppression_mapping)
567 << Input.getBufferIdentifier() << Error;
568 return;
569 }
570 WarningSuppressionList->processSections(*this);
571 DiagSuppressionMapping =
572 [WarningSuppressionList(std::move(WarningSuppressionList))](
573 diag::kind DiagId, SourceLocation DiagLoc, const SourceManager &SM) {
574 return WarningSuppressionList->isDiagSuppressed(DiagId, DiagLoc, SM);
575 };
576}
577
578bool WarningsSpecialCaseList::isDiagSuppressed(diag::kind DiagId,
579 SourceLocation DiagLoc,
580 const SourceManager &SM) const {
581 PresumedLoc PLoc = SM.getPresumedLoc(DiagLoc);
582 if (!PLoc.isValid())
583 return false;
584 const Section *DiagSection = DiagToSection.lookup(DiagId);
585 if (!DiagSection)
586 return false;
587
588 StringRef F = llvm::sys::path::remove_leading_dotslash(PLoc.getFilename());
589
590 unsigned LastSup = DiagSection->getLastMatch("src", F, "");
591 if (LastSup == 0)
592 return false;
593
594 unsigned LastEmit = DiagSection->getLastMatch("src", F, "emit");
595 return LastSup > LastEmit;
596}
597
599 SourceLocation DiagLoc) const {
600 if (!hasSourceManager() || !DiagSuppressionMapping)
601 return false;
602 return DiagSuppressionMapping(DiagId, DiagLoc, getSourceManager());
603}
604
606 DiagnosticStorage DiagStorage;
607 DiagStorage.DiagRanges.append(storedDiag.range_begin(),
608 storedDiag.range_end());
609
610 DiagStorage.FixItHints.append(storedDiag.fixit_begin(),
611 storedDiag.fixit_end());
612
613 assert(Client && "DiagnosticConsumer not set!");
614 Level DiagLevel = storedDiag.getLevel();
615 Diagnostic Info(this, storedDiag.getLocation(), storedDiag.getID(),
616 DiagStorage, storedDiag.getMessage());
617 Report(DiagLevel, Info);
618}
619
620void DiagnosticsEngine::Report(Level DiagLevel, const Diagnostic &Info) {
621 assert(DiagLevel != Ignored && "Cannot emit ignored diagnostics!");
622 assert(!getDiagnosticIDs()->isTrapDiag(Info.getID()) &&
623 "Trap diagnostics should not be consumed by the DiagnosticsEngine");
624 Client->HandleDiagnostic(DiagLevel, Info);
625 if (Client->IncludeInDiagnosticCounts()) {
626 if (DiagLevel == Warning)
627 ++NumWarnings;
628 }
629}
630
631/// ProcessDiag - This is the method used to report a diagnostic that is
632/// finally fully formed.
633bool DiagnosticsEngine::ProcessDiag(const DiagnosticBuilder &DiagBuilder) {
634 Diagnostic Info(this, DiagBuilder);
635
636 assert(getClient() && "DiagnosticClient not set!");
637
638 // Figure out the diagnostic level of this message.
639 unsigned DiagID = Info.getID();
640 Level DiagLevel = getDiagnosticLevel(DiagID, Info.getLocation());
641
642 // Update counts for DiagnosticErrorTrap even if a fatal error occurred
643 // or diagnostics are suppressed.
644 if (DiagLevel >= Error) {
645 ++TrapNumErrorsOccurred;
646 if (Diags->isUnrecoverable(DiagID))
647 ++TrapNumUnrecoverableErrorsOccurred;
648 }
649
650 if (SuppressAllDiagnostics)
651 return false;
652
653 if (DiagLevel != Note) {
654 // Record that a fatal error occurred only when we see a second
655 // non-note diagnostic. This allows notes to be attached to the
656 // fatal error, but suppresses any diagnostics that follow those
657 // notes.
658 if (LastDiagLevel == Fatal)
659 FatalErrorOccurred = true;
660
661 LastDiagLevel = DiagLevel;
662 }
663
664 // If a fatal error has already been emitted, silence all subsequent
665 // diagnostics.
666 if (FatalErrorOccurred) {
667 if (DiagLevel >= Error && Client->IncludeInDiagnosticCounts())
668 ++NumErrors;
669
670 return false;
671 }
672
673 // If the client doesn't care about this message, don't issue it. If this is
674 // a note and the last real diagnostic was ignored, ignore it too.
675 if (DiagLevel == Ignored || (DiagLevel == Note && LastDiagLevel == Ignored))
676 return false;
677
678 if (DiagLevel >= Error) {
679 if (Diags->isUnrecoverable(DiagID))
680 UnrecoverableErrorOccurred = true;
681
682 // Warnings which have been upgraded to errors do not prevent compilation.
683 if (Diags->isDefaultMappingAsError(DiagID))
684 UncompilableErrorOccurred = true;
685
686 ErrorOccurred = true;
687 if (Client->IncludeInDiagnosticCounts())
688 ++NumErrors;
689
690 // If we've emitted a lot of errors, emit a fatal error instead of it to
691 // stop a flood of bogus errors.
692 if (ErrorLimit && NumErrors > ErrorLimit && DiagLevel == Error) {
693 Report(diag::fatal_too_many_errors);
694 return false;
695 }
696 }
697
698 // Make sure we set FatalErrorOccurred to ensure that the notes from the
699 // diagnostic that caused `fatal_too_many_errors` won't be emitted.
700 if (Info.getID() == diag::fatal_too_many_errors)
701 FatalErrorOccurred = true;
702
703 // Finally, report it.
704 Report(DiagLevel, Info);
705 return true;
706}
707
709 bool Force) {
710 assert(getClient() && "DiagnosticClient not set!");
711
712 bool Emitted;
713 if (Force) {
714 Diagnostic Info(this, DB);
715
716 // Figure out the diagnostic level of this message.
717 Level DiagLevel = getDiagnosticLevel(Info.getID(), Info.getLocation());
718
719 // Emit the diagnostic regardless of suppression level.
720 Emitted = DiagLevel != Ignored;
721 if (Emitted)
722 Report(DiagLevel, Info);
723 } else {
724 // Process the diagnostic, sending the accumulated information to the
725 // DiagnosticConsumer.
726 Emitted = ProcessDiag(DB);
727 }
728
729 return Emitted;
730}
731
732DiagnosticBuilder::DiagnosticBuilder(DiagnosticsEngine *DiagObj,
733 SourceLocation DiagLoc, unsigned DiagID)
734 : StreamingDiagnostic(DiagObj->DiagAllocator), DiagObj(DiagObj),
735 DiagLoc(DiagLoc), DiagID(DiagID), IsActive(true) {
736 assert(DiagObj && "DiagnosticBuilder requires a valid DiagnosticsEngine!");
737}
738
739DiagnosticBuilder::DiagnosticBuilder(const DiagnosticBuilder &D)
741 DiagLoc = D.DiagLoc;
742 DiagID = D.DiagID;
743 FlagValue = D.FlagValue;
744 DiagObj = D.DiagObj;
746 D.DiagStorage = nullptr;
748 IsActive = D.IsActive;
749 IsForceEmit = D.IsForceEmit;
750 D.Clear();
751}
752
754 const DiagnosticBuilder &DiagBuilder)
755 : DiagObj(DO), DiagLoc(DiagBuilder.DiagLoc), DiagID(DiagBuilder.DiagID),
756 FlagValue(DiagBuilder.FlagValue), DiagStorage(*DiagBuilder.getStorage()) {
757}
758
760 unsigned DiagID, const DiagnosticStorage &DiagStorage,
761 StringRef StoredDiagMessage)
762 : DiagObj(DO), DiagLoc(DiagLoc), DiagID(DiagID), DiagStorage(DiagStorage),
763 StoredDiagMessage(StoredDiagMessage) {}
764
766
768 const Diagnostic &Info) {
770 return;
771
772 if (DiagLevel == DiagnosticsEngine::Warning)
773 ++NumWarnings;
774 else if (DiagLevel >= DiagnosticsEngine::Error)
775 ++NumErrors;
776}
777
778/// ModifierIs - Return true if the specified modifier matches specified string.
779template <std::size_t StrLen>
780static bool ModifierIs(const char *Modifier, unsigned ModifierLen,
781 const char (&Str)[StrLen]) {
782 return StrLen - 1 == ModifierLen && memcmp(Modifier, Str, StrLen - 1) == 0;
783}
784
785/// ScanForward - Scans forward, looking for the given character, skipping
786/// nested clauses and escaped characters.
787static const char *ScanFormat(const char *I, const char *E, char Target) {
788 unsigned Depth = 0;
789
790 for (; I != E; ++I) {
791 if (Depth == 0 && *I == Target)
792 return I;
793 if (Depth != 0 && *I == '}')
794 Depth--;
795
796 if (*I == '%') {
797 I++;
798 if (I == E)
799 break;
800
801 // Escaped characters get implicitly skipped here.
802
803 // Format specifier.
804 if (!isDigit(*I) && !isPunctuation(*I)) {
805 for (I++; I != E && !isDigit(*I) && *I != '{'; I++)
806 ;
807 if (I == E)
808 break;
809 if (*I == '{')
810 Depth++;
811 }
812 }
813 }
814 return E;
815}
816
817/// HandleSelectModifier - Handle the integer 'select' modifier. This is used
818/// like this: %select{foo|bar|baz}2. This means that the integer argument
819/// "%2" has a value from 0-2. If the value is 0, the diagnostic prints 'foo'.
820/// If the value is 1, it prints 'bar'. If it has the value 2, it prints 'baz'.
821/// This is very useful for certain classes of variant diagnostics.
822static void HandleSelectModifier(const Diagnostic &DInfo, unsigned ValNo,
823 const char *Argument, unsigned ArgumentLen,
824 SmallVectorImpl<char> &OutStr) {
825 const char *ArgumentEnd = Argument + ArgumentLen;
826
827 // Skip over 'ValNo' |'s.
828 while (ValNo) {
829 const char *NextVal = ScanFormat(Argument, ArgumentEnd, '|');
830 assert(NextVal != ArgumentEnd &&
831 "Value for integer select modifier was"
832 " larger than the number of options in the diagnostic string!");
833 Argument = NextVal + 1; // Skip this string.
834 --ValNo;
835 }
836
837 // Get the end of the value. This is either the } or the |.
838 const char *EndPtr = ScanFormat(Argument, ArgumentEnd, '|');
839
840 // Recursively format the result of the select clause into the output string.
841 DInfo.FormatDiagnostic(Argument, EndPtr, OutStr);
842}
843
844/// HandleIntegerSModifier - Handle the integer 's' modifier. This adds the
845/// letter 's' to the string if the value is not 1. This is used in cases like
846/// this: "you idiot, you have %4 parameter%s4!".
847static void HandleIntegerSModifier(unsigned ValNo,
848 SmallVectorImpl<char> &OutStr) {
849 if (ValNo != 1)
850 OutStr.push_back('s');
851}
852
853/// HandleOrdinalModifier - Handle the integer 'ord' modifier. This
854/// prints the ordinal form of the given integer, with 1 corresponding
855/// to the first ordinal. Currently this is hard-coded to use the
856/// English form.
857static void HandleOrdinalModifier(unsigned ValNo,
858 SmallVectorImpl<char> &OutStr) {
859 assert(ValNo != 0 && "ValNo must be strictly positive!");
860
861 llvm::raw_svector_ostream Out(OutStr);
862
863 // We could use text forms for the first N ordinals, but the numeric
864 // forms are actually nicer in diagnostics because they stand out.
865 Out << ValNo << llvm::getOrdinalSuffix(ValNo);
866}
867
868// 123 -> "123".
869// 1234 -> "1.23k".
870// 123456 -> "123.46k".
871// 1234567 -> "1.23M".
872// 1234567890 -> "1.23G".
873// 1234567890123 -> "1.23T".
874static void HandleIntegerHumanModifier(int64_t ValNo,
875 SmallVectorImpl<char> &OutStr) {
876 static constexpr std::array<std::pair<int64_t, char>, 4> Units = {
877 {{1'000'000'000'000L, 'T'},
878 {1'000'000'000L, 'G'},
879 {1'000'000L, 'M'},
880 {1'000L, 'k'}}};
881
882 llvm::raw_svector_ostream Out(OutStr);
883 if (ValNo < 0) {
884 Out << "-";
885 ValNo = -ValNo;
886 }
887 for (const auto &[UnitSize, UnitSign] : Units) {
888 if (ValNo >= UnitSize) {
889 Out << llvm::format("%0.2f%c", ValNo / static_cast<double>(UnitSize),
890 UnitSign);
891 return;
892 }
893 }
894 Out << ValNo;
895}
896
897/// PluralNumber - Parse an unsigned integer and advance Start.
898static unsigned PluralNumber(const char *&Start, const char *End) {
899 // Programming 101: Parse a decimal number :-)
900 unsigned Val = 0;
901 while (Start != End && *Start >= '0' && *Start <= '9') {
902 Val *= 10;
903 Val += *Start - '0';
904 ++Start;
905 }
906 return Val;
907}
908
909/// TestPluralRange - Test if Val is in the parsed range. Modifies Start.
910static bool TestPluralRange(unsigned Val, const char *&Start, const char *End) {
911 if (*Start != '[') {
912 unsigned Ref = PluralNumber(Start, End);
913 return Ref == Val;
914 }
915
916 ++Start;
917 unsigned Low = PluralNumber(Start, End);
918 assert(*Start == ',' && "Bad plural expression syntax: expected ,");
919 ++Start;
920 unsigned High = PluralNumber(Start, End);
921 assert(*Start == ']' && "Bad plural expression syntax: expected )");
922 ++Start;
923 return Low <= Val && Val <= High;
924}
925
926/// EvalPluralExpr - Actual expression evaluator for HandlePluralModifier.
927static bool EvalPluralExpr(unsigned ValNo, const char *Start, const char *End) {
928 // Empty condition?
929 if (*Start == ':')
930 return true;
931
932 while (true) {
933 char C = *Start;
934 if (C == '%') {
935 // Modulo expression
936 ++Start;
937 unsigned Arg = PluralNumber(Start, End);
938 assert(*Start == '=' && "Bad plural expression syntax: expected =");
939 ++Start;
940 unsigned ValMod = ValNo % Arg;
941 if (TestPluralRange(ValMod, Start, End))
942 return true;
943 } else {
944 assert((C == '[' || (C >= '0' && C <= '9')) &&
945 "Bad plural expression syntax: unexpected character");
946 // Range expression
947 if (TestPluralRange(ValNo, Start, End))
948 return true;
949 }
950
951 // Scan for next or-expr part.
952 Start = std::find(Start, End, ',');
953 if (Start == End)
954 break;
955 ++Start;
956 }
957 return false;
958}
959
960/// HandlePluralModifier - Handle the integer 'plural' modifier. This is used
961/// for complex plural forms, or in languages where all plurals are complex.
962/// The syntax is: %plural{cond1:form1|cond2:form2|:form3}, where condn are
963/// conditions that are tested in order, the form corresponding to the first
964/// that applies being emitted. The empty condition is always true, making the
965/// last form a default case.
966/// Conditions are simple boolean expressions, where n is the number argument.
967/// Here are the rules.
968/// condition := expression | empty
969/// empty := -> always true
970/// expression := numeric [',' expression] -> logical or
971/// numeric := range -> true if n in range
972/// | '%' number '=' range -> true if n % number in range
973/// range := number
974/// | '[' number ',' number ']' -> ranges are inclusive both ends
975///
976/// Here are some examples from the GNU gettext manual written in this form:
977/// English:
978/// {1:form0|:form1}
979/// Latvian:
980/// {0:form2|%100=11,%10=0,%10=[2,9]:form1|:form0}
981/// Gaeilge:
982/// {1:form0|2:form1|:form2}
983/// Romanian:
984/// {1:form0|0,%100=[1,19]:form1|:form2}
985/// Lithuanian:
986/// {%10=0,%100=[10,19]:form2|%10=1:form0|:form1}
987/// Russian (requires repeated form):
988/// {%100=[11,14]:form2|%10=1:form0|%10=[2,4]:form1|:form2}
989/// Slovak
990/// {1:form0|[2,4]:form1|:form2}
991/// Polish (requires repeated form):
992/// {1:form0|%100=[10,20]:form2|%10=[2,4]:form1|:form2}
993static void HandlePluralModifier(const Diagnostic &DInfo, unsigned ValNo,
994 const char *Argument, unsigned ArgumentLen,
995 SmallVectorImpl<char> &OutStr) {
996 const char *ArgumentEnd = Argument + ArgumentLen;
997 while (true) {
998 assert(Argument < ArgumentEnd && "Plural expression didn't match.");
999 const char *ExprEnd = Argument;
1000 while (*ExprEnd != ':') {
1001 assert(ExprEnd != ArgumentEnd && "Plural missing expression end");
1002 ++ExprEnd;
1003 }
1004 if (EvalPluralExpr(ValNo, Argument, ExprEnd)) {
1005 Argument = ExprEnd + 1;
1006 ExprEnd = ScanFormat(Argument, ArgumentEnd, '|');
1007
1008 // Recursively format the result of the plural clause into the
1009 // output string.
1010 DInfo.FormatDiagnostic(Argument, ExprEnd, OutStr);
1011 return;
1012 }
1013 Argument = ScanFormat(Argument, ArgumentEnd - 1, '|') + 1;
1014 }
1015}
1016
1017/// Returns the friendly description for a token kind that will appear
1018/// without quotes in diagnostic messages. These strings may be translatable in
1019/// future.
1021 switch (Kind) {
1022 case tok::identifier:
1023 return "identifier";
1024 default:
1025 return nullptr;
1026 }
1027}
1028
1029/// FormatDiagnostic - Format this diagnostic into a string, substituting the
1030/// formal arguments into the %0 slots. The result is appended onto the Str
1031/// array.
1033 if (StoredDiagMessage.has_value()) {
1034 OutStr.append(StoredDiagMessage->begin(), StoredDiagMessage->end());
1035 return;
1036 }
1037
1038 StringRef Diag = getDiags()->getDiagnosticIDs()->getDescription(getID());
1039
1040 FormatDiagnostic(Diag.begin(), Diag.end(), OutStr);
1041}
1042
1043/// EscapeStringForDiagnostic - Append Str to the diagnostic buffer,
1044/// escaping non-printable characters and ill-formed code unit sequences.
1046 SmallVectorImpl<char> &OutStr) {
1047 OutStr.reserve(OutStr.size() + Str.size());
1048 auto *Begin = reinterpret_cast<const unsigned char *>(Str.data());
1049 llvm::raw_svector_ostream OutStream(OutStr);
1050 const unsigned char *End = Begin + Str.size();
1051 while (Begin != End) {
1052 // ASCII case
1053 if (isPrintable(*Begin) || isWhitespace(*Begin)) {
1054 OutStream << *Begin;
1055 ++Begin;
1056 continue;
1057 }
1058 if (llvm::isLegalUTF8Sequence(Begin, End)) {
1059 llvm::UTF32 CodepointValue;
1060 llvm::UTF32 *CpPtr = &CodepointValue;
1061 const unsigned char *CodepointBegin = Begin;
1062 const unsigned char *CodepointEnd =
1063 Begin + llvm::getNumBytesForUTF8(*Begin);
1064 llvm::ConversionResult Res = llvm::ConvertUTF8toUTF32(
1065 &Begin, CodepointEnd, &CpPtr, CpPtr + 1, llvm::strictConversion);
1066 (void)Res;
1067 assert(
1068 llvm::conversionOK == Res &&
1069 "the sequence is legal UTF-8 but we couldn't convert it to UTF-32");
1070 assert(Begin == CodepointEnd &&
1071 "we must be further along in the string now");
1072 if (llvm::sys::unicode::isPrintable(CodepointValue) ||
1073 llvm::sys::unicode::isFormatting(CodepointValue)) {
1074 OutStr.append(CodepointBegin, CodepointEnd);
1075 continue;
1076 }
1077 // Unprintable code point.
1078 OutStream << "<U+" << llvm::format_hex_no_prefix(CodepointValue, 4, true)
1079 << ">";
1080 continue;
1081 }
1082 // Invalid code unit.
1083 OutStream << "<" << llvm::format_hex_no_prefix(*Begin, 2, true) << ">";
1084 ++Begin;
1085 }
1086}
1087
1088void Diagnostic::FormatDiagnostic(const char *DiagStr, const char *DiagEnd,
1089 SmallVectorImpl<char> &OutStr) const {
1090 // When the diagnostic string is only "%0", the entire string is being given
1091 // by an outside source. Remove unprintable characters from this string
1092 // and skip all the other string processing.
1093 if (DiagEnd - DiagStr == 2 && StringRef(DiagStr, DiagEnd - DiagStr) == "%0" &&
1095 const std::string &S = getArgStdStr(0);
1096 EscapeStringForDiagnostic(S, OutStr);
1097 return;
1098 }
1099
1100 /// FormattedArgs - Keep track of all of the arguments formatted by
1101 /// ConvertArgToString and pass them into subsequent calls to
1102 /// ConvertArgToString, allowing the implementation to avoid redundancies in
1103 /// obvious cases.
1105
1106 /// QualTypeVals - Pass a vector of arrays so that QualType names can be
1107 /// compared to see if more information is needed to be printed.
1108 SmallVector<intptr_t, 2> QualTypeVals;
1109 SmallString<64> Tree;
1110
1111 for (unsigned i = 0, e = getNumArgs(); i < e; ++i)
1113 QualTypeVals.push_back(getRawArg(i));
1114
1115 while (DiagStr != DiagEnd) {
1116 if (DiagStr[0] != '%') {
1117 // Append non-%0 substrings to Str if we have one.
1118 const char *StrEnd = std::find(DiagStr, DiagEnd, '%');
1119 OutStr.append(DiagStr, StrEnd);
1120 DiagStr = StrEnd;
1121 continue;
1122 } else if (isPunctuation(DiagStr[1])) {
1123 OutStr.push_back(DiagStr[1]); // %% -> %.
1124 DiagStr += 2;
1125 continue;
1126 }
1127
1128 // Skip the %.
1129 ++DiagStr;
1130
1131 // This must be a placeholder for a diagnostic argument. The format for a
1132 // placeholder is one of "%0", "%modifier0", or "%modifier{arguments}0".
1133 // The digit is a number from 0-9 indicating which argument this comes from.
1134 // The modifier is a string of digits from the set [-a-z]+, arguments is a
1135 // brace enclosed string.
1136 const char *Modifier = nullptr, *Argument = nullptr;
1137 unsigned ModifierLen = 0, ArgumentLen = 0;
1138
1139 // Check to see if we have a modifier. If so eat it.
1140 if (!isDigit(DiagStr[0])) {
1141 Modifier = DiagStr;
1142 while (DiagStr[0] == '-' || (DiagStr[0] >= 'a' && DiagStr[0] <= 'z'))
1143 ++DiagStr;
1144 ModifierLen = DiagStr - Modifier;
1145
1146 // If we have an argument, get it next.
1147 if (DiagStr[0] == '{') {
1148 ++DiagStr; // Skip {.
1149 Argument = DiagStr;
1150
1151 DiagStr = ScanFormat(DiagStr, DiagEnd, '}');
1152 assert(DiagStr != DiagEnd && "Mismatched {}'s in diagnostic string!");
1153 ArgumentLen = DiagStr - Argument;
1154 ++DiagStr; // Skip }.
1155 }
1156 }
1157
1158 assert(isDigit(*DiagStr) && "Invalid format for argument in diagnostic");
1159 unsigned ArgNo = *DiagStr++ - '0';
1160
1161 // Only used for type diffing.
1162 unsigned ArgNo2 = ArgNo;
1163
1165 if (ModifierIs(Modifier, ModifierLen, "diff")) {
1166 assert(*DiagStr == ',' && isDigit(*(DiagStr + 1)) &&
1167 "Invalid format for diff modifier");
1168 ++DiagStr; // Comma.
1169 ArgNo2 = *DiagStr++ - '0';
1171 if (Kind == DiagnosticsEngine::ak_qualtype &&
1174 else {
1175 // %diff only supports QualTypes. For other kinds of arguments,
1176 // use the default printing. For example, if the modifier is:
1177 // "%diff{compare $ to $|other text}1,2"
1178 // treat it as:
1179 // "compare %1 to %2"
1180 const char *ArgumentEnd = Argument + ArgumentLen;
1181 const char *Pipe = ScanFormat(Argument, ArgumentEnd, '|');
1182 assert(ScanFormat(Pipe + 1, ArgumentEnd, '|') == ArgumentEnd &&
1183 "Found too many '|'s in a %diff modifier!");
1184 const char *FirstDollar = ScanFormat(Argument, Pipe, '$');
1185 const char *SecondDollar = ScanFormat(FirstDollar + 1, Pipe, '$');
1186 const char ArgStr1[] = {'%', static_cast<char>('0' + ArgNo)};
1187 const char ArgStr2[] = {'%', static_cast<char>('0' + ArgNo2)};
1188 FormatDiagnostic(Argument, FirstDollar, OutStr);
1189 FormatDiagnostic(ArgStr1, ArgStr1 + 2, OutStr);
1190 FormatDiagnostic(FirstDollar + 1, SecondDollar, OutStr);
1191 FormatDiagnostic(ArgStr2, ArgStr2 + 2, OutStr);
1192 FormatDiagnostic(SecondDollar + 1, Pipe, OutStr);
1193 continue;
1194 }
1195 }
1196
1197 switch (Kind) {
1198 // ---- STRINGS ----
1201 StringRef S = [&]() -> StringRef {
1203 return getArgStdStr(ArgNo);
1204 const char *SZ = getArgCStr(ArgNo);
1205 // Don't crash if get passed a null pointer by accident.
1206 return SZ ? SZ : "(null)";
1207 }();
1208 bool Quoted = false;
1209 if (ModifierIs(Modifier, ModifierLen, "quoted")) {
1210 Quoted = true;
1211 OutStr.push_back('\'');
1212 } else {
1213 assert(ModifierLen == 0 && "unknown modifier for string");
1214 }
1215 EscapeStringForDiagnostic(S, OutStr);
1216 if (Quoted)
1217 OutStr.push_back('\'');
1218 break;
1219 }
1220 // ---- INTEGERS ----
1222 int64_t Val = getArgSInt(ArgNo);
1223
1224 if (ModifierIs(Modifier, ModifierLen, "select")) {
1225 HandleSelectModifier(*this, (unsigned)Val, Argument, ArgumentLen,
1226 OutStr);
1227 } else if (ModifierIs(Modifier, ModifierLen, "s")) {
1228 HandleIntegerSModifier(Val, OutStr);
1229 } else if (ModifierIs(Modifier, ModifierLen, "plural")) {
1230 HandlePluralModifier(*this, (unsigned)Val, Argument, ArgumentLen,
1231 OutStr);
1232 } else if (ModifierIs(Modifier, ModifierLen, "ordinal")) {
1233 HandleOrdinalModifier((unsigned)Val, OutStr);
1234 } else if (ModifierIs(Modifier, ModifierLen, "human")) {
1235 HandleIntegerHumanModifier(Val, OutStr);
1236 } else {
1237 assert(ModifierLen == 0 && "Unknown integer modifier");
1238 llvm::raw_svector_ostream(OutStr) << Val;
1239 }
1240 break;
1241 }
1243 uint64_t Val = getArgUInt(ArgNo);
1244
1245 if (ModifierIs(Modifier, ModifierLen, "select")) {
1246 HandleSelectModifier(*this, Val, Argument, ArgumentLen, OutStr);
1247 } else if (ModifierIs(Modifier, ModifierLen, "s")) {
1248 HandleIntegerSModifier(Val, OutStr);
1249 } else if (ModifierIs(Modifier, ModifierLen, "plural")) {
1250 HandlePluralModifier(*this, (unsigned)Val, Argument, ArgumentLen,
1251 OutStr);
1252 } else if (ModifierIs(Modifier, ModifierLen, "ordinal")) {
1253 HandleOrdinalModifier(Val, OutStr);
1254 } else if (ModifierIs(Modifier, ModifierLen, "human")) {
1255 HandleIntegerHumanModifier(Val, OutStr);
1256 } else {
1257 assert(ModifierLen == 0 && "Unknown integer modifier");
1258 llvm::raw_svector_ostream(OutStr) << Val;
1259 }
1260 break;
1261 }
1262 // ---- TOKEN SPELLINGS ----
1264 tok::TokenKind Kind = static_cast<tok::TokenKind>(getRawArg(ArgNo));
1265 assert(ModifierLen == 0 && "No modifiers for token kinds yet");
1266
1267 llvm::raw_svector_ostream Out(OutStr);
1268 if (const char *S = tok::getPunctuatorSpelling(Kind))
1269 // Quoted token spelling for punctuators.
1270 Out << '\'' << S << '\'';
1271 else if ((S = tok::getKeywordSpelling(Kind)))
1272 // Unquoted token spelling for keywords.
1273 Out << S;
1274 else if ((S = getTokenDescForDiagnostic(Kind)))
1275 // Unquoted translatable token name.
1276 Out << S;
1277 else if ((S = tok::getTokenName(Kind)))
1278 // Debug name, shouldn't appear in user-facing diagnostics.
1279 Out << '<' << S << '>';
1280 else
1281 Out << "(null)";
1282 break;
1283 }
1284 // ---- NAMES and TYPES ----
1286 const IdentifierInfo *II = getArgIdentifier(ArgNo);
1287 assert(ModifierLen == 0 && "No modifiers for strings yet");
1288
1289 // Don't crash if get passed a null pointer by accident.
1290 if (!II) {
1291 const char *S = "(null)";
1292 OutStr.append(S, S + strlen(S));
1293 continue;
1294 }
1295
1296 llvm::raw_svector_ostream(OutStr) << '\'' << II->getName() << '\'';
1297 break;
1298 }
1309 getDiags()->ConvertArgToString(Kind, getRawArg(ArgNo),
1310 StringRef(Modifier, ModifierLen),
1311 StringRef(Argument, ArgumentLen),
1312 FormattedArgs, OutStr, QualTypeVals);
1313 break;
1315 // Create a struct with all the info needed for printing.
1317 TDT.FromType = getRawArg(ArgNo);
1318 TDT.ToType = getRawArg(ArgNo2);
1319 TDT.ElideType = getDiags()->ElideType;
1320 TDT.ShowColors = getDiags()->ShowColors;
1321 TDT.TemplateDiffUsed = false;
1322 intptr_t val = reinterpret_cast<intptr_t>(&TDT);
1323
1324 const char *ArgumentEnd = Argument + ArgumentLen;
1325 const char *Pipe = ScanFormat(Argument, ArgumentEnd, '|');
1326
1327 // Print the tree. If this diagnostic already has a tree, skip the
1328 // second tree.
1329 if (getDiags()->PrintTemplateTree && Tree.empty()) {
1330 TDT.PrintFromType = true;
1331 TDT.PrintTree = true;
1332 getDiags()->ConvertArgToString(Kind, val,
1333 StringRef(Modifier, ModifierLen),
1334 StringRef(Argument, ArgumentLen),
1335 FormattedArgs, Tree, QualTypeVals);
1336 // If there is no tree information, fall back to regular printing.
1337 if (!Tree.empty()) {
1338 FormatDiagnostic(Pipe + 1, ArgumentEnd, OutStr);
1339 break;
1340 }
1341 }
1342
1343 // Non-tree printing, also the fall-back when tree printing fails.
1344 // The fall-back is triggered when the types compared are not templates.
1345 const char *FirstDollar = ScanFormat(Argument, ArgumentEnd, '$');
1346 const char *SecondDollar = ScanFormat(FirstDollar + 1, ArgumentEnd, '$');
1347
1348 // Append before text
1349 FormatDiagnostic(Argument, FirstDollar, OutStr);
1350
1351 // Append first type
1352 TDT.PrintTree = false;
1353 TDT.PrintFromType = true;
1354 getDiags()->ConvertArgToString(Kind, val,
1355 StringRef(Modifier, ModifierLen),
1356 StringRef(Argument, ArgumentLen),
1357 FormattedArgs, OutStr, QualTypeVals);
1358 if (!TDT.TemplateDiffUsed)
1359 FormattedArgs.push_back(
1360 std::make_pair(DiagnosticsEngine::ak_qualtype, TDT.FromType));
1361
1362 // Append middle text
1363 FormatDiagnostic(FirstDollar + 1, SecondDollar, OutStr);
1364
1365 // Append second type
1366 TDT.PrintFromType = false;
1367 getDiags()->ConvertArgToString(Kind, val,
1368 StringRef(Modifier, ModifierLen),
1369 StringRef(Argument, ArgumentLen),
1370 FormattedArgs, OutStr, QualTypeVals);
1371 if (!TDT.TemplateDiffUsed)
1372 FormattedArgs.push_back(
1373 std::make_pair(DiagnosticsEngine::ak_qualtype, TDT.ToType));
1374
1375 // Append end text
1376 FormatDiagnostic(SecondDollar + 1, Pipe, OutStr);
1377 break;
1378 }
1379 }
1380
1381 // Remember this argument info for subsequent formatting operations. Turn
1382 // std::strings into a null terminated string to make it be the same case as
1383 // all the other ones.
1385 continue;
1386 else if (Kind != DiagnosticsEngine::ak_std_string)
1387 FormattedArgs.push_back(std::make_pair(Kind, getRawArg(ArgNo)));
1388 else
1389 FormattedArgs.push_back(
1390 std::make_pair(DiagnosticsEngine::ak_c_string,
1391 (intptr_t)getArgStdStr(ArgNo).c_str()));
1392 }
1393
1394 // Append the type tree to the end of the diagnostics.
1395 OutStr.append(Tree.begin(), Tree.end());
1396}
1397
1399 StringRef Message)
1400 : ID(ID), Level(Level), Message(Message) {}
1401
1403 const Diagnostic &Info)
1404 : ID(Info.getID()), Level(Level) {
1405 assert(
1406 (Info.getLocation().isInvalid() || Info.hasSourceManager()) &&
1407 "Valid source location without setting a source manager for diagnostic");
1408 if (Info.getLocation().isValid())
1409 Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
1410 SmallString<64> Message;
1411 Info.FormatDiagnostic(Message);
1412 this->Message.assign(Message.begin(), Message.end());
1413 this->Ranges.assign(Info.getRanges().begin(), Info.getRanges().end());
1414 this->FixIts.assign(Info.getFixItHints().begin(), Info.getFixItHints().end());
1415}
1416
1418 StringRef Message, FullSourceLoc Loc,
1420 ArrayRef<FixItHint> FixIts)
1421 : ID(ID), Level(Level), Loc(Loc), Message(Message),
1422 Ranges(Ranges.begin(), Ranges.end()),
1423 FixIts(FixIts.begin(), FixIts.end()) {}
1424
1425llvm::raw_ostream &clang::operator<<(llvm::raw_ostream &OS,
1426 const StoredDiagnostic &SD) {
1427 if (SD.getLocation().hasManager())
1428 OS << SD.getLocation().printToString(SD.getLocation().getManager()) << ": ";
1429 OS << SD.getMessage();
1430 return OS;
1431}
1432
1433/// IncludeInDiagnosticCounts - This method (whose default implementation
1434/// returns true) indicates whether the diagnostics handled by this
1435/// DiagnosticConsumer should be included in the number of diagnostics
1436/// reported by DiagnosticsEngine.
1438
1439void IgnoringDiagConsumer::anchor() {}
1440
1442
1444 DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
1445 Target.HandleDiagnostic(DiagLevel, Info);
1446}
1447
1450 Target.clear();
1451}
1452
1454 return Target.IncludeInDiagnosticCounts();
1455}
1456
1458 for (unsigned I = 0; I != NumCached; ++I)
1459 FreeList[I] = Cached + I;
1460 NumFreeListEntries = NumCached;
1461}
1462
1464 // Don't assert if we are in a CrashRecovery context, as this invariant may
1465 // be invalidated during a crash.
1466 assert((NumFreeListEntries == NumCached ||
1467 llvm::CrashRecoveryContext::isRecoveringFromCrash()) &&
1468 "A partial is on the lam");
1469}
1470
static const char * ScanFormat(const char *I, const char *E, char Target)
ScanForward - Scans forward, looking for the given character, skipping nested clauses and escaped cha...
static void HandlePluralModifier(const Diagnostic &DInfo, unsigned ValNo, const char *Argument, unsigned ArgumentLen, SmallVectorImpl< char > &OutStr)
HandlePluralModifier - Handle the integer 'plural' modifier.
static void HandleIntegerSModifier(unsigned ValNo, SmallVectorImpl< char > &OutStr)
HandleIntegerSModifier - Handle the integer 's' modifier.
static void DummyArgToStringFn(DiagnosticsEngine::ArgumentKind AK, intptr_t QT, StringRef Modifier, StringRef Argument, ArrayRef< DiagnosticsEngine::ArgumentValue > PrevArgs, SmallVectorImpl< char > &Output, void *Cookie, ArrayRef< intptr_t > QualTypeVals)
static bool EvalPluralExpr(unsigned ValNo, const char *Start, const char *End)
EvalPluralExpr - Actual expression evaluator for HandlePluralModifier.
static void HandleIntegerHumanModifier(int64_t ValNo, SmallVectorImpl< char > &OutStr)
static unsigned PluralNumber(const char *&Start, const char *End)
PluralNumber - Parse an unsigned integer and advance Start.
static void HandleSelectModifier(const Diagnostic &DInfo, unsigned ValNo, const char *Argument, unsigned ArgumentLen, SmallVectorImpl< char > &OutStr)
HandleSelectModifier - Handle the integer 'select' modifier.
static bool ModifierIs(const char *Modifier, unsigned ModifierLen, const char(&Str)[StrLen])
ModifierIs - Return true if the specified modifier matches specified string.
static bool TestPluralRange(unsigned Val, const char *&Start, const char *End)
TestPluralRange - Test if Val is in the parsed range. Modifies Start.
static void HandleOrdinalModifier(unsigned ValNo, SmallVectorImpl< char > &OutStr)
HandleOrdinalModifier - Handle the integer 'ord' modifier.
static const char * getTokenDescForDiagnostic(tok::TokenKind Kind)
Returns the friendly description for a token kind that will appear without quotes in diagnostic messa...
Defines the Diagnostic-related interfaces.
static llvm::GlobalValue::DLLStorageClassTypes getStorage(CodeGenModule &CGM, StringRef Name)
Defines the Diagnostic IDs-related interfaces.
Defines the clang::IdentifierInfo, clang::IdentifierTable, and clang::Selector interfaces.
static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)
Produce a diagnostic highlighting some portion of a literal.
llvm::MachO::Target Target
Definition MachO.h:51
#define SM(sm)
static std::string toString(const clang::SanitizerSet &Sanitizers)
Produce a string containing comma-separated names of sanitizers in Sanitizers set.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Defines various enumerations that describe declaration and type specifiers.
Defines the clang::TokenKind enum and support functions.
A little helper class used to produce diagnostics.
void Clear() const
Clear out the current diagnostic.
friend class DiagnosticsEngine
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info)
Handle this diagnostic, reporting it to the user or capturing it to a log as needed.
unsigned NumErrors
Number of errors reported.
unsigned NumWarnings
Number of warnings reported.
virtual bool IncludeInDiagnosticCounts() const
Indicates whether the diagnostics handled by this DiagnosticConsumer should be included in the number...
void initCustomDiagMapping(DiagnosticMapping &, unsigned DiagID)
static StringRef getNearestOption(diag::Flavor Flavor, StringRef Group)
Get the diagnostic option with the closest edit distance to the given group name.
DiagnosticMapping getDefaultMapping(unsigned DiagID) const
Get the default mapping for this diagnostic.
static bool IsCustomDiag(diag::kind Diag)
static void getAllDiagnostics(diag::Flavor Flavor, std::vector< diag::kind > &Diags)
Get the set of all diagnostic IDs.
void setNoWarningAsError(bool Value)
void setSeverity(diag::Severity Value)
diag::Severity getSeverity() const
void setUpgradedFromWarning(bool Value)
void setNoErrorAsFatal(bool Value)
bool hasNoWarningAsError() const
Options for controlling the compiler diagnostics engine.
A little helper class (which is basically a smart pointer that forwards info from DiagnosticsEngine a...
Diagnostic(const DiagnosticsEngine *DO, const DiagnosticBuilder &DiagBuilder)
const SourceLocation & getLocation() const
const std::string & getArgStdStr(unsigned Idx) const
Return the provided argument string specified by Idx.
void FormatDiagnostic(SmallVectorImpl< char > &OutStr) const
Format this diagnostic into a string, substituting the formal arguments into the %0 slots.
uint64_t getRawArg(unsigned Idx) const
Return the specified non-string argument in an opaque form.
const char * getArgCStr(unsigned Idx) const
Return the specified C string argument.
const IdentifierInfo * getArgIdentifier(unsigned Idx) const
Return the specified IdentifierInfo argument.
SourceManager & getSourceManager() const
ArrayRef< FixItHint > getFixItHints() const
unsigned getNumArgs() const
bool hasSourceManager() const
unsigned getID() const
DiagnosticsEngine::ArgumentKind getArgKind(unsigned Idx) const
Return the kind of the specified index.
int64_t getArgSInt(unsigned Idx) const
Return the specified signed integer argument.
uint64_t getArgUInt(unsigned Idx) const
Return the specified unsigned integer argument.
ArrayRef< CharSourceRange > getRanges() const
Return an array reference for this diagnostic's ranges.
const DiagnosticsEngine * getDiags() const
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:232
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
bool hasSourceManager() const
Definition Diagnostic.h:617
bool EmitDiagnostic(const DiagnosticBuilder &DB, bool Force=false)
Emit the diagnostic.
void setDiagSuppressionMapping(llvm::MemoryBuffer &Input)
Diagnostic suppression mappings can be used to suppress specific diagnostics in specific files.
DiagnosticsEngine(IntrusiveRefCntPtr< DiagnosticIDs > Diags, DiagnosticOptions &DiagOpts, DiagnosticConsumer *client=nullptr, bool ShouldOwnClient=true)
bool isSuppressedViaMapping(diag::kind DiagId, SourceLocation DiagLoc) const
void setSeverityForAll(diag::Flavor Flavor, diag::Severity Map, SourceLocation Loc=SourceLocation())
Add the specified mapping to all diagnostics of the specified flavor.
LLVM_DUMP_METHOD void dump() const
void ResetPragmas()
We keep a cache of FileIDs for diagnostics mapped by pragmas.
void setClient(DiagnosticConsumer *client, bool ShouldOwnClient=true)
Set the diagnostic client associated with this diagnostic object.
SourceManager & getSourceManager() const
Definition Diagnostic.h:619
void pushMappings(SourceLocation Loc)
Copies the current DiagMappings and pushes the new copy onto the top of the stack.
void setSeverity(diag::kind Diag, diag::Severity Map, SourceLocation Loc)
This allows the client to specify that certain warnings are ignored.
Level
The level of the diagnostic, after it has been through mapping.
Definition Diagnostic.h:237
friend class DiagnosticBuilder
DiagnosticConsumer * getClient()
Definition Diagnostic.h:607
@ ak_nameddecl
NamedDecl *.
Definition Diagnostic.h:278
@ ak_declcontext
DeclContext *.
Definition Diagnostic.h:284
@ ak_addrspace
address space
Definition Diagnostic.h:266
@ ak_identifierinfo
IdentifierInfo.
Definition Diagnostic.h:263
@ ak_qualtype_pair
pair<QualType, QualType>
Definition Diagnostic.h:287
@ ak_attr_info
AttributeCommonInfo *.
Definition Diagnostic.h:296
@ ak_c_string
const char *
Definition Diagnostic.h:251
@ ak_declarationname
DeclarationName.
Definition Diagnostic.h:275
@ ak_tokenkind
enum TokenKind : unsigned
Definition Diagnostic.h:260
@ ak_std_string
std::string
Definition Diagnostic.h:248
@ ak_nestednamespec
NestedNameSpecifier *.
Definition Diagnostic.h:281
Level getDiagnosticLevel(unsigned DiagID, SourceLocation Loc) const
Based on the way the client configured the DiagnosticsEngine object, classify the specified diagnosti...
Definition Diagnostic.h:966
bool setDiagnosticGroupErrorAsFatal(StringRef Group, bool Enabled)
Set the error-as-fatal flag for the given diagnostic group.
bool setDiagnosticGroupWarningAsError(StringRef Group, bool Enabled)
Set the warning-as-error flag for the given diagnostic group.
void ConvertArgToString(ArgumentKind Kind, intptr_t Val, StringRef Modifier, StringRef Argument, ArrayRef< ArgumentValue > PrevArgs, SmallVectorImpl< char > &Output, ArrayRef< intptr_t > QualTypeVals) const
Converts a diagnostic argument (as an intptr_t) into the string that represents it.
Definition Diagnostic.h:912
bool setSeverityForGroup(diag::Flavor Flavor, StringRef Group, diag::Severity Map, SourceLocation Loc=SourceLocation())
Change an entire diagnostic group (e.g.
bool popMappings(SourceLocation Loc)
Pops the current DiagMappings off the top of the stack, causing the new top of the stack to be the ac...
const IntrusiveRefCntPtr< DiagnosticIDs > & getDiagnosticIDs() const
Definition Diagnostic.h:592
void Reset(bool soft=false)
Reset the state of the diagnostic object to its initial configuration.
bool IncludeInDiagnosticCounts() const override
Indicates whether the diagnostics handled by this DiagnosticConsumer should be included in the number...
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) override
Handle this diagnostic, reporting it to the user or capturing it to a log as needed.
A SourceLocation and its associated SourceManager.
bool hasManager() const
Checks whether the SourceManager is present.
const SourceManager & getManager() const
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
Represents an unpacked "presumed" location which can be presented to the user.
const char * getFilename() const
Return the presumed filename of this location.
Encodes a location in the source.
std::string printToString(const SourceManager &SM) const
bool isValid() const
Return true if this is a valid SourceLocation object.
void print(raw_ostream &OS, const SourceManager &SM) const
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
FileIDAndOffset getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
DiagnosticsEngine & getDiagnostics() const
llvm::MemoryBufferRef getBufferOrFake(FileID FID, SourceLocation Loc=SourceLocation()) const
Return the buffer for the specified FileID.
FileIDAndOffset getDecomposedIncludedLoc(FileID FID) const
Returns the "included/expanded in" decomposed location of the given FileID.
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file.
Represents a diagnostic in a form that can be retained until its corresponding source manager is dest...
unsigned getID() const
range_iterator range_begin() const
DiagnosticsEngine::Level getLevel() const
fixit_iterator fixit_begin() const
const FullSourceLoc & getLocation() const
range_iterator range_end() const
StringRef getMessage() const
fixit_iterator fixit_end() const
The streaming interface shared between DiagnosticBuilder and PartialDiagnostic.
DiagStorageAllocator * Allocator
Allocator used to allocate storage for this diagnostic.
DiagnosticStorage * DiagStorage
void AddString(StringRef V) const
Flavor
Flavors of diagnostics we can emit.
@ WarningOrError
A diagnostic that indicates a problem or potential problem.
unsigned kind
All of the diagnostics that can be emitted by the frontend.
Severity
Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs to either Ignore (nothing),...
@ Warning
Present this diagnostic as a warning.
@ Fatal
Present this diagnostic as a fatal error.
@ Error
Present this diagnostic as an error.
@ Remark
Present this diagnostic as a remark.
@ Ignored
Do not present this diagnostic, ignore it.
const char * getTokenName(TokenKind Kind) LLVM_READNONE
Determines the name of a token as used within the front end.
const char * getKeywordSpelling(TokenKind Kind) LLVM_READNONE
Determines the spelling of simple keyword and contextual keyword tokens like 'int' and 'dynamic_cast'...
TokenKind
Provides a simple uniform namespace for tokens from all C languages.
Definition TokenKinds.h:25
const char * getPunctuatorSpelling(TokenKind Kind) LLVM_READNONE
Determines the spelling of simple punctuation tokens like '!
The JSON file list parser is used to communicate input to InstallAPI.
LLVM_READONLY bool isPrintable(unsigned char c)
Return true if this character is an ASCII printable character; that is, a character that should take ...
Definition CharInfo.h:160
std::pair< FileID, unsigned > FileIDAndOffset
std::pair< NullabilityKind, bool > DiagNullabilityKind
A nullability kind paired with a bit indicating whether it used a context-sensitive keyword.
@ Result
The result type of a method or function.
Definition TypeBase.h:905
llvm::StringRef getNullabilitySpelling(NullabilityKind kind, bool isContextSensitive=false)
Retrieve the spelling of the given nullability kind.
void EscapeStringForDiagnostic(StringRef Str, SmallVectorImpl< char > &OutStr)
EscapeStringForDiagnostic - Append Str to the diagnostic buffer, escaping non-printable characters an...
LLVM_READONLY bool isDigit(unsigned char c)
Return true if this character is an ASCII digit: [0-9].
Definition CharInfo.h:114
LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...
Definition CharInfo.h:108
const StreamingDiagnostic & operator<<(const StreamingDiagnostic &DB, const ConceptReference *C)
Insertion operator for diagnostics.
LLVM_READONLY bool isPunctuation(unsigned char c)
Return true if this character is an ASCII punctuation character.
Definition CharInfo.h:152
__INTPTR_TYPE__ intptr_t
A signed integer type with the property that any valid pointer to void can be converted to this type,...
#define true
Definition stdbool.h:25
SmallVector< CharSourceRange, 8 > DiagRanges
The list of ranges added to this diagnostic.
Definition Diagnostic.h:182
SmallVector< FixItHint, 6 > FixItHints
If valid, provides a hint with some code to insert, remove, or modify at a particular position.
Definition Diagnostic.h:186