Warn on uses of deprecated APIs

Emit a warning when the developer uses an API that has been marked deprecated with an
availability attribute. Following the Clang behavior, we will only warn if the API is
deprecated on all deployment targets. For example, if an API is deprecated as of
OS X 10.11 but the minimum deployment target is 10.10 then no warning will be emitted.

rdar://problem/17406050

Swift SVN r25288
This commit is contained in:
Devin Coughlin
2015-02-13 23:44:28 +00:00
parent 55e76bf15a
commit 503e824e12
8 changed files with 96 additions and 1 deletions

View File

@@ -1052,6 +1052,10 @@ public:
/// a declaration is unavailable, or null otherwise.
const AvailabilityAttr *getUnavailable(const ASTContext &ctx) const;
/// Returns the first @availability attribute that indicates
/// a declaration is deprecated on all deployment targets, or null otherwise.
const AvailabilityAttr *getDeprecated(const ASTContext &ctx) const;
void dump() const;
void print(ASTPrinter &Printer, const PrintOptions &Options) const;

View File

@@ -1900,6 +1900,14 @@ NOTE(availability_obsoleted, sema_avail, none,
"%0 was obsoleted in %1 version %2",
(DeclName, StringRef, clang::VersionTuple))
WARNING(availability_deprecated, sema_avail, none,
"%0 was deprecated in %1 version %2",
(DeclName, StringRef, clang::VersionTuple))
WARNING(availability_deprecated_msg, sema_avail, none,
"%0 was deprecated in %1 version %2: %3",
(DeclName, StringRef, clang::VersionTuple, StringRef))
ERROR(availability_decl_only_version_greater, sema_avail, none,
"%0 is only available on %1 version %2 or greater",
(DeclName, StringRef, clang::VersionTuple))

View File

@@ -94,6 +94,33 @@ const AvailabilityAttr *DeclAttributes::getUnavailable(
return nullptr;
}
const AvailabilityAttr *
DeclAttributes::getDeprecated(const ASTContext &ctx) const {
for (auto Attr : *this) {
if (auto AvAttr = dyn_cast<AvailabilityAttr>(Attr)) {
if (AvAttr->isInvalid() || !AvAttr->isActivePlatform(ctx))
continue;
Optional<clang::VersionTuple> DeprecatedVersion = AvAttr->Deprecated;
if (!DeprecatedVersion.hasValue())
continue;
auto MinVersion = ctx.LangOpts.getMinPlatformVersion();
// We treat the declaration as deprecated if it is deprecated on
// all deployment targets.
// Once availability checking is enabled by default, we should
// query the type refinement context hierarchy to determine
// whether a declaration is deprecated on all versions
// allowed by the context containing the reference.
if (DeprecatedVersion.getValue() <= MinVersion) {
return AvAttr;
}
}
}
return nullptr;
}
void DeclAttributes::dump() const {
StreamPrinter P(llvm::errs());
PrintOptions PO = PrintOptions::printEverything();

View File

@@ -541,6 +541,11 @@ static bool diagAvailability(TypeChecker &TC, const ValueDecl *D,
return true;
}
// Diagnose for deprecation
if (const AvailabilityAttr *Attr = D->getAttrs().getDeprecated(TC.Context)) {
TC.diagnoseDeprecated(R.Start, Attr, D->getFullName());
}
// We only diagnose potentially unavailability here if availability checking
// is turned on, but we are not treating unavailable symbols as having
// optional type.

View File

@@ -892,6 +892,10 @@ static bool checkTypeDeclAvailability(Decl *TypeDecl, IdentTypeRepr *IdType,
return true;
}
if (auto *Attr = TypeDecl->getAttrs().getDeprecated(TC.Context)) {
TC.diagnoseDeprecated(Loc, Attr, CI->getIdentifier());
}
// Check for potential unavailability because of the minimum
// deployment version.
// We should probably unify this checking for deployment-version API

View File

@@ -1199,6 +1199,22 @@ void TypeChecker::diagnosePotentialAccessorUnavailability(
Reason.getRequiredOSVersionRange().getLowerEndpoint());
}
void TypeChecker::diagnoseDeprecated(SourceLoc ReferenceLoc,
const AvailabilityAttr *Attr,
DeclName Name) {
StringRef Platform = Attr->prettyPlatformString();
clang::VersionTuple DeprecatedVersion = Attr->Deprecated.getValue();
if (Attr->Message.empty()) {
diagnose(ReferenceLoc, diag::availability_deprecated, Name, Platform,
DeprecatedVersion).highlight(Attr->getRange());
return;
}
diagnose(ReferenceLoc, diag::availability_deprecated_msg, Name, Platform,
DeprecatedVersion, Attr->Message).highlight(Attr->getRange());
}
// checkForForbiddenPrefix is for testing purposes.
void TypeChecker::checkForForbiddenPrefix(const Decl *D) {

View File

@@ -1273,6 +1273,10 @@ public:
void diagnosePotentialAccessorUnavailability(
FuncDecl *Accessor, SourceLoc referenceLoc,
const UnavailabilityReason &Reason, bool ForInout);
/// Emits a diagnostic for a reference to a declaration that is deprecated.
void diagnoseDeprecated(SourceLoc ReferenceLoc, const AvailabilityAttr *Attr,
DeclName Name);
/// @}
/// If LangOptions::DebugForbidTypecheckPrefix is set and the given decl

View File

@@ -25,3 +25,30 @@ func doSomethingReallyOld() { }
// expected-note @-1{{'doSomethingReallyOld()' was obsoleted in OS X version 10}}
doSomethingReallyOld() // expected-error{{'doSomethingReallyOld()' is unavailable}}
// Test deprecations in 10.10 and later
@availability(OSX, introduced=10.5, deprecated=10.10,
message="Use another function")
func deprecatedFunctionWithMessage() { }
deprecatedFunctionWithMessage() // expected-warning{{'deprecatedFunctionWithMessage()' was deprecated in OS X version 10.10: Use another function}}
@availability(OSX, introduced=10.5, deprecated=10.10)
func deprecatedFunctionWithoutMessage() { }
deprecatedFunctionWithoutMessage() // expected-warning{{'deprecatedFunctionWithoutMessage()' was deprecated in OS X version 10.10}}
@availability(OSX, introduced=10.5, deprecated=10.10,
message="Use BetterClass instead")
class DeprecatedClass { }
func functionWithDeprecatedParameter(p: DeprecatedClass) { } // expected-warning{{'DeprecatedClass' was deprecated in OS X version 10.10: Use BetterClass instead}}
@availability(OSX, introduced=10.5, deprecated=10.11,
message="Use BetterClass instead")
class DeprecatedClassIn10_11 { }
// Elements deprecated later than the minimum deployment target (which is 10.10, in this case) should not generate warnings
func functionWithDeprecatedLaterParameter(p: DeprecatedClassIn10_11) { }