From e1cacdc23a6bc700b61a0dabc7787f46a830c8d8 Mon Sep 17 00:00:00 2001 From: Brian Wilkerson Date: Tue, 5 May 2026 09:18:55 -0700 Subject: [PATCH] Add a fix when an extension or mixin has a primary constructor Neither is allowed to have a primary constructor, This adds a fix to remove the primary constructor. Closes https://github.com/dart-lang/sdk/issues/63073 Change-Id: I363d2fa02d18e2a63a2bfc8c64a57256f861195d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/500584 Reviewed-by: Keerti Parthasarathy Commit-Queue: Brian Wilkerson --- .../correction/dart/remove_constructor.dart | 50 +++++++++++++++ .../services/correction/error_fix_status.yaml | 4 +- .../src/services/correction/fix_internal.dart | 2 + .../fix/remove_constructor_test.dart | 63 +++++++++++++++++++ 4 files changed, 117 insertions(+), 2 deletions(-) diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_constructor.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_constructor.dart index d2097677075..b85a8759ec0 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/remove_constructor.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_constructor.dart @@ -26,6 +26,15 @@ class RemoveConstructor extends ResolvedCorrectionProducer { Future compute(ChangeBuilder builder) async { var container = _findContainer(); if (container == null) { + // If there's no container, then it must be a primary constructor. + var primary = _findPrimaryConstructor(); + if (primary != null) { + await builder.addDartFileEdit(file, (builder) { + builder.addDeletion( + range.startEnd(primary.leftParen, primary.rightParen), + ); + }); + } return; } @@ -83,6 +92,40 @@ class RemoveConstructor extends ResolvedCorrectionProducer { } return null; } + + _PrimaryConstructor? _findPrimaryConstructor() { + switch (node) { + case ExtensionDeclaration extension: + var leftParen = + extension.typeParameters?.endToken.next ?? extension.name?.next; + var rightParen = extension.onClause?.onKeyword.previous; + if (leftParen != null && + leftParen.type == TokenType.OPEN_PAREN && + rightParen != null && + rightParen.type == TokenType.CLOSE_PAREN) { + return _PrimaryConstructor( + leftParen: leftParen, + rightParen: rightParen, + ); + } + case MixinDeclaration mixin: + var leftParen = mixin.typeParameters?.endToken.next ?? mixin.name.next; + var rightParen = + mixin.onClause?.onKeyword.previous ?? + mixin.implementsClause?.implementsKeyword.previous ?? + mixin.body.beginToken.previous; + if (leftParen != null && + leftParen.type == TokenType.OPEN_PAREN && + rightParen != null && + rightParen.type == TokenType.CLOSE_PAREN) { + return _PrimaryConstructor( + leftParen: leftParen, + rightParen: rightParen, + ); + } + } + return null; + } } class _Container { @@ -91,3 +134,10 @@ class _Container { _Container({required this.leftBracket, required this.members}); } + +class _PrimaryConstructor { + final Token leftParen; + final Token rightParen; + + _PrimaryConstructor({required this.leftParen, required this.rightParen}); +} diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml index 4193d110c26..33f57c803fa 100644 --- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml +++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml @@ -715,7 +715,7 @@ extension_override_with_cascade: extension_override_without_access: status: noFix extension_primary_constructor: - status: needsEvaluation + status: hasFix extension_type_constructor_with_super_formal_parameter: status: needsFix notes: |- @@ -1141,7 +1141,7 @@ mixin_of_type_alias_expands_to_type_parameter: mixin_on_type_alias_expands_to_type_parameter: status: noFix mixin_primary_constructor: - status: needsEvaluation + status: hasFix mixin_subtype_of_base_is_not_base: status: hasFix mixin_subtype_of_final_is_not_base: diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart index 64cdbf03494..49ada6217e4 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart @@ -572,6 +572,7 @@ final _builtInNonLintGenerators = >{ diag.extensionTypeDeclaresInstanceField: [ConvertIntoGetter.new], diag.extensionOverrideAccessToStaticMember: [ReplaceWithExtensionName.new], diag.extensionOverrideWithCascade: [ReplaceCascadeWithDot.new], + diag.extensionPrimaryConstructor: [RemoveConstructor.new], diag.extensionTypeWithAbstractMember: [ConvertIntoBlockBody.missingBody], diag.extraPositionalArguments: [CreateConstructor.new], diag.extraPositionalArgumentsCouldBeNamed: [ @@ -659,6 +660,7 @@ final _builtInNonLintGenerators = >{ diag.missingRequiredArgument: [AddMissingRequiredArgument.new], diag.mixinApplicationNotImplementedInterface: [ExtendClassForMixin.new], diag.mixinClassDeclarationExtendsNotObject: [RemoveExtendsClause.new], + diag.mixinPrimaryConstructor: [RemoveConstructor.new], diag.mixinSubtypeOfBaseIsNotBase: [AddClassModifier.baseModifier], diag.mixinSubtypeOfFinalIsNotBase: [AddClassModifier.baseModifier], diag.mixinOfDisallowedClass: [RemoveNameFromDeclarationClause.new], diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_constructor_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_constructor_test.dart index a4e1dedf6dd..ba78ea2d551 100644 --- a/pkg/analysis_server/test/src/services/correction/fix/remove_constructor_test.dart +++ b/pkg/analysis_server/test/src/services/correction/fix/remove_constructor_test.dart @@ -103,6 +103,24 @@ extension E on int { await assertHasFix(''' extension E on int { } +'''); + } + + Future test_primaryConstructor() async { + await resolveTestCode(''' +extension E() on int; +'''); + await assertHasFix(''' +extension E on int; +'''); + } + + Future test_primaryConstructor_withTypeParameters() async { + await resolveTestCode(''' +extension E() on int; +'''); + await assertHasFix(''' +extension E on int; '''); } } @@ -195,6 +213,51 @@ mixin M { await assertHasFix(''' mixin M { } +'''); + } + + Future test_primaryConstructor_emptyBody() async { + await resolveTestCode(''' +mixin M() {} +'''); + await assertHasFix(''' +mixin M {} +'''); + } + + Future test_primaryConstructor_implements() async { + await resolveTestCode(''' +mixin M() implements Object {} +'''); + await assertHasFix(''' +mixin M implements Object {} +'''); + } + + Future test_primaryConstructor_semicolon() async { + await resolveTestCode(''' +mixin M(); +'''); + await assertHasFix(''' +mixin M; +'''); + } + + Future test_primaryConstructor_withOnClause() async { + await resolveTestCode(''' +mixin M() on Object {} +'''); + await assertHasFix(''' +mixin M on Object {} +'''); + } + + Future test_primaryConstructor_withTypeParameters() async { + await resolveTestCode(''' +mixin M() {} +'''); + await assertHasFix(''' +mixin M {} '''); } }