Skip to content

Commit 6b1a3d3

Browse files
srawlinsCommit Queue
authored andcommitted
analyzer: Report omitted args for Deprecated.optional parameters in redirected constructor
Change-Id: I733f9436b924a23d60a0b2657c6330e2adc59098 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/457120 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Paul Berry <[email protected]>
1 parent 5825a34 commit 6b1a3d3

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class DeprecatedFunctionalityVerifier {
3636

3737
void constructorDeclaration(ConstructorDeclaration node) {
3838
_checkForDeprecatedOptionalSuperParameters(node);
39+
_checkForDeprecatedOptionalRedirectedParameters(node);
3940

4041
// Check redirectiong constructor invocations in the initializer list.
4142
for (var redirectingConstructorInvocation
@@ -223,6 +224,46 @@ class DeprecatedFunctionalityVerifier {
223224
}
224225
}
225226

227+
void _checkForDeprecatedOptionalRedirectedParameters(
228+
ConstructorDeclaration node,
229+
) {
230+
if (node.redirectedConstructor?.element case var redirectedConstructor?) {
231+
var SourceRange(offset: errorOffset, length: errorLength) =
232+
node.errorRange;
233+
var positionalArgumentCount = node.parameters.parameters
234+
.where((p) => p.isPositional)
235+
.length;
236+
var namedArgumentNames = node.parameters.parameters
237+
.where((p) => p.isNamed)
238+
.map((p) => p.name?.lexeme)
239+
.nonNulls
240+
.toList();
241+
var redirectedConstructorPositionalParameterCount = 0;
242+
for (var parameter in redirectedConstructor.formalParameters) {
243+
if (parameter.isPositional) {
244+
redirectedConstructorPositionalParameterCount++;
245+
}
246+
if (!parameter.isOptional) continue;
247+
if (!parameter.isDeprecatedWithKind('optional')) continue;
248+
if (parameter.isPositional) {
249+
if (redirectedConstructorPositionalParameterCount <=
250+
positionalArgumentCount) {
251+
continue;
252+
}
253+
} else {
254+
if (namedArgumentNames.contains(parameter.name)) continue;
255+
}
256+
257+
_diagnosticReporter.atOffset(
258+
offset: errorOffset,
259+
length: errorLength,
260+
diagnosticCode: WarningCode.deprecatedOptional,
261+
arguments: [parameter.name ?? '<unknown>'],
262+
);
263+
}
264+
}
265+
}
266+
226267
void _checkForDeprecatedOptionalSuperParameters(ConstructorDeclaration node) {
227268
var superConstructorInvocations = node.initializers
228269
.whereType<SuperConstructorInvocation>();

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

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,32 @@ void g() {
5959
''');
6060
}
6161

62+
test_argumentGiven_redirectedFromFactory_parameter_named() async {
63+
await assertNoErrorsInCode(r'''
64+
class C {
65+
C();
66+
factory C.two({int? p}) = D;
67+
}
68+
69+
class D extends C {
70+
D({@Deprecated.optional() int? p});
71+
}
72+
''');
73+
}
74+
75+
test_argumentGiven_redirectedFromFactory_parameter_positional() async {
76+
await assertNoErrorsInCode(r'''
77+
class C {
78+
C();
79+
factory C.two([int? p]) = D;
80+
}
81+
82+
class D extends C {
83+
D([@Deprecated.optional() int? p]);
84+
}
85+
''');
86+
}
87+
6288
test_argumentGiven_superInvocation() async {
6389
await assertNoErrorsInCode(r'''
6490
class C {
@@ -276,6 +302,67 @@ class C {
276302
);
277303
}
278304

305+
test_argumentOmitted_redirectedFromFactory_parameter_named() async {
306+
await assertErrorsInCode(
307+
r'''
308+
class C {
309+
C();
310+
factory C.two() = D;
311+
}
312+
313+
class D extends C {
314+
D({@Deprecated.optional() int? p});
315+
}
316+
''',
317+
[error(WarningCode.deprecatedOptional, 27, 5)],
318+
);
319+
}
320+
321+
test_argumentOmitted_redirectedFromFactory_parameter_positional() async {
322+
await assertErrorsInCode(
323+
r'''
324+
class C {
325+
C();
326+
factory C.two() = D;
327+
}
328+
329+
class D extends C {
330+
D([@Deprecated.optional() int? p]);
331+
}
332+
''',
333+
[error(WarningCode.deprecatedOptional, 27, 5)],
334+
);
335+
}
336+
337+
test_argumentOmitted_redirectedIndirectlyFromFactory_parameter_named() async {
338+
// In this test, we ensure that no warning is reported for `C.two`. That is,
339+
// we do not report indirect deprecation issues with redirecting factory
340+
// constructors. The signature of `C.two` cannot be atomically adjusted to
341+
// prepare for the new signature in `E.new`. The only way to prepare `C.two`
342+
// is to simultaneously prepare `D.two`, and preparing `C.two` will either
343+
// produce a new error or warning at `C.two` (if the signature is adjusted),
344+
// or will make `C.two` indirectly compliant (like if `D.two` is changed to
345+
// redirect differently, or not redirect, etc.).
346+
await assertErrorsInCode(
347+
r'''
348+
class C {
349+
C();
350+
factory C.two() = D.two;
351+
}
352+
353+
class D extends C {
354+
D();
355+
factory D.two() = E;
356+
}
357+
358+
class E extends D {
359+
E({@Deprecated.optional() int? p});
360+
}
361+
''',
362+
[error(WarningCode.deprecatedOptional, 84, 5)],
363+
);
364+
}
365+
279366
test_argumentOmitted_superInvocation() async {
280367
await assertErrorsInCode(
281368
r'''

0 commit comments

Comments
 (0)