blob: 5e7e7bc59a878b0601830614437e322ea59c4172 [file] [log] [blame]
// Copyright (c) 2014, 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/protocol_server.dart' hide Element;
import 'package:analysis_server/src/services/correction/status.dart';
import 'package:analysis_server/src/services/correction/util.dart';
import 'package:analysis_server/src/services/refactoring/naming_conventions.dart';
import 'package:analysis_server/src/services/refactoring/refactoring.dart';
import 'package:analysis_server/src/services/refactoring/refactoring_internal.dart';
import 'package:analysis_server/src/services/refactoring/rename.dart';
import 'package:analysis_server/src/services/search/hierarchy.dart';
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/analysis/session_helper.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer_plugin/utilities/range_factory.dart';
/// A [Refactoring] for renaming [ConstructorElement]s.
class RenameConstructorRefactoringImpl extends RenameRefactoringImpl {
final AnalysisSession session;
RenameConstructorRefactoringImpl(
RefactoringWorkspace workspace, this.session, ConstructorElement element)
: super(workspace, element);
@override
ConstructorElement get element => super.element as ConstructorElement;
@override
String get refactoringName {
return 'Rename Constructor';
}
@override
Future<RefactoringStatus> checkFinalConditions() {
var result = RefactoringStatus();
return Future.value(result);
}
@override
RefactoringStatus checkNewName() {
var result = super.checkNewName();
result.addStatus(validateConstructorName(newName));
_analyzePossibleConflicts(result);
return result;
}
@override
Future<void> fillChange() async {
// prepare references
var matches = await searchEngine.searchReferences(element);
var references = getSourceReferences(matches);
// update references
for (var reference in references) {
String replacement;
if (newName.isNotEmpty) {
replacement = '.$newName';
} else {
replacement = reference.isConstructorTearOff ? '.new' : '';
}
if (reference.isInvocationByEnumConstantWithoutArguments) {
replacement += '()';
}
reference.addEdit(change, replacement);
}
// Update the declaration.
if (element.isSynthetic) {
await _replaceSynthetic();
} else {
doSourceChange_addSourceEdit(
change,
element.source,
newSourceEdit_range(
_declarationNameRange(),
newName.isNotEmpty ? '.$newName' : '',
),
);
}
}
void _analyzePossibleConflicts(RefactoringStatus result) {
var parentClass = element.enclosingElement;
// Check if the "newName" is the name of the enclosing class.
if (parentClass.name == newName) {
result.addError('The constructor should not have the same name '
'as the name of the enclosing class.');
}
// check if there are members with "newName" in the same ClassElement
for (var newNameMember in getChildren(parentClass, newName)) {
var message = format("Class '{0}' already declares {1} with name '{2}'.",
parentClass.displayName, getElementKindName(newNameMember), newName);
result.addError(message, newLocation_fromElement(newNameMember));
}
}
SourceRange _declarationNameRange() {
var offset = element.periodOffset;
var nameEnd = element.nameEnd!;
if (offset != null) {
return range.startOffsetEndOffset(offset, nameEnd);
} else {
return SourceRange(nameEnd, 0);
}
}
Future<void> _replaceSynthetic() async {
var classElement = element.enclosingElement;
var result = await AnalysisSessionHelper(session)
.getElementDeclaration(classElement);
if (result == null) {
return;
}
var resolvedUnit = result.resolvedUnit;
if (resolvedUnit == null) {
return;
}
var node = result.node;
if (node is ClassDeclaration) {
var utils = CorrectionUtils(resolvedUnit);
var location = utils.prepareNewConstructorLocation(session, node);
if (location == null) {
return;
}
var header = '${classElement.name}.$newName();';
doSourceChange_addElementEdit(
change,
classElement,
SourceEdit(
location.offset,
0,
location.prefix + header + location.suffix,
),
);
} else if (node is EnumDeclaration) {
var utils = CorrectionUtils(resolvedUnit);
var location = utils.prepareEnumNewConstructorLocation(node);
if (location == null) {
return;
}
var header = 'const ${classElement.name}.$newName();';
doSourceChange_addElementEdit(
change,
classElement,
SourceEdit(
location.offset,
0,
location.prefix + header + location.suffix,
),
);
}
}
}