[ClangImporter] Refactor availability attribute importing logic. NFC.

Refactor the PlatformAvailability logic for determining
which Clang availability attributes are relevant when importing. The goal
is to separate the logic for attribute relevance for a given platform from
the logic to determine whether a deprecated attribute should be imported as
unavailable in Swift.

This also makes it possible for the "deprecated-as-unavailable" logic to
refer to the underlying Clang declaration, which is functionality that will
be used in a later commit.

This commit has no intended functional change.

Part of rdar://problem/48348822
This commit is contained in:
Devin Coughlin
2019-02-24 17:39:34 -08:00
parent f2c3c693cf
commit 8b14600372
4 changed files with 104 additions and 80 deletions

View File

@@ -718,19 +718,17 @@ bool importer::isUnavailableInSwift(
if (attr->getPlatform()->getName() == "swift") if (attr->getPlatform()->getName() == "swift")
return true; return true;
if (platformAvailability.filter && if (!platformAvailability.isPlatformRelevant(
!platformAvailability.filter(attr->getPlatform()->getName())) { attr->getPlatform()->getName())) {
continue; continue;
} }
if (platformAvailability.deprecatedAsUnavailableFilter) {
llvm::VersionTuple version = attr->getDeprecated(); llvm::VersionTuple version = attr->getDeprecated();
if (version.empty()) if (version.empty())
continue; continue;
if (platformAvailability.deprecatedAsUnavailableFilter( if (platformAvailability.treatDeprecatedAsUnavailable(decl, version)) {
version.getMajor(), version.getMinor())) { return true;
return true;
}
} }
} }

View File

@@ -1684,65 +1684,88 @@ ModuleDecl *ClangImporter::getImportedHeaderModule() const {
return Impl.ImportedHeaderUnit->getParentModule(); return Impl.ImportedHeaderUnit->getParentModule();
} }
PlatformAvailability::PlatformAvailability(LangOptions &langOpts) { PlatformAvailability::PlatformAvailability(LangOptions &langOpts)
// Add filters to determine if a Clang availability attribute : platformKind(targetPlatform(langOpts)) {
// applies in Swift, and if so, what is the cutoff for deprecated switch (platformKind) {
// declarations that are now considered unavailable in Swift. case PlatformKind::iOS:
case PlatformKind::iOSApplicationExtension:
case PlatformKind::tvOS:
case PlatformKind::tvOSApplicationExtension:
deprecatedAsUnavailableMessage =
"APIs deprecated as of iOS 7 and earlier are unavailable in Swift";
break;
if (langOpts.Target.isiOS() && !langOpts.Target.isTvOS()) { case PlatformKind::watchOS:
if (!langOpts.EnableAppExtensionRestrictions) { case PlatformKind::watchOSApplicationExtension:
filter = [](StringRef Platform) { return Platform == "ios"; };
} else {
filter = [](StringRef Platform) {
return Platform == "ios" || Platform == "ios_app_extension";
};
}
// Anything deprecated in iOS 7.x and earlier is unavailable in Swift.
deprecatedAsUnavailableFilter = [](
unsigned major, llvm::Optional<unsigned> minor) { return major <= 7; };
deprecatedAsUnavailableMessage =
"APIs deprecated as of iOS 7 and earlier are unavailable in Swift";
} else if (langOpts.Target.isTvOS()) {
if (!langOpts.EnableAppExtensionRestrictions) {
filter = [](StringRef Platform) { return Platform == "tvos"; };
} else {
filter = [](StringRef Platform) {
return Platform == "tvos" || Platform == "tvos_app_extension";
};
}
// Anything deprecated in iOS 7.x and earlier is unavailable in Swift.
deprecatedAsUnavailableFilter = [](
unsigned major, llvm::Optional<unsigned> minor) { return major <= 7; };
deprecatedAsUnavailableMessage =
"APIs deprecated as of iOS 7 and earlier are unavailable in Swift";
} else if (langOpts.Target.isWatchOS()) {
if (!langOpts.EnableAppExtensionRestrictions) {
filter = [](StringRef Platform) { return Platform == "watchos"; };
} else {
filter = [](StringRef Platform) {
return Platform == "watchos" || Platform == "watchos_app_extension";
};
}
// No deprecation filter on watchOS
deprecatedAsUnavailableFilter = [](
unsigned major, llvm::Optional<unsigned> minor) { return false; };
deprecatedAsUnavailableMessage = ""; deprecatedAsUnavailableMessage = "";
} else if (langOpts.Target.isMacOSX()) { break;
if (!langOpts.EnableAppExtensionRestrictions) {
filter = [](StringRef Platform) { return Platform == "macos"; }; case PlatformKind::OSX:
} else { case PlatformKind::OSXApplicationExtension:
filter = [](StringRef Platform) {
return Platform == "macos" || Platform == "macos_app_extension";
};
}
// Anything deprecated in OSX 10.9.x and earlier is unavailable in Swift.
deprecatedAsUnavailableFilter = [](unsigned major,
llvm::Optional<unsigned> minor) {
return major < 10 ||
(major == 10 && (!minor.hasValue() || minor.getValue() <= 9));
};
deprecatedAsUnavailableMessage = deprecatedAsUnavailableMessage =
"APIs deprecated as of OS X 10.9 and earlier are unavailable in Swift"; "APIs deprecated as of OS X 10.9 and earlier are unavailable in Swift";
break;
default:
break;
}
}
bool PlatformAvailability::isPlatformRelevant(StringRef name) const {
switch (platformKind) {
case PlatformKind::OSX:
return name == "macos";
case PlatformKind::OSXApplicationExtension:
return name == "macos" || name == "macos_app_extension";
case PlatformKind::iOS:
return name == "ios";
case PlatformKind::iOSApplicationExtension:
return name == "ios" || name == "ios_app_extension";
case PlatformKind::tvOS:
return name == "tvos";
case PlatformKind::tvOSApplicationExtension:
return name == "tvos" || name == "tvos_app_extension";
case PlatformKind::watchOS:
return name == "watchos";
case PlatformKind::watchOSApplicationExtension:
return name == "watchos" || name == "watchos_app_extension";
case PlatformKind::none:
return false;
}
llvm_unreachable("Unexpected platform");
}
bool PlatformAvailability::treatDeprecatedAsUnavailable(
const clang::Decl *clangDecl, const llvm::VersionTuple &version) const {
assert(!version.empty() && "Must provide version when deprecated");
unsigned major = version.getMajor();
Optional<unsigned> minor = version.getMinor();
switch (platformKind) {
case PlatformKind::OSX:
// Anything deprecated in OSX 10.9.x and earlier is unavailable in Swift.
return major < 10 ||
(major == 10 && (!minor.hasValue() || minor.getValue() <= 9));
case PlatformKind::iOS:
case PlatformKind::iOSApplicationExtension:
case PlatformKind::tvOS:
case PlatformKind::tvOSApplicationExtension:
// Anything deprecated in iOS 7.x and earlier is unavailable in Swift.
return major <= 7;
case PlatformKind::watchOS:
case PlatformKind::watchOSApplicationExtension:
// No deprecation filter on watchOS
return false;
default:
return false;
} }
} }

View File

@@ -5913,10 +5913,10 @@ SwiftDeclConverter::findLatestIntroduction(const clang::Decl *D) {
// Does this availability attribute map to the platform we are // Does this availability attribute map to the platform we are
// currently targeting? // currently targeting?
if (!Impl.platformAvailability.filter || if (!Impl.platformAvailability.isPlatformRelevant(
!Impl.platformAvailability.filter(attr->getPlatform()->getName())) attr->getPlatform()->getName())) {
continue; continue;
}
// Take advantage of the empty version being 0.0.0.0. // Take advantage of the empty version being 0.0.0.0.
result = std::max(result, attr->getIntroduced()); result = std::max(result, attr->getIntroduced());
} }
@@ -7321,8 +7321,7 @@ void ClangImporter::Implementation::importAttributes(
// Does this availability attribute map to the platform we are // Does this availability attribute map to the platform we are
// currently targeting? // currently targeting?
if (!platformAvailability.filter || if (!platformAvailability.isPlatformRelevant(Platform))
!platformAvailability.filter(Platform))
continue; continue;
auto platformK = auto platformK =
@@ -7354,9 +7353,8 @@ void ClangImporter::Implementation::importAttributes(
llvm::VersionTuple deprecated = avail->getDeprecated(); llvm::VersionTuple deprecated = avail->getDeprecated();
if (!deprecated.empty()) { if (!deprecated.empty()) {
if (platformAvailability.deprecatedAsUnavailableFilter && if (platformAvailability.treatDeprecatedAsUnavailable(ClangDecl,
platformAvailability.deprecatedAsUnavailableFilter( deprecated)) {
deprecated.getMajor(), deprecated.getMinor())) {
AnyUnavailable = true; AnyUnavailable = true;
PlatformAgnostic = PlatformAgnosticAvailabilityKind::Unavailable; PlatformAgnostic = PlatformAgnosticAvailabilityKind::Unavailable;
if (message.empty()) if (message.empty())

View File

@@ -248,14 +248,19 @@ enum class FactoryAsInitKind {
namespace importer { namespace importer {
struct PlatformAvailability { struct PlatformAvailability {
/// A predicate that indicates if the given platform should be private:
/// considered for availability. PlatformKind platformKind;
std::function<bool(StringRef PlatformName)> filter;
/// A predicate that indicates if the given platform version should public:
/// should be included in the cutoff of deprecated APIs marked unavailable. /// Returns true when the given platform should be considered for
std::function<bool(unsigned major, llvm::Optional<unsigned> minor)> /// availabilityon imported declarations.
deprecatedAsUnavailableFilter; bool isPlatformRelevant(StringRef platform) const;
/// Returns true when the given declaration with the given deprecation
/// should be inlucded in the cutoff of imported deprecated APIs marked
/// unavailable.
bool treatDeprecatedAsUnavailable(const clang::Decl *clangDecl,
const llvm::VersionTuple &version) const;
/// The message to embed for implicitly unavailability if a deprecated /// The message to embed for implicitly unavailability if a deprecated
/// API is now unavailable. /// API is now unavailable.