From 85444fd1e580f4a88bbddfcdb22e5963777a42d0 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 6 Jun 2025 17:22:55 -0700 Subject: [PATCH] AST: Diagnose unrecognized platforms as errors with CustomAvailability enabled. When the CustomAvailability experimental feature is enabled, make it an error to specify an unrecognized availability domain name. Also, add these diagnostics to a diagnostic group so that developers can control their behavior when they are warnings. Resolves rdar://152741624. --- include/swift/AST/DiagnosticGroups.def | 1 + include/swift/AST/DiagnosticsSema.def | 11 ++++++----- lib/AST/AvailabilityDomain.cpp | 15 +++++++++++---- .../availability_custom_domains_other.swift | 2 +- .../availability_custom_domains.swift | 2 +- .../availability_query_custom_domains.swift | 4 ++-- test/Sema/availability_custom_domains.swift | 2 +- .../attr_availability_custom_domains.swift | 2 +- .../availability-unrecognized-name.md | 18 ++++++++++++++++++ 9 files changed, 42 insertions(+), 15 deletions(-) create mode 100644 userdocs/diagnostics/availability-unrecognized-name.md diff --git a/include/swift/AST/DiagnosticGroups.def b/include/swift/AST/DiagnosticGroups.def index f5eab826de0f8..dd1e8a8cd3390 100644 --- a/include/swift/AST/DiagnosticGroups.def +++ b/include/swift/AST/DiagnosticGroups.def @@ -41,6 +41,7 @@ GROUP(no_group, "") GROUP(ActorIsolatedCall, "actor-isolated-call") +GROUP(AvailabilityUnrecognizedName, "availability-unrecognized-name") GROUP(ClangDeclarationImport, "clang-declaration-import") GROUP(ConformanceIsolation, "conformance-isolation") GROUP(DeprecatedDeclaration, "deprecated-declaration") diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index f79d13ab8af2a..3541aed714058 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -6913,11 +6913,12 @@ ERROR(availability_must_occur_alone, none, (AvailabilityDomain, bool)) ERROR(availability_unexpected_version, none, "unexpected version number for %0", (AvailabilityDomain)) -WARNING(availability_unrecognized_platform_name, - PointsToFirstBadToken, "unrecognized platform name %0", (Identifier)) -WARNING(availability_suggest_platform_name, - PointsToFirstBadToken, "unrecognized platform name %0;" - " did you mean '%1'?", +GROUPED_ERROR(availability_unrecognized_platform_name, + AvailabilityUnrecognizedName, PointsToFirstBadToken, + "unrecognized platform name %0", (Identifier)) +GROUPED_ERROR(availability_suggest_platform_name, + AvailabilityUnrecognizedName, PointsToFirstBadToken, + "unrecognized platform name %0; did you mean '%1'?", (Identifier, StringRef)) WARNING(availability_unsupported_version_number, none, "'%0' is not a supported version number", (llvm::VersionTuple)) diff --git a/lib/AST/AvailabilityDomain.cpp b/lib/AST/AvailabilityDomain.cpp index 96e5d48d07cdd..bc2e0a2fa9272 100644 --- a/lib/AST/AvailabilityDomain.cpp +++ b/lib/AST/AvailabilityDomain.cpp @@ -368,22 +368,29 @@ AvailabilityDomainOrIdentifier::lookUpInDeclContext( domain = results.front(); } + bool hasCustomAvailability = + ctx.LangOpts.hasFeature(Feature::CustomAvailability); + if (!domain) { auto domainString = identifier.str(); + bool downgradeErrors = + !hasCustomAvailability || declContext->isInSwiftinterface(); if (auto suggestion = closestCorrectedPlatformString(domainString)) { diags .diagnose(loc, diag::availability_suggest_platform_name, identifier, *suggestion) + .limitBehaviorIf(downgradeErrors, DiagnosticBehavior::Warning) .fixItReplace(SourceRange(loc), *suggestion); } else { - diags.diagnose(loc, diag::availability_unrecognized_platform_name, - identifier); + diags + .diagnose(loc, diag::availability_unrecognized_platform_name, + identifier) + .limitBehaviorIf(downgradeErrors, DiagnosticBehavior::Warning); } return std::nullopt; } - if (domain->isCustom() && - !ctx.LangOpts.hasFeature(Feature::CustomAvailability) && + if (domain->isCustom() && !hasCustomAvailability && !declContext->isInSwiftinterface()) { diags.diagnose(loc, diag::attr_availability_requires_custom_availability, *domain); diff --git a/test/ClangImporter/Inputs/availability_custom_domains_other.swift b/test/ClangImporter/Inputs/availability_custom_domains_other.swift index f51a436531000..43477f09c8185 100644 --- a/test/ClangImporter/Inputs/availability_custom_domains_other.swift +++ b/test/ClangImporter/Inputs/availability_custom_domains_other.swift @@ -1,6 +1,6 @@ import Seas -@available(Arctic) // expected-warning {{unrecognized platform name 'Arctic'}} +@available(Arctic) // expected-error {{unrecognized platform name 'Arctic'}} func availableInArctic() { } @available(Mediterranean) diff --git a/test/ClangImporter/availability_custom_domains.swift b/test/ClangImporter/availability_custom_domains.swift index 0a87fae61bddb..ca81219d69645 100644 --- a/test/ClangImporter/availability_custom_domains.swift +++ b/test/ClangImporter/availability_custom_domains.swift @@ -44,7 +44,7 @@ func availableInPacific() { } func unavailableInColorado() { } // expected-note {{'unavailableInColorado()' has been explicitly marked unavailable here}} // The Seas module is only imported directly by the other source file. -@available(Baltic) // expected-warning {{unrecognized platform name 'Baltic'}} +@available(Baltic) // expected-error {{unrecognized platform name 'Baltic'}} func availableInBaltic() { } // expected-note {{did you mean 'availableInBaltic'}} func testSwiftDecls() { diff --git a/test/Parse/availability_query_custom_domains.swift b/test/Parse/availability_query_custom_domains.swift index 1b176b437bc42..e556a17151bc9 100644 --- a/test/Parse/availability_query_custom_domains.swift +++ b/test/Parse/availability_query_custom_domains.swift @@ -9,13 +9,13 @@ if #available(EnabledDomain) { } if #available(DisabledDomain) { } if #available(DynamicDomain) { } -if #available(UnknownDomain) { } // expected-warning {{unrecognized platform name 'UnknownDomain'}} +if #available(UnknownDomain) { } // expected-error {{unrecognized platform name 'UnknownDomain'}} // expected-error@-1 {{condition required for target platform}} if #unavailable(EnabledDomain) { } if #unavailable(DisabledDomain) { } if #unavailable(DynamicDomain) { } -if #unavailable(UnknownDomain) { } // expected-warning {{unrecognized platform name 'UnknownDomain'}} +if #unavailable(UnknownDomain) { } // expected-error {{unrecognized platform name 'UnknownDomain'}} if #available(EnabledDomain 1.0) { } // expected-error {{unexpected version number for EnabledDomain}} if #available(EnabledDomain, DisabledDomain) { } // expected-error {{EnabledDomain availability must be specified alone}} diff --git a/test/Sema/availability_custom_domains.swift b/test/Sema/availability_custom_domains.swift index 4caac2ec87824..39a18d0408eab 100644 --- a/test/Sema/availability_custom_domains.swift +++ b/test/Sema/availability_custom_domains.swift @@ -26,7 +26,7 @@ func deprecatedInDynamicDomain() { } @available(DynamicDomain, unavailable) func unavailableInDynamicDomain() { } // expected-note * {{'unavailableInDynamicDomain()' has been explicitly marked unavailable here}} -@available(UnknownDomain) // expected-warning {{unrecognized platform name 'UnknownDomain'}} +@available(UnknownDomain) // expected-error {{unrecognized platform name 'UnknownDomain'}} func availableInUnknownDomain() { } func testDeployment() { diff --git a/test/attr/attr_availability_custom_domains.swift b/test/attr/attr_availability_custom_domains.swift index 4dba8063eec3e..14d0f3f62d897 100644 --- a/test/attr/attr_availability_custom_domains.swift +++ b/test/attr/attr_availability_custom_domains.swift @@ -57,5 +57,5 @@ func deprecatedInRedefinedDomain() { } @available(DynamicDomain) func availableInDynamicDomain() { } -@available(UnknownDomain) // expected-warning {{unrecognized platform name 'UnknownDomain'}} +@available(UnknownDomain) // expected-error {{unrecognized platform name 'UnknownDomain'}} func availableInUnknownDomain() { } diff --git a/userdocs/diagnostics/availability-unrecognized-name.md b/userdocs/diagnostics/availability-unrecognized-name.md new file mode 100644 index 0000000000000..1caf5bb107c11 --- /dev/null +++ b/userdocs/diagnostics/availability-unrecognized-name.md @@ -0,0 +1,18 @@ +# Unrecognized availability platforms (AvailabilityUnrecognizedName) + +Warnings that identify unrecognized platform names in `@available` attributes and `if #available` statements. + +## Overview + +The `AvailabilityUnrecognizedName` group covers warnings emitted when the platform name specified in an availability related construct is unrecognized by the compiler: + +``` +@available(NotAValidPlatform, introduced: 1.0) // warning: unrecognized platform name 'NotAValidPlatform' +public func function() { + if #available(NotAValidPlatform 2.0, *) { // warning: unrecognized platform name 'NotAValidPlatform' + // ... + } +} +``` + +Availability specifications with unrecognized platform names in `@available` attributes and `#available` queries are ignored by the compiler.