Skip to content

Commit af05904

Browse files
authored
Merge pull request #65456 from tshortli/improve-override-of-unavailable-diagnostics
Sema: Improve diagnostics for overrides of unavailable decls
2 parents 2c08dc2 + 6e5562a commit af05904

7 files changed

+330
-81
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2764,9 +2764,9 @@ bool TypeChecker::diagnoseIfDeprecated(SourceLoc loc,
27642764
return true;
27652765
}
27662766

2767-
void swift::diagnoseUnavailableOverride(ValueDecl *override,
2768-
const ValueDecl *base,
2769-
const AvailableAttr *attr) {
2767+
void swift::diagnoseOverrideOfUnavailableDecl(ValueDecl *override,
2768+
const ValueDecl *base,
2769+
const AvailableAttr *attr) {
27702770
ASTContext &ctx = override->getASTContext();
27712771
auto &diags = ctx.Diags;
27722772
if (attr->Rename.empty()) {

lib/Sema/TypeCheckAvailability.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,11 @@ bool diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
245245
const Expr *call, const ExportContext &where,
246246
DeclAvailabilityFlags flags = None);
247247

248-
void diagnoseUnavailableOverride(ValueDecl *override,
249-
const ValueDecl *base,
250-
const AvailableAttr *attr);
248+
/// Emit a diagnostic for an available declaration that overrides an
249+
/// unavailable declaration.
250+
void diagnoseOverrideOfUnavailableDecl(ValueDecl *override,
251+
const ValueDecl *base,
252+
const AvailableAttr *attr);
251253

252254
/// Emit a diagnostic for references to declarations that have been
253255
/// marked as unavailable, either through "unavailable" or "obsoleted:".

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1835,6 +1835,52 @@ static bool diagnoseOverrideForAvailability(ValueDecl *override,
18351835
return true;
18361836
}
18371837

1838+
enum class OverrideUnavailabilityStatus {
1839+
/// The unavailability of the base decl and override decl are compatible.
1840+
Compatible,
1841+
/// The base decl is unavailable but the override decl is not.
1842+
BaseUnavailable,
1843+
/// Do not diagnose the unavailability of these decls.
1844+
Ignored,
1845+
};
1846+
1847+
static std::pair<OverrideUnavailabilityStatus, const AvailableAttr *>
1848+
checkOverrideUnavailability(ValueDecl *override, ValueDecl *base) {
1849+
if (auto *overrideParent = override->getDeclContext()->getAsDecl()) {
1850+
// If the parent of the override is unavailable, then the unavailability of
1851+
// the override decl is irrelevant.
1852+
if (overrideParent->getSemanticUnavailableAttr())
1853+
return {OverrideUnavailabilityStatus::Ignored, nullptr};
1854+
}
1855+
1856+
if (auto *baseAccessor = dyn_cast<AccessorDecl>(base)) {
1857+
// Ignore implicit accessors since the diagnostics are likely to duplicate
1858+
// the diagnostics for the explicit accessors that availability was inferred
1859+
// from.
1860+
if (baseAccessor->isImplicit())
1861+
return {OverrideUnavailabilityStatus::Ignored, nullptr};
1862+
1863+
if (auto *overrideAccessor = dyn_cast<AccessorDecl>(override)) {
1864+
// If base and override are accessors, check whether the unavailability of
1865+
// their storage matches. Diagnosing accessors with invalid storage
1866+
// produces redundant diagnostics.
1867+
if (checkOverrideUnavailability(overrideAccessor->getStorage(),
1868+
baseAccessor->getStorage())
1869+
.first != OverrideUnavailabilityStatus::Compatible)
1870+
return {OverrideUnavailabilityStatus::Ignored, nullptr};
1871+
}
1872+
}
1873+
1874+
auto &ctx = override->getASTContext();
1875+
auto *baseUnavailableAttr = base->getAttrs().getUnavailable(ctx);
1876+
auto *overrideUnavailableAttr = override->getAttrs().getUnavailable(ctx);
1877+
1878+
if (baseUnavailableAttr && !overrideUnavailableAttr)
1879+
return {OverrideUnavailabilityStatus::BaseUnavailable, baseUnavailableAttr};
1880+
1881+
return {OverrideUnavailabilityStatus::Compatible, nullptr};
1882+
}
1883+
18381884
static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
18391885
// This can happen with circular inheritance.
18401886
// FIXME: This shouldn't be possible once name lookup goes through the
@@ -2066,17 +2112,28 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
20662112
}
20672113

20682114
// FIXME: Possibly should extend to more availability checking.
2069-
if (auto *attr = base->getAttrs().getUnavailable(ctx)) {
2070-
diagnoseUnavailableOverride(override, base, attr);
2115+
auto unavailabilityStatusAndAttr =
2116+
checkOverrideUnavailability(override, base);
2117+
auto *unavailableAttr = unavailabilityStatusAndAttr.second;
2118+
2119+
switch (unavailabilityStatusAndAttr.first) {
2120+
case OverrideUnavailabilityStatus::BaseUnavailable: {
2121+
diagnoseOverrideOfUnavailableDecl(override, base, unavailableAttr);
20712122

20722123
if (isUnavailableInAllVersions(base)) {
20732124
auto modifier = override->getAttrs().getAttribute<OverrideAttr>();
20742125
if (modifier && modifier->isValid()) {
2075-
diags.diagnose(override, diag::suggest_removing_override,
2076-
override->getBaseName())
2077-
.fixItRemove(modifier->getRange());
2126+
diags
2127+
.diagnose(override, diag::suggest_removing_override,
2128+
override->getBaseName())
2129+
.fixItRemove(modifier->getRange());
20782130
}
20792131
}
2132+
break;
2133+
}
2134+
case OverrideUnavailabilityStatus::Compatible:
2135+
case OverrideUnavailabilityStatus::Ignored:
2136+
break;
20802137
}
20812138

20822139
if (!ctx.LangOpts.DisableAvailabilityChecking) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4384,7 +4384,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
43844384

43854385
case CheckKind::Unavailable: {
43864386
auto *attr = requirement->getAttrs().getUnavailable(getASTContext());
4387-
diagnoseUnavailableOverride(witness, requirement, attr);
4387+
diagnoseOverrideOfUnavailableDecl(witness, requirement, attr);
43884388
break;
43894389
}
43904390

test/Sema/availability.swift

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -31,64 +31,6 @@ func foo(x : NSUInteger) { // expected-error {{'NSUInteger' is unavailable: use
3131
// expected-error@-1 {{cannot convert value of type 'Int' to specified type 'Outer.NSUInteger'}}
3232
}
3333

34-
// Test preventing overrides (but allowing shadowing) of unavailable methods.
35-
class ClassWithUnavailable {
36-
@available(*, unavailable)
37-
func doNotOverride() {} // expected-note {{'doNotOverride()' has been explicitly marked unavailable here}}
38-
39-
// FIXME: extraneous diagnostic here
40-
@available(*, unavailable)
41-
init(int _: Int) {} // expected-note 3 {{'init(int:)' has been explicitly marked unavailable here}}
42-
43-
@available(*, unavailable)
44-
convenience init(otherInt: Int) {
45-
self.init(int: otherInt) // expected-error {{'init(int:)' is unavailable}}
46-
}
47-
48-
@available(*, unavailable)
49-
required init(necessaryInt: Int) {}
50-
51-
@available(*, unavailable)
52-
subscript (i: Int) -> Int { // expected-note 2 {{'subscript(_:)' has been explicitly marked unavailable here}}
53-
return i
54-
}
55-
}
56-
57-
class ClassWithOverride : ClassWithUnavailable {
58-
override func doNotOverride() {}
59-
// expected-error@-1 {{cannot override 'doNotOverride' which has been marked unavailable}}
60-
// expected-note@-2 {{remove 'override' modifier to declare a new 'doNotOverride'}} {{3-12=}}
61-
62-
override init(int: Int) {}
63-
// expected-error@-1 {{cannot override 'init' which has been marked unavailable}}
64-
// expected-note@-2 {{remove 'override' modifier to declare a new 'init'}} {{3-12=}}
65-
66-
// can't override convenience inits
67-
// can't override required inits
68-
69-
override subscript (i: Int) -> Int { return i }
70-
// expected-error@-1 {{cannot override 'subscript' which has been marked unavailable}}
71-
// expected-note@-2 {{remove 'override' modifier to declare a new 'subscript'}} {{3-12=}}
72-
}
73-
74-
class ClassWithShadowing : ClassWithUnavailable {
75-
func doNotOverride() {} // no-error
76-
init(int: Int) {} // no-error
77-
convenience init(otherInt: Int) { self.init(int: otherInt) } // no-error
78-
required init(necessaryInt: Int) {} // no-error
79-
subscript (i: Int) -> Int { return i } // no-error
80-
}
81-
82-
func testInit() {
83-
ClassWithUnavailable(int: 0) // expected-error {{'init(int:)' is unavailable}} // expected-warning{{unused}}
84-
ClassWithShadowing(int: 0) // expected-warning {{unused}}
85-
}
86-
87-
func testSubscript(cwu: ClassWithUnavailable, cws: ClassWithShadowing) {
88-
_ = cwu[5] // expected-error{{'subscript(_:)' is unavailable}}
89-
_ = cws[5] // no-error
90-
}
91-
9234
/* FIXME 'nil == a' fails to type-check with a bogus error message
9335
* <rdar://problem/17540796>
9436
func markUsed<T>(t: T) {}

0 commit comments

Comments
 (0)