Sema: Diagnose availability of availability domains in if #available queries.

When emitting statement diagnostics for `if #available` queries, diagnose the
availability of the decls representing the referenced availability domains.
Among other things, this checks that the domains are sufficiently visible to be
used in the containing function body context.
This commit is contained in:
Allan Shortlidge
2025-08-19 18:59:31 -07:00
parent 6c1a9770dc
commit 9de88624b2
5 changed files with 56 additions and 3 deletions

View File

@@ -41,9 +41,7 @@ getCustomDomainKind(clang::FeatureAvailKind featureAvailKind) {
static const CustomAvailabilityDomain *
customDomainForClangDecl(ValueDecl *decl) {
auto *clangDecl = decl->getClangDecl();
ASSERT(clangDecl);
auto *varDecl = dyn_cast<clang::VarDecl>(clangDecl);
auto *varDecl = dyn_cast_or_null<clang::VarDecl>(clangDecl);
if (!varDecl)
return nullptr;

View File

@@ -16,6 +16,7 @@
//===----------------------------------------------------------------------===//
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/AvailabilityDomain.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/Decl.h"
@@ -877,6 +878,20 @@ static void formatDiagnosticArgument(StringRef Modifier,
assert(Modifier.empty() && "Improper modifier for ValueDecl argument");
}
// Handle declarations representing an AvailabilityDomain specially.
if (auto VD = dyn_cast<ValueDecl>(D)) {
if (auto domain = AvailabilityDomain::forCustom(const_cast<ValueDecl *>(VD))) {
Out << "availability domain";
if (includeName) {
Out << " " << FormatOpts.OpeningQuotationMark;
Out << domain->getNameForDiagnostics();
Out << FormatOpts.ClosingQuotationMark;
}
break;
}
}
if (includeName) {
if (auto accessor = dyn_cast<AccessorDecl>(D)) {
// If it's an accessor, describe that and then switch to discussing its

View File

@@ -5257,6 +5257,12 @@ static bool diagnoseAvailabilityCondition(PoundAvailableInfo *info,
return true;
}
// Check the availability of the domain decl.
if (auto *domainDecl = spec.getDomain().getDecl()) {
auto where = ExportContext::forFunctionBody(DC, loc);
diagnoseDeclAvailability(domainDecl, loc, nullptr, where);
}
hasValidSpecs = true;
if (!domain.isPlatform())
allValidSpecsArePlatform = false;

View File

@@ -0,0 +1,30 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify \
// RUN: -import-objc-header %S/Inputs/availability_domains_bridging_header.h \
// RUN: -I %S/../Inputs/custom-modules/availability-domains \
// RUN: -enable-experimental-feature CustomAvailability \
// RUN: %s
// REQUIRES: swift_feature_CustomAvailability
private import Rivers // also re-exported by Oceans
internal import Oceans
// expected-note@-1 {{availability domain 'Arctic' imported as 'internal' from 'Oceans' here}}
// expected-note@-2 {{availability domain 'Colorado' imported as 'internal' from 'Oceans' here}}
// expected-note@-3 {{availability domain 'Grand' imported as 'internal' from 'Oceans' here}}
public import Seas
@inlinable public func inlinableFunc() {
if #available(Colorado) { } // expected-error {{availability domain 'Colorado' is internal and cannot be referenced from an '@inlinable' function}}
if #available(Grand) { } // expected-error {{availability domain 'Grand' is internal and cannot be referenced from an '@inlinable' function}}
if #available(Arctic) { } // expected-error {{availability domain 'Arctic' is internal and cannot be referenced from an '@inlinable' function}}
if #available(Baltic) { }
if #available(BayBridge) { }
}
public func nonInlinablePublicFunc() {
if #available(Colorado) { }
if #available(Grand) { } // expected-warning {{availability domain 'Grand' is deprecated: Use Colorado instead}}
if #available(Arctic) { }
if #available(Baltic) { }
if #available(BayBridge) { }
}

View File

@@ -3,6 +3,10 @@
static struct __AvailabilityDomain colorado_domain __attribute__((
availability_domain(Colorado))) = {__AVAILABILITY_DOMAIN_DISABLED, 0};
__attribute__((deprecated("Use Colorado instead")))
static struct __AvailabilityDomain grand_domain __attribute__((
availability_domain(Grand))) = {__AVAILABILITY_DOMAIN_DISABLED, 0};
#define AVAIL 0
#define UNAVAIL 1