Elements. Migrate CreateMethod.

Change-Id: I7e631dd63baf74060a94bd413f5ccdd527fd21f1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/387352
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov
2024-09-30 16:56:50 +00:00
committed by Commit Queue
parent 4038b97713
commit 3c5bdb63cf
12 changed files with 310 additions and 39 deletions
@@ -281,6 +281,7 @@ lib/src/services/correction/dart/create_field.dart
lib/src/services/correction/dart/create_function.dart
lib/src/services/correction/dart/create_getter.dart
lib/src/services/correction/dart/create_local_variable.dart
lib/src/services/correction/dart/create_method.dart
lib/src/services/correction/dart/create_method_or_function.dart
lib/src/services/correction/dart/create_no_such_method.dart
lib/src/services/correction/dart/create_parameter.dart
@@ -8,7 +8,6 @@ import 'package:analysis_server/src/services/correction/util.dart';
import 'package:analysis_server/src/utilities/extensions/ast.dart';
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
@@ -6,7 +6,6 @@ import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/services/correction/util.dart';
import 'package:analysis_server/src/utilities/extensions/ast.dart';
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
@@ -6,7 +6,8 @@ import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:analyzer_plugin/utilities/range_factory.dart';
@@ -59,27 +60,31 @@ class CreateMethod extends ResolvedCorrectionProducer {
return;
}
var classElement = classDecl.declaredElement!;
var classElement = classDecl.declaredFragment!.element;
var missingEquals = memberDecl is FieldDeclaration ||
(memberDecl as MethodDeclaration).name.lexeme == 'hashCode';
await builder.addDartFileEdit(file, (fileBuilder) {
fileBuilder.insertIntoUnitMember(classDecl, (builder) {
ExecutableElement? element;
ExecutableElement2? element;
if (missingEquals) {
_memberName = '==';
element = classElement.lookUpInheritedMethod(
_memberName, classElement.library);
element = inheritanceManager.getInherited4(
classElement,
Name.forLibrary(classElement.library2, _memberName),
);
} else {
_memberName = 'hashCode';
element = classElement.lookUpInheritedConcreteGetter(
_memberName, classElement.library);
element = inheritanceManager.getInherited4(
classElement,
Name.forLibrary(classElement.library2, _memberName),
);
}
if (element == null) {
return;
}
builder.writeOverride(element, invokeSuper: true);
builder.writeOverride2(element, invokeSuper: true);
});
});
}
@@ -91,31 +96,30 @@ class CreateMethod extends ResolvedCorrectionProducer {
_memberName = (node as SimpleIdentifier).name;
var invocation = node.parent as MethodInvocation;
// Prepare environment.
Element? targetElement;
Fragment? targetFragment;
var staticModifier = false;
CompilationUnitMember? targetNode;
var target = invocation.realTarget;
if (target is ExtensionOverride) {
targetElement = target.element;
if (targetElement is ExtensionElement) {
targetNode = await getExtensionDeclaration(targetElement);
targetFragment = target.element2.firstFragment;
if (targetFragment is ExtensionFragment) {
targetNode = await getExtensionDeclaration2(targetFragment);
if (targetNode == null) {
return;
}
}
} else if (target is Identifier &&
target.staticElement is ExtensionElement) {
targetElement = target.staticElement;
if (targetElement is ExtensionElement) {
targetNode = await getExtensionDeclaration(targetElement);
} else if (target is Identifier && target.element is ExtensionElement2) {
targetFragment = (target.element as ExtensionElement2).firstFragment;
if (targetFragment is ExtensionFragment) {
targetNode = await getExtensionDeclaration2(targetFragment);
if (targetNode == null) {
return;
}
}
staticModifier = true;
} else if (target == null) {
targetElement = unit.declaredElement;
targetFragment = unit.declaredFragment;
var enclosingMember = node.thisOrAncestorOfType<ClassMember>();
if (enclosingMember == null) {
// If the undefined identifier isn't inside a class member, then it
@@ -128,40 +132,43 @@ class CreateMethod extends ResolvedCorrectionProducer {
staticModifier = inStaticContext;
}
} else {
var targetClassElement = getTargetInterfaceElement(target);
var targetClassElement = getTargetInterfaceElement2(target);
if (targetClassElement == null) {
return;
}
targetElement = targetClassElement;
if (targetClassElement.library.isInSdk) {
targetFragment = targetClassElement.firstFragment;
if (targetClassElement.library2.isInSdk) {
return;
}
// Prepare target ClassDeclaration.
if (targetClassElement is MixinElement) {
targetNode = await getMixinDeclaration(targetClassElement);
} else if (targetClassElement is ClassElement) {
targetNode = await getClassDeclaration(targetClassElement);
} else if (targetClassElement is ExtensionTypeElement) {
targetNode = await getExtensionTypeDeclaration(targetClassElement);
if (targetClassElement is MixinElement2) {
var fragment = targetClassElement.firstFragment;
targetNode = await getMixinDeclaration2(fragment);
} else if (targetClassElement is ClassElement2) {
var fragment = targetClassElement.firstFragment;
targetNode = await getClassDeclaration2(fragment);
} else if (targetClassElement is ExtensionTypeElement2) {
var fragment = targetClassElement.firstFragment;
targetNode = await getExtensionTypeDeclaration2(fragment);
}
if (targetNode == null) {
return;
}
// Maybe static.
if (target is Identifier) {
staticModifier = target.staticElement?.kind == ElementKind.CLASS ||
target.staticElement?.kind == ElementKind.EXTENSION_TYPE ||
target.staticElement?.kind == ElementKind.MIXIN;
staticModifier = target.element?.kind == ElementKind.CLASS ||
target.element?.kind == ElementKind.EXTENSION_TYPE ||
target.element?.kind == ElementKind.MIXIN;
}
// Use different utils.
var targetPath = targetClassElement.source.fullName;
var targetPath = targetFragment.libraryFragment.source.fullName;
var targetResolveResult =
await unitResult.session.getResolvedUnit(targetPath);
if (targetResolveResult is! ResolvedUnitResult) {
return;
}
}
var targetSource = targetElement?.source;
var targetSource = targetFragment?.libraryFragment.source;
if (targetSource == null) {
return;
}
@@ -7,7 +7,6 @@ import 'package:analysis_server/src/services/correction/util.dart';
import 'package:analysis_server/src/utilities/extensions/ast.dart';
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
@@ -10,8 +10,6 @@ import 'package:analysis_server/src/services/correction/fix/data_driven/transfor
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_manager.dart';
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
import 'package:analyzer/dart/element/element.dart'
show DirectiveUriWithRelativeUri;
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
@@ -434,6 +434,18 @@ abstract class ResolvedCorrectionProducer
return null;
}
/// Returns the extension declaration for the given [fragment], or `null` if
/// there is no such extension.
Future<ExtensionDeclaration?> getExtensionDeclaration2(
ExtensionFragment fragment) async {
var result = await sessionHelper.getElementDeclaration2(fragment);
var node = result?.node;
if (node is ExtensionDeclaration) {
return node;
}
return null;
}
/// Returns the extension type for the given [element], or `null` if there
/// is no such extension type.
Future<ExtensionTypeDeclaration?> getExtensionTypeDeclaration(
@@ -446,6 +458,18 @@ abstract class ResolvedCorrectionProducer
return null;
}
/// Returns the extension type for the given [fragment], or `null` if there
/// is no such extension type.
Future<ExtensionTypeDeclaration?> getExtensionTypeDeclaration2(
ExtensionTypeFragment fragment) async {
var result = await sessionHelper.getElementDeclaration2(fragment);
var node = result?.node;
if (node is ExtensionTypeDeclaration) {
return node;
}
return null;
}
/// Returns the mixin declaration for the given [element], or `null` if there
/// is no such mixin.
Future<MixinDeclaration?> getMixinDeclaration(MixinElement element) async {
+16 -1
View File
@@ -66,7 +66,16 @@ import 'package:analyzer/source/source.dart';
import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:pub_semver/pub_semver.dart';
export 'package:analyzer/dart/element/element.dart' show DirectiveUriWithSource;
export 'package:analyzer/dart/element/element.dart'
show
DirectiveUri,
DirectiveUriWithLibrary,
DirectiveUriWithRelativeUri,
DirectiveUriWithRelativeUriString,
DirectiveUriWithSource,
DirectiveUriWithUnit,
ElementAnnotation,
ElementKind;
abstract class BindPatternVariableElement2 implements PatternVariableElement2 {}
@@ -483,6 +492,9 @@ abstract class ExecutableFragment implements FunctionTypedFragment {
abstract class ExtensionElement2 implements InstanceElement2 {
/// The type that is extended by this extension.
DartType get extendedType;
@override
ExtensionFragment get firstFragment;
}
/// The portion of an [ExtensionElement2] contributed by a single
@@ -498,6 +510,9 @@ abstract class ExtensionFragment implements InstanceFragment {
///
/// Clients may not extend, implement or mix-in this class.
abstract class ExtensionTypeElement2 implements InterfaceElement2 {
@override
ExtensionTypeFragment get firstFragment;
/// The primary constructor of this extension.
ConstructorElement2 get primaryConstructor2;
@@ -6714,6 +6714,9 @@ mixin MaybeAugmentedExtensionElementMixin on MaybeAugmentedInstanceElementMixin
@override
ExtensionElementImpl get declaration;
@override
ExtensionElementImpl get firstFragment => declaration;
@override
DartType get thisType => extendedType;
}
@@ -6733,6 +6736,9 @@ mixin MaybeAugmentedExtensionTypeElementMixin
@override
ExtensionTypeElementImpl get declaration;
@override
ExtensionTypeElementImpl get firstFragment => declaration;
@override
ConstructorElement2 get primaryConstructor2 =>
representation as ConstructorElement2;
@@ -641,6 +641,137 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
displayTextBuffer?.write(' { … }');
}
@override
void writeOverride2(
ExecutableElement2 element, {
StringBuffer? displayTextBuffer,
String? returnTypeGroupName,
bool invokeSuper = false,
bool setSelection = true,
}) {
void withCarbonCopyBuffer(void Function() f) {
_carbonCopyBuffer = displayTextBuffer;
try {
f();
} finally {
_carbonCopyBuffer = null;
}
}
void selectAllIfSetSelection(void Function() writer) =>
setSelection ? selectAll(writer) : writer();
var prefix = getIndent(1);
var prefix2 = getIndent(2);
var elementKind = element.kind;
var isGetter = elementKind == ElementKind.GETTER;
var isSetter = elementKind == ElementKind.SETTER;
var isMethod = elementKind == ElementKind.METHOD;
var isOperator = isMethod && (element as MethodElement2).isOperator;
var memberName = element.displayName;
// `@override` annotation.
writeln('@override');
write(prefix);
if (isGetter) {
writeln('// TODO: implement $memberName');
write(prefix);
}
// Return type.
var returnType = element.returnType;
if (!isSetter) {
var typeWritten = writeType2(returnType,
groupName: returnTypeGroupName, methodBeingCopied: element);
if (typeWritten) {
write(' ');
}
}
if (isGetter) {
write(Keyword.GET.lexeme);
write(' ');
} else if (isSetter) {
write(Keyword.SET.lexeme);
write(' ');
} else if (isOperator) {
write(Keyword.OPERATOR.lexeme);
write(' ');
}
// Name.
withCarbonCopyBuffer(() {
write(memberName);
});
// Parameters and body.
if (isGetter) {
if (invokeSuper) {
write(' => ');
selectAllIfSetSelection(() => write('super.$memberName'));
writeln(';');
} else {
write(' => ');
selectAllIfSetSelection(() => write('throw UnimplementedError()'));
write(';');
}
displayTextBuffer?.write(' => …');
return;
}
// Method.
var parameters = element.formalParameters;
withCarbonCopyBuffer(() {
writeTypeParameters2(element.type.typeParameters,
methodBeingCopied: element);
writeFormalParameters(parameters, methodBeingCopied: element);
});
writeln(' {');
// TO-DO comment.
write(prefix2);
write('// TODO: implement $memberName');
if (isSetter) {
if (invokeSuper) {
writeln();
write(prefix2);
selectAllIfSetSelection(
() => write('super.$memberName = ${parameters[0].name};'));
} else {
if (setSelection) selectHere();
}
} else if (returnType is VoidType) {
if (invokeSuper) {
writeln();
write(prefix2);
selectAllIfSetSelection(() {
write('super');
_writeSuperMemberInvocation2(element, memberName, parameters);
});
} else {
if (setSelection) selectHere();
}
} else {
writeln();
write(prefix2);
if (invokeSuper) {
selectAllIfSetSelection(() {
write('return super');
_writeSuperMemberInvocation2(element, memberName, parameters);
});
} else {
selectAllIfSetSelection(() => write('throw UnimplementedError();'));
}
}
writeln();
// Close method.
write(prefix);
write('}');
displayTextBuffer?.write(' { … }');
}
@override
void writeParameter(String name,
{bool isCovariant = false,
@@ -879,6 +1010,34 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
return wroteType;
}
@override
bool writeType2(
DartType? type, {
bool addSupertypeProposals = false,
String? groupName,
ExecutableElement2? methodBeingCopied,
bool required = false,
}) {
var wroteType = false;
if (type != null && type is! DynamicType) {
if (groupName != null) {
addLinkedEdit(groupName, (LinkedEditBuilder builder) {
wroteType = _writeType2(type, methodBeingCopied: methodBeingCopied);
if (wroteType && addSupertypeProposals) {
_addSuperTypeProposals(builder, type, {});
}
});
} else {
wroteType = _writeType2(type, methodBeingCopied: methodBeingCopied);
}
}
if (!wroteType && required) {
write(Keyword.VAR.lexeme);
return true;
}
return wroteType;
}
@override
void writeTypeParameter(TypeParameterElement typeParameter,
{ExecutableElement? methodBeingCopied}) {
@@ -1362,6 +1521,25 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
write(isOperator ? ';' : ');');
}
void _writeSuperMemberInvocation2(ExecutableElement2 element,
String memberName, List<FormalParameterElement> parameters) {
var isOperator = element is MethodElement2 && element.isOperator;
write(isOperator ? ' ' : '.');
write(memberName);
write(isOperator ? ' ' : '(');
for (var i = 0; i < parameters.length; i++) {
if (i > 0) {
write(', ');
}
if (parameters[i].isNamed) {
write(parameters[i].name);
write(': ');
}
write(parameters[i].name);
}
write(isOperator ? ';' : ');');
}
/// Writes the code to reference [type] in this compilation unit.
///
/// If a [methodBeingCopied] is provided, then the type parameters of that
@@ -11,6 +11,7 @@ import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/dart/element/type_system.dart';
import 'package:analyzer/source/source_range.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:meta/meta.dart';
/// The optional generator for prefix that should be used for new imports.
typedef ImportPrefixGenerator = String Function(Uri);
@@ -130,6 +131,7 @@ abstract class DartEditBuilder implements EditBuilder {
/// included in the parameter declaration.
///
/// If [isRequiredType] is `true` then the type is always written.
@experimental
void writeFormalParameter(String name,
{bool isCovariant,
bool isRequiredNamed,
@@ -147,6 +149,7 @@ abstract class DartEditBuilder implements EditBuilder {
/// types.
///
/// If [requiredTypes] is `true`, then the types are always written.
@experimental
void writeFormalParameters(Iterable<FormalParameterElement> parameters,
{ExecutableElement2? methodBeingCopied,
bool includeDefaultValues = true,
@@ -248,6 +251,22 @@ abstract class DartEditBuilder implements EditBuilder {
bool setSelection = true,
});
/// Appends a placeholder for an override of the specified inherited
/// [element].
///
/// If provided, writes a string value suitable for display (e.g., in a
/// completion popup) in the given [displayTextBuffer]. If [invokeSuper] is
/// `true`, then the corresponding `super.name()` will be added in the body.
/// If [setSelection] is `true`, then the cursor will be placed in the body of
/// the override.
@experimental
void writeOverride2(
ExecutableElement2 element, {
StringBuffer? displayTextBuffer,
bool invokeSuper = false,
bool setSelection = true,
});
/// Writes the code for a single parameter with the given [name].
///
/// If a [methodBeingCopied] is provided, then type parameters defined by that
@@ -354,6 +373,30 @@ abstract class DartEditBuilder implements EditBuilder {
ExecutableElement? methodBeingCopied,
bool required = false});
/// Writes the code for a type annotation for the given [type].
///
/// If the [type] is either `null` or represents the type `dynamic`, then the
/// behavior depends on whether a type is [required]. If [required] is `true`,
/// then the keyword `var` will be written; otherwise, nothing is written.
///
/// If the [groupName] is not `null`, then the name of the type (including
/// type parameters) will be included as a region in the linked edit group
/// with that name. If the [groupName] is not `null` and
/// [addSupertypeProposals] is `true`, then all of the supertypes of the
/// [type] will be added as suggestions for alternatives to the type name.
///
/// If a [methodBeingCopied] is provided, then type parameters defined by that
/// method are assumed to be part of what is being written and hence valid
/// types.
///
/// Returns `true` if any text was written.
@experimental
bool writeType2(DartType? type,
{bool addSupertypeProposals = false,
String? groupName,
ExecutableElement2? methodBeingCopied,
bool required = false});
/// Writes the code to declare the given [typeParameter].
///
/// The enclosing angle brackets are not automatically written.
@@ -371,6 +414,7 @@ abstract class DartEditBuilder implements EditBuilder {
/// If a [methodBeingCopied] is provided, then type parameters defined by that
/// method are assumed to be part of what is being written and hence valid
/// types.
@experimental
void writeTypeParameter2(TypeParameterElement2 typeParameter,
{ExecutableElement2? methodBeingCopied});
@@ -389,6 +433,7 @@ abstract class DartEditBuilder implements EditBuilder {
/// If a [methodBeingCopied] is provided, then type parameters defined by that
/// method are assumed to be part of what is being written and hence valid
/// types.
@experimental
void writeTypeParameters2(List<TypeParameterElement2> typeParameters,
{ExecutableElement2? methodBeingCopied});
+1 -1
View File
@@ -10,6 +10,7 @@ dependencies:
analyzer: ^6.0.0
collection: ^1.15.0
dart_style: ^2.2.1
meta: ^1.15.0
pub_semver: ^2.1.0
yaml: ^3.1.0
path: ^1.9.0
@@ -22,6 +23,5 @@ dev_dependencies:
analyzer_utilities: any
lints: any
linter: any
meta: any
test_reflective_loader: any
test: any