mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #34565 from slavapestov/conformance-availability-part1
Sema: (Mostly) check conformance availability
This commit is contained in:
@@ -182,46 +182,19 @@ private:
|
||||
|
||||
/// Records the reason a declaration is potentially unavailable.
|
||||
class UnavailabilityReason {
|
||||
public:
|
||||
enum class Kind {
|
||||
/// The declaration is potentially unavailable because it requires an OS
|
||||
/// version range that is not guaranteed by the minimum deployment
|
||||
/// target.
|
||||
RequiresOSVersionRange,
|
||||
|
||||
/// The declaration is potentially unavailable because it is explicitly
|
||||
/// weakly linked.
|
||||
ExplicitlyWeakLinked
|
||||
};
|
||||
|
||||
private:
|
||||
// A value of None indicates the declaration is potentially unavailable
|
||||
// because it is explicitly weak linked.
|
||||
Optional<VersionRange> RequiredDeploymentRange;
|
||||
VersionRange RequiredDeploymentRange;
|
||||
|
||||
UnavailabilityReason(const Optional<VersionRange> &RequiredDeploymentRange)
|
||||
explicit UnavailabilityReason(const VersionRange RequiredDeploymentRange)
|
||||
: RequiredDeploymentRange(RequiredDeploymentRange) {}
|
||||
|
||||
public:
|
||||
static UnavailabilityReason explicitlyWeaklyLinked() {
|
||||
return UnavailabilityReason(None);
|
||||
}
|
||||
|
||||
static UnavailabilityReason requiresVersionRange(const VersionRange Range) {
|
||||
return UnavailabilityReason(Range);
|
||||
}
|
||||
|
||||
Kind getReasonKind() const {
|
||||
if (RequiredDeploymentRange.hasValue()) {
|
||||
return Kind::RequiresOSVersionRange;
|
||||
} else {
|
||||
return Kind::ExplicitlyWeakLinked;
|
||||
}
|
||||
}
|
||||
|
||||
const VersionRange &getRequiredOSVersionRange() const {
|
||||
assert(getReasonKind() == Kind::RequiresOSVersionRange);
|
||||
return RequiredDeploymentRange.getValue();
|
||||
return RequiredDeploymentRange;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -5040,6 +5040,39 @@ ERROR(availabilty_string_subscript_migration, none,
|
||||
"subscripts returning String were obsoleted in Swift 4; explicitly "
|
||||
"construct a String from subscripted result", ())
|
||||
|
||||
// Conformance availability checking diagnostics
|
||||
|
||||
ERROR(conformance_availability_unavailable, none,
|
||||
"conformance of %0 to %1 is unavailable"
|
||||
"%select{ in %3|}2%select{|: %4}4",
|
||||
(Type, Type, bool, StringRef, StringRef))
|
||||
|
||||
NOTE(conformance_availability_marked_unavailable, none,
|
||||
"conformance of %0 to %1 has been explicitly marked "
|
||||
"unavailable here", (Type, Type))
|
||||
|
||||
NOTE(conformance_availability_introduced_in_version, none,
|
||||
"conformance of %0 to %1 was introduced in %2 %3",
|
||||
(Type, Type, StringRef, llvm::VersionTuple))
|
||||
|
||||
NOTE(conformance_availability_obsoleted, none,
|
||||
"conformance of %0 to %1 was obsoleted in %2 %3",
|
||||
(Type, Type, StringRef, llvm::VersionTuple))
|
||||
|
||||
WARNING(conformance_availability_deprecated, none,
|
||||
"conformance of %0 to %1 %select{is|%select{is|was}4}2 "
|
||||
"deprecated%select{| in %3%select{| %5}4}2%select{|: %6}6",
|
||||
(Type, Type, bool, StringRef, bool, llvm::VersionTuple,
|
||||
StringRef))
|
||||
|
||||
ERROR(conformance_availability_only_version_newer, none,
|
||||
"conformance of %0 to %1 is only available in %2 %3 or newer",
|
||||
(Type, Type, StringRef, llvm::VersionTuple))
|
||||
|
||||
WARNING(conformance_availability_only_version_newer_warn, none,
|
||||
"conformance of %0 to %1 is only available in %2 %3 or newer",
|
||||
(Type, Type, StringRef, llvm::VersionTuple))
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// MARK: @discardableResult
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -95,6 +95,9 @@ namespace swift {
|
||||
/// Disable API availability checking.
|
||||
bool DisableAvailabilityChecking = false;
|
||||
|
||||
/// Should conformance availability violations be diagnosed as errors?
|
||||
bool EnableConformanceAvailabilityErrors = false;
|
||||
|
||||
/// Maximum number of typo corrections we are allowed to perform.
|
||||
/// This is disabled by default until we can get typo-correction working within acceptable performance bounds.
|
||||
unsigned TypoCorrectionLimit = 0;
|
||||
|
||||
@@ -405,6 +405,14 @@ def disable_availability_checking : Flag<["-"],
|
||||
"disable-availability-checking">,
|
||||
HelpText<"Disable checking for potentially unavailable APIs">;
|
||||
|
||||
def enable_conformance_availability_errors : Flag<["-"],
|
||||
"enable-conformance-availability-errors">,
|
||||
HelpText<"Diagnose conformance availability violations as errors">;
|
||||
|
||||
def disable_conformance_availability_errors : Flag<["-"],
|
||||
"disable-conformance-availability-errors">,
|
||||
HelpText<"Diagnose conformance availability violations as warnings">;
|
||||
|
||||
def report_errors_to_debugger : Flag<["-"], "report-errors-to-debugger">,
|
||||
HelpText<"Deprecated, will be removed in future versions.">;
|
||||
|
||||
|
||||
@@ -391,6 +391,12 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
||||
Opts.DisableAvailabilityChecking |=
|
||||
Args.hasArg(OPT_disable_availability_checking);
|
||||
|
||||
if (auto A = Args.getLastArg(OPT_enable_conformance_availability_errors,
|
||||
OPT_disable_conformance_availability_errors)) {
|
||||
Opts.EnableConformanceAvailabilityErrors
|
||||
= A->getOption().matches(OPT_enable_conformance_availability_errors);
|
||||
}
|
||||
|
||||
if (auto A = Args.getLastArg(OPT_enable_access_control,
|
||||
OPT_disable_access_control)) {
|
||||
Opts.EnableAccessControl
|
||||
|
||||
@@ -160,17 +160,16 @@ TypeChecker::diagnoseDeclRefExportability(SourceLoc loc,
|
||||
bool
|
||||
TypeChecker::diagnoseConformanceExportability(SourceLoc loc,
|
||||
const RootProtocolConformance *rootConf,
|
||||
const ExtensionDecl *ext,
|
||||
const ExportContext &where) {
|
||||
if (!where.mustOnlyReferenceExportedDecls())
|
||||
return false;
|
||||
|
||||
auto originKind = getDisallowedOriginKind(
|
||||
rootConf->getDeclContext()->getAsDecl(),
|
||||
where);
|
||||
auto originKind = getDisallowedOriginKind(ext, where);
|
||||
if (originKind == DisallowedOriginKind::None)
|
||||
return false;
|
||||
|
||||
ModuleDecl *M = rootConf->getDeclContext()->getParentModule();
|
||||
ModuleDecl *M = ext->getParentModule();
|
||||
ASTContext &ctx = M->getASTContext();
|
||||
|
||||
auto reason = where.getExportabilityReason();
|
||||
|
||||
@@ -958,6 +958,13 @@ TypeChecker::checkDeclarationAvailability(const Decl *D,
|
||||
return UnavailabilityReason::requiresVersionRange(version);
|
||||
}
|
||||
|
||||
Optional<UnavailabilityReason>
|
||||
TypeChecker::checkConformanceAvailability(const RootProtocolConformance *conf,
|
||||
const ExtensionDecl *ext,
|
||||
const ExportContext &where) {
|
||||
return checkDeclarationAvailability(ext, where);
|
||||
}
|
||||
|
||||
/// A class that walks the AST to find the innermost (i.e., deepest) node that
|
||||
/// contains a target SourceRange and matches a particular criterion.
|
||||
/// This class finds the innermost nodes of interest by walking
|
||||
@@ -1563,13 +1570,6 @@ void TypeChecker::diagnosePotentialOpaqueTypeUnavailability(
|
||||
const UnavailabilityReason &Reason) {
|
||||
ASTContext &Context = ReferenceDC->getASTContext();
|
||||
|
||||
// We only emit diagnostics for API unavailability, not for explicitly
|
||||
// weak-linked symbols.
|
||||
if (Reason.getReasonKind() !=
|
||||
UnavailabilityReason::Kind::RequiresOSVersionRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto RequiredRange = Reason.getRequiredOSVersionRange();
|
||||
{
|
||||
auto Err =
|
||||
@@ -1593,13 +1593,6 @@ void TypeChecker::diagnosePotentialUnavailability(
|
||||
const UnavailabilityReason &Reason) {
|
||||
ASTContext &Context = ReferenceDC->getASTContext();
|
||||
|
||||
// We only emit diagnostics for API unavailability, not for explicitly
|
||||
// weak-linked symbols.
|
||||
if (Reason.getReasonKind() !=
|
||||
UnavailabilityReason::Kind::RequiresOSVersionRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto RequiredRange = Reason.getRequiredOSVersionRange();
|
||||
{
|
||||
auto Err =
|
||||
@@ -1652,6 +1645,37 @@ void TypeChecker::diagnosePotentialAccessorUnavailability(
|
||||
fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context);
|
||||
}
|
||||
|
||||
void TypeChecker::diagnosePotentialUnavailability(
|
||||
const RootProtocolConformance *rootConf,
|
||||
const ExtensionDecl *ext,
|
||||
SourceLoc loc,
|
||||
const DeclContext *dc,
|
||||
const UnavailabilityReason &reason) {
|
||||
ASTContext &ctx = dc->getASTContext();
|
||||
|
||||
auto requiredRange = reason.getRequiredOSVersionRange();
|
||||
{
|
||||
auto type = rootConf->getType();
|
||||
auto proto = rootConf->getProtocol()->getDeclaredInterfaceType();
|
||||
|
||||
auto diagID = (ctx.LangOpts.EnableConformanceAvailabilityErrors
|
||||
? diag::conformance_availability_only_version_newer
|
||||
: diag::conformance_availability_only_version_newer_warn);
|
||||
auto err =
|
||||
ctx.Diags.diagnose(
|
||||
loc, diagID,
|
||||
type, proto, prettyPlatformString(targetPlatform(ctx.LangOpts)),
|
||||
reason.getRequiredOSVersionRange().getLowerEndpoint());
|
||||
|
||||
// Direct a fixit to the error if an existing guard is nearly-correct
|
||||
if (fixAvailabilityByNarrowingNearbyVersionCheck(loc, dc,
|
||||
requiredRange, ctx, err))
|
||||
return;
|
||||
}
|
||||
|
||||
fixAvailability(loc, dc, requiredRange, ctx);
|
||||
}
|
||||
|
||||
const AvailableAttr *TypeChecker::getDeprecated(const Decl *D) {
|
||||
if (auto *Attr = D->getAttrs().getDeprecated(D->getASTContext()))
|
||||
return Attr;
|
||||
@@ -1669,7 +1693,7 @@ const AvailableAttr *TypeChecker::getDeprecated(const Decl *D) {
|
||||
/// Returns true if the reference or any of its parents is an
|
||||
/// unconditional unavailable declaration for the same platform.
|
||||
static bool isInsideCompatibleUnavailableDeclaration(
|
||||
const ValueDecl *D, const ExportContext &where,
|
||||
const Decl *D, const ExportContext &where,
|
||||
const AvailableAttr *attr) {
|
||||
auto referencedPlatform = where.getUnavailablePlatformKind();
|
||||
if (!referencedPlatform)
|
||||
@@ -2148,6 +2172,60 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
|
||||
}
|
||||
}
|
||||
|
||||
void TypeChecker::diagnoseIfDeprecated(
|
||||
SourceLoc loc,
|
||||
const RootProtocolConformance *rootConf,
|
||||
const ExtensionDecl *ext,
|
||||
const ExportContext &where) {
|
||||
const AvailableAttr *attr = TypeChecker::getDeprecated(ext);
|
||||
if (!attr)
|
||||
return;
|
||||
|
||||
// We match the behavior of clang to not report deprecation warnings
|
||||
// inside declarations that are themselves deprecated on all deployment
|
||||
// targets.
|
||||
if (where.isDeprecated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto *dc = where.getDeclContext();
|
||||
auto &ctx = dc->getASTContext();
|
||||
if (!ctx.LangOpts.DisableAvailabilityChecking) {
|
||||
AvailabilityContext runningOSVersion = where.getAvailabilityContext();
|
||||
if (runningOSVersion.isKnownUnreachable()) {
|
||||
// Suppress a deprecation warning if the availability checking machinery
|
||||
// thinks the reference program location will not execute on any
|
||||
// deployment target for the current platform.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto type = rootConf->getType();
|
||||
auto proto = rootConf->getProtocol()->getDeclaredInterfaceType();
|
||||
|
||||
StringRef platform = attr->prettyPlatformString();
|
||||
llvm::VersionTuple deprecatedVersion;
|
||||
if (attr->Deprecated)
|
||||
deprecatedVersion = attr->Deprecated.getValue();
|
||||
|
||||
if (attr->Message.empty()) {
|
||||
ctx.Diags.diagnose(
|
||||
loc, diag::conformance_availability_deprecated,
|
||||
type, proto, attr->hasPlatform(), platform,
|
||||
attr->Deprecated.hasValue(), deprecatedVersion,
|
||||
/*message*/ StringRef())
|
||||
.highlight(attr->getRange());
|
||||
return;
|
||||
}
|
||||
|
||||
EncodedDiagnosticMessage encodedMessage(attr->Message);
|
||||
ctx.Diags.diagnose(
|
||||
loc, diag::conformance_availability_deprecated,
|
||||
type, proto, attr->hasPlatform(), platform,
|
||||
attr->Deprecated.hasValue(), deprecatedVersion,
|
||||
encodedMessage.Message)
|
||||
.highlight(attr->getRange());
|
||||
}
|
||||
|
||||
void swift::diagnoseUnavailableOverride(ValueDecl *override,
|
||||
const ValueDecl *base,
|
||||
@@ -2212,6 +2290,102 @@ bool swift::diagnoseExplicitUnavailability(const ValueDecl *D,
|
||||
});
|
||||
}
|
||||
|
||||
/// Emit a diagnostic for references to declarations that have been
|
||||
/// marked as unavailable, either through "unavailable" or "obsoleted:".
|
||||
bool swift::diagnoseExplicitUnavailability(SourceLoc loc,
|
||||
const RootProtocolConformance *rootConf,
|
||||
const ExtensionDecl *ext,
|
||||
const ExportContext &where) {
|
||||
auto *attr = AvailableAttr::isUnavailable(ext);
|
||||
if (!attr)
|
||||
return false;
|
||||
|
||||
// Calling unavailable code from within code with the same
|
||||
// unavailability is OK -- the eventual caller can't call the
|
||||
// enclosing code in the same situations it wouldn't be able to
|
||||
// call this code.
|
||||
if (isInsideCompatibleUnavailableDeclaration(ext, where, attr))
|
||||
return false;
|
||||
|
||||
ASTContext &ctx = ext->getASTContext();
|
||||
auto &diags = ctx.Diags;
|
||||
|
||||
auto type = rootConf->getType();
|
||||
auto proto = rootConf->getProtocol()->getDeclaredInterfaceType();
|
||||
|
||||
StringRef platform;
|
||||
switch (attr->getPlatformAgnosticAvailability()) {
|
||||
case PlatformAgnosticAvailabilityKind::Deprecated:
|
||||
llvm_unreachable("shouldn't see deprecations in explicit unavailability");
|
||||
|
||||
case PlatformAgnosticAvailabilityKind::None:
|
||||
case PlatformAgnosticAvailabilityKind::Unavailable:
|
||||
if (attr->Platform != PlatformKind::none) {
|
||||
// This was platform-specific; indicate the platform.
|
||||
platform = attr->prettyPlatformString();
|
||||
break;
|
||||
}
|
||||
LLVM_FALLTHROUGH;
|
||||
|
||||
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
|
||||
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
|
||||
// We don't want to give further detail about these.
|
||||
platform = "";
|
||||
break;
|
||||
|
||||
case PlatformAgnosticAvailabilityKind::UnavailableInSwift:
|
||||
// This API is explicitly unavailable in Swift.
|
||||
platform = "Swift";
|
||||
break;
|
||||
}
|
||||
|
||||
EncodedDiagnosticMessage EncodedMessage(attr->Message);
|
||||
diags.diagnose(loc, diag::conformance_availability_unavailable,
|
||||
type, proto,
|
||||
platform.empty(), platform, EncodedMessage.Message);
|
||||
|
||||
switch (attr->getVersionAvailability(ctx)) {
|
||||
case AvailableVersionComparison::Available:
|
||||
case AvailableVersionComparison::PotentiallyUnavailable:
|
||||
llvm_unreachable("These aren't considered unavailable");
|
||||
|
||||
case AvailableVersionComparison::Unavailable:
|
||||
if ((attr->isLanguageVersionSpecific() ||
|
||||
attr->isPackageDescriptionVersionSpecific())
|
||||
&& attr->Introduced.hasValue())
|
||||
diags.diagnose(ext, diag::conformance_availability_introduced_in_version,
|
||||
type, proto,
|
||||
(attr->isLanguageVersionSpecific() ?
|
||||
"Swift" : "PackageDescription"),
|
||||
*attr->Introduced)
|
||||
.highlight(attr->getRange());
|
||||
else
|
||||
diags.diagnose(ext, diag::conformance_availability_marked_unavailable,
|
||||
type, proto)
|
||||
.highlight(attr->getRange());
|
||||
break;
|
||||
|
||||
case AvailableVersionComparison::Obsoleted:
|
||||
// FIXME: Use of the platformString here is non-awesome for application
|
||||
// extensions.
|
||||
|
||||
StringRef platformDisplayString;
|
||||
if (attr->isLanguageVersionSpecific()) {
|
||||
platformDisplayString = "Swift";
|
||||
} else if (attr->isPackageDescriptionVersionSpecific()) {
|
||||
platformDisplayString = "PackageDescription";
|
||||
} else {
|
||||
platformDisplayString = platform;
|
||||
}
|
||||
|
||||
diags.diagnose(ext, diag::conformance_availability_obsoleted,
|
||||
type, proto, platformDisplayString, *attr->Obsoleted)
|
||||
.highlight(attr->getRange());
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Check if this is a subscript declaration inside String or
|
||||
/// Substring that returns String, and if so return true.
|
||||
bool isSubscriptReturningString(const ValueDecl *D, ASTContext &Context) {
|
||||
@@ -3201,19 +3375,42 @@ bool
|
||||
swift::diagnoseConformanceAvailability(SourceLoc loc,
|
||||
ProtocolConformanceRef conformance,
|
||||
const ExportContext &where) {
|
||||
assert(!where.isImplicit());
|
||||
|
||||
if (!conformance.isConcrete())
|
||||
return false;
|
||||
|
||||
const ProtocolConformance *concreteConf = conformance.getConcrete();
|
||||
const RootProtocolConformance *rootConf = concreteConf->getRootConformance();
|
||||
|
||||
auto *DC = where.getDeclContext();
|
||||
|
||||
if (auto *ext = dyn_cast<ExtensionDecl>(rootConf->getDeclContext())) {
|
||||
if (TypeChecker::diagnoseConformanceExportability(loc, rootConf, ext, where))
|
||||
return true;
|
||||
|
||||
if (diagnoseExplicitUnavailability(loc, rootConf, ext, where))
|
||||
return true;
|
||||
|
||||
// Diagnose for deprecation
|
||||
TypeChecker::diagnoseIfDeprecated(loc, rootConf, ext, where);
|
||||
|
||||
// Diagnose (and possibly signal) for potential unavailability
|
||||
auto maybeUnavail = TypeChecker::checkConformanceAvailability(
|
||||
rootConf, ext, where);
|
||||
if (maybeUnavail.hasValue()) {
|
||||
TypeChecker::diagnosePotentialUnavailability(rootConf, ext, loc, DC,
|
||||
maybeUnavail.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// Now, check associated conformances.
|
||||
SubstitutionMap subConformanceSubs =
|
||||
concreteConf->getSubstitutions(DC->getParentModule());
|
||||
diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, where);
|
||||
const RootProtocolConformance *rootConf =
|
||||
concreteConf->getRootConformance();
|
||||
if (diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, where))
|
||||
return true;
|
||||
|
||||
return TypeChecker::diagnoseConformanceExportability(
|
||||
loc, rootConf, where);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace swift {
|
||||
class InFlightDiagnostic;
|
||||
class Decl;
|
||||
class ProtocolConformanceRef;
|
||||
class RootProtocolConformance;
|
||||
class Stmt;
|
||||
class SubstitutionMap;
|
||||
class Type;
|
||||
@@ -247,6 +248,14 @@ bool diagnoseExplicitUnavailability(
|
||||
DeclAvailabilityFlags Flags,
|
||||
llvm::function_ref<void(InFlightDiagnostic &)> attachRenameFixIts);
|
||||
|
||||
/// Emit a diagnostic for references to declarations that have been
|
||||
/// marked as unavailable, either through "unavailable" or "obsoleted:".
|
||||
bool diagnoseExplicitUnavailability(
|
||||
SourceLoc loc,
|
||||
const RootProtocolConformance *rootConf,
|
||||
const ExtensionDecl *ext,
|
||||
const ExportContext &where);
|
||||
|
||||
/// Check if \p decl has a introduction version required by -require-explicit-availability
|
||||
void checkExplicitAvailability(Decl *decl);
|
||||
|
||||
|
||||
@@ -966,6 +966,7 @@ bool diagnoseDeclRefExportability(SourceLoc loc,
|
||||
/// conformance is SPI or visible via an implementation-only import.
|
||||
bool diagnoseConformanceExportability(SourceLoc loc,
|
||||
const RootProtocolConformance *rootConf,
|
||||
const ExtensionDecl *ext,
|
||||
const ExportContext &where);
|
||||
|
||||
/// \name Availability checking
|
||||
@@ -1010,7 +1011,16 @@ diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D);
|
||||
/// declaration is unavailable. Returns None is the declaration is
|
||||
/// definitely available.
|
||||
Optional<UnavailabilityReason>
|
||||
checkDeclarationAvailability(const Decl *D, const ExportContext &where);
|
||||
checkDeclarationAvailability(const Decl *D, const ExportContext &Where);
|
||||
|
||||
/// Checks whether a conformance should be considered unavailable when
|
||||
/// referred to at the given location and, if so, returns the reason why the
|
||||
/// declaration is unavailable. Returns None is the declaration is
|
||||
/// definitely available.
|
||||
Optional<UnavailabilityReason>
|
||||
checkConformanceAvailability(const RootProtocolConformance *Conf,
|
||||
const ExtensionDecl *Ext,
|
||||
const ExportContext &Where);
|
||||
|
||||
/// Checks an "ignored" expression to see if it's okay for it to be ignored.
|
||||
///
|
||||
@@ -1025,6 +1035,14 @@ void diagnosePotentialUnavailability(const ValueDecl *D,
|
||||
const DeclContext *ReferenceDC,
|
||||
const UnavailabilityReason &Reason);
|
||||
|
||||
// Emits a diagnostic, if necessary, for a reference to a declaration
|
||||
// that is potentially unavailable at the given source location.
|
||||
void diagnosePotentialUnavailability(const RootProtocolConformance *rootConf,
|
||||
const ExtensionDecl *ext,
|
||||
SourceLoc loc,
|
||||
const DeclContext *dc,
|
||||
const UnavailabilityReason &reason);
|
||||
|
||||
void
|
||||
diagnosePotentialOpaqueTypeUnavailability(SourceRange ReferenceRange,
|
||||
const DeclContext *ReferenceDC,
|
||||
@@ -1042,12 +1060,16 @@ void diagnosePotentialAccessorUnavailability(
|
||||
const AvailableAttr *getDeprecated(const Decl *D);
|
||||
|
||||
/// Emits a diagnostic for a reference to a declaration that is deprecated.
|
||||
/// Callers can provide a lambda that adds additional information (such as a
|
||||
/// fixit hint) to the deprecation diagnostic, if it is emitted.
|
||||
void diagnoseIfDeprecated(SourceRange SourceRange,
|
||||
const ExportContext &Where,
|
||||
const ValueDecl *DeprecatedDecl,
|
||||
const ApplyExpr *Call);
|
||||
|
||||
/// Emits a diagnostic for a reference to a conformnace that is deprecated.
|
||||
void diagnoseIfDeprecated(SourceLoc Loc,
|
||||
const RootProtocolConformance *DeprecatedConf,
|
||||
const ExtensionDecl *Ext,
|
||||
const ExportContext &Where);
|
||||
/// @}
|
||||
|
||||
/// If LangOptions::DebugForbidTypecheckPrefix is set and the given decl
|
||||
|
||||
215
test/Sema/conformance_availability.swift
Normal file
215
test/Sema/conformance_availability.swift
Normal file
@@ -0,0 +1,215 @@
|
||||
// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-conformance-availability-errors
|
||||
|
||||
// REQUIRES: OS=macosx
|
||||
|
||||
public protocol Horse {}
|
||||
func takesHorse<T : Horse>(_: T) {}
|
||||
|
||||
extension Horse {
|
||||
func giddyUp() {}
|
||||
}
|
||||
|
||||
struct UsesHorse<T : Horse> {}
|
||||
|
||||
// Unconditional unavailability
|
||||
public struct HasUnavailableConformance1 {}
|
||||
|
||||
@available(*, unavailable)
|
||||
extension HasUnavailableConformance1 : Horse {}
|
||||
// expected-note@-1 6{{conformance of 'HasUnavailableConformance1' to 'Horse' has been explicitly marked unavailable here}}
|
||||
|
||||
func passUnavailableConformance1(x: HasUnavailableConformance1) {
|
||||
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
|
||||
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
|
||||
_ = UsesHorse<HasUnavailableConformance1>.self // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
func passUnavailableConformance1a(x: HasUnavailableConformance1) {
|
||||
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
|
||||
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
|
||||
_ = UsesHorse<HasUnavailableConformance1>.self // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
|
||||
}
|
||||
|
||||
// Platform unavailability
|
||||
public struct HasUnavailableConformance2 {}
|
||||
|
||||
@available(macOS, unavailable)
|
||||
extension HasUnavailableConformance2 : Horse {}
|
||||
// expected-note@-1 3{{conformance of 'HasUnavailableConformance2' to 'Horse' has been explicitly marked unavailable here}}
|
||||
|
||||
func passUnavailableConformance2(x: HasUnavailableConformance2) {
|
||||
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
|
||||
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
|
||||
_ = UsesHorse<HasUnavailableConformance2>.self // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
|
||||
}
|
||||
|
||||
@available(macOS, unavailable)
|
||||
func passUnavailableConformance2a(x: HasUnavailableConformance2) {
|
||||
// This is allowed
|
||||
takesHorse(x)
|
||||
x.giddyUp()
|
||||
}
|
||||
|
||||
// Swift version unavailability
|
||||
public struct HasUnavailableConformance3 {}
|
||||
|
||||
@available(swift 12)
|
||||
extension HasUnavailableConformance3 : Horse {}
|
||||
// expected-note@-1 6{{conformance of 'HasUnavailableConformance3' to 'Horse' was introduced in Swift 12}}
|
||||
|
||||
func passUnavailableConformance3(x: HasUnavailableConformance3) {
|
||||
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
|
||||
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
|
||||
_ = UsesHorse<HasUnavailableConformance3>.self // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
|
||||
}
|
||||
|
||||
@available(swift 12)
|
||||
func passUnavailableConformance3a(x: HasUnavailableConformance3) {
|
||||
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
|
||||
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
|
||||
_ = UsesHorse<HasUnavailableConformance3>.self // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
|
||||
}
|
||||
|
||||
// Platform obsoleted
|
||||
public struct HasUnavailableConformance4 {}
|
||||
|
||||
@available(macOS, obsoleted: 10.1)
|
||||
extension HasUnavailableConformance4 : Horse {}
|
||||
// expected-note@-1 6{{conformance of 'HasUnavailableConformance4' to 'Horse' was obsoleted in macOS 10.1}}
|
||||
|
||||
func passUnavailableConformance4(x: HasUnavailableConformance4) {
|
||||
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
|
||||
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
|
||||
_ = UsesHorse<HasUnavailableConformance4>.self // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
|
||||
}
|
||||
|
||||
@available(macOS, obsoleted: 10.1)
|
||||
func passUnavailableConformance4a(x: HasUnavailableConformance4) {
|
||||
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
|
||||
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
|
||||
_ = UsesHorse<HasUnavailableConformance4>.self // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
|
||||
}
|
||||
|
||||
// Swift obsoleted
|
||||
public struct HasUnavailableConformance5 {}
|
||||
|
||||
@available(swift, obsoleted: 4)
|
||||
extension HasUnavailableConformance5 : Horse {}
|
||||
// expected-note@-1 6{{conformance of 'HasUnavailableConformance5' to 'Horse' was obsoleted in Swift 4}}
|
||||
|
||||
func passUnavailableConformance5(x: HasUnavailableConformance5) {
|
||||
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
|
||||
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
|
||||
_ = UsesHorse<HasUnavailableConformance5>.self // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
|
||||
}
|
||||
|
||||
@available(swift, obsoleted: 4)
|
||||
func passUnavailableConformance5a(x: HasUnavailableConformance5) {
|
||||
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
|
||||
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
|
||||
_ = UsesHorse<HasUnavailableConformance5>.self // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
|
||||
}
|
||||
|
||||
// Unavailable with message
|
||||
public struct HasUnavailableConformance6 {}
|
||||
|
||||
@available(*, unavailable, message: "This conformance is bad")
|
||||
extension HasUnavailableConformance6 : Horse {}
|
||||
// expected-note@-1 3{{conformance of 'HasUnavailableConformance6' to 'Horse' has been explicitly marked unavailable here}}
|
||||
|
||||
func passUnavailableConformance6(x: HasUnavailableConformance6) {
|
||||
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
|
||||
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
|
||||
_ = UsesHorse<HasUnavailableConformance6>.self // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
public struct HasDeprecatedConformance1 {}
|
||||
|
||||
@available(*, deprecated)
|
||||
extension HasDeprecatedConformance1 : Horse {}
|
||||
|
||||
func passDeprecatedConformance1(x: HasDeprecatedConformance1) {
|
||||
takesHorse(x) // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
|
||||
x.giddyUp() // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
|
||||
_ = UsesHorse<HasDeprecatedConformance1>.self // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
|
||||
}
|
||||
|
||||
@available(*, deprecated)
|
||||
func passDeprecatedConformance1a(x: HasDeprecatedConformance1) {
|
||||
takesHorse(x)
|
||||
x.giddyUp()
|
||||
_ = UsesHorse<HasDeprecatedConformance1>.self
|
||||
}
|
||||
|
||||
// Deprecated with message
|
||||
public struct HasDeprecatedConformance2 {}
|
||||
|
||||
@available(*, deprecated, message: "This conformance is deprecated")
|
||||
extension HasDeprecatedConformance2 : Horse {}
|
||||
|
||||
func passDeprecatedConformance2(x: HasDeprecatedConformance2) {
|
||||
takesHorse(x) // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
|
||||
x.giddyUp() // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
|
||||
_ = UsesHorse<HasDeprecatedConformance2>.self // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
|
||||
}
|
||||
|
||||
@available(*, deprecated)
|
||||
func passDeprecatedConformance2a(x: HasDeprecatedConformance2) {
|
||||
takesHorse(x)
|
||||
x.giddyUp()
|
||||
_ = UsesHorse<HasDeprecatedConformance2>.self
|
||||
}
|
||||
|
||||
// Deprecated with version
|
||||
public struct HasDeprecatedConformance3 {}
|
||||
|
||||
@available(macOS, introduced: 10.7, deprecated: 10.8)
|
||||
extension HasDeprecatedConformance3 : Horse {}
|
||||
|
||||
func passDeprecatedConformance3(x: HasDeprecatedConformance3) {
|
||||
takesHorse(x) // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
|
||||
x.giddyUp() // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
|
||||
_ = UsesHorse<HasDeprecatedConformance3>.self // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
|
||||
}
|
||||
|
||||
func passDeprecatedConformance3a(x: HasDeprecatedConformance3) {
|
||||
if #available(macOS 10.8, *) {
|
||||
} else {
|
||||
// This branch is dead with our minimum deployment target, so don't emit
|
||||
// deprecation diagnostics in it.
|
||||
takesHorse(x)
|
||||
x.giddyUp()
|
||||
_ = UsesHorse<HasDeprecatedConformance3>.self
|
||||
}
|
||||
}
|
||||
|
||||
// Availability with version
|
||||
public struct HasAvailableConformance1 {}
|
||||
|
||||
@available(macOS 100, *)
|
||||
extension HasAvailableConformance1 : Horse {}
|
||||
|
||||
// These availability violations are errors because this test passes the
|
||||
// -enable-conformance-availability-errors flag. See the other test case
|
||||
// in test/Sema/conformance_availability_warn.swift for the same example
|
||||
// but without this flag.
|
||||
|
||||
func passAvailableConformance1(x: HasAvailableConformance1) { // expected-note 3{{add @available attribute to enclosing global function}}
|
||||
takesHorse(x) // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
|
||||
// expected-note@-1 {{add 'if #available' version check}}
|
||||
|
||||
x.giddyUp() // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
|
||||
// expected-note@-1 {{add 'if #available' version check}}
|
||||
|
||||
_ = UsesHorse<HasAvailableConformance1>.self // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
|
||||
// expected-note@-1 {{add 'if #available' version check}}
|
||||
}
|
||||
|
||||
@available(macOS 100, *)
|
||||
func passAvailableConformance1a(x: HasAvailableConformance1) {
|
||||
takesHorse(x)
|
||||
x.giddyUp()
|
||||
_ = UsesHorse<HasAvailableConformance1>.self
|
||||
}
|
||||
41
test/Sema/conformance_availability_warn.swift
Normal file
41
test/Sema/conformance_availability_warn.swift
Normal file
@@ -0,0 +1,41 @@
|
||||
// RUN: %target-typecheck-verify-swift -swift-version 5
|
||||
|
||||
// REQUIRES: OS=macosx
|
||||
|
||||
public protocol Horse {}
|
||||
func takesHorse<T : Horse>(_: T) {}
|
||||
|
||||
extension Horse {
|
||||
func giddyUp() {}
|
||||
}
|
||||
|
||||
struct UsesHorse<T : Horse> {}
|
||||
|
||||
// Availability with version
|
||||
public struct HasAvailableConformance1 {}
|
||||
|
||||
@available(macOS 100, *)
|
||||
extension HasAvailableConformance1 : Horse {}
|
||||
|
||||
// These availability violations are warnings because this test does not
|
||||
// pass the -enable-conformance-availability-errors flag. See the other
|
||||
// test case in test/Sema/conformance_availability.swift for the same
|
||||
// example but with this flag.
|
||||
|
||||
func passAvailableConformance1(x: HasAvailableConformance1) { // expected-note 3{{add @available attribute to enclosing global function}}
|
||||
takesHorse(x) // expected-warning {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
|
||||
// expected-note@-1 {{add 'if #available' version check}}
|
||||
|
||||
x.giddyUp() // expected-warning {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
|
||||
// expected-note@-1 {{add 'if #available' version check}}
|
||||
|
||||
_ = UsesHorse<HasAvailableConformance1>.self // expected-warning {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
|
||||
// expected-note@-1 {{add 'if #available' version check}}
|
||||
}
|
||||
|
||||
@available(macOS 100, *)
|
||||
func passAvailableConformance1a(x: HasAvailableConformance1) {
|
||||
takesHorse(x)
|
||||
x.giddyUp()
|
||||
_ = UsesHorse<HasAvailableConformance1>.self
|
||||
}
|
||||
Reference in New Issue
Block a user