Skip to content

Commit

Permalink
Sema: Reject version specs in @available attributes for custom domains.
Browse files Browse the repository at this point in the history
  • Loading branch information
tshortli committed Jan 31, 2025
1 parent d469854 commit 358d55a
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 31 deletions.
8 changes: 7 additions & 1 deletion include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3319,7 +3319,7 @@ class SemanticAvailableAttr final {
return attr->getRawIntroduced();
}

/// The source range of the `introduced:` component.
/// The source range of the `introduced:` version component.
SourceRange getIntroducedSourceRange() const { return attr->IntroducedRange; }

/// Returns the effective range in which the declaration with this attribute
Expand All @@ -3331,11 +3331,17 @@ class SemanticAvailableAttr final {
return attr->getRawDeprecated();
}

/// The source range of the `deprecated:` version component.
SourceRange getDeprecatedSourceRange() const { return attr->DeprecatedRange; }

/// The version tuple written in source for the `obsoleted:` component.
std::optional<llvm::VersionTuple> getObsoleted() const {
return attr->getRawObsoleted();
}

/// The source range of the `obsoleted:` version component.
SourceRange getObsoletedSourceRange() const { return attr->ObsoletedRange; }

/// Returns the `message:` field of the attribute, or an empty string.
StringRef getMessage() const { return attr->Message; }

Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/AvailabilityDomain.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ class AvailabilityDomain final {
return PlatformKind::none;
}

/// Returns true if availability for this domain can be specified in terms of
/// version ranges.
bool isVersioned() const;

/// Returns true if this domain is considered active in the current
/// compilation context.
bool isActive(const ASTContext &ctx) const;
Expand Down
4 changes: 0 additions & 4 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -1579,10 +1579,6 @@ ERROR(attr_availability_expected_equal,none,
ERROR(attr_availability_expected_version,none,
"expected version number in '%0' attribute", (StringRef))

WARNING(attr_availability_nonspecific_platform_unexpected_version,none,
"unexpected version number in '%0' attribute for non-specific platform "
"'*'", (StringRef))

WARNING(attr_availability_wildcard_ignored,none,
"* as platform name has no effect in '%0' attribute", (StringRef))

Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6735,6 +6735,9 @@ ERROR(attr_availability_requires_custom_availability, none,
"specifying '%0' in '%1' attribute requires "
"-enable-experimental-feature CustomAvailability",
(StringRef, const DeclAttribute))
WARNING(attr_availability_unexpected_version,none,
"unexpected version number in '%0' attribute for '%1'",
(const DeclAttribute, StringRef))

ERROR(availability_decl_unavailable, none,
"%0 is unavailable%select{ in %2|}1%select{|: %3}3",
Expand Down
15 changes: 15 additions & 0 deletions lib/AST/AvailabilityDomain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ AvailabilityDomain::builtinDomainForString(StringRef string,
return std::nullopt;
}

bool AvailabilityDomain::isVersioned() const {
switch (getKind()) {
case Kind::Universal:
case Kind::Embedded:
return false;
case Kind::SwiftLanguage:
case Kind::PackageDescription:
case Kind::Platform:
return true;
case Kind::Custom:
// FIXME: [availability] Support versioned custom availability domains
return false;
}
}

bool AvailabilityDomain::isActive(const ASTContext &ctx) const {
switch (getKind()) {
case Kind::Universal:
Expand Down
20 changes: 0 additions & 20 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,26 +546,6 @@ ParserResult<AvailableAttr> Parser::parseExtendedAvailabilitySpecList(
if (AnyArgumentInvalid)
return nullptr;

// Warn if any version is specified with the universal domain ('*').
bool SomeVersion = (!Introduced.empty() ||
!Deprecated.empty() ||
!Obsoleted.empty());
if (Platform == "*" && SomeVersion) {
auto diag = diagnose(AttrLoc,
diag::attr_availability_nonspecific_platform_unexpected_version,
AttrName);
if (!Introduced.empty())
diag.fixItRemove(SourceRange(Introduced.DelimiterLoc,
Introduced.Range.End));
if (!Deprecated.empty())
diag.fixItRemove(SourceRange(Deprecated.DelimiterLoc,
Deprecated.Range.End));
if (!Obsoleted.empty())
diag.fixItRemove(SourceRange(Obsoleted.DelimiterLoc,
Obsoleted.Range.End));
return nullptr;
}

auto Attr = new (Context) AvailableAttr(
AtLoc, SourceRange(AttrLoc, Tok.getLoc()), Platform, PlatformLoc,
AttrKind, Message, Renamed, Introduced.Version, Introduced.Range,
Expand Down
24 changes: 21 additions & 3 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8390,6 +8390,26 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
}

auto domainName = domain->getNameForAttributePrinting();
auto semanticAttr = SemanticAvailableAttr(attr);

bool hasVersionSpec =
(introducedVersion || deprecatedVersion || obsoletedVersion);

if (!domain->isVersioned() && hasVersionSpec) {
SourceRange versionSourceRange;
if (introducedVersion)
versionSourceRange = semanticAttr.getIntroducedSourceRange();
else if (deprecatedVersion)
versionSourceRange = semanticAttr.getDeprecatedSourceRange();
else if (obsoletedVersion)
versionSourceRange = semanticAttr.getObsoletedSourceRange();

diags
.diagnose(attrLoc, diag::attr_availability_unexpected_version, attr,
domainName)
.highlight(versionSourceRange);
return std::nullopt;
}

if (domain->isSwiftLanguage() || domain->isPackageDescription()) {
switch (attr->getKind()) {
Expand All @@ -8413,8 +8433,6 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
break;
}

bool hasVersionSpec =
(introducedVersion || deprecatedVersion || obsoletedVersion);
if (!hasVersionSpec) {
diags.diagnose(attrLoc, diag::attr_availability_expected_version_spec,
attrName, domainName);
Expand All @@ -8438,7 +8456,7 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
mutableAttr->setRawObsoleted(canonicalizeVersion(*obsoletedVersion));
}

return SemanticAvailableAttr(attr);
return semanticAttr;
}

template <typename ATTR>
Expand Down
6 changes: 3 additions & 3 deletions test/Parse/diagnose_availability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ func twoShorthandsFollowedByDeprecated() {}
// Missing/wrong warning message for '*' or 'swift' platform.

@available(*, deprecated: 4.2)
// expected-warning@-1 {{unexpected version number in 'available' attribute for non-specific platform '*'}} {{25-30=}}
// expected-warning@-1 {{unexpected version number in '@available' attribute for '*'}}
func allPlatformsDeprecatedVersion() {}

@available(*, deprecated, obsoleted: 4.2)
// expected-warning@-1 {{unexpected version number in 'available' attribute for non-specific platform '*'}} {{36-41=}}
// expected-warning@-1 {{unexpected version number in '@available' attribute for '*'}}
func allPlatformsDeprecatedAndObsoleted() {}

@available(*, introduced: 4.0, deprecated: 4.1, obsoleted: 4.2)
// expected-warning@-1 {{unexpected version number in 'available' attribute for non-specific platform '*'}} {{25-30=}} {{42-47=}} {{58-63=}}
// expected-warning@-1 {{unexpected version number in '@available' attribute for '*'}}
func allPlatformsDeprecatedAndObsoleted2() {}

@available(swift, unavailable)
Expand Down
9 changes: 9 additions & 0 deletions test/attr/attr_availability_custom_domains.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
@available(EnabledDomain)
func availableInEnabledDomain() { }

@available(EnabledDomain, introduced: 1.0) // expected-warning {{unexpected version number in '@available' attribute for 'EnabledDomain'}}
func introducedInEnabledDomain() { }

@available(EnabledDomain, deprecated: 1.0) // expected-warning {{unexpected version number in '@available' attribute for 'EnabledDomain'}}
func deprecatedInEnabledDomain() { }

@available(EnabledDomain, obsoleted: 1.0) // expected-warning {{unexpected version number in '@available' attribute for 'EnabledDomain'}}
func obsoletedInEnabledDomain() { }

@available(DisabledDomain, unavailable)
func unavailableInDisabledDomain() { } // expected-note {{'unavailableInDisabledDomain()' has been explicitly marked unavailable here}}

Expand Down

0 comments on commit 358d55a

Please sign in to comment.