[DAS] Fixes create method for enums and extension types

Fixes: https://github.com/dart-lang/sdk/issues/60562
Change-Id: I8bbc92205f5900c025e9dc31110f3e1cb2343643
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/423240
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Auto-Submit: Felipe Morschel <git@fmorschel.dev>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
FMorschel
2025-04-18 16:52:11 -07:00
committed by Commit Queue
parent aab4777cf2
commit ae6bdae111
5 changed files with 200 additions and 14 deletions
@@ -150,6 +150,9 @@ class CreateMethod extends ResolvedCorrectionProducer {
} else if (targetClassElement is ExtensionTypeElement2) {
var fragment = targetClassElement.firstFragment;
targetNode = await getExtensionTypeDeclaration(fragment);
} else if (targetClassElement is EnumElement2) {
var fragment = targetClassElement.firstFragment;
targetNode = await getEnumDeclaration(fragment);
}
if (targetNode == null) {
return;
@@ -158,6 +161,7 @@ class CreateMethod extends ResolvedCorrectionProducer {
if (target is Identifier) {
staticModifier =
target.element?.kind == ElementKind.CLASS ||
target.element?.kind == ElementKind.ENUM ||
target.element?.kind == ElementKind.EXTENSION_TYPE ||
target.element?.kind == ElementKind.MIXIN;
}
@@ -36,6 +36,7 @@ class CreateMethodOrFunction extends ResolvedCorrectionProducer {
@override
Future<void> compute(ChangeBuilder builder) async {
var isStatic = false;
var nameNode = node;
if (nameNode is SimpleIdentifier) {
// prepare argument expression (to get parameter)
@@ -47,6 +48,13 @@ class CreateMethodOrFunction extends ResolvedCorrectionProducer {
if (targetType is InterfaceType) {
targetElement = targetType.element3;
argument = target.parent as Expression;
} else if (target case SimpleIdentifier(
:InterfaceElement2? element,
:Expression parent,
)) {
isStatic = true;
targetElement = element;
argument = parent;
} else {
return;
}
@@ -107,7 +115,12 @@ class CreateMethodOrFunction extends ResolvedCorrectionProducer {
}
// add proposal
if (targetElement != null) {
await _createMethod(builder, targetElement, parameterType);
await _createMethod(
builder,
targetElement,
parameterType,
isStatic: isStatic,
);
} else {
await _createFunction(builder, parameterType);
}
@@ -195,8 +208,9 @@ class CreateMethodOrFunction extends ResolvedCorrectionProducer {
Future<void> _createMethod(
ChangeBuilder builder,
InterfaceElement2 targetClassElement,
FunctionType functionType,
) async {
FunctionType functionType, {
required bool isStatic,
}) async {
var name = (node as SimpleIdentifier).name;
// prepare environment
var targetSource = targetClassElement.firstFragment.libraryFragment.source;
@@ -211,6 +225,14 @@ class CreateMethodOrFunction extends ResolvedCorrectionProducer {
var fragment = targetClassElement.firstFragment;
var node = targetNode = await getClassDeclaration(fragment);
classMembers = node?.members;
} else if (targetClassElement is ExtensionTypeElement2) {
var fragment = targetClassElement.firstFragment;
var node = targetNode = await getExtensionTypeDeclaration(fragment);
classMembers = node?.members;
} else if (targetClassElement is EnumElement2) {
var fragment = targetClassElement.firstFragment;
var node = targetNode = await getEnumDeclaration(fragment);
classMembers = node?.members;
}
if (targetNode == null || classMembers == null) {
return;
@@ -231,7 +253,7 @@ class CreateMethodOrFunction extends ResolvedCorrectionProducer {
name,
targetSource.fullName,
insertOffset,
inStaticContext,
isStatic || inStaticContext,
prefix,
sourcePrefix,
sourceSuffix,
@@ -965,6 +965,7 @@ final _builtInNonLintGenerators = <ErrorCode, List<ProducerGenerator>>{
CompileTimeErrorCode.UNDEFINED_ENUM_CONSTANT: [
AddEnumConstant.new,
ChangeTo.getterOrSetter,
CreateMethodOrFunction.new,
],
CompileTimeErrorCode.UNDEFINED_ENUM_CONSTRUCTOR_NAMED: [
CreateConstructor.new,
@@ -728,6 +728,164 @@ class A {
''');
}
Future<void> test_enum_invocation() async {
await resolveTestCode('''
enum E {
e1,
e2;
}
void test(E e) {
e.bar();
}
''');
await assertHasFix('''
enum E {
e1,
e2;
void bar() {}
}
void test(E e) {
e.bar();
}
''');
}
Future<void> test_enum_invocation_static() async {
await resolveTestCode('''
enum E {
e1,
e2;
}
void test() {
E.bar();
}
''');
await assertHasFix('''
enum E {
e1,
e2;
static void bar() {}
}
void test() {
E.bar();
}
''');
}
Future<void> test_enum_tearoff() async {
await resolveTestCode('''
enum E {
e1,
e2;
}
void g(int Function() f) {}
void test(E e) {
g(e.bar);
}
''');
await assertHasFix('''
enum E {
e1,
e2;
int bar() {
}
}
void g(int Function() f) {}
void test(E e) {
g(e.bar);
}
''');
}
Future<void> test_enum_tearoff_static() async {
await resolveTestCode('''
enum E {
e1,
e2;
}
void g(int Function() f) {}
void test() {
g(E.bar);
}
''');
await assertHasFix('''
enum E {
e1,
e2;
static int bar() {
}
}
void g(int Function() f) {}
void test() {
g(E.bar);
}
''');
}
Future<void> test_extensionType_tearoff() async {
await resolveTestCode('''
extension type E(int i) {
}
void g(int Function() f) {}
void test(E e) {
g(e.bar);
}
''');
await assertHasFix('''
extension type E(int i) {
int bar() {
}
}
void g(int Function() f) {}
void test(E e) {
g(e.bar);
}
''');
}
Future<void> test_extensionType_tearoff_static() async {
await resolveTestCode('''
extension type E(int i) {
}
void g(int Function() f) {}
void test() {
g(E.bar);
}
''');
await assertHasFix('''
extension type E(int i) {
static int bar() {
}
}
void g(int Function() f) {}
void test() {
g(E.bar);
}
''');
}
Future<void> test_functionType_argument() async {
await resolveTestCode('''
class A {
@@ -1465,14 +1623,4 @@ void f() {
}
''');
}
Future<void> test_targetIsEnum() async {
await resolveTestCode('''
enum MyEnum {A, B}
void f() {
MyEnum.foo();
}
''');
await assertNoFix();
}
}
@@ -429,6 +429,17 @@ abstract class ResolvedCorrectionProducer
return null;
}
/// Returns the class declaration for the given [fragment], or `null` if there
/// is no such class.
Future<EnumDeclaration?> getEnumDeclaration(EnumFragment fragment) async {
var result = await sessionHelper.getFragmentDeclaration(fragment);
var node = result?.node;
if (node is EnumDeclaration) {
return node;
}
return null;
}
/// Returns the extension declaration for the given [fragment], or `null` if
/// there is no such extension.
Future<ExtensionDeclaration?> getExtensionDeclaration(