Update UNUSED_ELEMENT_PARAMETER to support redirected factory constructors.
Change-Id: Id296fbce50316f872b505f034a58608d54b75be3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/226962
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart b/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart
index c7a1de2..8a93e39 100644
--- a/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart
+++ b/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:collection';
+import 'dart:math' as math;
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
@@ -79,6 +80,27 @@
}
@override
+ void visitConstructorDeclaration(ConstructorDeclaration node) {
+ var element = node.declaredElement!;
+ var redirectedConstructor = node.redirectedConstructor;
+ if (redirectedConstructor != null) {
+ var redirectedElement = redirectedConstructor.staticElement;
+ if (redirectedElement != null) {
+ // TODO(scheglov) Only if not _isPubliclyAccessible
+ _matchParameters(
+ element.parameters,
+ redirectedElement.parameters,
+ (first, second) {
+ usedElements.addElement(second);
+ },
+ );
+ }
+ }
+
+ super.visitConstructorDeclaration(node);
+ }
+
+ @override
void visitFunctionDeclaration(FunctionDeclaration node) {
var enclosingExecOld = _enclosingExec;
try {
@@ -170,6 +192,10 @@
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
+ // TODO(scheglov) Remove after https://dart-review.googlesource.com/c/sdk/+/226960
+ if (node.parent is FieldFormalParameter) {
+ return;
+ }
if (node.inDeclarationContext()) {
return;
}
@@ -341,6 +367,51 @@
// OK
return true;
}
+
+ /// Invokes [f] for corresponding positional and named parameters.
+ /// Ignores parameters that don't have a corresponding pair.
+ /// TODO(scheglov) There might be a better place for this function.
+ static void _matchParameters(
+ List<ParameterElement> firstList,
+ List<ParameterElement> secondList,
+ void Function(ParameterElement first, ParameterElement second) f,
+ ) {
+ Map<String, ParameterElement>? firstNamed;
+ Map<String, ParameterElement>? secondNamed;
+ var firstPositional = <ParameterElement>[];
+ var secondPositional = <ParameterElement>[];
+ for (var element in firstList) {
+ if (element.isNamed) {
+ (firstNamed ??= {})[element.name] = element;
+ } else {
+ firstPositional.add(element);
+ }
+ }
+ for (var element in secondList) {
+ if (element.isNamed) {
+ (secondNamed ??= {})[element.name] = element;
+ } else {
+ secondPositional.add(element);
+ }
+ }
+
+ var positionalLength = math.min(
+ firstPositional.length,
+ secondPositional.length,
+ );
+ for (var i = 0; i < positionalLength; i++) {
+ f(firstPositional[i], secondPositional[i]);
+ }
+
+ if (firstNamed != null && secondNamed != null) {
+ for (var firstEntry in firstNamed.entries) {
+ var second = secondNamed[firstEntry.key];
+ if (second != null) {
+ f(firstEntry.value, second);
+ }
+ }
+ }
+ }
}
/// Instances of the class [UnusedLocalElementsVerifier] traverse an AST
diff --git a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
index b9104c2..fb070cc 100644
--- a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
@@ -1858,6 +1858,98 @@
expect(result.errors, isNotEmpty);
}
+ test_parameter_optionalNamed_fieldFormal_isUsed_constructorInvocation() async {
+ await assertNoErrorsInCode(r'''
+class _A {
+ final int? f;
+ _A({this.f});
+}
+f() => _A(f: 0);
+''');
+ }
+
+ test_parameter_optionalNamed_fieldFormal_isUsed_factoryRedirect() async {
+ await assertNoErrorsInCode(r'''
+class _A {
+ final int? f;
+ _A({this.f});
+ factory _A.named({int? f}) = _A;
+}
+f() => _A.named(f: 0);
+''');
+ }
+
+ test_parameter_optionalNamed_fieldFormal_notUsed() async {
+ await assertErrorsInCode(r'''
+class _A {
+ final int? f;
+ _A({this.f});
+}
+f() => _A();
+''', [
+ error(HintCode.UNUSED_ELEMENT_PARAMETER, 38, 1),
+ ]);
+ }
+
+ test_parameter_optionalNamed_fieldFormal_notUsed_factoryRedirect() async {
+ await assertErrorsInCode(r'''
+class _A {
+ final int? f;
+ _A({this.f});
+ factory _A.named() = _A;
+}
+f() => _A.named();
+''', [
+ error(HintCode.UNUSED_ELEMENT_PARAMETER, 38, 1),
+ ]);
+ }
+
+ test_parameter_optionalPositional_fieldFormal_isUsed_constructorInvocation() async {
+ await assertNoErrorsInCode(r'''
+class _A {
+ final int? f;
+ _A([this.f]);
+}
+f() => _A(0);
+''');
+ }
+
+ test_parameter_optionalPositional_fieldFormal_isUsed_factoryRedirect() async {
+ await assertNoErrorsInCode(r'''
+class _A {
+ final int? f;
+ _A([this.f]);
+ factory _A.named([int a]) = _A;
+}
+f() => _A.named(0);
+''');
+ }
+
+ test_parameter_optionalPositional_fieldFormal_notUsed() async {
+ await assertErrorsInCode(r'''
+class _A {
+ final int? f;
+ _A([this.f]);
+}
+f() => _A();
+''', [
+ error(HintCode.UNUSED_ELEMENT_PARAMETER, 38, 1),
+ ]);
+ }
+
+ test_parameter_optionalPositional_fieldFormal_notUsed_factoryRedirect() async {
+ await assertErrorsInCode(r'''
+class _A {
+ final int? f;
+ _A([this.f]);
+ factory _A.named() = _A;
+}
+f() => _A.named();
+''', [
+ error(HintCode.UNUSED_ELEMENT_PARAMETER, 38, 1),
+ ]);
+ }
+
test_typeAlias_interfaceType_isUsed_typeName_isExpression() async {
await assertNoErrorsInCode(r'''
typedef _A = List<int>;