Skip to content

Commit d579b69

Browse files
committed
Sema: Teach ExportContext to compute whether we're inside a deprecated declaration
1 parent 548b96a commit d579b69

File tree

6 files changed

+205
-30
lines changed

6 files changed

+205
-30
lines changed

lib/Sema/TypeCheckAccess.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,7 +1513,7 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
15131513
loc = varDecl->getNameLoc();
15141514

15151515
diagnoseTypeAvailability(typeRepr, type, loc,
1516-
Where.forReason(reason), flags);
1516+
Where.withReason(reason), flags);
15171517
}
15181518

15191519
void checkGenericParams(const GenericContext *ownerCtx,
@@ -1751,14 +1751,14 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
17511751
return isExported(valueMember);
17521752
});
17531753

1754-
Where = wasWhere.forExported(hasExportedMembers);
1755-
checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED,
1754+
Where = wasWhere.withExported(hasExportedMembers);
1755+
checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED,
17561756
ExportabilityReason::ExtensionWithPublicMembers);
17571757

17581758
// 3) If the extension contains exported members or defines conformances,
17591759
// the 'where' clause must only name exported types.
1760-
Where = wasWhere.forExported(hasExportedMembers ||
1761-
!ED->getInherited().empty());
1760+
Where = wasWhere.withExported(hasExportedMembers ||
1761+
!ED->getInherited().empty());
17621762
checkConstrainedExtensionRequirements(ED, hasExportedMembers);
17631763
}
17641764

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 91 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@
3838
using namespace swift;
3939

4040
ExportContext::ExportContext(DeclContext *DC, FragileFunctionKind kind,
41-
bool spi, bool exported)
41+
bool spi, bool exported, bool deprecated)
4242
: DC(DC), FragileKind(kind) {
4343
SPI = spi;
4444
Exported = exported;
45+
Deprecated = deprecated;
4546
Reason = ExportabilityReason::General;
4647
}
4748

@@ -95,7 +96,73 @@ bool swift::isExported(const Decl *D) {
9596
return true;
9697
}
9798

99+
template<typename Fn>
100+
static void forEachOuterDecl(DeclContext *DC, Fn fn) {
101+
for (; !DC->isModuleScopeContext(); DC = DC->getParent()) {
102+
switch (DC->getContextKind()) {
103+
case DeclContextKind::AbstractClosureExpr:
104+
case DeclContextKind::TopLevelCodeDecl:
105+
case DeclContextKind::SerializedLocal:
106+
case DeclContextKind::Module:
107+
case DeclContextKind::FileUnit:
108+
break;
109+
110+
case DeclContextKind::Initializer:
111+
if (auto *PBI = dyn_cast<PatternBindingInitializer>(DC))
112+
fn(PBI->getBinding());
113+
break;
114+
115+
case DeclContextKind::SubscriptDecl:
116+
fn(cast<SubscriptDecl>(DC));
117+
break;
118+
119+
case DeclContextKind::EnumElementDecl:
120+
fn(cast<EnumElementDecl>(DC));
121+
break;
122+
123+
case DeclContextKind::AbstractFunctionDecl:
124+
fn(cast<AbstractFunctionDecl>(DC));
125+
126+
if (auto *AD = dyn_cast<AccessorDecl>(DC))
127+
fn(AD->getStorage());
128+
break;
129+
130+
case DeclContextKind::GenericTypeDecl:
131+
fn(cast<GenericTypeDecl>(DC));
132+
break;
133+
134+
case DeclContextKind::ExtensionDecl:
135+
fn(cast<ExtensionDecl>(DC));
136+
break;
137+
}
138+
}
139+
}
140+
141+
static void computeExportContextBits(Decl *D,
142+
bool *implicit, bool *deprecated) {
143+
if (D->isImplicit())
144+
*implicit = true;
145+
146+
if (D->getAttrs().getDeprecated(D->getASTContext()))
147+
*deprecated = true;
148+
149+
if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
150+
for (unsigned i = 0, e = PBD->getNumPatternEntries(); i < e; ++i) {
151+
if (auto *VD = PBD->getAnchoringVarDecl(i))
152+
computeExportContextBits(VD, implicit, deprecated);
153+
}
154+
}
155+
}
156+
98157
ExportContext ExportContext::forDeclSignature(Decl *D) {
158+
bool implicit = false;
159+
bool deprecated = false;
160+
computeExportContextBits(D, &implicit, &deprecated);
161+
forEachOuterDecl(D->getDeclContext(),
162+
[&](Decl *D) {
163+
computeExportContextBits(D, &implicit, &deprecated);
164+
});
165+
99166
auto *DC = D->getInnermostDeclContext();
100167
auto fragileKind = DC->getFragileFunctionKind();
101168

@@ -113,11 +180,19 @@ ExportContext ExportContext::forDeclSignature(Decl *D) {
113180

114181
bool exported = ::isExported(D);
115182

116-
return ExportContext(DC, fragileKind, spi, exported);
183+
return ExportContext(DC, fragileKind, spi, exported, deprecated);
117184
}
118185

119186
ExportContext ExportContext::forFunctionBody(DeclContext *DC) {
120-
;
187+
assert(DC && "Use ExportContext::forImplicit() for implicit decls");
188+
189+
bool implicit = false;
190+
bool deprecated = false;
191+
forEachOuterDecl(DC,
192+
[&](Decl *D) {
193+
computeExportContextBits(D, &implicit, &deprecated);
194+
});
195+
121196
auto fragileKind = DC->getFragileFunctionKind();
122197

123198
bool spi = false;
@@ -129,16 +204,21 @@ ExportContext ExportContext::forFunctionBody(DeclContext *DC) {
129204
assert(fragileKind.kind == FragileFunctionKind::None);
130205
}
131206

132-
return ExportContext(DC, fragileKind, spi, exported);
207+
return ExportContext(DC, fragileKind, spi, exported, deprecated);
208+
}
209+
210+
ExportContext ExportContext::forImplicit() {
211+
return ExportContext(nullptr, FragileFunctionKind(),
212+
false, false, false);
133213
}
134214

135-
ExportContext ExportContext::forReason(ExportabilityReason reason) const {
215+
ExportContext ExportContext::withReason(ExportabilityReason reason) const {
136216
auto copy = *this;
137217
copy.Reason = reason;
138218
return copy;
139219
}
140220

141-
ExportContext ExportContext::forExported(bool exported) const {
221+
ExportContext ExportContext::withExported(bool exported) const {
142222
auto copy = *this;
143223
copy.Exported = isExported() && exported;
144224
return copy;
@@ -1672,17 +1752,6 @@ static bool isInsideCompatibleUnavailableDeclaration(
16721752
return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsUnavailable);
16731753
}
16741754

1675-
/// Returns true if the reference is lexically contained in a declaration
1676-
/// that is deprecated on all deployment targets.
1677-
static bool isInsideDeprecatedDeclaration(SourceRange ReferenceRange,
1678-
const DeclContext *ReferenceDC){
1679-
auto IsDeprecated = [](const Decl *D) {
1680-
return D->getAttrs().getDeprecated(D->getASTContext());
1681-
};
1682-
1683-
return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsDeprecated);
1684-
}
1685-
16861755
static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
16871756
SourceRange referenceRange,
16881757
const ValueDecl *renamedDecl,
@@ -2059,13 +2128,15 @@ getAccessorKindAndNameForDiagnostics(const ValueDecl *D) {
20592128
}
20602129

20612130
void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
2062-
const DeclContext *ReferenceDC,
2131+
ExportContext Where,
20632132
const ValueDecl *DeprecatedDecl,
20642133
const ApplyExpr *Call) {
20652134
const AvailableAttr *Attr = TypeChecker::getDeprecated(DeprecatedDecl);
20662135
if (!Attr)
20672136
return;
20682137

2138+
auto *ReferenceDC = Where.getDeclContext();
2139+
20692140
// We don't want deprecated declarations to trigger warnings
20702141
// in synthesized code.
20712142
if (ReferenceRange.isInvalid() &&
@@ -2075,7 +2146,7 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
20752146
// We match the behavior of clang to not report deprecation warnings
20762147
// inside declarations that are themselves deprecated on all deployment
20772148
// targets.
2078-
if (isInsideDeprecatedDeclaration(ReferenceRange, ReferenceDC)) {
2149+
if (Where.isDeprecated()) {
20792150
return;
20802151
}
20812152

@@ -2788,7 +2859,7 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R,
27882859

27892860
// Diagnose for deprecation
27902861
if (!isAccessorWithDeprecatedStorage)
2791-
TypeChecker::diagnoseIfDeprecated(R, DC, D, call);
2862+
TypeChecker::diagnoseIfDeprecated(R, Where, D, call);
27922863

27932864
if (Flags.contains(DeclAvailabilityFlag::AllowPotentiallyUnavailable))
27942865
return false;

lib/Sema/TypeCheckAvailability.h

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,11 @@ class ExportContext {
9797
FragileFunctionKind FragileKind;
9898
unsigned SPI : 1;
9999
unsigned Exported : 1;
100+
unsigned Deprecated : 1;
100101
ExportabilityReason Reason;
101102

102-
ExportContext(DeclContext *DC, FragileFunctionKind kind, bool spi, bool exported);
103+
ExportContext(DeclContext *DC, FragileFunctionKind kind,
104+
bool spi, bool exported, bool deprecated);
103105

104106
public:
105107

@@ -119,30 +121,46 @@ class ExportContext {
119121
/// it can reference anything.
120122
static ExportContext forFunctionBody(DeclContext *DC);
121123

124+
/// Produce a new context describing an implicit declaration. Implicit code
125+
/// does not have source locations. We simply trust the compiler synthesizes
126+
/// implicit declarations correctly, and skip the checks.
127+
static ExportContext forImplicit();
128+
122129
/// Produce a new context with the same properties as this one, except
123130
/// changing the ExportabilityReason. This only affects diagnostics.
124-
ExportContext forReason(ExportabilityReason reason) const;
131+
ExportContext withReason(ExportabilityReason reason) const;
125132

126133
/// Produce a new context with the same properties as this one, except
127134
/// that if 'exported' is false, the resulting context can reference
128135
/// declarations that are not exported. If 'exported' is true, the
129136
/// resulting context is indentical to this one.
130137
///
131138
/// That is, this will perform a 'bitwise and' on the 'exported' bit.
132-
ExportContext forExported(bool exported) const;
139+
ExportContext withExported(bool exported) const;
133140

134-
DeclContext *getDeclContext() const { return DC; }
141+
DeclContext *getDeclContext() const {
142+
assert(DC != nullptr && "This is an implicit context");
143+
return DC;
144+
}
135145

136146
/// If not 'None', the context has the inlinable function body restriction.
137147
FragileFunctionKind getFragileFunctionKind() const { return FragileKind; }
138148

149+
/// If true, the context is part of a synthesized declaration, and
150+
/// availability checking should be disabled.
151+
bool isImplicit() const { return DC == nullptr; }
152+
139153
/// If true, the context is SPI and can reference SPI declarations.
140154
bool isSPI() const { return SPI; }
141155

142156
/// If true, the context is exported and cannot reference SPI declarations
143157
/// or declarations from `@_implementationOnly` imports.
144158
bool isExported() const { return Exported; }
145159

160+
/// If true, the context is part of a deprecated declaration and can
161+
/// reference other deprecated declarations without warning.
162+
bool isDeprecated() const { return Deprecated; }
163+
146164
/// If true, the context can only reference exported declarations, either
147165
/// because it is the signature context of an exported declaration, or
148166
/// because it is the function body context of an inlinable function.

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,7 @@ const AvailableAttr *getDeprecated(const Decl *D);
10471047
/// Callers can provide a lambda that adds additional information (such as a
10481048
/// fixit hint) to the deprecation diagnostic, if it is emitted.
10491049
void diagnoseIfDeprecated(SourceRange SourceRange,
1050-
const DeclContext *ReferenceDC,
1050+
ExportContext Where,
10511051
const ValueDecl *DeprecatedDecl,
10521052
const ApplyExpr *Call);
10531053
/// @}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %target-typecheck-verify-swift -parse-as-library
2+
3+
struct DummyType {}
4+
5+
@available(*, deprecated, renamed: "&-")
6+
func -(x: DummyType, y: DummyType) {}
7+
8+
// We don't warn if a deprecated declaration is referenced from
9+
// within another deprecated declaration.
10+
11+
@available(*, deprecated)
12+
func testDeprecatedReferencingDeprecated1(x: DummyType, y: DummyType) {
13+
x - y // no-warning
14+
}
15+
16+
@available(*, deprecated)
17+
var testDeprecatedReferencingDeprecated2: () {
18+
let x = DummyType()
19+
let y = DummyType()
20+
x - y // no-warning
21+
}
22+
23+
// FIXME: This doesn't work because the file is parsed in script mode.
24+
@available(*, deprecated)
25+
var testDeprecatedReferencingDeprecated3: () = DummyType() - DummyType() // no-warning
26+
27+
struct HasDeprecatedMembers {
28+
@available(*, deprecated)
29+
func testDeprecatedReferencingDeprecated1(x: DummyType, y: DummyType) {
30+
x - y // no-warning
31+
}
32+
33+
@available(*, deprecated)
34+
var testDeprecatedReferencingDeprecated2: () {
35+
let x = DummyType()
36+
let y = DummyType()
37+
x - y // no-warning
38+
}
39+
40+
@available(*, deprecated)
41+
var testDeprecatedReferencingDeprecated3: () = DummyType() - DummyType() // no-warning
42+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// We don't warn if a deprecated declaration is referenced from
4+
// within another deprecated declaration.
5+
6+
struct DummyType {}
7+
8+
@available(*, deprecated, renamed: "&-")
9+
func -(x: DummyType, y: DummyType) {}
10+
11+
@available(*, deprecated)
12+
func testDeprecatedReferencingDeprecated1(x: DummyType, y: DummyType) {
13+
x - y // no-warning
14+
}
15+
16+
@available(*, deprecated)
17+
var testDeprecatedReferencingDeprecated2: () {
18+
let x = DummyType()
19+
let y = DummyType()
20+
x - y // no-warning
21+
}
22+
23+
// FIXME: This doesn't work because the file is parsed in script mode.
24+
@available(*, deprecated)
25+
var testDeprecatedReferencingDeprecated3: () = DummyType() - DummyType()
26+
// expected-warning@-1 {{'-' is deprecated}}
27+
// expected-note@-2 {{use '&-' instead}}
28+
29+
struct HasDeprecatedMembers {
30+
@available(*, deprecated)
31+
func testDeprecatedReferencingDeprecated1(x: DummyType, y: DummyType) {
32+
x - y // no-warning
33+
}
34+
35+
@available(*, deprecated)
36+
var testDeprecatedReferencingDeprecated2: () {
37+
let x = DummyType()
38+
let y = DummyType()
39+
x - y // no-warning
40+
}
41+
42+
@available(*, deprecated)
43+
var testDeprecatedReferencingDeprecated3: () = DummyType() - DummyType() // no-warning
44+
}

0 commit comments

Comments
 (0)