mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Sema: Improve diagnostics for decls more available than their containers.
Adopt new request-based utilities for looking up the enclosing declaration's availability when type checking an `@available` attribute. This consolidates implementations of the lookup and improves diagnostics by catching more cases where declarations are more available than their containers.
This commit is contained in:
@@ -1101,17 +1101,21 @@ public:
|
|||||||
/// Retrieve the @available attribute that provides the OS version range that
|
/// Retrieve the @available attribute that provides the OS version range that
|
||||||
/// this declaration is available in.
|
/// this declaration is available in.
|
||||||
///
|
///
|
||||||
/// The attribute may come from another declaration, since availability
|
/// This attribute may come from an enclosing decl since availability is
|
||||||
/// could be inherited from a parent declaration.
|
/// inherited. The second member of the returned pair is the decl that owns
|
||||||
|
/// the attribute.
|
||||||
Optional<std::pair<const AvailableAttr *, const Decl *>>
|
Optional<std::pair<const AvailableAttr *, const Decl *>>
|
||||||
getSemanticAvailableRangeAttr() const;
|
getSemanticAvailableRangeAttr() const;
|
||||||
|
|
||||||
/// Retrieve the @available attribute that makes this declaration unavailable,
|
/// Retrieve the @available attribute that makes this declaration unavailable,
|
||||||
/// if any.
|
/// if any.
|
||||||
///
|
///
|
||||||
/// The attribute may come from another declaration, since unavailability
|
/// This attribute may come from an enclosing decl since availability is
|
||||||
/// could be inherited from a parent declaration. This is a broader notion of
|
/// inherited. The second member of the returned pair is the decl that owns
|
||||||
/// unavailability than is checked by \c AvailableAttr::isUnavailable.
|
/// the attribute.
|
||||||
|
///
|
||||||
|
/// Note that this notion of unavailability is broader than that which is
|
||||||
|
/// checked by \c AvailableAttr::isUnavailable.
|
||||||
Optional<std::pair<const AvailableAttr *, const Decl *>>
|
Optional<std::pair<const AvailableAttr *, const Decl *>>
|
||||||
getSemanticUnavailableAttr() const;
|
getSemanticUnavailableAttr() const;
|
||||||
|
|
||||||
|
|||||||
@@ -1882,51 +1882,48 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) {
|
|||||||
// is fully contained within that declaration's range. If there is no such
|
// is fully contained within that declaration's range. If there is no such
|
||||||
// enclosing declaration, then there is nothing to check.
|
// enclosing declaration, then there is nothing to check.
|
||||||
Optional<AvailabilityContext> EnclosingAnnotatedRange;
|
Optional<AvailabilityContext> EnclosingAnnotatedRange;
|
||||||
bool EnclosingDeclIsUnavailable = false;
|
|
||||||
Decl *EnclosingDecl = getEnclosingDeclForDecl(D);
|
|
||||||
|
|
||||||
while (EnclosingDecl) {
|
|
||||||
if (EnclosingDecl->getAttrs().getUnavailable(Ctx)) {
|
|
||||||
EnclosingDeclIsUnavailable = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnclosingAnnotatedRange =
|
|
||||||
AvailabilityInference::annotatedAvailableRange(EnclosingDecl, Ctx);
|
|
||||||
|
|
||||||
if (EnclosingAnnotatedRange.has_value())
|
|
||||||
break;
|
|
||||||
|
|
||||||
EnclosingDecl = getEnclosingDeclForDecl(EnclosingDecl);
|
|
||||||
}
|
|
||||||
|
|
||||||
AvailabilityContext AttrRange{
|
AvailabilityContext AttrRange{
|
||||||
VersionRange::allGTE(attr->Introduced.value())};
|
VersionRange::allGTE(attr->Introduced.value())};
|
||||||
|
|
||||||
if (EnclosingDecl) {
|
if (auto *parent = getEnclosingDeclForDecl(D)) {
|
||||||
if (EnclosingDeclIsUnavailable) {
|
if (auto enclosingUnavailable = parent->getSemanticUnavailableAttr()) {
|
||||||
if (!AttrRange.isKnownUnreachable()) {
|
if (!AttrRange.isKnownUnreachable()) {
|
||||||
diagnose(D->isImplicit() ? EnclosingDecl->getLoc()
|
const Decl *enclosingDecl = enclosingUnavailable.value().second;
|
||||||
|
diagnose(D->isImplicit() ? enclosingDecl->getLoc()
|
||||||
: attr->getLocation(),
|
: attr->getLocation(),
|
||||||
diag::availability_decl_more_than_unavailable_enclosing,
|
diag::availability_decl_more_than_unavailable_enclosing,
|
||||||
D->getDescriptiveKind());
|
D->getDescriptiveKind());
|
||||||
diagnose(EnclosingDecl->getLoc(),
|
diagnose(parent->getLoc(),
|
||||||
diag::availability_decl_more_than_unavailable_enclosing_here);
|
diag::availability_decl_more_than_unavailable_enclosing_here);
|
||||||
}
|
}
|
||||||
} else if (!AttrRange.isContainedIn(EnclosingAnnotatedRange.value())) {
|
} else if (auto enclosingAvailable =
|
||||||
diagnose(D->isImplicit() ? EnclosingDecl->getLoc() : attr->getLocation(),
|
parent->getSemanticAvailableRangeAttr()) {
|
||||||
diag::availability_decl_more_than_enclosing,
|
const AvailableAttr *enclosingAttr = enclosingAvailable.value().first;
|
||||||
D->getDescriptiveKind());
|
const Decl *enclosingDecl = enclosingAvailable.value().second;
|
||||||
if (D->isImplicit())
|
EnclosingAnnotatedRange.emplace(
|
||||||
diagnose(EnclosingDecl->getLoc(),
|
VersionRange::allGTE(enclosingAttr->Introduced.value()));
|
||||||
diag::availability_implicit_decl_here,
|
if (!AttrRange.isContainedIn(*EnclosingAnnotatedRange)) {
|
||||||
D->getDescriptiveKind(),
|
// Members of extensions of nominal types with available ranges were
|
||||||
|
// not diagnosed previously, so only emit a warning in that case.
|
||||||
|
auto limit = (enclosingDecl != parent && isa<ExtensionDecl>(parent))
|
||||||
|
? DiagnosticBehavior::Warning
|
||||||
|
: DiagnosticBehavior::Unspecified;
|
||||||
|
diagnose(D->isImplicit() ? enclosingDecl->getLoc()
|
||||||
|
: attr->getLocation(),
|
||||||
|
diag::availability_decl_more_than_enclosing,
|
||||||
|
D->getDescriptiveKind())
|
||||||
|
.limitBehavior(limit);
|
||||||
|
if (D->isImplicit())
|
||||||
|
diagnose(enclosingDecl->getLoc(),
|
||||||
|
diag::availability_implicit_decl_here,
|
||||||
|
D->getDescriptiveKind(),
|
||||||
|
prettyPlatformString(targetPlatform(Ctx.LangOpts)),
|
||||||
|
AttrRange.getOSVersion().getLowerEndpoint());
|
||||||
|
diagnose(enclosingDecl->getLoc(),
|
||||||
|
diag::availability_decl_more_than_enclosing_here,
|
||||||
prettyPlatformString(targetPlatform(Ctx.LangOpts)),
|
prettyPlatformString(targetPlatform(Ctx.LangOpts)),
|
||||||
AttrRange.getOSVersion().getLowerEndpoint());
|
EnclosingAnnotatedRange->getOSVersion().getLowerEndpoint());
|
||||||
diagnose(EnclosingDecl->getLoc(),
|
}
|
||||||
diag::availability_decl_more_than_enclosing_here,
|
|
||||||
prettyPlatformString(targetPlatform(Ctx.LangOpts)),
|
|
||||||
EnclosingAnnotatedRange->getOSVersion().getLowerEndpoint());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ func doSomethingDeprecatedOniOS() { }
|
|||||||
|
|
||||||
doSomethingDeprecatedOniOS() // okay
|
doSomethingDeprecatedOniOS() // okay
|
||||||
|
|
||||||
|
@available(macOS 10.10, *)
|
||||||
struct TestStruct {}
|
struct TestStruct {} // expected-note {{enclosing scope requires availability of macOS 10.10 or newer}}
|
||||||
|
|
||||||
@available(macOS 10.10, *)
|
@available(macOS 10.10, *)
|
||||||
extension TestStruct { // expected-note {{enclosing scope requires availability of macOS 10.10 or newer}}
|
extension TestStruct { // expected-note {{enclosing scope requires availability of macOS 10.10 or newer}}
|
||||||
@@ -104,6 +104,11 @@ extension TestStruct { // expected-note {{enclosing scope requires availability
|
|||||||
func doDeprecatedThing() {}
|
func doDeprecatedThing() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension TestStruct {
|
||||||
|
@available(macOS 10.9, *) // expected-warning {{instance method cannot be more available than enclosing scope}}
|
||||||
|
func doFifthThing() {}
|
||||||
|
}
|
||||||
|
|
||||||
@available(macOS 10.11, *)
|
@available(macOS 10.11, *)
|
||||||
func testMemberAvailability() {
|
func testMemberAvailability() {
|
||||||
TestStruct().doTheThing() // expected-error {{'doTheThing()' is unavailable}}
|
TestStruct().doTheThing() // expected-error {{'doTheThing()' is unavailable}}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public struct AtInliningTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@available(macOS 10.14.5, *)
|
@available(macOS 10.14.5, *)
|
||||||
public struct BetweenTargets {
|
public struct BetweenTargets { // expected-note {{enclosing scope requires availability of macOS 10.14.5 or newer}}
|
||||||
@usableFromInline internal init() {}
|
@usableFromInline internal init() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1102,7 +1102,7 @@ extension BetweenTargets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension BetweenTargets {
|
extension BetweenTargets {
|
||||||
@available(macOS 10.10, *)
|
@available(macOS 10.10, *) // expected-warning {{instance method cannot be more available than enclosing scope}}
|
||||||
func excessivelyAvailableInternalFuncInExtension() {}
|
func excessivelyAvailableInternalFuncInExtension() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user