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