analyzer: Stop over-reporting 'unused' parameters
Currently analyzer is reporting optional parameters of private
functions, methods, and constructors as unused in some cases where the
function (or enclosing class for a constructor) is generic. In order to
support unused_element as a no-false-positives rule, this CL changes
the behavior to _always_ consider such parameters as used, and never
report them as unused.
The real fix is harder; I think it requires something like
ParameterMembers to be created during inference, but that is a pretty
sweeping change, and requiring TypeProviders to be passed around a lot
where they are not now.
Also move the `_hasPragmaVmEntryPoint` check to much later, as it is an
exceedingly uncommon annotation.
Bug: https://github.com/dart-lang/sdk/issues/47839
Change-Id: Ie2bc0dfbe2e4527676b1cd15045e67fb5f0f1719
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221992
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@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 0d64b24..9984e82 100644
--- a/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart
+++ b/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart
@@ -439,15 +439,12 @@
if (element.isSynthetic) {
return true;
}
- if (_hasPragmaVmEntryPoint(element)) {
- return true;
- }
if (element is LocalVariableElement ||
element is FunctionElement && !element.isStatic) {
// local variable or function
} else if (element is ParameterElement) {
var enclosingElement = element.enclosingElement;
- // Only report unused parameters of methods or functions.
+ // Only report unused parameters of constructors, methods, and functions.
if (enclosingElement is! ConstructorElement &&
enclosingElement is! FunctionElement &&
enclosingElement is! MethodElement) {
@@ -457,7 +454,22 @@
if (!element.isOptional) {
return true;
}
+ if (enclosingElement is ConstructorElement &&
+ enclosingElement.enclosingElement.typeParameters.isNotEmpty) {
+ // There is an issue matching arguments of instance creation
+ // expressions for generic classes with parameters, so for now,
+ // consider every parameter of a constructor of a generic class
+ // "used". See https://github.com/dart-lang/sdk/issues/47839.
+ return true;
+ }
if (enclosingElement is ExecutableElement) {
+ if (enclosingElement.typeParameters.isNotEmpty) {
+ // There is an issue matching arguments of generic function
+ // invocations with parameters, so for now, consider every parameter
+ // of a generic function "used". See
+ // https://github.com/dart-lang/sdk/issues/47839.
+ return true;
+ }
if (_isPubliclyAccessible(enclosingElement)) {
return true;
}
@@ -470,6 +482,9 @@
return true;
}
}
+ if (_hasPragmaVmEntryPoint(element)) {
+ return true;
+ }
return _usedElements.elements.contains(element);
}
diff --git a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
index b2827a4..c377ca0 100644
--- a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/src/dart/error/hint_codes.dart';
+import 'package:test/expect.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
@@ -1699,6 +1700,37 @@
@reflectiveTest
class UnusedElementWithNullSafetyTest extends PubPackageResolutionTest {
+ test_optionalParameter_isUsed_genericConstructor() async {
+ await assertNoErrorsInCode('''
+class C<T> {
+ C._([int? x]);
+}
+void foo() {
+ C._(7);
+}
+''');
+ }
+
+ test_optionalParameter_isUsed_genericFunction() async {
+ await assertNoErrorsInCode('''
+void _f<T>([int? x]) {}
+void foo() {
+ _f(7);
+}
+''');
+ }
+
+ test_optionalParameter_isUsed_genericMethod() async {
+ await assertNoErrorsInCode('''
+class C {
+ void _m<T>([int? x]) {}
+}
+void foo() {
+ C()._m(7);
+}
+''');
+ }
+
test_optionalParameter_isUsed_overrideRequiredNamed() async {
await assertNoErrorsInCode(r'''
class A {
@@ -1711,6 +1743,49 @@
''');
}
+ @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/47839')
+ test_optionalParameter_notUsed_genericConstructor() async {
+ // TODO(srawlins): Change to assertErrorsInCode when this is fixed.
+ addTestFile('''
+class C<T> {
+ C._([int? x]);
+}
+void foo() {
+ C._();
+}
+''');
+ await resolveTestFile();
+ expect(result.errors, isNotEmpty);
+ }
+
+ @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/47839')
+ test_optionalParameter_notUsed_genericFunction() async {
+ // TODO(srawlins): Change to assertErrorsInCode when this is fixed.
+ addTestFile('''
+void _f<T>([int? x]) {}
+void foo() {
+ _f();
+}
+''');
+ await resolveTestFile();
+ expect(result.errors, isNotEmpty);
+ }
+
+ @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/47839')
+ test_optionalParameter_notUsed_genericMethod() async {
+ // TODO(srawlins): Change to assertErrorsInCode when this is fixed.
+ addTestFile('''
+class C {
+ void _m<T>([int? x]) {}
+}
+void foo() {
+ C()._m();
+}
+''');
+ await resolveTestFile();
+ expect(result.errors, isNotEmpty);
+ }
+
test_typeAlias_interfaceType_isUsed_typeName_isExpression() async {
await assertNoErrorsInCode(r'''
typedef _A = List<int>;