Version 2.16.0-139.0.dev
Merge commit '69f2bae9d52895ebdf35176acbb55065a2ac15db' into 'dev'
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 9984e82..c7a1de2 100644
--- a/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart
+++ b/pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart
@@ -25,6 +25,12 @@
ClassElement? _enclosingClass;
ExecutableElement? _enclosingExec;
+ /// Non-null when the visitor is inside an [IsExpression]'s type.
+ IsExpression? _enclosingIsExpression;
+
+ /// Non-null when the visitor is inside a [VariableDeclarationList]'s type.
+ VariableDeclarationList? _enclosingVariableDeclaration;
+
GatherUsedLocalElementsVisitor(this._enclosingLibrary);
@override
@@ -114,6 +120,18 @@
}
@override
+ void visitIsExpression(IsExpression node) {
+ var enclosingIsExpressionOld = _enclosingIsExpression;
+ node.expression.accept(this);
+ try {
+ _enclosingIsExpression = node;
+ node.type.accept(this);
+ } finally {
+ _enclosingIsExpression = enclosingIsExpressionOld;
+ }
+ }
+
+ @override
void visitMethodDeclaration(MethodDeclaration node) {
var enclosingExecOld = _enclosingExec;
try {
@@ -178,21 +196,22 @@
usedElements.addElement(element);
}
} else {
- _useIdentifierElement(node, node.readElement);
- _useIdentifierElement(node, node.writeElement);
- _useIdentifierElement(node, node.staticElement);
var parent = node.parent!;
- // If [node] is a method tear-off, assume all parameters are used.
+ _useIdentifierElement(node, node.readElement, parent: parent);
+ _useIdentifierElement(node, node.writeElement, parent: parent);
+ _useIdentifierElement(node, node.staticElement, parent: parent);
+ var grandparent = parent.parent;
+ // If [node] is a tear-off, assume all parameters are used.
var functionReferenceIsCall =
(element is ExecutableElement && parent is MethodInvocation) ||
// named constructor
(element is ConstructorElement &&
parent is ConstructorName &&
- parent.parent is InstanceCreationExpression) ||
+ grandparent is InstanceCreationExpression) ||
// unnamed constructor
(element is ClassElement &&
- parent.parent is ConstructorName &&
- parent.parent!.parent is InstanceCreationExpression);
+ grandparent is ConstructorName &&
+ grandparent.parent is InstanceCreationExpression);
if (element is ExecutableElement &&
isIdentifierRead &&
!functionReferenceIsCall) {
@@ -224,6 +243,19 @@
}
}
+ @override
+ void visitVariableDeclarationList(VariableDeclarationList node) {
+ node.metadata.accept(this);
+ var enclosingVariableDeclarationOld = _enclosingVariableDeclaration;
+ try {
+ _enclosingVariableDeclaration = node;
+ node.type?.accept(this);
+ } finally {
+ _enclosingVariableDeclaration = enclosingVariableDeclarationOld;
+ }
+ node.variables.accept(this);
+ }
+
/// Add [element] as a used member and, if [element] is a setter, add its
/// corresponding getter as a used member.
void _addMemberAndCorrespondingGetter(Element element) {
@@ -236,7 +268,11 @@
}
/// Marks the [element] of [node] as used in the library.
- void _useIdentifierElement(Identifier node, Element? element) {
+ void _useIdentifierElement(
+ Identifier node,
+ Element? element, {
+ required AstNode parent,
+ }) {
if (element == null) {
return;
}
@@ -252,17 +288,17 @@
return;
}
// Ignore places where the element is not actually used.
- if (node.parent is NamedType) {
+ if (parent is NamedType) {
if (element is ClassElement) {
- AstNode parent2 = node.parent!.parent!;
- if (parent2 is IsExpression) {
- return;
- }
- if (parent2 is VariableDeclarationList) {
+ var enclosingVariableDeclaration = _enclosingVariableDeclaration;
+ if (enclosingVariableDeclaration != null) {
// If it's a field's type, it still counts as used.
- if (parent2.parent is! FieldDeclaration) {
+ if (enclosingVariableDeclaration.parent is! FieldDeclaration) {
return;
}
+ } else if (_enclosingIsExpression != null) {
+ // An interface type found in an `is` expression is not used.
+ return;
}
}
}
diff --git a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
index c377ca0..b9104c2 100644
--- a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
@@ -1700,6 +1700,78 @@
@reflectiveTest
class UnusedElementWithNullSafetyTest extends PubPackageResolutionTest {
+ test_class_isUsed_isExpression_expression() async {
+ await assertNoErrorsInCode('''
+class _A {}
+void f(Object p) {
+ if (_A() is int) {
+ }
+}
+''');
+ }
+
+ test_class_notUsed_isExpression_typeArgument() async {
+ await assertErrorsInCode(r'''
+class _A {}
+void f(Object p) {
+ if (p is List<_A>) {
+ }
+}
+''', [
+ error(HintCode.UNUSED_ELEMENT, 6, 2),
+ ]);
+ }
+
+ test_class_notUsed_isExpression_typeInFunctionType() async {
+ await assertErrorsInCode(r'''
+class _A {}
+void f(Object p) {
+ if (p is void Function(_A)) {
+ }
+}
+''', [
+ error(HintCode.UNUSED_ELEMENT, 6, 2),
+ ]);
+ }
+
+ test_class_notUsed_isExpression_typeInTypeParameter() async {
+ await assertErrorsInCode(r'''
+class _A {}
+void f(Object p) {
+ if (p is void Function<T extends _A>()) {
+ }
+}
+''', [
+ error(HintCode.UNUSED_ELEMENT, 6, 2),
+ ]);
+ }
+
+ test_class_notUsed_variableDeclaration() async {
+ await assertErrorsInCode('''
+class _A {}
+void f() {
+ _A? v;
+ print(v);
+}
+print(x) {}
+''', [
+ error(HintCode.UNUSED_ELEMENT, 6, 2),
+ ]);
+ }
+
+ test_class_notUsed_variableDeclaration_typeArgument() async {
+ await assertErrorsInCode('''
+class _A {}
+main() {
+ List<_A>? v;
+ print(v);
+}
+print(x) {}
+''', [
+ error(HintCode.UNUSED_ELEMENT, 6, 2),
+ ]);
+ }
+
test_optionalParameter_isUsed_genericConstructor() async {
await assertNoErrorsInCode('''
class C<T> {
diff --git a/tools/VERSION b/tools/VERSION
index 9a1c513..1fea6fb 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 16
PATCH 0
-PRERELEASE 138
+PRERELEASE 139
PRERELEASE_PATCH 0
\ No newline at end of file