Skip to content

Commit 3354249

Browse files
authored
Merge pull request #15102 from jrose-apple/deprecation-and-depreciation-are-two-different-words
Check deprecation for getters and setters
2 parents edf4681 + b0e12f4 commit 3354249

File tree

6 files changed

+224
-70
lines changed

6 files changed

+224
-70
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3590,22 +3590,23 @@ NOTE(availability_obsoleted, none,
35903590
(DeclName, StringRef, clang::VersionTuple))
35913591

35923592
WARNING(availability_deprecated, none,
3593-
"%0 %select{is|%select{is|was}3}1 deprecated"
3594-
"%select{| %select{on|in}3 %2%select{| %4}3}1",
3595-
(DeclName, bool, StringRef, bool, clang::VersionTuple))
3593+
"%select{getter for |setter for |}0%1 %select{is|%select{is|was}4}2 "
3594+
"deprecated%select{| %select{on|in}4 %3%select{| %5}4}2",
3595+
(unsigned, DeclName, bool, StringRef, bool, clang::VersionTuple))
35963596

35973597
WARNING(availability_deprecated_msg, none,
3598-
"%0 %select{is|%select{is|was}3}1 deprecated"
3599-
"%select{| %select{on|in}3 %2%select{| %4}3}1: %5",
3600-
(DeclName, bool, StringRef, bool, clang::VersionTuple, StringRef))
3598+
"%select{getter for |setter for |}0%1 %select{is|%select{is|was}4}2 "
3599+
"deprecated%select{| %select{on|in}4 %3%select{| %5}4}2: %6",
3600+
(unsigned, DeclName, bool, StringRef, bool, clang::VersionTuple,
3601+
StringRef))
36013602

36023603
WARNING(availability_deprecated_rename, none,
3603-
"%0 %select{is|%select{is|was}3}1 deprecated"
3604-
"%select{| %select{on|in}3 %2%select{| %4}3}1: "
3605-
"%select{renamed to|replaced by}5%" REPLACEMENT_DECL_KIND_SELECT "6 "
3606-
"'%7'",
3607-
(DeclName, bool, StringRef, bool, clang::VersionTuple, bool, unsigned,
3608-
StringRef))
3604+
"%select{getter for |setter for |}0%1 %select{is|%select{is|was}4}2 "
3605+
"deprecated%select{| %select{on|in}4 %3%select{| %5}4}2: "
3606+
"%select{renamed to|replaced by}6%" REPLACEMENT_DECL_KIND_SELECT "7 "
3607+
"'%8'",
3608+
(unsigned, DeclName, bool, StringRef, bool, clang::VersionTuple, bool,
3609+
unsigned, StringRef))
36093610
#undef REPLACEMENT_DECL_KIND_SELECT
36103611

36113612
NOTE(note_deprecated_rename, none,

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,10 +1404,7 @@ const AvailableAttr *TypeChecker::getDeprecated(const Decl *D) {
14041404
static bool
14051405
someEnclosingDeclMatches(SourceRange ReferenceRange,
14061406
const DeclContext *ReferenceDC,
1407-
TypeChecker &TC,
14081407
llvm::function_ref<bool(const Decl *)> Pred) {
1409-
ASTContext &Ctx = TC.Context;
1410-
14111408
// Climb the DeclContext hierarchy to see if any of the containing
14121409
// declarations matches the predicate.
14131410
const DeclContext *DC = ReferenceDC;
@@ -1446,6 +1443,7 @@ someEnclosingDeclMatches(SourceRange ReferenceRange,
14461443
if (ReferenceRange.isInvalid())
14471444
return false;
14481445

1446+
ASTContext &Ctx = ReferenceDC->getASTContext();
14491447
const Decl *DeclToSearch =
14501448
findContainingDeclaration(ReferenceRange, ReferenceDC, Ctx.SourceMgr);
14511449

@@ -1474,28 +1472,32 @@ someEnclosingDeclMatches(SourceRange ReferenceRange,
14741472
return false;
14751473
}
14761474

1477-
bool TypeChecker::isInsideImplicitFunction(SourceRange ReferenceRange,
1478-
const DeclContext *DC) {
1475+
/// Returns true if the reference or any of its parents is an
1476+
/// implicit function.
1477+
static bool isInsideImplicitFunction(SourceRange ReferenceRange,
1478+
const DeclContext *DC) {
14791479
auto IsInsideImplicitFunc = [](const Decl *D) {
14801480
auto *AFD = dyn_cast<AbstractFunctionDecl>(D);
14811481
return AFD && AFD->isImplicit();
14821482
};
14831483

1484-
return someEnclosingDeclMatches(ReferenceRange, DC, *this,
1485-
IsInsideImplicitFunc);
1484+
return someEnclosingDeclMatches(ReferenceRange, DC, IsInsideImplicitFunc);
14861485
}
14871486

1488-
bool TypeChecker::isInsideUnavailableDeclaration(
1489-
SourceRange ReferenceRange, const DeclContext *ReferenceDC) {
1487+
/// Returns true if the reference or any of its parents is an
1488+
/// unavailable (or obsoleted) declaration.
1489+
static bool isInsideUnavailableDeclaration(SourceRange ReferenceRange,
1490+
const DeclContext *ReferenceDC) {
14901491
auto IsUnavailable = [](const Decl *D) {
14911492
return D->getAttrs().getUnavailable(D->getASTContext());
14921493
};
14931494

1494-
return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, *this,
1495-
IsUnavailable);
1495+
return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsUnavailable);
14961496
}
14971497

1498-
bool TypeChecker::isInsideCompatibleUnavailableDeclaration(
1498+
/// Returns true if the reference or any of its parents is an
1499+
/// unconditional unavailable declaration for the same platform.
1500+
static bool isInsideCompatibleUnavailableDeclaration(
14991501
SourceRange ReferenceRange, const DeclContext *ReferenceDC,
15001502
const AvailableAttr *attr) {
15011503
if (!attr->isUnconditionallyUnavailable()) {
@@ -1512,18 +1514,18 @@ bool TypeChecker::isInsideCompatibleUnavailableDeclaration(
15121514
return EnclosingUnavailable && EnclosingUnavailable->Platform == platform;
15131515
};
15141516

1515-
return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, *this,
1516-
IsUnavailable);
1517+
return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsUnavailable);
15171518
}
15181519

1519-
bool TypeChecker::isInsideDeprecatedDeclaration(SourceRange ReferenceRange,
1520-
const DeclContext *ReferenceDC){
1520+
/// Returns true if the reference is lexically contained in a declaration
1521+
/// that is deprecated on all deployment targets.
1522+
static bool isInsideDeprecatedDeclaration(SourceRange ReferenceRange,
1523+
const DeclContext *ReferenceDC){
15211524
auto IsDeprecated = [](const Decl *D) {
15221525
return D->getAttrs().getDeprecated(D->getASTContext());
15231526
};
15241527

1525-
return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, *this,
1526-
IsDeprecated);
1528+
return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsDeprecated);
15271529
}
15281530

15291531
static void fixItAvailableAttrRename(TypeChecker &TC,
@@ -1930,14 +1932,25 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
19301932
}
19311933
}
19321934

1933-
DeclName Name = DeprecatedDecl->getFullName();
1935+
DeclName Name;
1936+
Optional<unsigned> rawAccessorKind;
1937+
if (auto *accessor = dyn_cast<AccessorDecl>(DeprecatedDecl)) {
1938+
Name = accessor->getStorage()->getFullName();
1939+
assert(accessor->isGetterOrSetter());
1940+
rawAccessorKind = static_cast<unsigned>(accessor->getAccessorKind());
1941+
} else {
1942+
Name = DeprecatedDecl->getFullName();
1943+
}
1944+
19341945
StringRef Platform = Attr->prettyPlatformString();
19351946
clang::VersionTuple DeprecatedVersion;
19361947
if (Attr->Deprecated)
19371948
DeprecatedVersion = Attr->Deprecated.getValue();
19381949

1950+
static const unsigned NOT_ACCESSOR_INDEX = 2;
19391951
if (Attr->Message.empty() && Attr->Rename.empty()) {
1940-
diagnose(ReferenceRange.Start, diag::availability_deprecated, Name,
1952+
diagnose(ReferenceRange.Start, diag::availability_deprecated,
1953+
rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name,
19411954
Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(),
19421955
DeprecatedVersion)
19431956
.highlight(Attr->getRange());
@@ -1951,21 +1964,23 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
19511964

19521965
if (!Attr->Message.empty()) {
19531966
EncodedDiagnosticMessage EncodedMessage(Attr->Message);
1954-
diagnose(ReferenceRange.Start, diag::availability_deprecated_msg, Name,
1967+
diagnose(ReferenceRange.Start, diag::availability_deprecated_msg,
1968+
rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name,
19551969
Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(),
19561970
DeprecatedVersion, EncodedMessage.Message)
19571971
.highlight(Attr->getRange());
19581972
} else {
19591973
unsigned rawReplaceKind = static_cast<unsigned>(
19601974
replacementDeclKind.getValueOr(ReplacementDeclKind::None));
1961-
diagnose(ReferenceRange.Start, diag::availability_deprecated_rename, Name,
1975+
diagnose(ReferenceRange.Start, diag::availability_deprecated_rename,
1976+
rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name,
19621977
Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(),
19631978
DeprecatedVersion, replacementDeclKind.hasValue(), rawReplaceKind,
19641979
newName)
19651980
.highlight(Attr->getRange());
19661981
}
19671982

1968-
if (!Attr->Rename.empty()) {
1983+
if (!Attr->Rename.empty() && !rawAccessorKind.hasValue()) {
19691984
auto renameDiag = diagnose(ReferenceRange.Start,
19701985
diag::note_deprecated_rename,
19711986
newName);
@@ -2232,9 +2247,11 @@ class AvailabilityWalker : public ASTWalker {
22322247
return std::make_pair(false, E);
22332248
};
22342249

2235-
if (auto DR = dyn_cast<DeclRefExpr>(E))
2250+
if (auto DR = dyn_cast<DeclRefExpr>(E)) {
22362251
diagAvailability(DR->getDecl(), DR->getSourceRange(),
22372252
getEnclosingApplyExpr());
2253+
maybeDiagStorageAccess(DR->getDecl(), DR->getSourceRange(), DC);
2254+
}
22382255
if (auto MR = dyn_cast<MemberRefExpr>(E)) {
22392256
walkMemberRef(MR);
22402257
return skipChildren();
@@ -2250,8 +2267,10 @@ class AvailabilityWalker : public ASTWalker {
22502267
if (auto DS = dyn_cast<DynamicSubscriptExpr>(E))
22512268
diagAvailability(DS->getMember().getDecl(), DS->getSourceRange());
22522269
if (auto S = dyn_cast<SubscriptExpr>(E)) {
2253-
if (S->hasDecl())
2270+
if (S->hasDecl()) {
22542271
diagAvailability(S->getDecl().getDecl(), S->getSourceRange());
2272+
maybeDiagStorageAccess(S->getDecl().getDecl(), S->getSourceRange(), DC);
2273+
}
22552274
}
22562275
if (auto A = dyn_cast<AssignExpr>(E)) {
22572276
walkAssignExpr(A);
@@ -2334,20 +2353,17 @@ class AvailabilityWalker : public ASTWalker {
23342353
/// Walk a member reference expression, checking for availability.
23352354
void walkMemberRef(MemberRefExpr *E) {
23362355
// Walk the base in a getter context.
2356+
// FIXME: We may need to look at the setter too, if we're going to do
2357+
// writeback. The AST should have this information.
23372358
walkInContext(E, E->getBase(), MemberAccessContext::Getter);
23382359

23392360
ValueDecl *D = E->getMember().getDecl();
23402361
// Diagnose for the member declaration itself.
23412362
if (diagAvailability(D, E->getNameLoc().getSourceRange()))
23422363
return;
23432364

2344-
if (TC.getLangOpts().DisableAvailabilityChecking)
2345-
return;
2346-
2347-
if (auto *ASD = dyn_cast<AbstractStorageDecl>(D)) {
2348-
// Diagnose for appropriate accessors, given the access context.
2349-
diagStorageAccess(ASD, E->getSourceRange(), DC);
2350-
}
2365+
// Diagnose for appropriate accessors, given the access context.
2366+
maybeDiagStorageAccess(D, E->getSourceRange(), DC);
23512367
}
23522368

23532369
/// Walk an inout expression, checking for availability.
@@ -2365,9 +2381,16 @@ class AvailabilityWalker : public ASTWalker {
23652381

23662382
/// Emit diagnostics, if necessary, for accesses to storage where
23672383
/// the accessor for the AccessContext is not available.
2368-
void diagStorageAccess(AbstractStorageDecl *D,
2369-
SourceRange ReferenceRange,
2370-
const DeclContext *ReferenceDC) const {
2384+
void maybeDiagStorageAccess(const ValueDecl *VD,
2385+
SourceRange ReferenceRange,
2386+
const DeclContext *ReferenceDC) const {
2387+
if (TC.getLangOpts().DisableAvailabilityChecking)
2388+
return;
2389+
2390+
auto *D = dyn_cast<AbstractStorageDecl>(VD);
2391+
if (!D)
2392+
return;
2393+
23712394
if (!D->hasAccessorFunctions()) {
23722395
return;
23732396
}
@@ -2395,13 +2418,18 @@ class AvailabilityWalker : public ASTWalker {
23952418
}
23962419

23972420
/// Emit a diagnostic, if necessary for a potentially unavailable accessor.
2398-
/// Returns true if a diagnostic was emitted.
23992421
void diagAccessorAvailability(AccessorDecl *D, SourceRange ReferenceRange,
24002422
const DeclContext *ReferenceDC,
24012423
bool ForInout) const {
24022424
if (!D) {
24032425
return;
24042426
}
2427+
2428+
// Make sure not to diagnose an accessor if we already complained about
2429+
// the property/subscript.
2430+
if (!TypeChecker::getDeprecated(D->getStorage()))
2431+
TC.diagnoseIfDeprecated(ReferenceRange, ReferenceDC, D, /*call*/nullptr);
2432+
24052433
auto MaybeUnavail = TC.checkDeclarationAvailability(D, ReferenceRange.Start,
24062434
DC);
24072435
if (MaybeUnavail.hasValue()) {

lib/Sema/TypeChecker.h

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2404,27 +2404,6 @@ class TypeChecker final : public LazyResolver {
24042404
const DeclContext *ReferenceDC, const UnavailabilityReason &Reason,
24052405
bool ForInout);
24062406

2407-
/// Returns true if the reference or any of its parents is an
2408-
/// implicit function.
2409-
bool isInsideImplicitFunction(SourceRange ReferenceRange,
2410-
const DeclContext *DC);
2411-
2412-
/// Returns true if the reference or any of its parents is an
2413-
/// unavailable (or obsoleted) declaration.
2414-
bool isInsideUnavailableDeclaration(SourceRange ReferenceRange,
2415-
const DeclContext *DC);
2416-
2417-
/// Returns true if the reference or any of its parents is an
2418-
/// unconditional unavailable declaration for the same platform.
2419-
bool isInsideCompatibleUnavailableDeclaration(SourceRange ReferenceRange,
2420-
const DeclContext *DC,
2421-
const AvailableAttr *attr);
2422-
2423-
/// Returns true if the reference is lexically contained in a declaration
2424-
/// that is deprecated on all deployment targets.
2425-
bool isInsideDeprecatedDeclaration(SourceRange ReferenceRange,
2426-
const DeclContext *DC);
2427-
24282407
/// Returns the availability attribute indicating deprecation if the
24292408
/// declaration is deprecated or null otherwise.
24302409
static const AvailableAttr *getDeprecated(const Decl *D);

test/ClangImporter/Inputs/custom-modules/AvailabilityExtras.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,17 @@ typedef NS_ENUM(NSInteger, NSEnumAddedCasesIn2017) {
9393
NSEnumAddedCasesIn2017ExistingCaseThree,
9494
NSEnumAddedCasesIn2017NewCaseOne __attribute__((availability(macosx,introduced=10.13))) __attribute__((availability(ios,introduced=11.0))) __attribute__((availability(tvos,introduced=11.0))) __attribute__((availability(watchos,introduced=4.0)))
9595
};
96+
97+
@interface AccessorDeprecations: NSObject
98+
@property int fullyDeprecated __attribute__((deprecated));
99+
100+
@property int getterDeprecated;
101+
- (int)getterDeprecated __attribute__((deprecated));
102+
@property (class) int getterDeprecatedClass;
103+
+ (int)getterDeprecatedClass __attribute__((deprecated));
104+
105+
@property int setterDeprecated;
106+
- (void)setSetterDeprecated:(int)setterDeprecated __attribute__((deprecated));
107+
@property (class) int setterDeprecatedClass;
108+
+ (void)setSetterDeprecatedClass:(int)setterDeprecated __attribute__((deprecated));
109+
@end

test/ClangImporter/availability.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,28 @@ func test_unavailable_func(_ x : NSObject) {
2525
NSDeallocateObject(x) // expected-error {{'NSDeallocateObject' is unavailable}}
2626
}
2727

28-
func test_deprecated_imported_as_unavailable(_ s:UnsafeMutablePointer<CChar>) {
28+
func test_deprecated(_ s:UnsafeMutablePointer<CChar>, _ obj: AccessorDeprecations) {
2929
_ = tmpnam(s) // expected-warning {{'tmpnam' is deprecated: Due to security concerns inherent in the design of tmpnam(3), it is highly recommended that you use mkstemp(3) instead.}}
30+
31+
_ = obj.fullyDeprecated // expected-warning {{'fullyDeprecated' is deprecated}}
32+
obj.fullyDeprecated = 0 // expected-warning {{'fullyDeprecated' is deprecated}}
33+
obj.fullyDeprecated += 1 // expected-warning {{'fullyDeprecated' is deprecated}}
34+
35+
_ = obj.getterDeprecated // expected-warning {{getter for 'getterDeprecated' is deprecated}}
36+
obj.getterDeprecated = 0
37+
obj.getterDeprecated += 1 // expected-warning {{getter for 'getterDeprecated' is deprecated}}
38+
39+
_ = AccessorDeprecations.getterDeprecatedClass // expected-warning {{getter for 'getterDeprecatedClass' is deprecated}}
40+
AccessorDeprecations.getterDeprecatedClass = 0
41+
AccessorDeprecations.getterDeprecatedClass += 1 // expected-warning {{getter for 'getterDeprecatedClass' is deprecated}}
42+
43+
_ = obj.setterDeprecated
44+
obj.setterDeprecated = 0 // expected-warning {{setter for 'setterDeprecated' is deprecated}}
45+
obj.setterDeprecated += 1 // expected-warning {{setter for 'setterDeprecated' is deprecated}}
46+
47+
_ = AccessorDeprecations.setterDeprecatedClass
48+
AccessorDeprecations.setterDeprecatedClass = 0 // expected-warning {{setter for 'setterDeprecatedClass' is deprecated}}
49+
AccessorDeprecations.setterDeprecatedClass += 1 // expected-warning {{setter for 'setterDeprecatedClass' is deprecated}}
3050
}
3151

3252
func test_NSInvocation(_ x: NSInvocation, // expected-error {{'NSInvocation' is unavailable}}

0 commit comments

Comments
 (0)