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