blob: febb28b31d33d1e99f2bfcc36c6f3bc1824648da [file] [log] [blame]
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/source/source_range.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:analyzer_plugin/utilities/range_factory.dart';
class RemoveUnusedElement extends _RemoveUnused {
@override
FixKind get fixKind => DartFixKind.REMOVE_UNUSED_ELEMENT;
@override
Future<void> compute(DartChangeBuilder builder) async {
final sourceRanges = <SourceRange>[];
final referencedNode = node.parent;
if (referencedNode is ClassDeclaration ||
referencedNode is EnumDeclaration ||
referencedNode is FunctionDeclaration ||
referencedNode is FunctionTypeAlias ||
referencedNode is MethodDeclaration ||
referencedNode is VariableDeclaration) {
final element = referencedNode is Declaration
? referencedNode.declaredElement
: (referencedNode as NamedCompilationUnitMember).declaredElement;
final references = _findAllReferences(unit, element);
// todo (pq): consider filtering for references that are limited to within the class.
if (references.length == 1) {
var sourceRange;
if (referencedNode is VariableDeclaration) {
VariableDeclarationList parent = referencedNode.parent;
if (parent.variables.length == 1) {
sourceRange = utils.getLinesRange(range.node(parent.parent));
} else {
sourceRange = range.nodeInList(parent.variables, referencedNode);
}
} else {
sourceRange = utils.getLinesRange(range.node(referencedNode));
}
sourceRanges.add(sourceRange);
}
}
await builder.addFileEdit(file, (builder) {
for (var sourceRange in sourceRanges) {
builder.addDeletion(sourceRange);
}
});
}
}
class RemoveUnusedField extends _RemoveUnused {
@override
FixKind get fixKind => DartFixKind.REMOVE_UNUSED_FIELD;
@override
Future<void> compute(DartChangeBuilder builder) async {
final declaration = node.parent;
if (declaration is! VariableDeclaration) {
return;
}
final element = (declaration as VariableDeclaration).declaredElement;
if (element is! FieldElement) {
return;
}
final sourceRanges = <SourceRange>[];
final references = _findAllReferences(unit, element);
for (var reference in references) {
// todo (pq): consider scoping this to parent or parent.parent.
final referenceNode = reference.thisOrAncestorMatching((node) =>
node is VariableDeclaration ||
node is ExpressionStatement ||
node is ConstructorFieldInitializer ||
node is FieldFormalParameter);
if (referenceNode == null) {
return;
}
var sourceRange;
if (referenceNode is VariableDeclaration) {
VariableDeclarationList parent = referenceNode.parent;
if (parent.variables.length == 1) {
sourceRange = utils.getLinesRange(range.node(parent.parent));
} else {
sourceRange = range.nodeInList(parent.variables, referenceNode);
}
} else if (referenceNode is ConstructorFieldInitializer) {
ConstructorDeclaration cons =
referenceNode.parent as ConstructorDeclaration;
// A() : _f = 0;
if (cons.initializers.length == 1) {
sourceRange = range.endEnd(cons.parameters, referenceNode);
} else {
sourceRange = range.nodeInList(cons.initializers, referenceNode);
}
} else if (referenceNode is FieldFormalParameter) {
FormalParameterList params =
referenceNode.parent as FormalParameterList;
if (params.parameters.length == 1) {
sourceRange =
range.endStart(params.leftParenthesis, params.rightParenthesis);
} else {
sourceRange = range.nodeInList(params.parameters, referenceNode);
}
} else {
sourceRange = utils.getLinesRange(range.node(referenceNode));
}
sourceRanges.add(sourceRange);
}
await builder.addFileEdit(file, (builder) {
for (var sourceRange in sourceRanges) {
builder.addDeletion(sourceRange);
}
});
}
}
class _ElementReferenceCollector extends RecursiveAstVisitor<void> {
final Element element;
final List<SimpleIdentifier> references = [];
_ElementReferenceCollector(this.element);
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
final staticElement = node.staticElement;
if (staticElement == element) {
references.add(node);
} else if (staticElement is PropertyAccessorElement) {
if (staticElement.variable == element) {
references.add(node);
}
} else if (staticElement is FieldFormalParameterElement) {
if (staticElement.field == element) {
references.add(node);
}
}
}
}
abstract class _RemoveUnused extends CorrectionProducer {
List<SimpleIdentifier> _findAllReferences(AstNode root, Element element) {
var collector = _ElementReferenceCollector(element);
root.accept(collector);
return collector.references;
}
}