Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions include/swift/AST/AvailabilityRestriction.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ namespace swift {
class ASTContext;
class AvailabilityContext;
class Decl;
class ExtensionDecl;
class ValueDecl;
class RootProtocolConformance;

/// Represents the reason a declaration is considered not available in a
/// specific `AvailabilityContext`.
Expand Down Expand Up @@ -199,6 +202,13 @@ class AvailabilityRestriction {
/// be translated directly into an `if #available(...)` runtime query.
bool isActiveForRuntimeQueries(const ASTContext &ctx) const;

/// Emit a note indicating the source of availability restriction.
bool emitNoteForDecl(const ValueDecl *decl) const;

/// Emit a note indicating the source of availability restriction.
bool emitNoteForConformance(const ExtensionDecl *ext,
const RootProtocolConformance *rootConf) const;

void print(raw_ostream &os) const;
};

Expand Down
80 changes: 80 additions & 0 deletions lib/AST/AvailabilityRestriction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "swift/AST/ASTContext.h"
#include "swift/AST/AvailabilityContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/ProtocolConformance.h"

using namespace swift;

Expand Down Expand Up @@ -63,6 +65,84 @@ bool AvailabilityRestriction::isActiveForRuntimeQueries(
/*forRuntimeQuery=*/true);
}

bool AvailabilityRestriction::emitNoteForDecl(const ValueDecl *decl) const {
auto &ctx = decl->getASTContext();
auto &diags = ctx.Diags;
auto parsedAttr = getAttr().getParsedAttr();
auto sourceRange = parsedAttr->getRangeWithAt();
auto domainAndRange = getDomainAndRange(ctx);

// Point at the attribute so that the reason for the restriction is always
// rendered. Implicit attributes, like the ones on imported or synthesized
// declarations, have no location; refer to the declaration instead.
auto loc = parsedAttr->AtLoc;
auto diagnose = [&](const Diagnostic &diag) {
return loc.isValid() ? diags.diagnose(loc, diag)
: diags.diagnose(decl, diag);
};

switch (getReason()) {
case Reason::UnavailableUnconditionally:
diagnose({diag::availability_marked_unavailable, decl})
.highlight(sourceRange);
break;
case Reason::UnavailableUnintroduced:
diagnose({diag::availability_introduced_in_version, decl,
domainAndRange.getDomain(), domainAndRange.getRange()})
.highlight(sourceRange);
break;
case Reason::UnavailableObsolete:
diagnose({diag::availability_obsoleted, decl, domainAndRange.getDomain(),
domainAndRange.getRange()})
.highlight(sourceRange);
break;
case Reason::Unintroduced:
case Reason::Deprecated:
return false;
}
return true;
}

bool AvailabilityRestriction::emitNoteForConformance(
const ExtensionDecl *ext, const RootProtocolConformance *rootConf) const {
auto &ctx = ext->getASTContext();
auto &diags = ctx.Diags;
auto parsedAttr = getAttr().getParsedAttr();
auto sourceRange = parsedAttr->getRangeWithAt();
auto type = rootConf->getType();
auto proto = rootConf->getProtocol()->getDeclaredInterfaceType();
auto domainAndRange = getDomainAndRange(ctx);

// Point at the attribute so that the reason for the restriction is always
// rendered. Implicit attributes, like the ones on imported or synthesized
// extensions, have no location; refer to the extension instead.
auto loc = parsedAttr->AtLoc;
auto diagnose = [&](const Diagnostic &diag) {
return loc.isValid() ? diags.diagnose(loc, diag)
: diags.diagnose(ext, diag);
};

switch (getReason()) {
case Reason::UnavailableUnconditionally:
diagnose({diag::conformance_availability_marked_unavailable, type, proto})
.highlight(sourceRange);
break;
case Reason::UnavailableUnintroduced:
diagnose({diag::conformance_availability_introduced_in_version, type, proto,
domainAndRange.getDomain(), domainAndRange.getRange()});
break;
case Reason::UnavailableObsolete:
diagnose({diag::conformance_availability_obsoleted, type, proto,
domainAndRange.getDomain(), domainAndRange.getRange()})
.highlight(sourceRange);
break;
case Reason::Unintroduced:
case Reason::Deprecated:
return false;
}
return true;
}

void AvailabilityRestriction::print(llvm::raw_ostream &os) const {
os << "AvailabilityRestriction(";
getAttr().getDomain().print(os);
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5848,6 +5848,7 @@ void AttributeChecker::checkBackDeployedAttrs(
// Find the attribute that makes the declaration unavailable.
const Decl *attrDecl = D;
do {
// FIXME: Adopt AvailabilityRestriction::emitNoteForDecl()
if (auto unavailableAttr = attrDecl->getUnavailableAttr()) {
diagnose(unavailableAttr->getParsedAttr()->AtLoc,
diag::availability_marked_unavailable, VD)
Expand Down
47 changes: 2 additions & 45 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1918,28 +1918,7 @@ bool diagnoseExplicitUnavailability(SourceLoc loc,
.warnUntilLanguageModeIf(warnIfConformanceUnavailablePreSwift6,
LanguageMode::v6);

switch (restriction.getReason()) {
case AvailabilityRestriction::Reason::UnavailableUnconditionally:
diags
.diagnose(ext, diag::conformance_availability_marked_unavailable, type,
proto)
.highlight(attr.getParsedAttr()->getRangeWithAt());
break;
case AvailabilityRestriction::Reason::UnavailableUnintroduced:
diags.diagnose(ext, diag::conformance_availability_introduced_in_version,
type, proto, domainAndRange.getDomain(),
domainAndRange.getRange());
break;
case AvailabilityRestriction::Reason::UnavailableObsolete:
diags
.diagnose(ext, diag::conformance_availability_obsoleted, type, proto,
domainAndRange.getDomain(), domainAndRange.getRange())
.highlight(attr.getParsedAttr()->getRangeWithAt());
break;
case AvailabilityRestriction::Reason::Unintroduced:
case AvailabilityRestriction::Reason::Deprecated:
llvm_unreachable("unexpected restriction");
}
restriction.emitNoteForConformance(ext, rootConf);
return true;
}

Expand Down Expand Up @@ -2279,29 +2258,7 @@ bool diagnoseExplicitUnavailability(
.limitBehavior(limit);
}

auto sourceRange = Attr.getParsedAttr()->getRangeWithAt();
switch (restriction.getReason()) {
case AvailabilityRestriction::Reason::UnavailableUnconditionally:
diags.diagnose(D, diag::availability_marked_unavailable, D)
.highlight(sourceRange);
break;
case AvailabilityRestriction::Reason::UnavailableUnintroduced:
diags
.diagnose(D, diag::availability_introduced_in_version, D,
domainAndRange.getDomain(), domainAndRange.getRange())
.highlight(sourceRange);
break;
case AvailabilityRestriction::Reason::UnavailableObsolete:
diags
.diagnose(D, diag::availability_obsoleted, D,
domainAndRange.getDomain(), domainAndRange.getRange())
.highlight(sourceRange);
break;
case AvailabilityRestriction::Reason::Unintroduced:
case AvailabilityRestriction::Reason::Deprecated:
llvm_unreachable("unexpected restriction");
break;
}
restriction.emitNoteForDecl(D);
return true;
}

Expand Down
12 changes: 6 additions & 6 deletions test/ASTGen/availability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@ public class ClassWithMembers {
public func spiFunc() {}
}

@available(*, unavailable, renamed: "`class`")
func keyword_renamed() {} // expected-note {{'keyword_renamed()' has been explicitly marked unavailable here}}
@available(*, unavailable, renamed: "`class`") // expected-note {{'keyword_renamed()' has been explicitly marked unavailable here}}
func keyword_renamed() {}

@available(*, unavailable, renamed: "`foo bar`")
func spaces_renamed() {} // expected-note {{'spaces_renamed()' has been explicitly marked unavailable here}}
@available(*, unavailable, renamed: "`foo bar`") // expected-note {{'spaces_renamed()' has been explicitly marked unavailable here}}
func spaces_renamed() {}

@available(*, unavailable, renamed: "foo(`3bar baz`:)")
func keywords_in_arguments(x: Int) {} // expected-note {{'keywords_in_arguments(x:)' has been explicitly marked unavailable here}}
@available(*, unavailable, renamed: "foo(`3bar baz`:)") // expected-note {{'keywords_in_arguments(x:)' has been explicitly marked unavailable here}}
func keywords_in_arguments(x: Int) {}

func testEscapedRenamed() {
keyword_renamed() // expected-error {{'keyword_renamed()' has been renamed to '`class`'}}
Expand Down
12 changes: 6 additions & 6 deletions test/ASTGen/embedded_availability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@

@_unavailableInEmbedded
public struct UnavailableInEmbedded {}
// expected-note@-1 {{'UnavailableInEmbedded' has been explicitly marked unavailable here}}
// expected-note@-2 {{'UnavailableInEmbedded' has been explicitly marked unavailable here}}

@available(*, unavailable, message: "always unavailable")
public struct UniverallyUnavailable {}
// expected-note@-1 3 {{'UniverallyUnavailable' has been explicitly marked unavailable here}}
// expected-note@-2 3 {{'UniverallyUnavailable' has been explicitly marked unavailable here}}

@_unavailableInEmbedded
public func unavailable_in_embedded() { }
// expected-note@-1 {{'unavailable_in_embedded()' has been explicitly marked unavailable here}}
// expected-note@-2 {{'unavailable_in_embedded()' has been explicitly marked unavailable here}}

@available(*, unavailable, message: "always unavailable")
public func universally_unavailable() { }
// expected-note@-1 4 {{'universally_unavailable()' has been explicitly marked unavailable here}}
// expected-note@-2 4 {{'universally_unavailable()' has been explicitly marked unavailable here}}

@_unavailableInEmbedded
public func unused() { } // no error
Expand All @@ -38,9 +38,9 @@ public func has_universally_unavailable_overload(_ s2: S2) { }

public struct Available {}

@_unavailableInEmbedded
@_unavailableInEmbedded // expected-note {{'unavailable_in_embedded_method' has been explicitly marked unavailable here}}
extension Available {
public func unavailable_in_embedded_method( // expected-note {{'unavailable_in_embedded_method' has been explicitly marked unavailable here}}
public func unavailable_in_embedded_method(
_ uie: UnavailableInEmbedded,
_ uu: UniverallyUnavailable, // expected-error {{'UniverallyUnavailable' is unavailable: always unavailable}}
_ a: Available,
Expand Down
28 changes: 14 additions & 14 deletions test/Availability/availability.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// RUN: %target-typecheck-verify-swift -parse-as-library -module-name MyModule

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

@_unavailableInEmbedded // no-op without -enable-experimental-feature Embedded
public func unavailable_in_embedded() { }
Expand All @@ -11,12 +11,12 @@ func test() {
unavailable_in_embedded() // ok
}

@available(*,unavailable,message: "use 'Int' instead")
struct NSUInteger {} // expected-note 3 {{explicitly marked unavailable here}}
@available(*,unavailable,message: "use 'Int' instead") // expected-note 3 {{explicitly marked unavailable here}}
struct NSUInteger {}

struct Outer {
@available(*,unavailable,message: "use 'UInt' instead")
struct NSUInteger {} // expected-note 2 {{explicitly marked unavailable here}}
@available(*,unavailable,message: "use 'UInt' instead") // expected-note 2 {{explicitly marked unavailable here}}
struct NSUInteger {}
}

func foo(x : NSUInteger) { // expected-error {{'NSUInteger' is unavailable: use 'Int' instead}}
Expand All @@ -34,14 +34,14 @@ func foo(x : NSUInteger) { // expected-error {{'NSUInteger' is unavailable: use
}

struct VarToFunc {
@available(*, unavailable, renamed: "function()")
var variable: Int { // expected-note 2 {{explicitly marked unavailable here}}
@available(*, unavailable, renamed: "function()") // expected-note 2 {{explicitly marked unavailable here}}
var variable: Int {
get { 0 }
set {}
}

@available(*, unavailable, renamed: "function()")
func oldFunction() -> Int { return 42 } // expected-note 2 {{explicitly marked unavailable here}}
@available(*, unavailable, renamed: "function()") // expected-note 2 {{explicitly marked unavailable here}}
func oldFunction() -> Int { return 42 }

func function() -> Int {
_ = variable // expected-error{{'variable' has been renamed to 'function()'}}{{9-17=function()}}
Expand Down Expand Up @@ -75,8 +75,8 @@ struct DeferBody {
}

func bar() {
@available(*, unavailable)
enum No: Error { // expected-note 2 {{'No' has been explicitly marked unavailable here}}
@available(*, unavailable) // expected-note 2 {{'No' has been explicitly marked unavailable here}}
enum No: Error {
case no
}
do {
Expand Down Expand Up @@ -122,8 +122,8 @@ func test_contextual_member_with_availability() {
_ = Test(.foo) // Ok
}

@available(*, unavailable)
func unavailableFunction(_ x: Int) -> Bool { true } // expected-note {{'unavailableFunction' has been explicitly marked unavailable here}}
@available(*, unavailable) // expected-note {{'unavailableFunction' has been explicitly marked unavailable here}}
func unavailableFunction(_ x: Int) -> Bool { true }

/// https://github.com/apple/swift/issues/55700
/// Availability checking not working in the `where` clause of a `for` loop
Expand Down
32 changes: 16 additions & 16 deletions test/Availability/availability_accessors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,22 @@ struct BaseStruct<T> {
}

var unavailableGetter: T {
@available(*, unavailable)
get { fatalError() } // expected-note 73 {{getter for 'unavailableGetter' has been explicitly marked unavailable here}}
@available(*, unavailable) // expected-note 73 {{getter for 'unavailableGetter' has been explicitly marked unavailable here}}
get { fatalError() }
set { }
}

var unavailableSetter: T {
get { fatalError() }
@available(*, unavailable)
set { fatalError() } // expected-note 34 {{setter for 'unavailableSetter' has been explicitly marked unavailable here}}
@available(*, unavailable) // expected-note 34 {{setter for 'unavailableSetter' has been explicitly marked unavailable here}}
set { fatalError() }
}

var unavailableGetterAndSetter: T {
@available(*, unavailable)
get { fatalError() } // expected-note 71 {{getter for 'unavailableGetterAndSetter' has been explicitly marked unavailable here}}
@available(*, unavailable)
set { fatalError() } // expected-note 34 {{setter for 'unavailableGetterAndSetter' has been explicitly marked unavailable here}}
@available(*, unavailable) // expected-note 71 {{getter for 'unavailableGetterAndSetter' has been explicitly marked unavailable here}}
get { fatalError() }
@available(*, unavailable) // expected-note 34 {{setter for 'unavailableGetterAndSetter' has been explicitly marked unavailable here}}
set { fatalError() }
}

var deprecatedGetter: T {
Expand All @@ -93,22 +93,22 @@ struct SubscriptHelper {
}

subscript<T>(unavailableGetter t: T) -> () {
@available(*, unavailable)
get { } // expected-note {{getter for 'subscript(unavailableGetter:)' has been explicitly marked unavailable here}}
@available(*, unavailable) // expected-note {{getter for 'subscript(unavailableGetter:)' has been explicitly marked unavailable here}}
get { }
set { }
}

subscript<T>(unavailableSetter t: T) -> () {
get { }
@available(*, unavailable)
set { } // expected-note {{setter for 'subscript(unavailableSetter:)' has been explicitly marked unavailable here}}
@available(*, unavailable) // expected-note {{setter for 'subscript(unavailableSetter:)' has been explicitly marked unavailable here}}
set { }
}

subscript<T>(unavailableGetterAndSetter t: T) -> () {
@available(*, unavailable)
get { } // expected-note {{getter for 'subscript(unavailableGetterAndSetter:)' has been explicitly marked unavailable here}}
@available(*, unavailable)
set { } // expected-note {{setter for 'subscript(unavailableGetterAndSetter:)' has been explicitly marked unavailable here}}
@available(*, unavailable) // expected-note {{getter for 'subscript(unavailableGetterAndSetter:)' has been explicitly marked unavailable here}}
get { }
@available(*, unavailable) // expected-note {{setter for 'subscript(unavailableGetterAndSetter:)' has been explicitly marked unavailable here}}
set { }
}
}

Expand Down
Loading