Skip to content

Commit ab9f62d

Browse files
authored
Merge pull request #15864 from jrose-apple/get-set-stop
Diagnose unavailable getters and setters
2 parents 1cc1832 + 0b632aa commit ab9f62d

File tree

7 files changed

+294
-57
lines changed

7 files changed

+294
-57
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3622,38 +3622,45 @@ ERROR(dynamic_requires_objc,none,
36223622
//------------------------------------------------------------------------------
36233623

36243624
ERROR(availability_decl_unavailable, none,
3625-
"%0 is unavailable", (DeclName))
3625+
"%select{getter for |setter for |}0%1 is unavailable",
3626+
(unsigned, DeclName))
36263627

36273628
#define REPLACEMENT_DECL_KIND_SELECT "select{| instance method| property}"
36283629
ERROR(availability_decl_unavailable_rename, none,
3629-
"%0 has been %select{renamed to|replaced by}1"
3630-
"%" REPLACEMENT_DECL_KIND_SELECT "2 '%3'",
3631-
(DeclName, bool, unsigned, StringRef))
3630+
"%select{getter for |setter for |}0%1 has been "
3631+
"%select{renamed to|replaced by}2%" REPLACEMENT_DECL_KIND_SELECT "3 "
3632+
"'%4'",
3633+
(unsigned, DeclName, bool, unsigned, StringRef))
36323634

36333635
ERROR(availability_decl_unavailable_rename_msg, none,
3634-
"%0 has been %select{renamed to|replaced by}1"
3635-
"%" REPLACEMENT_DECL_KIND_SELECT "2 '%3': %4",
3636-
(DeclName, bool, unsigned, StringRef, StringRef))
3636+
"%select{getter for |setter for |}0%1 has been "
3637+
"%select{renamed to|replaced by}2%" REPLACEMENT_DECL_KIND_SELECT "3 "
3638+
"'%4': %5",
3639+
(unsigned, DeclName, bool, unsigned, StringRef, StringRef))
36373640

36383641
ERROR(availability_decl_unavailable_msg, none,
3639-
"%0 is unavailable: %1", (DeclName, StringRef))
3642+
"%select{getter for |setter for |}0%1 is unavailable: %2",
3643+
(unsigned, DeclName, StringRef))
36403644

36413645
ERROR(availability_decl_unavailable_in_swift, none,
3642-
"%0 is unavailable in Swift", (DeclName))
3646+
"%select{getter for |setter for |}0%1 is unavailable in Swift",
3647+
(unsigned, DeclName))
36433648

36443649
ERROR(availability_decl_unavailable_in_swift_msg, none,
3645-
"%0 is unavailable in Swift: %1", (DeclName, StringRef))
3650+
"%select{getter for |setter for |}0%1 is unavailable in Swift: %2",
3651+
(unsigned, DeclName, StringRef))
36463652

36473653
NOTE(availability_marked_unavailable, none,
3648-
"%0 has been explicitly marked unavailable here", (DeclName))
3654+
"%select{getter for |setter for |}0%1 has been explicitly marked "
3655+
"unavailable here", (unsigned, DeclName))
36493656

36503657
NOTE(availability_introduced_in_swift, none,
3651-
"%0 was introduced in Swift %1",
3652-
(DeclName, clang::VersionTuple))
3658+
"%select{getter for |setter for |}0%1 was introduced in Swift %2",
3659+
(unsigned, DeclName, clang::VersionTuple))
36533660

36543661
NOTE(availability_obsoleted, none,
3655-
"%0 was obsoleted in %1 %2",
3656-
(DeclName, StringRef, clang::VersionTuple))
3662+
"%select{getter for |setter for |}0%1 was obsoleted in %2 %3",
3663+
(unsigned, DeclName, StringRef, clang::VersionTuple))
36573664

36583665
WARNING(availability_deprecated, none,
36593666
"%select{getter for |setter for |}0%1 %select{is|%select{is|was}4}2 "

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 82 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,9 @@ static void fixItAvailableAttrRename(TypeChecker &TC,
15341534
const ValueDecl *renamedDecl,
15351535
const AvailableAttr *attr,
15361536
const ApplyExpr *call) {
1537+
if (isa<AccessorDecl>(renamedDecl))
1538+
return;
1539+
15371540
ParsedDeclName parsed = swift::parseDeclName(attr->Rename);
15381541
if (!parsed)
15391542
return;
@@ -1906,6 +1909,26 @@ describeRename(ASTContext &ctx, const AvailableAttr *attr, const ValueDecl *D,
19061909
return ReplacementDeclKind::None;
19071910
}
19081911

1912+
/// Returns a value that can be used to select between accessor kinds in
1913+
/// diagnostics.
1914+
///
1915+
/// This is correlated with diag::availability_deprecated and others.
1916+
static std::pair<unsigned, DeclName>
1917+
getAccessorKindAndNameForDiagnostics(const ValueDecl *D) {
1918+
// This should always be one more than the last AccessorKind supported in
1919+
// the diagnostics. If you need to change it, change the assertion below as
1920+
// well.
1921+
static const unsigned NOT_ACCESSOR_INDEX = 2;
1922+
1923+
if (auto *accessor = dyn_cast<AccessorDecl>(D)) {
1924+
DeclName Name = accessor->getStorage()->getFullName();
1925+
assert(accessor->isGetterOrSetter());
1926+
return {static_cast<unsigned>(accessor->getAccessorKind()), Name};
1927+
}
1928+
1929+
return {NOT_ACCESSOR_INDEX, D->getFullName()};
1930+
}
1931+
19091932
void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
19101933
const DeclContext *ReferenceDC,
19111934
const ValueDecl *DeprecatedDecl,
@@ -1933,26 +1956,19 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
19331956
}
19341957

19351958
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-
}
1959+
unsigned RawAccessorKind;
1960+
std::tie(RawAccessorKind, Name) =
1961+
getAccessorKindAndNameForDiagnostics(DeprecatedDecl);
19441962

19451963
StringRef Platform = Attr->prettyPlatformString();
19461964
clang::VersionTuple DeprecatedVersion;
19471965
if (Attr->Deprecated)
19481966
DeprecatedVersion = Attr->Deprecated.getValue();
19491967

1950-
static const unsigned NOT_ACCESSOR_INDEX = 2;
19511968
if (Attr->Message.empty() && Attr->Rename.empty()) {
19521969
diagnose(ReferenceRange.Start, diag::availability_deprecated,
1953-
rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name,
1954-
Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(),
1955-
DeprecatedVersion)
1970+
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
1971+
Attr->Deprecated.hasValue(), DeprecatedVersion)
19561972
.highlight(Attr->getRange());
19571973
return;
19581974
}
@@ -1965,22 +1981,21 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
19651981
if (!Attr->Message.empty()) {
19661982
EncodedDiagnosticMessage EncodedMessage(Attr->Message);
19671983
diagnose(ReferenceRange.Start, diag::availability_deprecated_msg,
1968-
rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name,
1969-
Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(),
1970-
DeprecatedVersion, EncodedMessage.Message)
1984+
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
1985+
Attr->Deprecated.hasValue(), DeprecatedVersion,
1986+
EncodedMessage.Message)
19711987
.highlight(Attr->getRange());
19721988
} else {
19731989
unsigned rawReplaceKind = static_cast<unsigned>(
19741990
replacementDeclKind.getValueOr(ReplacementDeclKind::None));
19751991
diagnose(ReferenceRange.Start, diag::availability_deprecated_rename,
1976-
rawAccessorKind.getValueOr(NOT_ACCESSOR_INDEX), Name,
1977-
Attr->hasPlatform(), Platform, Attr->Deprecated.hasValue(),
1978-
DeprecatedVersion, replacementDeclKind.hasValue(), rawReplaceKind,
1979-
newName)
1992+
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
1993+
Attr->Deprecated.hasValue(), DeprecatedVersion,
1994+
replacementDeclKind.hasValue(), rawReplaceKind, newName)
19801995
.highlight(Attr->getRange());
19811996
}
19821997

1983-
if (!Attr->Rename.empty() && !rawAccessorKind.hasValue()) {
1998+
if (!Attr->Rename.empty() && !isa<AccessorDecl>(DeprecatedDecl)) {
19841999
auto renameDiag = diagnose(ReferenceRange.Start,
19852000
diag::note_deprecated_rename,
19862001
newName);
@@ -1999,8 +2014,13 @@ void TypeChecker::diagnoseUnavailableOverride(ValueDecl *override,
19992014
else
20002015
diagnose(override, diag::override_unavailable_msg,
20012016
override->getBaseName(), attr->Message);
2017+
2018+
DeclName name;
2019+
unsigned rawAccessorKind;
2020+
std::tie(rawAccessorKind, name) =
2021+
getAccessorKindAndNameForDiagnostics(base);
20022022
diagnose(base, diag::availability_marked_unavailable,
2003-
base->getFullName());
2023+
rawAccessorKind, name);
20042024
return;
20052025
}
20062026

@@ -2127,7 +2147,9 @@ bool TypeChecker::diagnoseExplicitUnavailability(
21272147
}
21282148

21292149
SourceLoc Loc = R.Start;
2130-
auto Name = D->getFullName();
2150+
DeclName Name;
2151+
unsigned RawAccessorKind;
2152+
std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D);
21312153

21322154
switch (Attr->getPlatformAgnosticAvailability()) {
21332155
case PlatformAgnosticAvailabilityKind::Deprecated:
@@ -2150,14 +2172,14 @@ bool TypeChecker::diagnoseExplicitUnavailability(
21502172

21512173
if (Attr->Message.empty()) {
21522174
auto diag = diagnose(Loc, diag::availability_decl_unavailable_rename,
2153-
Name, replaceKind.hasValue(), rawReplaceKind,
2154-
newName);
2175+
RawAccessorKind, Name, replaceKind.hasValue(),
2176+
rawReplaceKind, newName);
21552177
attachRenameFixIts(diag);
21562178
} else {
21572179
EncodedDiagnosticMessage EncodedMessage(Attr->Message);
21582180
auto diag = diagnose(Loc, diag::availability_decl_unavailable_rename_msg,
2159-
Name, replaceKind.hasValue(), rawReplaceKind,
2160-
newName, EncodedMessage.Message);
2181+
RawAccessorKind, Name, replaceKind.hasValue(),
2182+
rawReplaceKind, newName, EncodedMessage.Message);
21612183
attachRenameFixIts(diag);
21622184
}
21632185
} else if (isSubscriptReturningString(D, Context)) {
@@ -2171,12 +2193,13 @@ bool TypeChecker::diagnoseExplicitUnavailability(
21712193
} else if (Attr->Message.empty()) {
21722194
diagnose(Loc, inSwift ? diag::availability_decl_unavailable_in_swift
21732195
: diag::availability_decl_unavailable,
2174-
Name).highlight(R);
2196+
RawAccessorKind, Name)
2197+
.highlight(R);
21752198
} else {
21762199
EncodedDiagnosticMessage EncodedMessage(Attr->Message);
21772200
diagnose(Loc, inSwift ? diag::availability_decl_unavailable_in_swift_msg
21782201
: diag::availability_decl_unavailable_msg,
2179-
Name, EncodedMessage.Message)
2202+
RawAccessorKind, Name, EncodedMessage.Message)
21802203
.highlight(R);
21812204
}
21822205
break;
@@ -2191,20 +2214,23 @@ bool TypeChecker::diagnoseExplicitUnavailability(
21912214
case AvailableVersionComparison::Unavailable:
21922215
if (Attr->isLanguageVersionSpecific()
21932216
&& Attr->Introduced.hasValue())
2194-
diagnose(D, diag::availability_introduced_in_swift, Name,
2195-
*Attr->Introduced).highlight(Attr->getRange());
2217+
diagnose(D, diag::availability_introduced_in_swift,
2218+
RawAccessorKind, Name, *Attr->Introduced)
2219+
.highlight(Attr->getRange());
21962220
else
2197-
diagnose(D, diag::availability_marked_unavailable, Name)
2221+
diagnose(D, diag::availability_marked_unavailable, RawAccessorKind, Name)
21982222
.highlight(Attr->getRange());
21992223
break;
22002224

22012225
case AvailableVersionComparison::Obsoleted:
22022226
// FIXME: Use of the platformString here is non-awesome for application
22032227
// extensions.
2204-
diagnose(D, diag::availability_obsoleted, Name,
2228+
diagnose(D, diag::availability_obsoleted,
2229+
RawAccessorKind, Name,
22052230
(Attr->isLanguageVersionSpecific() ?
22062231
"Swift" : Attr->prettyPlatformString()),
2207-
*Attr->Obsoleted).highlight(Attr->getRange());
2232+
*Attr->Obsoleted)
2233+
.highlight(Attr->getRange());
22082234
break;
22092235
}
22102236
return true;
@@ -2433,8 +2459,18 @@ class AvailabilityWalker : public ASTWalker {
24332459
return;
24342460
}
24352461

2436-
// Make sure not to diagnose an accessor if we already complained about
2437-
// the property/subscript.
2462+
// If the property/subscript is unconditionally unavailable, don't bother
2463+
// with any of the rest of this.
2464+
if (AvailableAttr::isUnavailable(D->getStorage()))
2465+
return;
2466+
2467+
if (TC.diagnoseExplicitUnavailability(D, ReferenceRange, ReferenceDC,
2468+
/*call*/nullptr)) {
2469+
return;
2470+
}
2471+
2472+
// Make sure not to diagnose an accessor's deprecation if we already
2473+
// complained about the property/subscript.
24382474
if (!TypeChecker::getDeprecated(D->getStorage()))
24392475
TC.diagnoseIfDeprecated(ReferenceRange, ReferenceDC, D, /*call*/nullptr);
24402476

@@ -2549,9 +2585,14 @@ bool AvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D,
25492585
}
25502586

25512587
if (!replacement.empty()) {
2588+
DeclName Name;
2589+
unsigned RawAccessorKind;
2590+
std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D);
2591+
25522592
// If we emit a deprecation diagnostic, produce a fixit hint as well.
25532593
auto diag = TC.diagnose(R.Start, diag::availability_decl_unavailable_msg,
2554-
D->getFullName(), "it has been removed in Swift 3");
2594+
RawAccessorKind, Name,
2595+
"it has been removed in Swift 3");
25552596
if (isa<PrefixUnaryExpr>(call)) {
25562597
// Prefix: remove the ++ or --.
25572598
diag.fixItRemove(call->getFn()->getSourceRange());
@@ -2594,9 +2635,13 @@ bool AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D,
25942635
if (!args)
25952636
return false;
25962637

2638+
DeclName Name;
2639+
unsigned RawAccessorKind;
2640+
std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D);
2641+
25972642
EncodedDiagnosticMessage EncodedMessage(Attr->Message);
25982643
auto diag = TC.diagnose(R.Start, diag::availability_decl_unavailable_msg,
2599-
D->getFullName(), EncodedMessage.Message);
2644+
RawAccessorKind, Name, EncodedMessage.Message);
26002645
diag.highlight(R);
26012646

26022647
auto subject = args->getSubExpr();

lib/Sema/TypeCheckPattern.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,9 +1383,10 @@ bool TypeChecker::coercePatternToType(Pattern *&P, DeclContext *dc, Type type,
13831383
EEP->getName().str() == "Some") {
13841384
SmallString<4> Rename;
13851385
camel_case::toLowercaseWord(EEP->getName().str(), Rename);
1386-
diagnose(EEP->getLoc(), diag::availability_decl_unavailable_rename,
1387-
EEP->getName(), /*replaced*/false,
1388-
/*special kind*/0, Rename.str())
1386+
diagnose(EEP->getLoc(),
1387+
diag::availability_decl_unavailable_rename,
1388+
/*"getter" prefix*/2, EEP->getName(), /*replaced*/false,
1389+
/*special kind*/0, Rename.str())
13891390
.fixItReplace(EEP->getLoc(), Rename.str());
13901391

13911392
return true;

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,18 @@ typedef NS_ENUM(NSInteger, NSEnumAddedCasesIn2017) {
107107
@property (class) int setterDeprecatedClass;
108108
+ (void)setSetterDeprecatedClass:(int)setterDeprecated __attribute__((deprecated));
109109
@end
110+
111+
112+
@interface UnavailableAccessors: NSObject
113+
@property NSInteger fullyUnavailable __attribute__((unavailable));
114+
115+
@property NSInteger getterUnavailable;
116+
- (NSInteger)getterUnavailable __attribute__((unavailable));
117+
@property (class) NSInteger getterUnavailableClass;
118+
+ (NSInteger)getterUnavailableClass __attribute__((unavailable));
119+
120+
@property NSInteger setterUnavailable;
121+
- (void)setSetterUnavailable:(NSInteger)setterUnavailable __attribute__((unavailable));
122+
@property (class) NSInteger setterUnavailableClass;
123+
+ (void)setSetterUnavailableClass:(NSInteger)setterUnavailable __attribute__((unavailable));
124+
@end

test/ClangImporter/availability.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules %s
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -I %S/Inputs/custom-modules %s -verify-ignore-unknown
22

33
// REQUIRES: objc_interop
44

@@ -25,6 +25,28 @@ func test_unavailable_func(_ x : NSObject) {
2525
NSDeallocateObject(x) // expected-error {{'NSDeallocateObject' is unavailable}}
2626
}
2727

28+
func test_unavailable_accessors(_ obj: UnavailableAccessors) {
29+
_ = obj.fullyUnavailable // expected-error {{'fullyUnavailable' is unavailable}}
30+
obj.fullyUnavailable = 0 // expected-error {{'fullyUnavailable' is unavailable}}
31+
obj.fullyUnavailable += 1 // expected-error {{'fullyUnavailable' is unavailable}}
32+
33+
_ = obj.getterUnavailable // expected-error {{getter for 'getterUnavailable' is unavailable}}
34+
obj.getterUnavailable = 0
35+
obj.getterUnavailable += 1 // expected-error {{getter for 'getterUnavailable' is unavailable}}
36+
37+
_ = UnavailableAccessors.getterUnavailableClass // expected-error {{getter for 'getterUnavailableClass' is unavailable}}
38+
UnavailableAccessors.getterUnavailableClass = 0
39+
UnavailableAccessors.getterUnavailableClass += 1 // expected-error {{getter for 'getterUnavailableClass' is unavailable}}
40+
41+
_ = obj.setterUnavailable
42+
obj.setterUnavailable = 0 // expected-error {{setter for 'setterUnavailable' is unavailable}}
43+
obj.setterUnavailable += 1 // expected-error {{setter for 'setterUnavailable' is unavailable}}
44+
45+
_ = UnavailableAccessors.setterUnavailableClass
46+
UnavailableAccessors.setterUnavailableClass = 0 // expected-error {{setter for 'setterUnavailableClass' is unavailable}}
47+
UnavailableAccessors.setterUnavailableClass += 1 // expected-error {{setter for 'setterUnavailableClass' is unavailable}}
48+
}
49+
2850
func test_deprecated(_ s:UnsafeMutablePointer<CChar>, _ obj: AccessorDeprecations) {
2951
_ = 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.}}
3052

0 commit comments

Comments
 (0)