//===--- DiagnosticGroups.cpp - Diagnostic Groups ---------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file defines the diagnostic groups enumaration, group graph // and auxilary functions. // //===----------------------------------------------------------------------===// #include "swift/AST/DiagnosticGroups.h" #include "swift/AST/DiagnosticList.h" #include namespace swift { namespace { template struct GroupConnections { std::array supergroups; std::array subgroups; std::array diagnostics; constexpr GroupConnections( const std::array &supergroups, const std::array &subgroups, const std::array &diagnostics) : supergroups(supergroups), subgroups(subgroups), diagnostics(diagnostics) {} }; // This CTAD is needed in C++17 only. Remove after update to C++20. template GroupConnections(const std::array &, const std::array &, const std::array &) -> GroupConnections; constexpr const auto diagnosticGroupConnections = [] { constexpr auto sizes = [] { std::array supergroupsCount{}; std::array subgroupsCount{}; std::array diagnosticsCount{}; // Count edges for each diagnostic group #define GROUP_LINK(Parent, Child) \ subgroupsCount[(size_t)DiagGroupID::Parent]++; \ supergroupsCount[(size_t)DiagGroupID::Child]++; #include "swift/AST/DiagnosticGroups.def" // Count attached diagnostic IDs for each diagnostic group #define DIAG(KIND, ID, Group, Options, Text, Signature) \ diagnosticsCount[(size_t)DiagGroupID::Group]++; #include "swift/AST/DiagnosticsAll.def" return std::tuple{supergroupsCount, subgroupsCount, diagnosticsCount}; }(); constexpr auto supergroupsCount = std::get<0>(sizes); constexpr auto subgroupsCount = std::get<1>(sizes); constexpr auto diagnosticsCount = std::get<2>(sizes); // Declare all edges #define GROUP(Name, Option, DocsFile) \ std::array \ Name##_supergroups{}; \ std::array \ Name##_subgroups{}; \ std::array \ Name##_diagnostics{}; \ [[maybe_unused]] size_t Name##_supergroupsIndex = 0; \ [[maybe_unused]] size_t Name##_subgroupsIndex = 0; \ [[maybe_unused]] size_t Name##_diagnosticsIndex = 0; #include "swift/AST/DiagnosticGroups.def" // Bind all groups to each other #define GROUP_LINK(Parent, Child) \ Parent##_subgroups[Parent##_subgroupsIndex++] = DiagGroupID::Child; \ Child##_supergroups[Child##_supergroupsIndex++] = DiagGroupID::Parent; #include "swift/AST/DiagnosticGroups.def" // Bind all diagnostics to their groups #define DIAG(KIND, ID, Group, Options, Text, Signature) \ Group##_diagnostics[Group##_diagnosticsIndex++] = DiagID::ID; #include "swift/AST/DiagnosticsAll.def" // Produce the resulting structure with all the edges #define GROUP(Name, Option, DocsFile) \ GroupConnections(Name##_supergroups, Name##_subgroups, Name##_diagnostics), return std::tuple{ #include "swift/AST/DiagnosticGroups.def" }; }(); std::unordered_map nameToIDMap{ #define GROUP(Name, Option, DocsFile) {#Name, DiagGroupID::Name}, #include "swift/AST/DiagnosticGroups.def" }; void traverseDepthFirst(DiagGroupID id, std::unordered_set &visited, llvm::function_ref func) { if (visited.insert(id).second) { const auto &info = getDiagGroupInfoByID(id); func(info); for (const auto subgroup : info.subgroups) { traverseDepthFirst(subgroup, visited, func); } } } } // end anonymous namespace constexpr const std::array diagnosticGroupsInfo{ #define GROUP(Name, Option, DocsFile) \ DiagGroupInfo{ \ DiagGroupID::Name, \ #Name, \ DocsFile, \ DiagnosticGroupOptions::Option, \ llvm::ArrayRef( \ std::get<(size_t)DiagGroupID::Name>(diagnosticGroupConnections) \ .supergroups), \ llvm::ArrayRef( \ std::get<(size_t)DiagGroupID::Name>(diagnosticGroupConnections) \ .subgroups), \ llvm::ArrayRef( \ std::get<(size_t)DiagGroupID::Name>(diagnosticGroupConnections) \ .diagnostics)}, #include "swift/AST/DiagnosticGroups.def" }; const DiagGroupInfo &getDiagGroupInfoByID(DiagGroupID id) { return diagnosticGroupsInfo[(size_t)id]; } std::optional getDiagGroupIDByName(std::string_view name) { auto it = nameToIDMap.find(name); if (it == nameToIDMap.end()) return std::nullopt; return it->second; } void DiagGroupInfo::traverseDepthFirst( llvm::function_ref func) const { std::unordered_set visited; ::swift::traverseDepthFirst(id, visited, func); } namespace validation { template constexpr void unfold(F &&function, std::index_sequence) { (function(std::integral_constant{}), ...); } template constexpr void constexpr_for(F &&function) { unfold(function, std::make_index_sequence()); } template constexpr bool hasCycleFromGroup(std::array &visited, std::array &recursionStack) { if (!visited[Group]) { visited[Group] = true; recursionStack[Group] = true; constexpr auto subgroups = std::get(diagnosticGroupConnections).subgroups; bool isCycleFound = false; constexpr_for([&](auto i) { constexpr auto subgroup = (size_t)subgroups[i]; if (!visited[subgroup] && hasCycleFromGroup(visited, recursionStack)) isCycleFound = true; else if (recursionStack[subgroup]) isCycleFound = true; }); if (isCycleFound) return true; } recursionStack[Group] = false; return false; } constexpr bool hasCycle() { std::array recursionStack{}; std::array visited{}; bool isCycleFound = false; constexpr_for([&](auto i) { if (!visited[i] && hasCycleFromGroup(visited, recursionStack)) isCycleFound = true; }); return isCycleFound; } template constexpr bool isGroupInSupergroup() { for (const auto group : std::get<(size_t)Child>(diagnosticGroupConnections).supergroups) if (group == Parent) return true; return false; } // Check for isGroupInSupergroup itself static_assert(!isGroupInSupergroup() && "Bug in isGroupInSupergroup"); static_assert(!hasCycle(), "Diagnostic groups graph has a cycle!"); // Sanity check for the "no_group" group static_assert((uint16_t)DiagGroupID::no_group == 0, "0 isn't no_group"); static_assert(std::get<0>(diagnosticGroupConnections).supergroups.size() == 0, "no_group isn't a top-level group"); static_assert(std::get<0>(diagnosticGroupConnections).subgroups.size() == 0, "no_group shouldn't have subgroups"); // Check groups have associated diagnostics #define CHECK_NOT_EMPTY(Group) \ static_assert( \ std::get<(uint16_t)DiagGroupID::Group>(diagnosticGroupConnections) \ .diagnostics.size() > 0, \ "'" #Group "' group shouldn't be empty."); CHECK_NOT_EMPTY(DeprecatedDeclaration) CHECK_NOT_EMPTY(UnknownWarningGroup) #undef CHECK_NOT_EMPTY } // end namespace validation } // end namespace swift