1 //===-- ClangDiagnosticsEmitter.cpp - Generate Clang diagnostics tables ---===// 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 // These tablegen backends emit Clang diagnostics tables. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "TableGenBackends.h" 14 #include "llvm/ADT/DenseSet.h" 15 #include "llvm/ADT/PointerUnion.h" 16 #include "llvm/ADT/STLExtras.h" 17 #include "llvm/ADT/SmallString.h" 18 #include "llvm/ADT/SmallVector.h" 19 #include "llvm/ADT/StringMap.h" 20 #include "llvm/ADT/StringSwitch.h" 21 #include "llvm/ADT/Twine.h" 22 #include "llvm/Support/Casting.h" 23 #include "llvm/TableGen/Error.h" 24 #include "llvm/TableGen/Record.h" 25 #include "llvm/TableGen/StringToOffsetTable.h" 26 #include "llvm/TableGen/TableGenBackend.h" 27 #include <algorithm> 28 #include <cctype> 29 #include <functional> 30 #include <map> 31 #include <optional> 32 #include <set> 33 using namespace llvm; 34 35 //===----------------------------------------------------------------------===// 36 // Diagnostic category computation code. 37 //===----------------------------------------------------------------------===// 38 39 namespace { 40 class DiagGroupParentMap { 41 const RecordKeeper &Records; 42 std::map<const Record *, std::vector<const Record *>> Mapping; 43 44 public: 45 DiagGroupParentMap(const RecordKeeper &records) : Records(records) { 46 for (const Record *Group : Records.getAllDerivedDefinitions("DiagGroup")) 47 for (const Record *SubGroup : Group->getValueAsListOfDefs("SubGroups")) 48 Mapping[SubGroup].push_back(Group); 49 } 50 51 ArrayRef<const Record *> getParents(const Record *Group) { 52 return Mapping[Group]; 53 } 54 }; 55 } // end anonymous namespace. 56 57 static StringRef 58 getCategoryFromDiagGroup(const Record *Group, 59 DiagGroupParentMap &DiagGroupParents) { 60 // If the DiagGroup has a category, return it. 61 StringRef CatName = Group->getValueAsString("CategoryName"); 62 if (!CatName.empty()) return CatName; 63 64 // The diag group may the subgroup of one or more other diagnostic groups, 65 // check these for a category as well. 66 for (const Record *Parent : DiagGroupParents.getParents(Group)) { 67 CatName = getCategoryFromDiagGroup(Parent, DiagGroupParents); 68 if (!CatName.empty()) return CatName; 69 } 70 return ""; 71 } 72 73 /// getDiagnosticCategory - Return the category that the specified diagnostic 74 /// lives in. 75 static StringRef getDiagnosticCategory(const Record *R, 76 DiagGroupParentMap &DiagGroupParents) { 77 // If the diagnostic is in a group, and that group has a category, use it. 78 if (const auto *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) { 79 // Check the diagnostic's diag group for a category. 80 StringRef CatName = 81 getCategoryFromDiagGroup(Group->getDef(), DiagGroupParents); 82 if (!CatName.empty()) return CatName; 83 } 84 85 // If the diagnostic itself has a category, get it. 86 return R->getValueAsString("CategoryName"); 87 } 88 89 namespace { 90 class DiagCategoryIDMap { 91 const RecordKeeper &Records; 92 StringMap<unsigned> CategoryIDs; 93 std::vector<StringRef> CategoryStrings; 94 95 public: 96 DiagCategoryIDMap(const RecordKeeper &records) : Records(records) { 97 DiagGroupParentMap ParentInfo(Records); 98 99 // The zero'th category is "". 100 CategoryStrings.push_back(""); 101 CategoryIDs[""] = 0; 102 103 for (const Record *Diag : 104 Records.getAllDerivedDefinitions("Diagnostic")) { 105 StringRef Category = getDiagnosticCategory(Diag, ParentInfo); 106 if (Category.empty()) continue; // Skip diags with no category. 107 108 unsigned &ID = CategoryIDs[Category]; 109 if (ID != 0) continue; // Already seen. 110 111 ID = CategoryStrings.size(); 112 CategoryStrings.push_back(Category); 113 } 114 } 115 116 unsigned getID(StringRef CategoryString) { 117 return CategoryIDs[CategoryString]; 118 } 119 120 typedef std::vector<StringRef>::const_iterator const_iterator; 121 const_iterator begin() const { return CategoryStrings.begin(); } 122 const_iterator end() const { return CategoryStrings.end(); } 123 }; 124 125 struct GroupInfo { 126 StringRef GroupName; 127 std::vector<const Record*> DiagsInGroup; 128 std::vector<StringRef> SubGroups; 129 unsigned IDNo = 0; 130 131 SmallVector<const Record *, 1> Defs; 132 133 GroupInfo() = default; 134 }; 135 } // end anonymous namespace. 136 137 static bool beforeThanCompare(const Record *LHS, const Record *RHS) { 138 assert(!LHS->getLoc().empty() && !RHS->getLoc().empty()); 139 return 140 LHS->getLoc().front().getPointer() < RHS->getLoc().front().getPointer(); 141 } 142 143 static bool diagGroupBeforeByName(const Record *LHS, const Record *RHS) { 144 return LHS->getValueAsString("GroupName") < 145 RHS->getValueAsString("GroupName"); 146 } 147 148 using DiagsInGroupTy = std::map<StringRef, GroupInfo>; 149 150 /// Invert the 1-[0/1] mapping of diags to group into a one to many 151 /// mapping of groups to diags in the group. 152 static void groupDiagnostics(ArrayRef<const Record *> Diags, 153 ArrayRef<const Record *> DiagGroups, 154 DiagsInGroupTy &DiagsInGroup) { 155 for (const Record *R : Diags) { 156 const auto *DI = dyn_cast<DefInit>(R->getValueInit("Group")); 157 if (!DI) 158 continue; 159 assert(R->getValueAsDef("Class")->getName() != "CLASS_NOTE" && 160 "Note can't be in a DiagGroup"); 161 StringRef GroupName = DI->getDef()->getValueAsString("GroupName"); 162 DiagsInGroup[GroupName].DiagsInGroup.push_back(R); 163 } 164 165 // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty 166 // groups (these are warnings that GCC supports that clang never produces). 167 for (const Record *Group : DiagGroups) { 168 GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")]; 169 GI.GroupName = Group->getName(); 170 GI.Defs.push_back(Group); 171 172 for (const Record *SubGroup : Group->getValueAsListOfDefs("SubGroups")) 173 GI.SubGroups.push_back(SubGroup->getValueAsString("GroupName")); 174 } 175 176 // Assign unique ID numbers to the groups. 177 for (auto [IdNo, Iter] : enumerate(DiagsInGroup)) 178 Iter.second.IDNo = IdNo; 179 180 // Warn if the same group is defined more than once (including implicitly). 181 for (auto &Group : DiagsInGroup) { 182 if (Group.second.Defs.size() == 1 && 183 (!Group.second.Defs.front()->isAnonymous() || 184 Group.second.DiagsInGroup.size() <= 1)) 185 continue; 186 187 bool First = true; 188 for (const Record *Def : Group.second.Defs) { 189 // Skip implicit definitions from diagnostics; we'll report those 190 // separately below. 191 bool IsImplicit = false; 192 for (const Record *Diag : Group.second.DiagsInGroup) { 193 if (cast<DefInit>(Diag->getValueInit("Group"))->getDef() == Def) { 194 IsImplicit = true; 195 break; 196 } 197 } 198 if (IsImplicit) 199 continue; 200 201 SMLoc Loc = Def->getLoc().front(); 202 if (First) { 203 SrcMgr.PrintMessage(Loc, SourceMgr::DK_Error, 204 Twine("group '") + Group.first + 205 "' is defined more than once"); 206 First = false; 207 } else { 208 SrcMgr.PrintMessage(Loc, SourceMgr::DK_Note, "also defined here"); 209 } 210 } 211 212 for (const Record *Diag : Group.second.DiagsInGroup) { 213 if (!cast<DefInit>(Diag->getValueInit("Group"))->getDef()->isAnonymous()) 214 continue; 215 216 SMLoc Loc = Diag->getLoc().front(); 217 if (First) { 218 SrcMgr.PrintMessage(Loc, SourceMgr::DK_Error, 219 Twine("group '") + Group.first + 220 "' is implicitly defined more than once"); 221 First = false; 222 } else { 223 SrcMgr.PrintMessage(Loc, SourceMgr::DK_Note, 224 "also implicitly defined here"); 225 } 226 } 227 } 228 } 229 230 //===----------------------------------------------------------------------===// 231 // Infer members of -Wpedantic. 232 //===----------------------------------------------------------------------===// 233 234 typedef std::vector<const Record *> RecordVec; 235 typedef DenseSet<const Record *> RecordSet; 236 typedef PointerUnion<RecordVec *, RecordSet *> VecOrSet; 237 238 namespace { 239 class InferPedantic { 240 typedef DenseMap<const Record *, std::pair<unsigned, std::optional<unsigned>>> 241 GMap; 242 243 DiagGroupParentMap &DiagGroupParents; 244 ArrayRef<const Record *> Diags; 245 const std::vector<const Record *> DiagGroups; 246 DiagsInGroupTy &DiagsInGroup; 247 DenseSet<const Record *> DiagsSet; 248 GMap GroupCount; 249 public: 250 InferPedantic(DiagGroupParentMap &DiagGroupParents, 251 ArrayRef<const Record *> Diags, 252 ArrayRef<const Record *> DiagGroups, 253 DiagsInGroupTy &DiagsInGroup) 254 : DiagGroupParents(DiagGroupParents), Diags(Diags), 255 DiagGroups(DiagGroups), DiagsInGroup(DiagsInGroup) {} 256 257 /// Compute the set of diagnostics and groups that are immediately 258 /// in -Wpedantic. 259 void compute(VecOrSet DiagsInPedantic, 260 VecOrSet GroupsInPedantic); 261 262 private: 263 /// Determine whether a group is a subgroup of another group. 264 bool isSubGroupOfGroup(const Record *Group, StringRef RootGroupName); 265 266 /// Determine if the diagnostic is an extension. 267 bool isExtension(const Record *Diag); 268 269 /// Determine if the diagnostic is off by default. 270 bool isOffByDefault(const Record *Diag); 271 272 /// Increment the count for a group, and transitively marked 273 /// parent groups when appropriate. 274 void markGroup(const Record *Group); 275 276 /// Return true if the diagnostic is in a pedantic group. 277 bool groupInPedantic(const Record *Group, bool increment = false); 278 }; 279 } // end anonymous namespace 280 281 bool InferPedantic::isSubGroupOfGroup(const Record *Group, StringRef GName) { 282 StringRef GroupName = Group->getValueAsString("GroupName"); 283 if (GName == GroupName) 284 return true; 285 286 for (const Record *Parent : DiagGroupParents.getParents(Group)) 287 if (isSubGroupOfGroup(Parent, GName)) 288 return true; 289 290 return false; 291 } 292 293 /// Determine if the diagnostic is an extension. 294 bool InferPedantic::isExtension(const Record *Diag) { 295 return Diag->getValueAsDef("Class")->getName() == "CLASS_EXTENSION"; 296 } 297 298 bool InferPedantic::isOffByDefault(const Record *Diag) { 299 return Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name") == 300 "Ignored"; 301 } 302 303 bool InferPedantic::groupInPedantic(const Record *Group, bool increment) { 304 GMap::mapped_type &V = GroupCount[Group]; 305 // Lazily compute the threshold value for the group count. 306 if (!V.second) { 307 const GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")]; 308 V.second = GI.SubGroups.size() + GI.DiagsInGroup.size(); 309 } 310 311 if (increment) 312 ++V.first; 313 314 // Consider a group in -Wpendatic IFF if has at least one diagnostic 315 // or subgroup AND all of those diagnostics and subgroups are covered 316 // by -Wpedantic via our computation. 317 return V.first != 0 && V.first == *V.second; 318 } 319 320 void InferPedantic::markGroup(const Record *Group) { 321 // If all the diagnostics and subgroups have been marked as being 322 // covered by -Wpedantic, increment the count of parent groups. Once the 323 // group's count is equal to the number of subgroups and diagnostics in 324 // that group, we can safely add this group to -Wpedantic. 325 if (groupInPedantic(Group, /* increment */ true)) 326 for (const Record *Parent : DiagGroupParents.getParents(Group)) 327 markGroup(Parent); 328 } 329 330 void InferPedantic::compute(VecOrSet DiagsInPedantic, 331 VecOrSet GroupsInPedantic) { 332 // All extensions that are not on by default are implicitly in the 333 // "pedantic" group. For those that aren't explicitly included in -Wpedantic, 334 // mark them for consideration to be included in -Wpedantic directly. 335 for (const Record *R : Diags) { 336 if (!isExtension(R) || !isOffByDefault(R)) 337 continue; 338 DiagsSet.insert(R); 339 if (const auto *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) { 340 const Record *GroupRec = Group->getDef(); 341 if (!isSubGroupOfGroup(GroupRec, "pedantic")) { 342 markGroup(GroupRec); 343 } 344 } 345 } 346 347 // Compute the set of diagnostics that are directly in -Wpedantic. We 348 // march through Diags a second time to ensure the results are emitted 349 // in deterministic order. 350 for (const Record *R : Diags) { 351 if (!DiagsSet.count(R)) 352 continue; 353 // Check if the group is implicitly in -Wpedantic. If so, 354 // the diagnostic should not be directly included in the -Wpedantic 355 // diagnostic group. 356 if (const auto *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) 357 if (groupInPedantic(Group->getDef())) 358 continue; 359 360 // The diagnostic is not included in a group that is (transitively) in 361 // -Wpedantic. Include it in -Wpedantic directly. 362 if (auto *V = DiagsInPedantic.dyn_cast<RecordVec *>()) 363 V->push_back(R); 364 else 365 cast<RecordSet *>(DiagsInPedantic)->insert(R); 366 } 367 368 if (!GroupsInPedantic) 369 return; 370 371 // Compute the set of groups that are directly in -Wpedantic. We 372 // march through the groups to ensure the results are emitted 373 /// in a deterministc order. 374 for (const Record *Group : DiagGroups) { 375 if (!groupInPedantic(Group)) 376 continue; 377 378 const std::vector<const Record *> &Parents = 379 DiagGroupParents.getParents(Group); 380 bool AllParentsInPedantic = 381 all_of(Parents, [&](const Record *R) { return groupInPedantic(R); }); 382 // If all the parents are in -Wpedantic, this means that this diagnostic 383 // group will be indirectly included by -Wpedantic already. In that 384 // case, do not add it directly to -Wpedantic. If the group has no 385 // parents, obviously it should go into -Wpedantic. 386 if (Parents.size() > 0 && AllParentsInPedantic) 387 continue; 388 389 if (auto *V = GroupsInPedantic.dyn_cast<RecordVec *>()) 390 V->push_back(Group); 391 else 392 cast<RecordSet *>(GroupsInPedantic)->insert(Group); 393 } 394 } 395 396 namespace { 397 enum PieceKind { 398 MultiPieceClass, 399 TextPieceClass, 400 PlaceholderPieceClass, 401 SelectPieceClass, 402 EnumSelectPieceClass, 403 PluralPieceClass, 404 DiffPieceClass, 405 SubstitutionPieceClass, 406 }; 407 408 enum ModifierType { 409 MT_Unknown, 410 MT_Placeholder, 411 MT_Select, 412 MT_EnumSelect, 413 MT_Sub, 414 MT_Plural, 415 MT_Diff, 416 MT_Ordinal, 417 MT_Human, 418 MT_S, 419 MT_Q, 420 MT_ObjCClass, 421 MT_ObjCInstance, 422 }; 423 424 static StringRef getModifierName(ModifierType MT) { 425 switch (MT) { 426 case MT_EnumSelect: 427 case MT_Select: 428 return "select"; 429 case MT_Sub: 430 return "sub"; 431 case MT_Diff: 432 return "diff"; 433 case MT_Plural: 434 return "plural"; 435 case MT_Ordinal: 436 return "ordinal"; 437 case MT_Human: 438 return "human"; 439 case MT_S: 440 return "s"; 441 case MT_Q: 442 return "q"; 443 case MT_Placeholder: 444 return ""; 445 case MT_ObjCClass: 446 return "objcclass"; 447 case MT_ObjCInstance: 448 return "objcinstance"; 449 case MT_Unknown: 450 llvm_unreachable("invalid modifier type"); 451 } 452 // Unhandled case 453 llvm_unreachable("invalid modifier type"); 454 } 455 456 struct Piece { 457 // This type and its derived classes are move-only. 458 Piece(PieceKind Kind) : ClassKind(Kind) {} 459 Piece(Piece const &O) = delete; 460 Piece &operator=(Piece const &) = delete; 461 virtual ~Piece() {} 462 463 PieceKind getPieceClass() const { return ClassKind; } 464 static bool classof(const Piece *) { return true; } 465 466 private: 467 PieceKind ClassKind; 468 }; 469 470 struct MultiPiece : Piece { 471 MultiPiece() : Piece(MultiPieceClass) {} 472 MultiPiece(std::vector<Piece *> Pieces) 473 : Piece(MultiPieceClass), Pieces(std::move(Pieces)) {} 474 475 std::vector<Piece *> Pieces; 476 477 static bool classof(const Piece *P) { 478 return P->getPieceClass() == MultiPieceClass; 479 } 480 }; 481 482 struct TextPiece : Piece { 483 StringRef Role; 484 std::string Text; 485 TextPiece(StringRef Text, StringRef Role = "") 486 : Piece(TextPieceClass), Role(Role), Text(Text.str()) {} 487 488 static bool classof(const Piece *P) { 489 return P->getPieceClass() == TextPieceClass; 490 } 491 }; 492 493 struct PlaceholderPiece : Piece { 494 ModifierType Kind; 495 int Index; 496 PlaceholderPiece(ModifierType Kind, int Index) 497 : Piece(PlaceholderPieceClass), Kind(Kind), Index(Index) {} 498 499 static bool classof(const Piece *P) { 500 return P->getPieceClass() == PlaceholderPieceClass; 501 } 502 }; 503 504 struct SelectPiece : Piece { 505 protected: 506 SelectPiece(PieceKind Kind, ModifierType ModKind) 507 : Piece(Kind), ModKind(ModKind) {} 508 509 public: 510 SelectPiece(ModifierType ModKind) : SelectPiece(SelectPieceClass, ModKind) {} 511 512 ModifierType ModKind; 513 std::vector<Piece *> Options; 514 int Index = 0; 515 516 static bool classof(const Piece *P) { 517 return P->getPieceClass() == SelectPieceClass || 518 P->getPieceClass() == EnumSelectPieceClass || 519 P->getPieceClass() == PluralPieceClass; 520 } 521 }; 522 523 struct EnumSelectPiece : SelectPiece { 524 EnumSelectPiece() : SelectPiece(EnumSelectPieceClass, MT_EnumSelect) {} 525 526 StringRef EnumName; 527 std::vector<StringRef> OptionEnumNames; 528 529 static bool classof(const Piece *P) { 530 return P->getPieceClass() == EnumSelectPieceClass; 531 } 532 }; 533 534 struct EnumValuePiece : Piece { 535 ModifierType Kind; 536 }; 537 538 struct PluralPiece : SelectPiece { 539 PluralPiece() : SelectPiece(PluralPieceClass, MT_Plural) {} 540 541 std::vector<Piece *> OptionPrefixes; 542 int Index = 0; 543 544 static bool classof(const Piece *P) { 545 return P->getPieceClass() == PluralPieceClass; 546 } 547 }; 548 549 struct DiffPiece : Piece { 550 DiffPiece() : Piece(DiffPieceClass) {} 551 552 Piece *Parts[4] = {}; 553 int Indexes[2] = {}; 554 555 static bool classof(const Piece *P) { 556 return P->getPieceClass() == DiffPieceClass; 557 } 558 }; 559 560 struct SubstitutionPiece : Piece { 561 SubstitutionPiece() : Piece(SubstitutionPieceClass) {} 562 563 std::string Name; 564 std::vector<int> Modifiers; 565 566 static bool classof(const Piece *P) { 567 return P->getPieceClass() == SubstitutionPieceClass; 568 } 569 }; 570 571 /// Diagnostic text, parsed into pieces. 572 573 574 struct DiagnosticTextBuilder { 575 DiagnosticTextBuilder(DiagnosticTextBuilder const &) = delete; 576 DiagnosticTextBuilder &operator=(DiagnosticTextBuilder const &) = delete; 577 578 DiagnosticTextBuilder(const RecordKeeper &Records) { 579 // Build up the list of substitution records. 580 for (auto *S : Records.getAllDerivedDefinitions("TextSubstitution")) { 581 EvaluatingRecordGuard Guard(&EvaluatingRecord, S); 582 Substitutions.try_emplace( 583 S->getName(), DiagText(*this, S->getValueAsString("Substitution"))); 584 } 585 586 // Check that no diagnostic definitions have the same name as a 587 // substitution. 588 for (const Record *Diag : Records.getAllDerivedDefinitions("Diagnostic")) { 589 StringRef Name = Diag->getName(); 590 if (Substitutions.count(Name)) 591 llvm::PrintFatalError( 592 Diag->getLoc(), 593 "Diagnostic '" + Name + 594 "' has same name as TextSubstitution definition"); 595 } 596 } 597 598 std::vector<std::string> buildForDocumentation(StringRef Role, 599 const Record *R); 600 std::string buildForDefinition(const Record *R); 601 llvm::SmallVector<std::pair< 602 std::string, llvm::SmallVector<std::pair<unsigned, std::string>>>> 603 buildForEnum(const Record *R); 604 605 Piece *getSubstitution(SubstitutionPiece *S) const { 606 auto It = Substitutions.find(S->Name); 607 if (It == Substitutions.end()) 608 llvm::PrintFatalError("Failed to find substitution with name: " + 609 S->Name); 610 return It->second.Root; 611 } 612 613 [[noreturn]] void PrintFatalError(Twine const &Msg) const { 614 assert(EvaluatingRecord && "not evaluating a record?"); 615 llvm::PrintFatalError(EvaluatingRecord->getLoc(), Msg); 616 } 617 618 private: 619 struct DiagText { 620 DiagnosticTextBuilder &Builder; 621 std::vector<Piece *> AllocatedPieces; 622 Piece *Root = nullptr; 623 624 template <class T, class... Args> T *New(Args &&... args) { 625 static_assert(std::is_base_of<Piece, T>::value, "must be piece"); 626 T *Mem = new T(std::forward<Args>(args)...); 627 AllocatedPieces.push_back(Mem); 628 return Mem; 629 } 630 631 DiagText(DiagnosticTextBuilder &Builder, StringRef Text) 632 : Builder(Builder), Root(parseDiagText(Text, StopAt::End)) {} 633 634 enum class StopAt { 635 // Parse until the end of the string. 636 End, 637 // Additionally stop if we hit a non-nested '|' or '}'. 638 PipeOrCloseBrace, 639 // Additionally stop if we hit a non-nested '$'. 640 Dollar, 641 }; 642 643 Piece *parseDiagText(StringRef &Text, StopAt Stop); 644 int parseModifier(StringRef &) const; 645 646 public: 647 DiagText(DiagText &&O) noexcept 648 : Builder(O.Builder), AllocatedPieces(std::move(O.AllocatedPieces)), 649 Root(O.Root) { 650 O.Root = nullptr; 651 } 652 // The move assignment operator is defined as deleted pending further 653 // motivation. 654 DiagText &operator=(DiagText &&) = delete; 655 656 // The copy constrcutor and copy assignment operator is defined as deleted 657 // pending further motivation. 658 DiagText(const DiagText &) = delete; 659 DiagText &operator=(const DiagText &) = delete; 660 661 ~DiagText() { 662 for (Piece *P : AllocatedPieces) 663 delete P; 664 } 665 }; 666 667 private: 668 const Record *EvaluatingRecord = nullptr; 669 struct EvaluatingRecordGuard { 670 EvaluatingRecordGuard(const Record **Dest, const Record *New) 671 : Dest(Dest), Old(*Dest) { 672 *Dest = New; 673 } 674 ~EvaluatingRecordGuard() { *Dest = Old; } 675 const Record **Dest; 676 const Record *Old; 677 }; 678 679 StringMap<DiagText> Substitutions; 680 }; 681 682 template <class Derived> struct DiagTextVisitor { 683 using ModifierMappingsType = std::optional<std::vector<int>>; 684 685 private: 686 Derived &getDerived() { return static_cast<Derived &>(*this); } 687 688 public: 689 std::vector<int> 690 getSubstitutionMappings(SubstitutionPiece *P, 691 const ModifierMappingsType &Mappings) const { 692 std::vector<int> NewMappings; 693 for (int Idx : P->Modifiers) 694 NewMappings.push_back(mapIndex(Idx, Mappings)); 695 return NewMappings; 696 } 697 698 struct SubstitutionContext { 699 SubstitutionContext(DiagTextVisitor &Visitor, SubstitutionPiece *P) 700 : Visitor(Visitor) { 701 Substitution = Visitor.Builder.getSubstitution(P); 702 OldMappings = std::move(Visitor.ModifierMappings); 703 std::vector<int> NewMappings = 704 Visitor.getSubstitutionMappings(P, OldMappings); 705 Visitor.ModifierMappings = std::move(NewMappings); 706 } 707 708 ~SubstitutionContext() { 709 Visitor.ModifierMappings = std::move(OldMappings); 710 } 711 712 private: 713 DiagTextVisitor &Visitor; 714 std::optional<std::vector<int>> OldMappings; 715 716 public: 717 Piece *Substitution; 718 }; 719 720 public: 721 DiagTextVisitor(DiagnosticTextBuilder &Builder) : Builder(Builder) {} 722 723 void Visit(Piece *P) { 724 switch (P->getPieceClass()) { 725 #define CASE(T) \ 726 case T##PieceClass: \ 727 return getDerived().Visit##T(static_cast<T##Piece *>(P)) 728 CASE(Multi); 729 CASE(Text); 730 CASE(Placeholder); 731 CASE(Select); 732 CASE(EnumSelect); 733 CASE(Plural); 734 CASE(Diff); 735 CASE(Substitution); 736 #undef CASE 737 } 738 } 739 740 void VisitSubstitution(SubstitutionPiece *P) { 741 SubstitutionContext Guard(*this, P); 742 Visit(Guard.Substitution); 743 } 744 745 int mapIndex(int Idx, 746 ModifierMappingsType const &ModifierMappings) const { 747 if (!ModifierMappings) 748 return Idx; 749 if (ModifierMappings->size() <= static_cast<unsigned>(Idx)) 750 Builder.PrintFatalError("Modifier value '" + std::to_string(Idx) + 751 "' is not valid for this mapping (has " + 752 std::to_string(ModifierMappings->size()) + 753 " mappings)"); 754 return (*ModifierMappings)[Idx]; 755 } 756 757 int mapIndex(int Idx) const { 758 return mapIndex(Idx, ModifierMappings); 759 } 760 761 protected: 762 DiagnosticTextBuilder &Builder; 763 ModifierMappingsType ModifierMappings; 764 }; 765 766 void escapeRST(StringRef Str, std::string &Out) { 767 for (auto K : Str) { 768 if (StringRef("`*|_[]\\").count(K)) 769 Out.push_back('\\'); 770 Out.push_back(K); 771 } 772 } 773 774 template <typename It> void padToSameLength(It Begin, It End) { 775 size_t Width = 0; 776 for (It I = Begin; I != End; ++I) 777 Width = std::max(Width, I->size()); 778 for (It I = Begin; I != End; ++I) 779 (*I) += std::string(Width - I->size(), ' '); 780 } 781 782 template <typename It> void makeTableRows(It Begin, It End) { 783 if (Begin == End) 784 return; 785 padToSameLength(Begin, End); 786 for (It I = Begin; I != End; ++I) 787 *I = "|" + *I + "|"; 788 } 789 790 void makeRowSeparator(std::string &Str) { 791 for (char &K : Str) 792 K = (K == '|' ? '+' : '-'); 793 } 794 795 struct DiagTextDocPrinter : DiagTextVisitor<DiagTextDocPrinter> { 796 using BaseTy = DiagTextVisitor<DiagTextDocPrinter>; 797 DiagTextDocPrinter(DiagnosticTextBuilder &Builder, 798 std::vector<std::string> &RST) 799 : BaseTy(Builder), RST(RST) {} 800 801 void gatherNodes( 802 Piece *OrigP, const ModifierMappingsType &CurrentMappings, 803 std::vector<std::pair<Piece *, ModifierMappingsType>> &Pieces) const { 804 if (auto *Sub = dyn_cast<SubstitutionPiece>(OrigP)) { 805 ModifierMappingsType NewMappings = 806 getSubstitutionMappings(Sub, CurrentMappings); 807 return gatherNodes(Builder.getSubstitution(Sub), NewMappings, Pieces); 808 } 809 if (auto *MD = dyn_cast<MultiPiece>(OrigP)) { 810 for (Piece *Node : MD->Pieces) 811 gatherNodes(Node, CurrentMappings, Pieces); 812 return; 813 } 814 Pieces.push_back(std::make_pair(OrigP, CurrentMappings)); 815 } 816 817 void VisitMulti(MultiPiece *P) { 818 if (P->Pieces.empty()) { 819 RST.push_back(""); 820 return; 821 } 822 823 if (P->Pieces.size() == 1) 824 return Visit(P->Pieces[0]); 825 826 // Flatten the list of nodes, replacing any substitution pieces with the 827 // recursively flattened substituted node. 828 std::vector<std::pair<Piece *, ModifierMappingsType>> Pieces; 829 gatherNodes(P, ModifierMappings, Pieces); 830 831 std::string EmptyLinePrefix; 832 size_t Start = RST.size(); 833 bool HasMultipleLines = true; 834 for (const std::pair<Piece *, ModifierMappingsType> &NodePair : Pieces) { 835 std::vector<std::string> Lines; 836 DiagTextDocPrinter Visitor{Builder, Lines}; 837 Visitor.ModifierMappings = NodePair.second; 838 Visitor.Visit(NodePair.first); 839 840 if (Lines.empty()) 841 continue; 842 843 // We need a vertical separator if either this or the previous piece is a 844 // multi-line piece, or this is the last piece. 845 const char *Separator = (Lines.size() > 1 || HasMultipleLines) ? "|" : ""; 846 HasMultipleLines = Lines.size() > 1; 847 848 if (Start + Lines.size() > RST.size()) 849 RST.resize(Start + Lines.size(), EmptyLinePrefix); 850 851 padToSameLength(Lines.begin(), Lines.end()); 852 for (size_t I = 0; I != Lines.size(); ++I) 853 RST[Start + I] += Separator + Lines[I]; 854 std::string Empty(Lines[0].size(), ' '); 855 for (size_t I = Start + Lines.size(); I != RST.size(); ++I) 856 RST[I] += Separator + Empty; 857 EmptyLinePrefix += Separator + Empty; 858 } 859 for (size_t I = Start; I != RST.size(); ++I) 860 RST[I] += "|"; 861 EmptyLinePrefix += "|"; 862 863 makeRowSeparator(EmptyLinePrefix); 864 RST.insert(RST.begin() + Start, EmptyLinePrefix); 865 RST.insert(RST.end(), EmptyLinePrefix); 866 } 867 868 void VisitText(TextPiece *P) { 869 RST.push_back(""); 870 auto &S = RST.back(); 871 872 StringRef T = P->Text; 873 while (T.consume_front(" ")) 874 RST.back() += " |nbsp| "; 875 876 std::string Suffix; 877 while (T.consume_back(" ")) 878 Suffix += " |nbsp| "; 879 880 if (!T.empty()) { 881 S += ':'; 882 S += P->Role; 883 S += ":`"; 884 escapeRST(T, S); 885 S += '`'; 886 } 887 888 S += Suffix; 889 } 890 891 void VisitPlaceholder(PlaceholderPiece *P) { 892 RST.push_back(std::string(":placeholder:`") + 893 char('A' + mapIndex(P->Index)) + "`"); 894 } 895 896 void VisitSelect(SelectPiece *P) { 897 std::vector<size_t> SeparatorIndexes; 898 SeparatorIndexes.push_back(RST.size()); 899 RST.emplace_back(); 900 for (auto *O : P->Options) { 901 Visit(O); 902 SeparatorIndexes.push_back(RST.size()); 903 RST.emplace_back(); 904 } 905 906 makeTableRows(RST.begin() + SeparatorIndexes.front(), 907 RST.begin() + SeparatorIndexes.back() + 1); 908 for (size_t I : SeparatorIndexes) 909 makeRowSeparator(RST[I]); 910 } 911 912 void VisitEnumSelect(EnumSelectPiece *P) { 913 // Document this as if it were a 'select', which properly prints all of the 914 // options correctly in a readable/reasonable manner. There isn't really 915 // anything valuable we could add to readers here. 916 VisitSelect(P); 917 } 918 919 void VisitPlural(PluralPiece *P) { VisitSelect(P); } 920 921 void VisitDiff(DiffPiece *P) { 922 // Render %diff{a $ b $ c|d}e,f as %select{a %e b %f c|d}. 923 PlaceholderPiece E(MT_Placeholder, P->Indexes[0]); 924 PlaceholderPiece F(MT_Placeholder, P->Indexes[1]); 925 926 MultiPiece FirstOption; 927 FirstOption.Pieces.push_back(P->Parts[0]); 928 FirstOption.Pieces.push_back(&E); 929 FirstOption.Pieces.push_back(P->Parts[1]); 930 FirstOption.Pieces.push_back(&F); 931 FirstOption.Pieces.push_back(P->Parts[2]); 932 933 SelectPiece Select(MT_Diff); 934 Select.Options.push_back(&FirstOption); 935 Select.Options.push_back(P->Parts[3]); 936 937 VisitSelect(&Select); 938 } 939 940 std::vector<std::string> &RST; 941 }; 942 943 struct DiagEnumPrinter : DiagTextVisitor<DiagEnumPrinter> { 944 public: 945 using BaseTy = DiagTextVisitor<DiagEnumPrinter>; 946 using EnumeratorItem = std::pair<unsigned, std::string>; 947 using EnumeratorList = llvm::SmallVector<EnumeratorItem>; 948 using ResultTy = llvm::SmallVector<std::pair<std::string, EnumeratorList>>; 949 950 DiagEnumPrinter(DiagnosticTextBuilder &Builder, ResultTy &Result) 951 : BaseTy(Builder), Result(Result) {} 952 953 ResultTy &Result; 954 955 void VisitMulti(MultiPiece *P) { 956 for (auto *Child : P->Pieces) 957 Visit(Child); 958 } 959 void VisitText(TextPiece *P) {} 960 void VisitPlaceholder(PlaceholderPiece *P) {} 961 void VisitDiff(DiffPiece *P) {} 962 void VisitSelect(SelectPiece *P) { 963 for (auto *D : P->Options) 964 Visit(D); 965 } 966 void VisitPlural(PluralPiece *P) { VisitSelect(P); } 967 void VisitEnumSelect(EnumSelectPiece *P) { 968 assert(P->Options.size() == P->OptionEnumNames.size()); 969 970 if (!P->EnumName.empty()) { 971 EnumeratorList List; 972 973 for (const auto &Tup : llvm::enumerate(P->OptionEnumNames)) 974 if (!Tup.value().empty()) 975 List.emplace_back(Tup.index(), Tup.value()); 976 977 Result.emplace_back(P->EnumName, List); 978 } 979 980 VisitSelect(P); 981 } 982 }; 983 984 struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> { 985 public: 986 using BaseTy = DiagTextVisitor<DiagTextPrinter>; 987 DiagTextPrinter(DiagnosticTextBuilder &Builder, std::string &Result) 988 : BaseTy(Builder), Result(Result) {} 989 990 void VisitMulti(MultiPiece *P) { 991 for (auto *Child : P->Pieces) 992 Visit(Child); 993 } 994 void VisitText(TextPiece *P) { Result += P->Text; } 995 void VisitPlaceholder(PlaceholderPiece *P) { 996 Result += "%"; 997 Result += getModifierName(P->Kind); 998 addInt(mapIndex(P->Index)); 999 } 1000 void VisitSelect(SelectPiece *P) { 1001 Result += "%"; 1002 Result += getModifierName(P->ModKind); 1003 if (P->ModKind == MT_Select || P->ModKind == MT_EnumSelect) { 1004 Result += "{"; 1005 for (auto *D : P->Options) { 1006 Visit(D); 1007 Result += '|'; 1008 } 1009 if (!P->Options.empty()) 1010 Result.erase(--Result.end()); 1011 Result += '}'; 1012 } 1013 addInt(mapIndex(P->Index)); 1014 } 1015 1016 void VisitPlural(PluralPiece *P) { 1017 Result += "%plural{"; 1018 assert(P->Options.size() == P->OptionPrefixes.size()); 1019 for (const auto [Prefix, Option] : 1020 zip_equal(P->OptionPrefixes, P->Options)) { 1021 if (Prefix) 1022 Visit(Prefix); 1023 Visit(Option); 1024 Result += "|"; 1025 } 1026 if (!P->Options.empty()) 1027 Result.erase(--Result.end()); 1028 Result += '}'; 1029 addInt(mapIndex(P->Index)); 1030 } 1031 1032 void VisitEnumSelect(EnumSelectPiece *P) { 1033 // Print as if we are a 'select', which will result in the compiler just 1034 // treating this like a normal select. This way we don't have to do any 1035 // special work for the compiler to consume these. 1036 VisitSelect(P); 1037 } 1038 1039 void VisitDiff(DiffPiece *P) { 1040 Result += "%diff{"; 1041 Visit(P->Parts[0]); 1042 Result += "$"; 1043 Visit(P->Parts[1]); 1044 Result += "$"; 1045 Visit(P->Parts[2]); 1046 Result += "|"; 1047 Visit(P->Parts[3]); 1048 Result += "}"; 1049 addInt(mapIndex(P->Indexes[0])); 1050 Result += ","; 1051 addInt(mapIndex(P->Indexes[1])); 1052 } 1053 1054 void addInt(int Val) { Result += std::to_string(Val); } 1055 1056 std::string &Result; 1057 }; 1058 1059 int DiagnosticTextBuilder::DiagText::parseModifier(StringRef &Text) const { 1060 if (Text.empty() || !isdigit(Text[0])) 1061 Builder.PrintFatalError("expected modifier in diagnostic"); 1062 int Val = 0; 1063 do { 1064 Val *= 10; 1065 Val += Text[0] - '0'; 1066 Text = Text.drop_front(); 1067 } while (!Text.empty() && isdigit(Text[0])); 1068 return Val; 1069 } 1070 1071 Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text, 1072 StopAt Stop) { 1073 std::vector<Piece *> Parsed; 1074 1075 constexpr StringLiteral StopSets[] = {"%", "%|}", "%|}$"}; 1076 StringRef StopSet = StopSets[static_cast<int>(Stop)]; 1077 1078 while (!Text.empty()) { 1079 size_t End = (size_t)-2; 1080 do 1081 End = Text.find_first_of(StopSet, End + 2); 1082 while ( 1083 End < Text.size() - 1 && Text[End] == '%' && 1084 (Text[End + 1] == '%' || Text[End + 1] == '|' || Text[End + 1] == '$')); 1085 1086 if (End) { 1087 Parsed.push_back(New<TextPiece>(Text.slice(0, End), "diagtext")); 1088 Text = Text.slice(End, StringRef::npos); 1089 if (Text.empty()) 1090 break; 1091 } 1092 1093 if (Text[0] == '|' || Text[0] == '}' || Text[0] == '$') 1094 break; 1095 1096 // Drop the '%'. 1097 Text = Text.drop_front(); 1098 1099 // Extract the (optional) modifier. 1100 size_t ModLength = Text.find_first_of("0123456789<{"); 1101 StringRef Modifier = Text.slice(0, ModLength); 1102 Text = Text.slice(ModLength, StringRef::npos); 1103 ModifierType ModType = StringSwitch<ModifierType>{Modifier} 1104 .Case("select", MT_Select) 1105 .Case("enum_select", MT_EnumSelect) 1106 .Case("sub", MT_Sub) 1107 .Case("diff", MT_Diff) 1108 .Case("plural", MT_Plural) 1109 .Case("s", MT_S) 1110 .Case("ordinal", MT_Ordinal) 1111 .Case("human", MT_Human) 1112 .Case("q", MT_Q) 1113 .Case("objcclass", MT_ObjCClass) 1114 .Case("objcinstance", MT_ObjCInstance) 1115 .Case("", MT_Placeholder) 1116 .Default(MT_Unknown); 1117 1118 auto ExpectAndConsume = [&](StringRef Prefix) { 1119 if (!Text.consume_front(Prefix)) 1120 Builder.PrintFatalError("expected '" + Prefix + "' while parsing %" + 1121 Modifier); 1122 }; 1123 1124 if (ModType != MT_EnumSelect && Text[0] == '<') 1125 Builder.PrintFatalError("modifier '<' syntax not valid with %" + 1126 Modifier); 1127 1128 switch (ModType) { 1129 case MT_Unknown: 1130 Builder.PrintFatalError("Unknown modifier type: " + Modifier); 1131 case MT_Select: { 1132 SelectPiece *Select = New<SelectPiece>(MT_Select); 1133 do { 1134 Text = Text.drop_front(); // '{' or '|' 1135 Select->Options.push_back( 1136 parseDiagText(Text, StopAt::PipeOrCloseBrace)); 1137 assert(!Text.empty() && "malformed %select"); 1138 } while (Text.front() == '|'); 1139 ExpectAndConsume("}"); 1140 Select->Index = parseModifier(Text); 1141 Parsed.push_back(Select); 1142 continue; 1143 } 1144 case MT_EnumSelect: { 1145 EnumSelectPiece *EnumSelect = New<EnumSelectPiece>(); 1146 if (Text[0] != '<') 1147 Builder.PrintFatalError("expected '<' after " + Modifier); 1148 1149 Text = Text.drop_front(); // Drop '<' 1150 size_t EnumNameLen = Text.find_first_of('>'); 1151 EnumSelect->EnumName = Text.slice(0, EnumNameLen); 1152 Text = Text.slice(EnumNameLen, StringRef::npos); 1153 ExpectAndConsume(">"); 1154 1155 if (Text[0] != '{') 1156 Builder.PrintFatalError("expected '{' after " + Modifier); 1157 1158 do { 1159 Text = Text.drop_front(); // '{' or '|' 1160 1161 bool BracketsRequired = false; 1162 if (Text[0] == '%') { 1163 BracketsRequired = true; 1164 Text = Text.drop_front(); // '%' 1165 size_t OptionNameLen = Text.find_first_of("{"); 1166 EnumSelect->OptionEnumNames.push_back(Text.slice(0, OptionNameLen)); 1167 Text = Text.slice(OptionNameLen, StringRef::npos); 1168 } else { 1169 EnumSelect->OptionEnumNames.push_back({}); 1170 } 1171 1172 if (BracketsRequired) 1173 ExpectAndConsume("{"); 1174 else if (Text.front() == '{') { 1175 Text = Text.drop_front(); 1176 BracketsRequired = true; 1177 } 1178 1179 EnumSelect->Options.push_back( 1180 parseDiagText(Text, StopAt::PipeOrCloseBrace)); 1181 1182 if (BracketsRequired) 1183 ExpectAndConsume("}"); 1184 1185 assert(!Text.empty() && "malformed %select"); 1186 } while (Text.front() == '|'); 1187 1188 ExpectAndConsume("}"); 1189 EnumSelect->Index = parseModifier(Text); 1190 Parsed.push_back(EnumSelect); 1191 continue; 1192 } 1193 case MT_Plural: { 1194 PluralPiece *Plural = New<PluralPiece>(); 1195 do { 1196 Text = Text.drop_front(); // '{' or '|' 1197 size_t End = Text.find_first_of(':'); 1198 if (End == StringRef::npos) 1199 Builder.PrintFatalError("expected ':' while parsing %plural"); 1200 ++End; 1201 assert(!Text.empty()); 1202 Plural->OptionPrefixes.push_back( 1203 New<TextPiece>(Text.slice(0, End), "diagtext")); 1204 Text = Text.slice(End, StringRef::npos); 1205 Plural->Options.push_back( 1206 parseDiagText(Text, StopAt::PipeOrCloseBrace)); 1207 assert(!Text.empty() && "malformed %plural"); 1208 } while (Text.front() == '|'); 1209 ExpectAndConsume("}"); 1210 Plural->Index = parseModifier(Text); 1211 Parsed.push_back(Plural); 1212 continue; 1213 } 1214 case MT_Sub: { 1215 SubstitutionPiece *Sub = New<SubstitutionPiece>(); 1216 ExpectAndConsume("{"); 1217 size_t NameSize = Text.find_first_of('}'); 1218 assert(NameSize != size_t(-1) && "failed to find the end of the name"); 1219 assert(NameSize != 0 && "empty name?"); 1220 Sub->Name = Text.substr(0, NameSize).str(); 1221 Text = Text.drop_front(NameSize); 1222 ExpectAndConsume("}"); 1223 if (!Text.empty()) { 1224 while (true) { 1225 if (!isdigit(Text[0])) 1226 break; 1227 Sub->Modifiers.push_back(parseModifier(Text)); 1228 if (!Text.consume_front(",")) 1229 break; 1230 assert(!Text.empty() && isdigit(Text[0]) && 1231 "expected another modifier"); 1232 } 1233 } 1234 Parsed.push_back(Sub); 1235 continue; 1236 } 1237 case MT_Diff: { 1238 DiffPiece *Diff = New<DiffPiece>(); 1239 ExpectAndConsume("{"); 1240 Diff->Parts[0] = parseDiagText(Text, StopAt::Dollar); 1241 ExpectAndConsume("$"); 1242 Diff->Parts[1] = parseDiagText(Text, StopAt::Dollar); 1243 ExpectAndConsume("$"); 1244 Diff->Parts[2] = parseDiagText(Text, StopAt::PipeOrCloseBrace); 1245 ExpectAndConsume("|"); 1246 Diff->Parts[3] = parseDiagText(Text, StopAt::PipeOrCloseBrace); 1247 ExpectAndConsume("}"); 1248 Diff->Indexes[0] = parseModifier(Text); 1249 ExpectAndConsume(","); 1250 Diff->Indexes[1] = parseModifier(Text); 1251 Parsed.push_back(Diff); 1252 continue; 1253 } 1254 case MT_S: { 1255 SelectPiece *Select = New<SelectPiece>(ModType); 1256 Select->Options.push_back(New<TextPiece>("")); 1257 Select->Options.push_back(New<TextPiece>("s", "diagtext")); 1258 Select->Index = parseModifier(Text); 1259 Parsed.push_back(Select); 1260 continue; 1261 } 1262 case MT_Q: 1263 case MT_Placeholder: 1264 case MT_ObjCClass: 1265 case MT_ObjCInstance: 1266 case MT_Ordinal: 1267 case MT_Human: { 1268 Parsed.push_back(New<PlaceholderPiece>(ModType, parseModifier(Text))); 1269 continue; 1270 } 1271 } 1272 } 1273 1274 return New<MultiPiece>(Parsed); 1275 } 1276 1277 std::vector<std::string> 1278 DiagnosticTextBuilder::buildForDocumentation(StringRef Severity, 1279 const Record *R) { 1280 EvaluatingRecordGuard Guard(&EvaluatingRecord, R); 1281 StringRef Text = R->getValueAsString("Summary"); 1282 1283 DiagText D(*this, Text); 1284 TextPiece *Prefix = D.New<TextPiece>(Severity, Severity); 1285 Prefix->Text += ": "; 1286 auto *MP = dyn_cast<MultiPiece>(D.Root); 1287 if (!MP) { 1288 MP = D.New<MultiPiece>(); 1289 MP->Pieces.push_back(D.Root); 1290 D.Root = MP; 1291 } 1292 MP->Pieces.insert(MP->Pieces.begin(), Prefix); 1293 std::vector<std::string> Result; 1294 DiagTextDocPrinter{*this, Result}.Visit(D.Root); 1295 return Result; 1296 } 1297 1298 DiagEnumPrinter::ResultTy DiagnosticTextBuilder::buildForEnum(const Record *R) { 1299 EvaluatingRecordGuard Guard(&EvaluatingRecord, R); 1300 StringRef Text = R->getValueAsString("Summary"); 1301 DiagText D(*this, Text); 1302 DiagEnumPrinter::ResultTy Result; 1303 DiagEnumPrinter{*this, Result}.Visit(D.Root); 1304 return Result; 1305 } 1306 1307 std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) { 1308 EvaluatingRecordGuard Guard(&EvaluatingRecord, R); 1309 StringRef Text = R->getValueAsString("Summary"); 1310 DiagText D(*this, Text); 1311 std::string Result; 1312 DiagTextPrinter{*this, Result}.Visit(D.Root); 1313 return Result; 1314 } 1315 1316 } // namespace 1317 1318 //===----------------------------------------------------------------------===// 1319 // Warning Tables (.inc file) generation. 1320 //===----------------------------------------------------------------------===// 1321 1322 static bool isError(const Record &Diag) { 1323 return Diag.getValueAsDef("Class")->getName() == "CLASS_ERROR"; 1324 } 1325 1326 static bool isRemark(const Record &Diag) { 1327 return Diag.getValueAsDef("Class")->getName() == "CLASS_REMARK"; 1328 } 1329 1330 // Presumes the text has been split at the first whitespace or hyphen. 1331 static bool isExemptAtStart(StringRef Text) { 1332 // Fast path, the first character is lowercase or not alphanumeric. 1333 if (Text.empty() || isLower(Text[0]) || !isAlnum(Text[0])) 1334 return true; 1335 1336 // If the text is all uppercase (or numbers, +, or _), then we assume it's an 1337 // acronym and that's allowed. This covers cases like ISO, C23, C++14, and 1338 // OBJECT_MODE. However, if there's only a single letter other than "C", we 1339 // do not exempt it so that we catch a case like "A really bad idea" while 1340 // still allowing a case like "C does not allow...". 1341 if (all_of(Text, [](char C) { 1342 return isUpper(C) || isDigit(C) || C == '+' || C == '_'; 1343 })) 1344 return Text.size() > 1 || Text[0] == 'C'; 1345 1346 // Otherwise, there are a few other exemptions. 1347 return StringSwitch<bool>(Text) 1348 .Case("AddressSanitizer", true) 1349 .Case("CFString", true) 1350 .Case("Clang", true) 1351 .Case("Fuchsia", true) 1352 .Case("GNUstep", true) 1353 .Case("IBOutletCollection", true) 1354 .Case("Microsoft", true) 1355 .Case("Neon", true) 1356 .StartsWith("NSInvocation", true) // NSInvocation, NSInvocation's 1357 .Case("Objective", true) // Objective-C (hyphen is a word boundary) 1358 .Case("OpenACC", true) 1359 .Case("OpenCL", true) 1360 .Case("OpenMP", true) 1361 .Case("Pascal", true) 1362 .Case("Swift", true) 1363 .Case("Unicode", true) 1364 .Case("Vulkan", true) 1365 .Case("WebAssembly", true) 1366 .Default(false); 1367 } 1368 1369 // Does not presume the text has been split at all. 1370 static bool isExemptAtEnd(StringRef Text) { 1371 // Rather than come up with a list of characters that are allowed, we go the 1372 // other way and look only for characters that are not allowed. 1373 switch (Text.back()) { 1374 default: 1375 return true; 1376 case '?': 1377 // Explicitly allowed to support "; did you mean?". 1378 return true; 1379 case '.': 1380 case '!': 1381 return false; 1382 } 1383 } 1384 1385 static void verifyDiagnosticWording(const Record &Diag) { 1386 StringRef FullDiagText = Diag.getValueAsString("Summary"); 1387 1388 auto DiagnoseStart = [&](StringRef Text) { 1389 // Verify that the text does not start with a capital letter, except for 1390 // special cases that are exempt like ISO and C++. Find the first word 1391 // by looking for a word breaking character. 1392 char Separators[] = {' ', '-', ',', '}'}; 1393 auto Iter = std::find_first_of( 1394 Text.begin(), Text.end(), std::begin(Separators), std::end(Separators)); 1395 1396 StringRef First = Text.substr(0, Iter - Text.begin()); 1397 if (!isExemptAtStart(First)) { 1398 PrintError(&Diag, 1399 "Diagnostics should not start with a capital letter; '" + 1400 First + "' is invalid"); 1401 } 1402 }; 1403 1404 auto DiagnoseEnd = [&](StringRef Text) { 1405 // Verify that the text does not end with punctuation like '.' or '!'. 1406 if (!isExemptAtEnd(Text)) { 1407 PrintError(&Diag, "Diagnostics should not end with punctuation; '" + 1408 Text.substr(Text.size() - 1, 1) + "' is invalid"); 1409 } 1410 }; 1411 1412 // If the diagnostic starts with %select, look through it to see whether any 1413 // of the options will cause a problem. 1414 if (FullDiagText.starts_with("%select{")) { 1415 // Do a balanced delimiter scan from the start of the text to find the 1416 // closing '}', skipping intermediary {} pairs. 1417 1418 size_t BraceCount = 1; 1419 constexpr size_t PercentSelectBraceLen = sizeof("%select{") - 1; 1420 auto Iter = FullDiagText.begin() + PercentSelectBraceLen; 1421 for (auto End = FullDiagText.end(); Iter != End; ++Iter) { 1422 char Ch = *Iter; 1423 if (Ch == '{') 1424 ++BraceCount; 1425 else if (Ch == '}') 1426 --BraceCount; 1427 if (!BraceCount) 1428 break; 1429 } 1430 // Defending against a malformed diagnostic string. 1431 if (BraceCount != 0) 1432 return; 1433 1434 StringRef SelectText = 1435 FullDiagText.substr(PercentSelectBraceLen, Iter - FullDiagText.begin() - 1436 PercentSelectBraceLen); 1437 SmallVector<StringRef, 4> SelectPieces; 1438 SelectText.split(SelectPieces, '|'); 1439 1440 // Walk over all of the individual pieces of select text to see if any of 1441 // them start with an invalid character. If any of the select pieces is 1442 // empty, we need to look at the first word after the %select to see 1443 // whether that is invalid or not. If all of the pieces are fine, then we 1444 // don't need to check anything else about the start of the diagnostic. 1445 bool CheckSecondWord = false; 1446 for (StringRef Piece : SelectPieces) { 1447 if (Piece.empty()) 1448 CheckSecondWord = true; 1449 else 1450 DiagnoseStart(Piece); 1451 } 1452 1453 if (CheckSecondWord) { 1454 // There was an empty select piece, so we need to check the second 1455 // word. This catches situations like '%select{|fine}0 Not okay'. Add 1456 // two to account for the closing curly brace and the number after it. 1457 StringRef AfterSelect = 1458 FullDiagText.substr(Iter - FullDiagText.begin() + 2).ltrim(); 1459 DiagnoseStart(AfterSelect); 1460 } 1461 } else { 1462 // If the start of the diagnostic is not %select, we can check the first 1463 // word and be done with it. 1464 DiagnoseStart(FullDiagText); 1465 } 1466 1467 // If the last character in the diagnostic is a number preceded by a }, scan 1468 // backwards to see if this is for a %select{...}0. If it is, we need to look 1469 // at each piece to see whether it ends in punctuation or not. 1470 bool StillNeedToDiagEnd = true; 1471 if (isDigit(FullDiagText.back()) && *(FullDiagText.end() - 2) == '}') { 1472 // Scan backwards to find the opening curly brace. 1473 size_t BraceCount = 1; 1474 auto Iter = FullDiagText.end() - sizeof("}0"); 1475 for (auto End = FullDiagText.begin(); Iter != End; --Iter) { 1476 char Ch = *Iter; 1477 if (Ch == '}') 1478 ++BraceCount; 1479 else if (Ch == '{') 1480 --BraceCount; 1481 if (!BraceCount) 1482 break; 1483 } 1484 // Defending against a malformed diagnostic string. 1485 if (BraceCount != 0) 1486 return; 1487 1488 // Continue the backwards scan to find the word before the '{' to see if it 1489 // is 'select'. 1490 constexpr size_t SelectLen = sizeof("select") - 1; 1491 bool IsSelect = 1492 (FullDiagText.substr(Iter - SelectLen - FullDiagText.begin(), 1493 SelectLen) == "select"); 1494 if (IsSelect) { 1495 // Gather the content between the {} for the select in question so we can 1496 // split it into pieces. 1497 StillNeedToDiagEnd = false; // No longer need to handle the end. 1498 StringRef SelectText = 1499 FullDiagText.substr(Iter - FullDiagText.begin() + /*{*/ 1, 1500 FullDiagText.end() - Iter - /*pos before }0*/ 3); 1501 SmallVector<StringRef, 4> SelectPieces; 1502 SelectText.split(SelectPieces, '|'); 1503 for (StringRef Piece : SelectPieces) { 1504 // Not worrying about a situation like: "this is bar. %select{foo|}0". 1505 if (!Piece.empty()) 1506 DiagnoseEnd(Piece); 1507 } 1508 } 1509 } 1510 1511 // If we didn't already cover the diagnostic because of a %select, handle it 1512 // now. 1513 if (StillNeedToDiagEnd) 1514 DiagnoseEnd(FullDiagText); 1515 1516 // FIXME: This could also be improved by looking for instances of clang or 1517 // gcc in the diagnostic and recommend Clang or GCC instead. However, this 1518 // runs into odd situations like [[clang::warn_unused_result]], 1519 // #pragma clang, or --unwindlib=libgcc. 1520 } 1521 /// ClangDiagsEnumsEmitter - The top-level class emits .def files containing 1522 /// declarations of Clang diagnostic enums for selects. 1523 void clang::EmitClangDiagsEnums(const RecordKeeper &Records, raw_ostream &OS, 1524 const std::string &Component) { 1525 DiagnosticTextBuilder DiagTextBuilder(Records); 1526 ArrayRef<const Record *> Diags = 1527 Records.getAllDerivedDefinitions("Diagnostic"); 1528 1529 llvm::SmallVector<std::pair<const Record *, std::string>> EnumerationNames; 1530 1531 for (const Record &R : make_pointee_range(Diags)) { 1532 DiagEnumPrinter::ResultTy Enums = DiagTextBuilder.buildForEnum(&R); 1533 1534 for (auto &Enumeration : Enums) { 1535 bool ShouldPrint = 1536 Component.empty() || Component == R.getValueAsString("Component"); 1537 1538 auto PreviousByName = llvm::find_if(EnumerationNames, [&](auto &Prev) { 1539 return Prev.second == Enumeration.first; 1540 }); 1541 1542 if (PreviousByName != EnumerationNames.end()) { 1543 PrintError(&R, 1544 "Duplicate enumeration name '" + Enumeration.first + "'"); 1545 PrintNote(PreviousByName->first->getLoc(), 1546 "Previous diagnostic is here"); 1547 } 1548 1549 EnumerationNames.emplace_back(&R, Enumeration.first); 1550 1551 if (ShouldPrint) 1552 OS << "DIAG_ENUM(" << Enumeration.first << ")\n"; 1553 1554 llvm::SmallVector<std::string> EnumeratorNames; 1555 for (auto &Enumerator : Enumeration.second) { 1556 if (llvm::find(EnumeratorNames, Enumerator.second) != 1557 EnumeratorNames.end()) 1558 PrintError(&R, 1559 "Duplicate enumerator name '" + Enumerator.second + "'"); 1560 EnumeratorNames.push_back(Enumerator.second); 1561 1562 if (ShouldPrint) 1563 OS << "DIAG_ENUM_ITEM(" << Enumerator.first << ", " 1564 << Enumerator.second << ")\n"; 1565 } 1566 if (ShouldPrint) 1567 OS << "DIAG_ENUM_END()\n"; 1568 } 1569 } 1570 } 1571 1572 /// ClangDiagsDefsEmitter - The top-level class emits .def files containing 1573 /// declarations of Clang diagnostics. 1574 void clang::EmitClangDiagsDefs(const RecordKeeper &Records, raw_ostream &OS, 1575 const std::string &Component) { 1576 // Write the #if guard 1577 if (!Component.empty()) { 1578 std::string ComponentName = StringRef(Component).upper(); 1579 OS << "#ifdef " << ComponentName << "START\n"; 1580 OS << "__" << ComponentName << "START = DIAG_START_" << ComponentName 1581 << ",\n"; 1582 OS << "#undef " << ComponentName << "START\n"; 1583 OS << "#endif\n\n"; 1584 } 1585 1586 DiagnosticTextBuilder DiagTextBuilder(Records); 1587 1588 ArrayRef<const Record *> Diags = 1589 Records.getAllDerivedDefinitions("Diagnostic"); 1590 1591 ArrayRef<const Record *> DiagGroups = 1592 Records.getAllDerivedDefinitions("DiagGroup"); 1593 1594 DiagsInGroupTy DiagsInGroup; 1595 groupDiagnostics(Diags, DiagGroups, DiagsInGroup); 1596 1597 DiagCategoryIDMap CategoryIDs(Records); 1598 DiagGroupParentMap DGParentMap(Records); 1599 1600 // Compute the set of diagnostics that are in -Wpedantic. 1601 RecordSet DiagsInPedantic; 1602 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup); 1603 inferPedantic.compute(&DiagsInPedantic, (RecordVec*)nullptr); 1604 1605 for (const Record &R : make_pointee_range(Diags)) { 1606 // Check if this is an error that is accidentally in a warning 1607 // group. 1608 if (isError(R)) { 1609 if (const auto *Group = dyn_cast<DefInit>(R.getValueInit("Group"))) { 1610 const Record *GroupRec = Group->getDef(); 1611 StringRef GroupName = GroupRec->getValueAsString("GroupName"); 1612 PrintFatalError(R.getLoc(), "Error " + R.getName() + 1613 " cannot be in a warning group [" + GroupName + "]"); 1614 } 1615 } 1616 1617 // Check that all remarks have an associated diagnostic group. 1618 if (isRemark(R)) { 1619 if (!isa<DefInit>(R.getValueInit("Group"))) { 1620 PrintFatalError(R.getLoc(), "Error " + R.getName() + 1621 " not in any diagnostic group"); 1622 } 1623 } 1624 1625 // Filter by component. 1626 if (!Component.empty() && Component != R.getValueAsString("Component")) 1627 continue; 1628 1629 // Validate diagnostic wording for common issues. 1630 verifyDiagnosticWording(R); 1631 1632 OS << "DIAG(" << R.getName() << ", "; 1633 OS << R.getValueAsDef("Class")->getName(); 1634 OS << ", (unsigned)diag::Severity::" 1635 << R.getValueAsDef("DefaultSeverity")->getValueAsString("Name"); 1636 1637 // Description string. 1638 OS << ", \""; 1639 OS.write_escaped(DiagTextBuilder.buildForDefinition(&R)) << '"'; 1640 1641 // Warning group associated with the diagnostic. This is stored as an index 1642 // into the alphabetically sorted warning group table. 1643 if (const auto *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) { 1644 auto I = DiagsInGroup.find(DI->getDef()->getValueAsString("GroupName")); 1645 assert(I != DiagsInGroup.end()); 1646 OS << ", " << I->second.IDNo; 1647 } else if (DiagsInPedantic.count(&R)) { 1648 auto I = DiagsInGroup.find("pedantic"); 1649 assert(I != DiagsInGroup.end() && "pedantic group not defined"); 1650 OS << ", " << I->second.IDNo; 1651 } else { 1652 OS << ", 0"; 1653 } 1654 1655 // SFINAE response. 1656 OS << ", " << R.getValueAsDef("SFINAE")->getName(); 1657 1658 // Default warning has no Werror bit. 1659 if (R.getValueAsBit("WarningNoWerror")) 1660 OS << ", true"; 1661 else 1662 OS << ", false"; 1663 1664 if (R.getValueAsBit("ShowInSystemHeader")) 1665 OS << ", true"; 1666 else 1667 OS << ", false"; 1668 1669 if (R.getValueAsBit("ShowInSystemMacro")) 1670 OS << ", true"; 1671 else 1672 OS << ", false"; 1673 1674 if (R.getValueAsBit("Deferrable")) 1675 OS << ", true"; 1676 else 1677 OS << ", false"; 1678 1679 // Category number. 1680 OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap)); 1681 OS << ")\n"; 1682 } 1683 } 1684 1685 //===----------------------------------------------------------------------===// 1686 // Warning Group Tables generation 1687 //===----------------------------------------------------------------------===// 1688 1689 static std::string getDiagCategoryEnum(StringRef name) { 1690 if (name.empty()) 1691 return "DiagCat_None"; 1692 SmallString<256> enumName = StringRef("DiagCat_"); 1693 for (StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I) 1694 enumName += isalnum(*I) ? *I : '_'; 1695 return std::string(enumName); 1696 } 1697 1698 /// Emit the array of diagnostic subgroups. 1699 /// 1700 /// The array of diagnostic subgroups contains for each group a list of its 1701 /// subgroups. The individual lists are separated by '-1'. Groups with no 1702 /// subgroups are skipped. 1703 /// 1704 /// \code 1705 /// static const int16_t DiagSubGroups[] = { 1706 /// /* Empty */ -1, 1707 /// /* DiagSubGroup0 */ 142, -1, 1708 /// /* DiagSubGroup13 */ 265, 322, 399, -1 1709 /// } 1710 /// \endcode 1711 /// 1712 static void emitDiagSubGroups(DiagsInGroupTy &DiagsInGroup, 1713 RecordVec &GroupsInPedantic, raw_ostream &OS) { 1714 OS << "static const int16_t DiagSubGroups[] = {\n" 1715 << " /* Empty */ -1,\n"; 1716 for (auto const &[Name, Group] : DiagsInGroup) { 1717 const bool IsPedantic = Name == "pedantic"; 1718 const std::vector<StringRef> &SubGroups = Group.SubGroups; 1719 if (!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty())) { 1720 OS << " /* DiagSubGroup" << Group.IDNo << " */ "; 1721 for (StringRef SubGroup : SubGroups) { 1722 auto RI = DiagsInGroup.find(SubGroup); 1723 assert(RI != DiagsInGroup.end() && "Referenced without existing?"); 1724 OS << RI->second.IDNo << ", "; 1725 } 1726 // Emit the groups implicitly in "pedantic". 1727 if (IsPedantic) { 1728 for (auto const &Group : GroupsInPedantic) { 1729 StringRef GroupName = Group->getValueAsString("GroupName"); 1730 auto RI = DiagsInGroup.find(GroupName); 1731 assert(RI != DiagsInGroup.end() && "Referenced without existing?"); 1732 OS << RI->second.IDNo << ", "; 1733 } 1734 } 1735 1736 OS << "-1,\n"; 1737 } 1738 } 1739 OS << "};\n\n"; 1740 } 1741 1742 /// Emit the list of diagnostic arrays. 1743 /// 1744 /// This data structure is a large array that contains itself arrays of varying 1745 /// size. Each array represents a list of diagnostics. The different arrays are 1746 /// separated by the value '-1'. 1747 /// 1748 /// \code 1749 /// static const int16_t DiagArrays[] = { 1750 /// /* Empty */ -1, 1751 /// /* DiagArray1 */ diag::warn_pragma_message, 1752 /// -1, 1753 /// /* DiagArray2 */ diag::warn_abs_too_small, 1754 /// diag::warn_unsigned_abs, 1755 /// diag::warn_wrong_absolute_value_type, 1756 /// -1 1757 /// }; 1758 /// \endcode 1759 /// 1760 static void emitDiagArrays(DiagsInGroupTy &DiagsInGroup, 1761 RecordVec &DiagsInPedantic, raw_ostream &OS) { 1762 OS << "static const int16_t DiagArrays[] = {\n" 1763 << " /* Empty */ -1,\n"; 1764 for (const auto &[Name, Group] : DiagsInGroup) { 1765 const bool IsPedantic = Name == "pedantic"; 1766 1767 const std::vector<const Record *> &V = Group.DiagsInGroup; 1768 if (!V.empty() || (IsPedantic && !DiagsInPedantic.empty())) { 1769 OS << " /* DiagArray" << Group.IDNo << " */ "; 1770 for (auto *Record : V) 1771 OS << "diag::" << Record->getName() << ", "; 1772 // Emit the diagnostics implicitly in "pedantic". 1773 if (IsPedantic) { 1774 for (auto const &Diag : DiagsInPedantic) 1775 OS << "diag::" << Diag->getName() << ", "; 1776 } 1777 OS << "-1,\n"; 1778 } 1779 } 1780 OS << "};\n\n"; 1781 } 1782 1783 /// Emit a list of group names. 1784 /// 1785 /// This creates an `llvm::StringTable` of all the diagnostic group names. 1786 static void emitDiagGroupNames(const StringToOffsetTable &GroupNames, 1787 raw_ostream &OS) { 1788 GroupNames.EmitStringTableDef(OS, "DiagGroupNames"); 1789 OS << "\n"; 1790 } 1791 1792 /// Emit diagnostic arrays and related data structures. 1793 /// 1794 /// This creates the actual diagnostic array, an array of diagnostic subgroups 1795 /// and an array of subgroup names. 1796 /// 1797 /// \code 1798 /// #ifdef GET_DIAG_ARRAYS 1799 /// static const int16_t DiagArrays[]; 1800 /// static const int16_t DiagSubGroups[]; 1801 /// static constexpr llvm::StringTable DiagGroupNames; 1802 /// #endif 1803 /// \endcode 1804 static void emitAllDiagArrays(DiagsInGroupTy &DiagsInGroup, 1805 RecordVec &DiagsInPedantic, 1806 RecordVec &GroupsInPedantic, 1807 const StringToOffsetTable &GroupNames, 1808 raw_ostream &OS) { 1809 OS << "\n#ifdef GET_DIAG_ARRAYS\n"; 1810 emitDiagArrays(DiagsInGroup, DiagsInPedantic, OS); 1811 emitDiagSubGroups(DiagsInGroup, GroupsInPedantic, OS); 1812 emitDiagGroupNames(GroupNames, OS); 1813 OS << "#endif // GET_DIAG_ARRAYS\n\n"; 1814 } 1815 1816 /// Emit diagnostic table. 1817 /// 1818 /// The table is sorted by the name of the diagnostic group. Each element 1819 /// consists of the name of the diagnostic group (given as offset in the 1820 /// group name table), a reference to a list of diagnostics (optional) and a 1821 /// reference to a set of subgroups (optional). 1822 /// 1823 /// \code 1824 /// #ifdef GET_DIAG_TABLE 1825 /// {/* abi */ 159, /* DiagArray11 */ 19, /* Empty */ 0}, 1826 /// {/* aggregate-return */ 180, /* Empty */ 0, /* Empty */ 0}, 1827 /// {/* all */ 197, /* Empty */ 0, /* DiagSubGroup13 */ 3}, 1828 /// {/* deprecated */ 1981,/* DiagArray1 */ 348, /* DiagSubGroup3 */ 9}, 1829 /// #endif 1830 /// \endcode 1831 static void emitDiagTable(DiagsInGroupTy &DiagsInGroup, 1832 RecordVec &DiagsInPedantic, 1833 RecordVec &GroupsInPedantic, 1834 const StringToOffsetTable &GroupNames, 1835 raw_ostream &OS) { 1836 unsigned MaxLen = 0; 1837 1838 for (auto const &I: DiagsInGroup) 1839 MaxLen = std::max(MaxLen, (unsigned)I.first.size()); 1840 1841 OS << "\n#ifdef DIAG_ENTRY\n"; 1842 unsigned SubGroupIndex = 1, DiagArrayIndex = 1; 1843 for (auto const &[Name, GroupInfo] : DiagsInGroup) { 1844 // Group option string. 1845 OS << "DIAG_ENTRY("; 1846 OS << GroupInfo.GroupName << " /* "; 1847 1848 if (Name.find_first_not_of("abcdefghijklmnopqrstuvwxyz" 1849 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 1850 "0123456789!@#$%^*-+=:?") != std::string::npos) 1851 PrintFatalError("Invalid character in diagnostic group '" + Name + "'"); 1852 OS << Name << " */, "; 1853 OS << *GroupNames.GetStringOffset(Name) << ", "; 1854 1855 // Special handling for 'pedantic'. 1856 const bool IsPedantic = Name == "pedantic"; 1857 1858 // Diagnostics in the group. 1859 const std::vector<const Record *> &V = GroupInfo.DiagsInGroup; 1860 const bool hasDiags = 1861 !V.empty() || (IsPedantic && !DiagsInPedantic.empty()); 1862 if (hasDiags) { 1863 OS << "/* DiagArray" << GroupInfo.IDNo << " */ " << DiagArrayIndex 1864 << ", "; 1865 if (IsPedantic) 1866 DiagArrayIndex += DiagsInPedantic.size(); 1867 DiagArrayIndex += V.size() + 1; 1868 } else { 1869 OS << "0, "; 1870 } 1871 1872 // Subgroups. 1873 const std::vector<StringRef> &SubGroups = GroupInfo.SubGroups; 1874 const bool hasSubGroups = 1875 !SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty()); 1876 if (hasSubGroups) { 1877 OS << "/* DiagSubGroup" << GroupInfo.IDNo << " */ " << SubGroupIndex 1878 << ", "; 1879 if (IsPedantic) 1880 SubGroupIndex += GroupsInPedantic.size(); 1881 SubGroupIndex += SubGroups.size() + 1; 1882 } else { 1883 OS << "0, "; 1884 } 1885 1886 std::string Documentation = GroupInfo.Defs.back() 1887 ->getValue("Documentation") 1888 ->getValue() 1889 ->getAsUnquotedString(); 1890 1891 OS << "R\"(" << StringRef(Documentation).trim() << ")\""; 1892 1893 OS << ")\n"; 1894 } 1895 OS << "#endif // DIAG_ENTRY\n\n"; 1896 } 1897 1898 /// Emit the table of diagnostic categories. 1899 /// 1900 /// The table has the form of macro calls that have two parameters. The 1901 /// category's name as well as an enum that represents the category. The 1902 /// table can be used by defining the macro 'CATEGORY' and including this 1903 /// table right after. 1904 /// 1905 /// \code 1906 /// #ifdef GET_CATEGORY_TABLE 1907 /// CATEGORY("Semantic Issue", DiagCat_Semantic_Issue) 1908 /// CATEGORY("Lambda Issue", DiagCat_Lambda_Issue) 1909 /// #endif 1910 /// \endcode 1911 static void emitCategoryTable(const RecordKeeper &Records, raw_ostream &OS) { 1912 DiagCategoryIDMap CategoriesByID(Records); 1913 OS << "\n#ifdef GET_CATEGORY_TABLE\n"; 1914 for (auto const &C : CategoriesByID) 1915 OS << "CATEGORY(\"" << C << "\", " << getDiagCategoryEnum(C) << ")\n"; 1916 OS << "#endif // GET_CATEGORY_TABLE\n\n"; 1917 } 1918 1919 void clang::EmitClangDiagGroups(const RecordKeeper &Records, raw_ostream &OS) { 1920 // Compute a mapping from a DiagGroup to all of its parents. 1921 DiagGroupParentMap DGParentMap(Records); 1922 1923 ArrayRef<const Record *> Diags = 1924 Records.getAllDerivedDefinitions("Diagnostic"); 1925 1926 ArrayRef<const Record *> DiagGroups = 1927 Records.getAllDerivedDefinitions("DiagGroup"); 1928 1929 DiagsInGroupTy DiagsInGroup; 1930 groupDiagnostics(Diags, DiagGroups, DiagsInGroup); 1931 1932 // All extensions are implicitly in the "pedantic" group. Record the 1933 // implicit set of groups in the "pedantic" group, and use this information 1934 // later when emitting the group information for Pedantic. 1935 RecordVec DiagsInPedantic; 1936 RecordVec GroupsInPedantic; 1937 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup); 1938 inferPedantic.compute(&DiagsInPedantic, &GroupsInPedantic); 1939 1940 StringToOffsetTable GroupNames; 1941 for (const auto &[Name, Group] : DiagsInGroup) { 1942 GroupNames.GetOrAddStringOffset(Name); 1943 } 1944 1945 emitAllDiagArrays(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames, 1946 OS); 1947 emitDiagTable(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames, 1948 OS); 1949 emitCategoryTable(Records, OS); 1950 } 1951 1952 //===----------------------------------------------------------------------===// 1953 // Diagnostic name index generation 1954 //===----------------------------------------------------------------------===// 1955 1956 void clang::EmitClangDiagsIndexName(const RecordKeeper &Records, 1957 raw_ostream &OS) { 1958 std::vector<const Record *> Diags = 1959 Records.getAllDerivedDefinitions("Diagnostic"); 1960 1961 sort(Diags, [](const Record *LHS, const Record *RHS) { 1962 return LHS->getName() < RHS->getName(); 1963 }); 1964 1965 for (const Record *Elem : Diags) 1966 OS << "DIAG_NAME_INDEX(" << Elem->getName() << ")\n"; 1967 } 1968 1969 //===----------------------------------------------------------------------===// 1970 // Diagnostic documentation generation 1971 //===----------------------------------------------------------------------===// 1972 1973 namespace docs { 1974 namespace { 1975 1976 bool isRemarkGroup(const Record *DiagGroup, 1977 const DiagsInGroupTy &DiagsInGroup) { 1978 bool AnyRemarks = false, AnyNonRemarks = false; 1979 1980 std::function<void(StringRef)> Visit = [&](StringRef GroupName) { 1981 auto &GroupInfo = DiagsInGroup.find(GroupName)->second; 1982 for (const Record *Diag : GroupInfo.DiagsInGroup) 1983 (isRemark(*Diag) ? AnyRemarks : AnyNonRemarks) = true; 1984 for (StringRef Name : GroupInfo.SubGroups) 1985 Visit(Name); 1986 }; 1987 Visit(DiagGroup->getValueAsString("GroupName")); 1988 1989 if (AnyRemarks && AnyNonRemarks) 1990 PrintFatalError( 1991 DiagGroup->getLoc(), 1992 "Diagnostic group contains both remark and non-remark diagnostics"); 1993 return AnyRemarks; 1994 } 1995 1996 std::string getDefaultSeverity(const Record *Diag) { 1997 return std::string( 1998 Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name")); 1999 } 2000 2001 std::set<std::string> getDefaultSeverities(const Record *DiagGroup, 2002 const DiagsInGroupTy &DiagsInGroup) { 2003 std::set<std::string> States; 2004 2005 std::function<void(StringRef)> Visit = [&](StringRef GroupName) { 2006 auto &GroupInfo = DiagsInGroup.find(GroupName)->second; 2007 for (const Record *Diag : GroupInfo.DiagsInGroup) 2008 States.insert(getDefaultSeverity(Diag)); 2009 for (const auto &Name : GroupInfo.SubGroups) 2010 Visit(Name); 2011 }; 2012 Visit(DiagGroup->getValueAsString("GroupName")); 2013 return States; 2014 } 2015 2016 void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') { 2017 OS << Str << "\n" << std::string(Str.size(), Kind) << "\n"; 2018 } 2019 2020 void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R, 2021 StringRef Role, raw_ostream &OS) { 2022 StringRef Text = R->getValueAsString("Summary"); 2023 if (Text == "%0") 2024 OS << "The text of this diagnostic is not controlled by Clang.\n\n"; 2025 else { 2026 std::vector<std::string> Out = Builder.buildForDocumentation(Role, R); 2027 for (auto &Line : Out) 2028 OS << Line << "\n"; 2029 OS << "\n"; 2030 } 2031 } 2032 2033 } // namespace 2034 } // namespace docs 2035 2036 void clang::EmitClangDiagDocs(const RecordKeeper &Records, raw_ostream &OS) { 2037 using namespace docs; 2038 2039 // Get the documentation introduction paragraph. 2040 const Record *Documentation = Records.getDef("GlobalDocumentation"); 2041 if (!Documentation) { 2042 PrintFatalError("The Documentation top-level definition is missing, " 2043 "no documentation will be generated."); 2044 return; 2045 } 2046 2047 OS << Documentation->getValueAsString("Intro") << "\n"; 2048 2049 DiagnosticTextBuilder Builder(Records); 2050 2051 ArrayRef<const Record *> Diags = 2052 Records.getAllDerivedDefinitions("Diagnostic"); 2053 2054 std::vector<const Record *> DiagGroups = 2055 Records.getAllDerivedDefinitions("DiagGroup"); 2056 sort(DiagGroups, diagGroupBeforeByName); 2057 2058 DiagGroupParentMap DGParentMap(Records); 2059 2060 DiagsInGroupTy DiagsInGroup; 2061 groupDiagnostics(Diags, DiagGroups, DiagsInGroup); 2062 2063 // Compute the set of diagnostics that are in -Wpedantic. 2064 { 2065 RecordSet DiagsInPedanticSet; 2066 RecordSet GroupsInPedanticSet; 2067 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup); 2068 inferPedantic.compute(&DiagsInPedanticSet, &GroupsInPedanticSet); 2069 auto &PedDiags = DiagsInGroup["pedantic"]; 2070 // Put the diagnostics into a deterministic order. 2071 RecordVec DiagsInPedantic(DiagsInPedanticSet.begin(), 2072 DiagsInPedanticSet.end()); 2073 RecordVec GroupsInPedantic(GroupsInPedanticSet.begin(), 2074 GroupsInPedanticSet.end()); 2075 sort(DiagsInPedantic, beforeThanCompare); 2076 sort(GroupsInPedantic, beforeThanCompare); 2077 PedDiags.DiagsInGroup.insert(PedDiags.DiagsInGroup.end(), 2078 DiagsInPedantic.begin(), 2079 DiagsInPedantic.end()); 2080 for (auto *Group : GroupsInPedantic) 2081 PedDiags.SubGroups.push_back(Group->getValueAsString("GroupName")); 2082 } 2083 2084 // FIXME: Write diagnostic categories and link to diagnostic groups in each. 2085 2086 // Write out the diagnostic groups. 2087 for (const Record *G : DiagGroups) { 2088 bool IsRemarkGroup = isRemarkGroup(G, DiagsInGroup); 2089 auto &GroupInfo = DiagsInGroup[G->getValueAsString("GroupName")]; 2090 bool IsSynonym = GroupInfo.DiagsInGroup.empty() && 2091 GroupInfo.SubGroups.size() == 1; 2092 2093 writeHeader(((IsRemarkGroup ? "-R" : "-W") + 2094 G->getValueAsString("GroupName")).str(), 2095 OS); 2096 2097 if (!IsSynonym) { 2098 // FIXME: Ideally, all the diagnostics in a group should have the same 2099 // default state, but that is not currently the case. 2100 auto DefaultSeverities = getDefaultSeverities(G, DiagsInGroup); 2101 if (!DefaultSeverities.empty() && !DefaultSeverities.count("Ignored")) { 2102 bool AnyNonErrors = DefaultSeverities.count("Warning") || 2103 DefaultSeverities.count("Remark"); 2104 if (!AnyNonErrors) 2105 OS << "This diagnostic is an error by default, but the flag ``-Wno-" 2106 << G->getValueAsString("GroupName") << "`` can be used to disable " 2107 << "the error.\n\n"; 2108 else 2109 OS << "This diagnostic is enabled by default.\n\n"; 2110 } else if (DefaultSeverities.size() > 1) { 2111 OS << "Some of the diagnostics controlled by this flag are enabled " 2112 << "by default.\n\n"; 2113 } 2114 } 2115 2116 if (!GroupInfo.SubGroups.empty()) { 2117 if (IsSynonym) 2118 OS << "Synonym for "; 2119 else if (GroupInfo.DiagsInGroup.empty()) 2120 OS << "Controls "; 2121 else 2122 OS << "Also controls "; 2123 2124 bool First = true; 2125 sort(GroupInfo.SubGroups); 2126 for (StringRef Name : GroupInfo.SubGroups) { 2127 if (!First) OS << ", "; 2128 OS << "`" << (IsRemarkGroup ? "-R" : "-W") << Name << "`_"; 2129 First = false; 2130 } 2131 OS << ".\n\n"; 2132 } 2133 2134 if (!GroupInfo.DiagsInGroup.empty()) { 2135 OS << "**Diagnostic text:**\n\n"; 2136 for (const Record *D : GroupInfo.DiagsInGroup) { 2137 auto Severity = getDefaultSeverity(D); 2138 Severity[0] = tolower(Severity[0]); 2139 if (Severity == "ignored") 2140 Severity = IsRemarkGroup ? "remark" : "warning"; 2141 2142 writeDiagnosticText(Builder, D, Severity, OS); 2143 } 2144 } 2145 2146 auto Doc = G->getValueAsString("Documentation"); 2147 if (!Doc.empty()) 2148 OS << Doc; 2149 else if (GroupInfo.SubGroups.empty() && GroupInfo.DiagsInGroup.empty()) 2150 OS << "This diagnostic flag exists for GCC compatibility, and has no " 2151 "effect in Clang.\n"; 2152 OS << "\n"; 2153 } 2154 } 2155