//===--- SupportedFeatures.cpp - Supported features printing --------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2025 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 // //===----------------------------------------------------------------------===// #include #include #include "swift/AST/DiagnosticGroups.h" #include "swift/Basic/Feature.h" #include "swift/Frontend/Frontend.h" #include "llvm/Support/raw_ostream.h" using namespace swift; namespace swift { namespace features { /// The subset of diagnostic groups (called categories by the diagnostic machinery) whose diagnostics should be /// considered to be part of the migration for this feature. /// /// When making a feature migratable, ensure that all of the warnings that are used to drive the migration are /// part of a diagnostic group, and put that diagnostic group into the list for that feature here. static std::vector migratableCategories(Feature feature) { switch (feature) { case Feature::InnerKind::ExistentialAny: return { DiagGroupID::ExistentialAny }; case Feature::InnerKind::InferIsolatedConformances: return { DiagGroupID::IsolatedConformances }; case Feature::InnerKind::MemberImportVisibility: return { DiagGroupID::MemberImportVisibility }; case Feature::InnerKind::NonisolatedNonsendingByDefault: return { DiagGroupID::NonisolatedNonsendingByDefault }; case Feature::InnerKind::StrictMemorySafety: return { DiagGroupID::StrictMemorySafety }; // Provide unreachable cases for all of the non-migratable features. #define LANGUAGE_FEATURE(FeatureName, SENumber, Description) case Feature::FeatureName: #define MIGRATABLE_UPCOMING_FEATURE(FeatureName, SENumber, LanguageMode) #define MIGRATABLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) #define MIGRATABLE_OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Name) #include "swift/Basic/Features.def" llvm_unreachable("Not a migratable feature"); } } /// For optional language features, return the flag name used by the compiler to enable the feature. For all others, /// returns an empty optional. static std::optional optionalFlagName(Feature feature) { switch (feature) { case Feature::StrictMemorySafety: return "-strict-memory-safety"; case Feature::LibraryEvolution: return "-enable-library-evolution"; #define LANGUAGE_FEATURE(FeatureName, SENumber, Description) case Feature::FeatureName: #define OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Description) #include "swift/Basic/Features.def" return std::nullopt; } } /// Print information about what features upcoming/experimental are /// supported by the compiler. /// The information includes whether a feature is adoptable and for /// upcoming features - what is the first mode it's introduced. void printSupportedFeatures(llvm::raw_ostream &out) { std::array optional{ #define LANGUAGE_FEATURE(FeatureName, SENumber, Description) #define OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Description) Feature::FeatureName, #include "swift/Basic/Features.def" }; std::array upcoming{ #define LANGUAGE_FEATURE(FeatureName, SENumber, Description) #define UPCOMING_FEATURE(FeatureName, SENumber, LanguageMode) \ Feature::FeatureName, #include "swift/Basic/Features.def" }; std::vector experimental{{ #define LANGUAGE_FEATURE(FeatureName, SENumber, Description) #define EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) Feature::FeatureName, #include "swift/Basic/Features.def" }}; // Include only experimental features that are available in production. llvm::erase_if(experimental, [](auto &feature) { return !feature.isAvailableInProduction(); }); out << "{\n"; auto printFeature = [&out](const Feature &feature) { out << " "; out << "{ \"name\": \"" << feature.getName() << "\""; if (feature.isMigratable()) { out << ", \"migratable\": true"; auto categories = migratableCategories(feature); out << ", \"categories\": ["; llvm::interleave(categories, [&out](DiagGroupID diagGroupID) { out << "\"" << getDiagGroupInfoByID(diagGroupID).name << "\""; }, [&out] { out << ", "; }); out << "]"; } if (auto version = feature.getLanguageMode()) { out << ", \"enabled_in\": \"" << *version << "\""; } if (auto flagName = optionalFlagName(feature)) { out << ", \"flag_name\": \"" << *flagName << "\""; } out << " }"; }; out << " \"features\": {\n"; out << " \"optional\": [\n"; llvm::interleave(optional, printFeature, [&out] { out << ",\n"; }); out << "\n ],\n"; out << " \"upcoming\": [\n"; llvm::interleave(upcoming, printFeature, [&out] { out << ",\n"; }); out << "\n ],\n"; out << " \"experimental\": [\n"; llvm::interleave(experimental, printFeature, [&out] { out << ",\n"; }); out << "\n ]\n"; out << " }\n"; out << "}\n"; } } // end namespace features } // end namespace swift