clang 24.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 char ResultBuf[UNI_MAX_UTF8_BYTES_PER_CODE_POINT];
1126 char *ResultPtr = ResultBuf;
1127 if (!llvm::ConvertCodePointToUTF8(CP, ResultPtr))
1128 return SmallString<16>(llvm::formatv("<{0:X+}>", CP).str());
1130 StringRef(ResultBuf, ResultPtr - ResultBuf));
1131}
1132
1133void Diagnostic::FormatDiagnostic(const char *DiagStr, const char *DiagEnd,
1134 SmallVectorImpl<char> &OutStr) const {
1135 // When the diagnostic string is only "%0", the entire string is being given
1136 // by an outside source. Remove unprintable characters from this string
1137 // and skip all the other string processing.
1138 if (DiagEnd - DiagStr == 2 && StringRef(DiagStr, DiagEnd - DiagStr) == "%0" &&
1140 const std::string &S = getArgStdStr(0);
1141 EscapeStringForDiagnostic(S, OutStr);
1142 return;
1143 }
1144
1145 /// FormattedArgs - Keep track of all of the arguments formatted by
1146 /// ConvertArgToString and pass them into subsequent calls to
1147 /// ConvertArgToString, allowing the implementation to avoid redundancies in
1148 /// obvious cases.
1150
1151 /// QualTypeVals - Pass a vector of arrays so that QualType names can be
1152 /// compared to see if more information is needed to be printed.
1153 SmallVector<intptr_t, 2> QualTypeVals;
1154 SmallString<64> Tree;
1155
1156 for (unsigned i = 0, e = getNumArgs(); i < e; ++i)
1158 QualTypeVals.push_back(getRawArg(i));
1159
1160 while (DiagStr != DiagEnd) {
1161 if (DiagStr[0] != '%') {
1162 // Append non-%0 substrings to Str if we have one.
1163 const char *StrEnd = std::find(DiagStr, DiagEnd, '%');
1164 OutStr.append(DiagStr, StrEnd);
1165 DiagStr = StrEnd;
1166 continue;
1167 } else if (isPunctuation(DiagStr[1])) {
1168 OutStr.push_back(DiagStr[1]); // %% -> %.
1169 DiagStr += 2;
1170 continue;
1171 }
1172
1173 // Skip the %.
1174 ++DiagStr;
1175
1176 // This must be a placeholder for a diagnostic argument. The format for a
1177 // placeholder is one of "%0", "%modifier0", or "%modifier{arguments}0".
1178 // The digit is a number from 0-9 indicating which argument this comes from.
1179 // The modifier is a string of digits from the set [-a-z]+, arguments is a
1180 // brace enclosed string.
1181 const char *Modifier = nullptr, *Argument = nullptr;
1182 unsigned ModifierLen = 0, ArgumentLen = 0;
1183
1184 // Check to see if we have a modifier. If so eat it.
1185 if (!isDigit(DiagStr[0])) {
1186 Modifier = DiagStr;
1187 while (DiagStr[0] == '-' || (DiagStr[0] >= 'a' && DiagStr[0] <= 'z'))
1188 ++DiagStr;
1189 ModifierLen = DiagStr - Modifier;
1190
1191 // If we have an argument, get it next.
1192 if (DiagStr[0] == '{') {
1193 ++DiagStr; // Skip {.
1194 Argument = DiagStr;
1195
1196 DiagStr = ScanFormat(DiagStr, DiagEnd, '}');
1197 assert(DiagStr != DiagEnd && "Mismatched {}'s in diagnostic string!");
1198 ArgumentLen = DiagStr - Argument;
1199 ++DiagStr; // Skip }.
1200 }
1201 }
1202
1203 assert(isDigit(*DiagStr) && "Invalid format for argument in diagnostic");
1204 unsigned ArgNo = *DiagStr++ - '0';
1205
1206 // Only used for type diffing.
1207 unsigned ArgNo2 = ArgNo;
1208
1210 if (ModifierIs(Modifier, ModifierLen, "diff")) {
1211 assert(*DiagStr == ',' && isDigit(*(DiagStr + 1)) &&
1212 "Invalid format for diff modifier");
1213 ++DiagStr; // Comma.
1214 ArgNo2 = *DiagStr++ - '0';
1216 if (Kind == DiagnosticsEngine::ak_qualtype &&
1219 else {
1220 // %diff only supports QualTypes. For other kinds of arguments,
1221 // use the default printing. For example, if the modifier is:
1222 // "%diff{compare $ to $|other text}1,2"
1223 // treat it as:
1224 // "compare %1 to %2"
1225 const char *ArgumentEnd = Argument + ArgumentLen;
1226 const char *Pipe = ScanFormat(Argument, ArgumentEnd, '|');
1227 assert(ScanFormat(Pipe + 1, ArgumentEnd, '|') == ArgumentEnd &&
1228 "Found too many '|'s in a %diff modifier!");
1229 const char *FirstDollar = ScanFormat(Argument, Pipe, '$');
1230 const char *SecondDollar = ScanFormat(FirstDollar + 1, Pipe, '$');
1231 const char ArgStr1[] = {'%', static_cast<char>('0' + ArgNo)};
1232 const char ArgStr2[] = {'%', static_cast<char>('0' + ArgNo2)};
1233 FormatDiagnostic(Argument, FirstDollar, OutStr);
1234 FormatDiagnostic(ArgStr1, ArgStr1 + 2, OutStr);
1235 FormatDiagnostic(FirstDollar + 1, SecondDollar, OutStr);
1236 FormatDiagnostic(ArgStr2, ArgStr2 + 2, OutStr);
1237 FormatDiagnostic(SecondDollar + 1, Pipe, OutStr);
1238 continue;
1239 }
1240 }
1241
1242 switch (Kind) {
1243 // ---- STRINGS ----
1246 StringRef S = [&]() -> StringRef {
1248 return getArgStdStr(ArgNo);
1249 const char *SZ = getArgCStr(ArgNo);
1250 // Don't crash if get passed a null pointer by accident.
1251 return SZ ? SZ : "(null)";
1252 }();
1253 bool Quoted = false;
1254 if (ModifierIs(Modifier, ModifierLen, "quoted")) {
1255 Quoted = true;
1256 OutStr.push_back('\'');
1257 } else {
1258 assert(ModifierLen == 0 && "unknown modifier for string");
1259 }
1260 EscapeStringForDiagnostic(S, OutStr);
1261 if (Quoted)
1262 OutStr.push_back('\'');
1263 break;
1264 }
1265 // ---- INTEGERS ----
1267 int64_t Val = getArgSInt(ArgNo);
1268
1269 if (ModifierIs(Modifier, ModifierLen, "select")) {
1270 HandleSelectModifier(*this, (unsigned)Val, Argument, ArgumentLen,
1271 OutStr);
1272 } else if (ModifierIs(Modifier, ModifierLen, "s")) {
1273 HandleIntegerSModifier(Val, OutStr);
1274 } else if (ModifierIs(Modifier, ModifierLen, "plural")) {
1275 HandlePluralModifier(*this, (unsigned)Val, Argument, ArgumentLen,
1276 OutStr);
1277 } else if (ModifierIs(Modifier, ModifierLen, "ordinal")) {
1278 HandleOrdinalModifier((unsigned)Val, OutStr);
1279 } else if (ModifierIs(Modifier, ModifierLen, "human")) {
1280 HandleIntegerHumanModifier(Val, OutStr);
1281 } else {
1282 assert(ModifierLen == 0 && "Unknown integer modifier");
1283 llvm::raw_svector_ostream(OutStr) << Val;
1284 }
1285 break;
1286 }
1288 uint64_t Val = getArgUInt(ArgNo);
1289
1290 if (ModifierIs(Modifier, ModifierLen, "select")) {
1291 HandleSelectModifier(*this, Val, Argument, ArgumentLen, OutStr);
1292 } else if (ModifierIs(Modifier, ModifierLen, "s")) {
1293 HandleIntegerSModifier(Val, OutStr);
1294 } else if (ModifierIs(Modifier, ModifierLen, "plural")) {
1295 HandlePluralModifier(*this, (unsigned)Val, Argument, ArgumentLen,
1296 OutStr);
1297 } else if (ModifierIs(Modifier, ModifierLen, "ordinal")) {
1298 HandleOrdinalModifier(Val, OutStr);
1299 } else if (ModifierIs(Modifier, ModifierLen, "human")) {
1300 HandleIntegerHumanModifier(Val, OutStr);
1301 } else {
1302 assert(ModifierLen == 0 && "Unknown integer modifier");
1303 llvm::raw_svector_ostream(OutStr) << Val;
1304 }
1305 break;
1306 }
1307 // ---- TOKEN SPELLINGS ----
1309 tok::TokenKind Kind = static_cast<tok::TokenKind>(getRawArg(ArgNo));
1310 assert(ModifierLen == 0 && "No modifiers for token kinds yet");
1311
1312 llvm::raw_svector_ostream Out(OutStr);
1313 if (const char *S = tok::getPunctuatorSpelling(Kind))
1314 // Quoted token spelling for punctuators.
1315 Out << '\'' << S << '\'';
1316 else if ((S = tok::getKeywordSpelling(Kind)))
1317 // Unquoted token spelling for keywords.
1318 Out << S;
1319 else if ((S = getTokenDescForDiagnostic(Kind)))
1320 // Unquoted translatable token name.
1321 Out << S;
1322 else if ((S = tok::getTokenName(Kind)))
1323 // Debug name, shouldn't appear in user-facing diagnostics.
1324 Out << '<' << S << '>';
1325 else
1326 Out << "(null)";
1327 break;
1328 }
1329 // ---- NAMES and TYPES ----
1331 const IdentifierInfo *II = getArgIdentifier(ArgNo);
1332 assert(ModifierLen == 0 && "No modifiers for strings yet");
1333
1334 // Don't crash if get passed a null pointer by accident.
1335 if (!II) {
1336 const char *S = "(null)";
1337 OutStr.append(S, S + strlen(S));
1338 continue;
1339 }
1340
1341 llvm::raw_svector_ostream(OutStr) << '\'' << II->getName() << '\'';
1342 break;
1343 }
1354 getDiags()->ConvertArgToString(Kind, getRawArg(ArgNo),
1355 StringRef(Modifier, ModifierLen),
1356 StringRef(Argument, ArgumentLen),
1357 FormattedArgs, OutStr, QualTypeVals);
1358 break;
1360 // Create a struct with all the info needed for printing.
1362 TDT.FromType = getRawArg(ArgNo);
1363 TDT.ToType = getRawArg(ArgNo2);
1364 TDT.ElideType = getDiags()->ElideType;
1365 TDT.ShowColors = getDiags()->ShowColors;
1366 TDT.TemplateDiffUsed = false;
1367 intptr_t val = reinterpret_cast<intptr_t>(&TDT);
1368
1369 const char *ArgumentEnd = Argument + ArgumentLen;
1370 const char *Pipe = ScanFormat(Argument, ArgumentEnd, '|');
1371
1372 // Print the tree. If this diagnostic already has a tree, skip the
1373 // second tree.
1374 if (getDiags()->PrintTemplateTree && Tree.empty()) {
1375 TDT.PrintFromType = true;
1376 TDT.PrintTree = true;
1377 getDiags()->ConvertArgToString(Kind, val,
1378 StringRef(Modifier, ModifierLen),
1379 StringRef(Argument, ArgumentLen),
1380 FormattedArgs, Tree, QualTypeVals);
1381 // If there is no tree information, fall back to regular printing.
1382 if (!Tree.empty()) {
1383 FormatDiagnostic(Pipe + 1, ArgumentEnd, OutStr);
1384 break;
1385 }
1386 }
1387
1388 // Non-tree printing, also the fall-back when tree printing fails.
1389 // The fall-back is triggered when the types compared are not templates.
1390 const char *FirstDollar = ScanFormat(Argument, ArgumentEnd, '$');
1391 const char *SecondDollar = ScanFormat(FirstDollar + 1, ArgumentEnd, '$');
1392
1393 // Append before text
1394 FormatDiagnostic(Argument, FirstDollar, OutStr);
1395
1396 // Append first type
1397 TDT.PrintTree = false;
1398 TDT.PrintFromType = true;
1399 getDiags()->ConvertArgToString(Kind, val,
1400 StringRef(Modifier, ModifierLen),
1401 StringRef(Argument, ArgumentLen),
1402 FormattedArgs, OutStr, QualTypeVals);
1403 if (!TDT.TemplateDiffUsed)
1404 FormattedArgs.push_back(
1405 std::make_pair(DiagnosticsEngine::ak_qualtype, TDT.FromType));
1406
1407 // Append middle text
1408 FormatDiagnostic(FirstDollar + 1, SecondDollar, OutStr);
1409
1410 // Append second type
1411 TDT.PrintFromType = false;
1412 getDiags()->ConvertArgToString(Kind, val,
1413 StringRef(Modifier, ModifierLen),
1414 StringRef(Argument, ArgumentLen),
1415 FormattedArgs, OutStr, QualTypeVals);
1416 if (!TDT.TemplateDiffUsed)
1417 FormattedArgs.push_back(
1418 std::make_pair(DiagnosticsEngine::ak_qualtype, TDT.ToType));
1419
1420 // Append end text
1421 FormatDiagnostic(SecondDollar + 1, Pipe, OutStr);
1422 break;
1423 }
1424 }
1425
1426 // Remember this argument info for subsequent formatting operations. Turn
1427 // std::strings into a null terminated string to make it be the same case as
1428 // all the other ones.
1430 continue;
1431 else if (Kind != DiagnosticsEngine::ak_std_string)
1432 FormattedArgs.push_back(std::make_pair(Kind, getRawArg(ArgNo)));
1433 else
1434 FormattedArgs.push_back(
1435 std::make_pair(DiagnosticsEngine::ak_c_string,
1436 (intptr_t)getArgStdStr(ArgNo).c_str()));
1437 }
1438
1439 // Append the type tree to the end of the diagnostics.
1440 OutStr.append(Tree.begin(), Tree.end());
1441}
1442
1444 StringRef Message)
1445 : ID(ID), Level(Level), Message(Message) {}
1446
1448 const Diagnostic &Info)
1449 : ID(Info.getID()), Level(Level) {
1450 assert(
1451 (Info.getLocation().isInvalid() || Info.hasSourceManager()) &&
1452 "Valid source location without setting a source manager for diagnostic");
1453 if (Info.getLocation().isValid())
1454 Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
1455 SmallString<64> Message;
1456 Info.FormatDiagnostic(Message);
1457 this->Message.assign(Message.begin(), Message.end());
1458 this->Ranges.assign(Info.getRanges().begin(), Info.getRanges().end());
1459 this->FixIts.assign(Info.getFixItHints().begin(), Info.getFixItHints().end());
1460}
1461
1463 StringRef Message, FullSourceLoc Loc,
1465 ArrayRef<FixItHint> FixIts)
1466 : ID(ID), Level(Level), Loc(Loc), Message(Message),
1467 Ranges(Ranges.begin(), Ranges.end()),
1468 FixIts(FixIts.begin(), FixIts.end()) {}
1469
1470llvm::raw_ostream &clang::operator<<(llvm::raw_ostream &OS,
1471 const StoredDiagnostic &SD) {
1472 if (SD.getLocation().hasManager())
1473 OS << SD.getLocation().printToString(SD.getLocation().getManager()) << ": ";
1474 OS << SD.getMessage();
1475 return OS;
1476}
1477
1478/// IncludeInDiagnosticCounts - This method (whose default implementation
1479/// returns true) indicates whether the diagnostics handled by this
1480/// DiagnosticConsumer should be included in the number of diagnostics
1481/// reported by DiagnosticsEngine.
1483
1484void IgnoringDiagConsumer::anchor() {}
1485
1487
1489 DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
1490 Target.HandleDiagnostic(DiagLevel, Info);
1491}
1492
1495 Target.clear();
1496}
1497
1499 return Target.IncludeInDiagnosticCounts();
1500}
1501
1503 for (unsigned I = 0; I != NumCached; ++I)
1504 FreeList[I] = Cached + I;
1505 NumFreeListEntries = NumCached;
1506}
1507
1509 // Don't assert if we are in a CrashRecovery context, as this invariant may
1510 // be invalidated during a crash.
1511 assert((NumFreeListEntries == NumCached ||
1512 llvm::CrashRecoveryContext::isRecoveringFromCrash()) &&
1513 "A partial is on the lam");
1514}
1515
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:33
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:906
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