blob: 92e23b95d1d3e73f781ca9709e7b1ee740d07310 [file] [log] [blame]
// Copyright (c) 2021, 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/data_driven.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/change.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
import 'package:analyzer/dart/ast/ast.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/range_factory.dart';
/// The data related to an element that has been replaced by another element.
class ReplacedBy extends Change<_Data> {
/// The replacing element.
final ElementDescriptor newElement;
/// Initialize a newly created transform to describe a replacement of an old
/// element by a [newElement].
ReplacedBy({required this.newElement});
@override
void apply(DartFileEditBuilder builder, DataDrivenFix fix, _Data data) {
var referenceRange = data.referenceRange;
builder.addSimpleReplacement(referenceRange, _referenceTo(newElement));
}
@override
_Data? validate(DataDrivenFix fix) {
var node = fix.node;
if (node is SimpleIdentifier) {
var components = fix.element.components;
if (components.isEmpty) {
return null;
} else if (components.length == 1) {
if (components[0] != node.name) {
return null;
}
// We have an '<element>' pattern, so we replace the name.
return _Data(range.node(node));
}
// The element being replaced is a member in a top-level element.
var containerName = components[1];
if (components[0].isEmpty && containerName == node.name) {
// We have a '<className>()' pattern, so we replace the class name.
return _Data(range.node(node));
}
var parent = node.parent;
if (parent is MethodInvocation) {
var target = parent.target;
if (target == null) {
// We have a '<member>()' pattern, so we replace the member name.
return _Data(range.node(node));
} else if (target is SimpleIdentifier && target.name == containerName) {
// We have a '<container>.<member>()' pattern, so we replace both parts.
return _Data(range.startEnd(target, node));
} else if (target is PrefixedIdentifier) {
if (target.prefix.staticElement is PrefixElement &&
target.identifier.name == containerName) {
// We have a '<prefix>.<container>.<member>()' pattern so we leave
// the prefix while replacing the rest.
return _Data(range.startEnd(target.identifier, node));
}
// We shouldn't get here.
return null;
}
} else if (parent is PrefixedIdentifier) {
if (parent.prefix.staticElement is PrefixElement) {
// We have a '<prefix>.<topLevel>' pattern so we leave the prefix
// while replacing the rest.
return _Data(range.node(node));
}
// We have a '<container>.<member>' pattern so we replace both parts.
return _Data(range.node(parent));
} else if (parent is PropertyAccess) {
var target = parent.target;
if (target is PrefixedIdentifier) {
// We have a '<prefix>.<container>.<member>' pattern so we leave the
// prefix while replacing the rest.
return _Data(range.startEnd(target.identifier, node));
}
// We have a '<container>.<member>' pattern so we replace both parts.
return _Data(range.node(parent));
}
// We have a '<member>' pattern so we replace the member name.
return _Data(range.node(node));
} else if (node is PrefixedIdentifier) {
var parent = node.parent;
if (parent is NamedType) {
var identifier = node.identifier;
var components = fix.element.components;
if (components.length > 1 &&
components[0].isEmpty &&
components[1] == identifier.name) {
// We have a '<prefix>.<className>' pattern, so we replace only the
// class name.
return _Data(range.node(identifier));
}
}
} else if (node is ConstructorName) {
var typeName = node.type2.name;
SimpleIdentifier classNameNode;
if (typeName is SimpleIdentifier) {
classNameNode = typeName;
} else if (typeName is PrefixedIdentifier) {
classNameNode = typeName.identifier;
} else {
return null;
}
var constructorNameNode = node.name;
var constructorName = constructorNameNode?.name ?? '';
var components = fix.element.components;
if (components.length == 2 &&
constructorName == components[0] &&
classNameNode.name == components[1]) {
if (constructorNameNode != null) {
return _Data(range.startEnd(classNameNode, constructorNameNode));
}
return _Data(range.node(classNameNode));
}
}
return null;
}
String _referenceTo(ElementDescriptor element) {
var components = element.components;
if (components[0].isEmpty) {
return components[1];
}
return components.reversed.join('.');
}
}
/// The data about a reference to an element that's been replaced.
class _Data {
final SourceRange referenceRange;
_Data(this.referenceRange);
}