Skip to content

Commit e417270

Browse files
srawlinsCommit Queue
authored andcommitted
analyzer: Check implicit super-constructor use in ElementUsageDetector
When a generative, non-redirecting constructor does not feature a super-initiailizer, then the supertype's unnamed constructor is called implicitly. If that super-constructor is marked for usage (e.g. marked with `@Deprecated` or `@experimental`), then we should report any implicit call as usage. Change-Id: I2c5ae31720a6024b0ff357db686b6d85d2ccdf58 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/455040 Reviewed-by: Paul Berry <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent 9b605e6 commit e417270

File tree

3 files changed

+78
-0
lines changed

3 files changed

+78
-0
lines changed

pkg/analyzer/lib/src/error/best_practices_verifier.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
316316
for (var v in _elementUsageFrontierDetectors) {
317317
v.pushElement(element);
318318
}
319+
for (var v in _elementUsageFrontierDetectors) {
320+
v.constructorDeclaration(node);
321+
}
319322

320323
// TODO(srawlins): Use _elementUsageFrontierDetectors to detect
321324
// `@Deprecated` or `@experimental` parameters in a redirecting factory

pkg/analyzer/lib/src/error/element_usage_detector.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ class ElementUsageDetector<TagInfo extends Object> {
113113
var invokeClass = invokeType.element;
114114
displayName = '${invokeClass.name}.${element.displayName}';
115115
}
116+
117+
// TODO(srawlins): Consider `node` being a `ConstructorDeclaration`, and use
118+
// `ConstructorDeclaration.errorRange` here. This would stray from the API
119+
// of passing a SyntacticEntity here.
120+
116121
elementUsageReporter.report(
117122
errorEntity,
118123
displayName,
@@ -121,6 +126,23 @@ class ElementUsageDetector<TagInfo extends Object> {
121126
);
122127
}
123128

129+
void constructorDeclaration(ConstructorDeclaration node) {
130+
// Check usage of any implicit super-constructor call.
131+
// There is only an implicit super-constructor if:
132+
// * this is not a factory constructor,
133+
// * there is no redirecting constructor invocation, and
134+
// * there is no explicit super constructor invocation.
135+
if (node.factoryKeyword != null) return;
136+
var hasConstructorInvocation = node.initializers.any(
137+
(i) =>
138+
i is SuperConstructorInvocation ||
139+
i is RedirectingConstructorInvocation,
140+
);
141+
if (hasConstructorInvocation) return;
142+
143+
checkUsage(node.declaredFragment!.element.superConstructor, node);
144+
}
145+
124146
void constructorName(ConstructorName node) {
125147
checkUsage(node.element, node);
126148
}

pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1947,6 +1947,41 @@ import '$externalLibUri' show A;
19471947
);
19481948
}
19491949

1950+
test_superConstructor_factoryConstructor() async {
1951+
await assertNoErrorsInCode2(
1952+
externalCode: r'''
1953+
class A {
1954+
@deprecated
1955+
A();
1956+
A.two();
1957+
}
1958+
''',
1959+
code: r'''
1960+
class B extends A {
1961+
factory B() => B.two();
1962+
B.two() : super.two();
1963+
}
1964+
''',
1965+
);
1966+
}
1967+
1968+
test_superConstructor_implicitCall() async {
1969+
await assertErrorsInCode2(
1970+
externalCode: r'''
1971+
class A {
1972+
@deprecated
1973+
A();
1974+
}
1975+
''',
1976+
code: r'''
1977+
class B extends A {
1978+
B();
1979+
}
1980+
''',
1981+
[error(HintCode.deprecatedMemberUse, 51, 4)],
1982+
);
1983+
}
1984+
19501985
test_superConstructor_namedConstructor() async {
19511986
await assertErrorsInCode2(
19521987
externalCode: r'''
@@ -1971,6 +2006,24 @@ class B extends A {
19712006
);
19722007
}
19732008

2009+
test_superConstructor_redirectingConstructor() async {
2010+
await assertNoErrorsInCode2(
2011+
externalCode: r'''
2012+
class A {
2013+
@deprecated
2014+
A();
2015+
A.two();
2016+
}
2017+
''',
2018+
code: r'''
2019+
class B extends A {
2020+
B() : this.two();
2021+
B.two() : super.two();
2022+
}
2023+
''',
2024+
);
2025+
}
2026+
19742027
test_superConstructor_unnamedConstructor() async {
19752028
await assertErrorsInCode2(
19762029
externalCode: r'''

0 commit comments

Comments
 (0)