From 503e824e1288335b51bf50d6528c72db4c6bdbc1 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 13 Feb 2015 23:44:28 +0000 Subject: [PATCH] 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 --- include/swift/AST/Attr.h | 4 ++++ include/swift/AST/DiagnosticsSema.def | 8 ++++++++ lib/AST/Attr.cpp | 27 +++++++++++++++++++++++++++ lib/Sema/MiscDiagnostics.cpp | 5 +++++ lib/Sema/TypeCheckType.cpp | 6 +++++- lib/Sema/TypeChecker.cpp | 16 ++++++++++++++++ lib/Sema/TypeChecker.h | 4 ++++ test/attr/attr_availability_osx.swift | 27 +++++++++++++++++++++++++++ 8 files changed, 96 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index c0e553bd653..9aee41034a9 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -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; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index d10b1db23bc..7346d16ac13 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -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)) diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index ee3f708bac3..1281da0cc6d 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -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(Attr)) { + if (AvAttr->isInvalid() || !AvAttr->isActivePlatform(ctx)) + continue; + + Optional 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(); diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 3ed2336d8bc..7aec120c769 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -540,6 +540,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 diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 4dfadb3ad5a..f64febd6c3c 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -891,7 +891,11 @@ static bool checkTypeDeclAvailability(Decl *TypeDecl, IdentTypeRepr *IdType, CI->getIdentifier()).highlight(Attr->getRange()); 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 diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index cd12f2526ca..6dd970f9d44 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -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) { diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index b09f7f3414d..342fc1ff61a 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -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 diff --git a/test/attr/attr_availability_osx.swift b/test/attr/attr_availability_osx.swift index 5843c71e767..bd2ca0d7de5 100644 --- a/test/attr/attr_availability_osx.swift +++ b/test/attr/attr_availability_osx.swift @@ -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) { }