From bc4e2013756c170c785f1554cd360e2e5c19a1ce Mon Sep 17 00:00:00 2001 From: FMorschel Date: Mon, 9 Jun 2025 15:10:50 -0700 Subject: [PATCH] [DAS] Fixes missing case for create class This also adds a new priority for mixins when the written type name starts with lowercase letters (to match the class existing ones) and tests the priority between it, create class and import fixes. Fixes: https://github.com/dart-lang/sdk/issues/60874 Change-Id: Ie451db6a273fe8eb52df21725276068b1980e7f1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/433500 Commit-Queue: Brian Wilkerson Auto-Submit: Felipe Morschel Reviewed-by: Samuel Rawlins Reviewed-by: Brian Wilkerson --- .../correction/dart/create_class.dart | 26 ++- .../correction/dart/create_mixin.dart | 139 ++++++++----- .../lib/src/services/correction/fix.dart | 29 ++- .../src/services/correction/fix_internal.dart | 33 ++- .../lib/src/utilities/extensions/string.dart | 4 + .../shared_code_actions_fixes_tests.dart | 2 +- .../correction/fix/create_class_test.dart | 85 ++++++++ .../correction/fix/create_mixin_test.dart | 194 +++++++++++++++++- 8 files changed, 438 insertions(+), 74 deletions(-) diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart index cb812c26430..351598878cd 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/create_class.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:analysis_server/src/services/correction/fix.dart'; +import 'package:analysis_server/src/utilities/extensions/string.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'; @@ -12,8 +13,6 @@ import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; import 'package:analyzer_plugin/utilities/range_factory.dart'; class CreateClass extends MultiCorrectionProducer { - static final _lowerCaseRegex = RegExp(r'([_\$]||[_\$]+[0-9])*[a-z]'); - CreateClass({required super.context}); @override @@ -22,6 +21,7 @@ class CreateClass extends MultiCorrectionProducer { Element? prefixElement; ArgumentList? arguments; + var withKeyword = false; String? className; bool requiresConstConstructor = false; if (targetNode is Annotation) { @@ -43,6 +43,7 @@ class CreateClass extends MultiCorrectionProducer { return const []; } } + withKeyword = node.parent is WithClause; className = targetNode.name.lexeme; requiresConstConstructor |= _requiresConstConstructor(targetNode); } else if (targetNode case SimpleIdentifier( @@ -50,6 +51,11 @@ class CreateClass extends MultiCorrectionProducer { ) when parent is! PropertyAccess && parent is! PrefixedIdentifier) { className = targetNode.nameOfType ?? targetNode.name; requiresConstConstructor |= _requiresConstConstructor(targetNode); + } else if (targetNode case SimpleIdentifier( + parent: PrefixedIdentifier(:var identifier), + ) when targetNode != identifier) { + className = targetNode.nameOfType ?? targetNode.name; + requiresConstConstructor |= _requiresConstConstructor(targetNode); } else if (targetNode is PrefixedIdentifier) { prefixElement = targetNode.prefix.element; if (prefixElement == null) { @@ -65,7 +71,7 @@ class CreateClass extends MultiCorrectionProducer { return const []; } // Lowercase class names are valid but not idiomatic so lower the priority. - if (className.startsWith(_lowerCaseRegex)) { + if (className.firstLetterIsLowercase) { return [ _CreateClass.lowercase( context: context, @@ -73,6 +79,7 @@ class CreateClass extends MultiCorrectionProducer { prefixElement: prefixElement, className: className, requiresConstConstructor: requiresConstConstructor, + withKeyword: withKeyword, arguments: arguments, ), ]; @@ -84,6 +91,7 @@ class CreateClass extends MultiCorrectionProducer { prefixElement: prefixElement, className: className, requiresConstConstructor: requiresConstConstructor, + withKeyword: withKeyword, arguments: arguments, ), ]; @@ -126,12 +134,16 @@ class _CreateClass extends ResolvedCorrectionProducer { required AstNode targetNode, required Element? prefixElement, required String className, + required bool withKeyword, }) : _className = className, _prefixElement = prefixElement, _targetNode = targetNode, _requiresConstConstructor = requiresConstConstructor, _arguments = arguments, - fixKind = DartFixKind.CREATE_CLASS_LOWERCASE; + fixKind = + withKeyword + ? DartFixKind.CREATE_CLASS_LOWERCASE_WITH + : DartFixKind.CREATE_CLASS_LOWERCASE; _CreateClass.uppercase({ required super.context, @@ -140,12 +152,16 @@ class _CreateClass extends ResolvedCorrectionProducer { required AstNode targetNode, required Element? prefixElement, required String className, + required bool withKeyword, }) : _className = className, _prefixElement = prefixElement, _targetNode = targetNode, _requiresConstConstructor = requiresConstConstructor, _arguments = arguments, - fixKind = DartFixKind.CREATE_CLASS_UPPERCASE; + fixKind = + withKeyword + ? DartFixKind.CREATE_CLASS_UPPERCASE_WITH + : DartFixKind.CREATE_CLASS_UPPERCASE; @override CorrectionApplicability get applicability => diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart index 7e13685613c..caa842b3b65 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/create_mixin.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:analysis_server/src/services/correction/fix.dart'; +import 'package:analysis_server/src/utilities/extensions/string.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'; @@ -11,11 +12,100 @@ import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dar import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; import 'package:analyzer_plugin/utilities/range_factory.dart'; -class CreateMixin extends ResolvedCorrectionProducer { - String _mixinName = ''; - +class CreateMixin extends MultiCorrectionProducer { CreateMixin({required super.context}); + @override + Future> get producers async { + String mixinName = ''; + Element? prefixElement; + var withKeyword = false; + var node = this.node; + if (node is NamedType) { + var importPrefix = node.importPrefix; + if (importPrefix != null) { + prefixElement = importPrefix.element2; + if (prefixElement == null) { + return const []; + } + } + withKeyword = node.parent is WithClause; + mixinName = node.name.lexeme; + } else if (node is SimpleIdentifier) { + var parent = node.parent; + switch (parent) { + // Not the first identifier or the body of a function + case PrefixedIdentifier(identifier: Expression invalid) || + PropertyAccess(propertyName: Expression invalid) || + ExpressionFunctionBody(expression: var invalid): + if (invalid == node) { + return const []; + } + } + mixinName = node.name; + } else if (node is PrefixedIdentifier) { + if (node.parent is InstanceCreationExpression) { + return const []; + } + prefixElement = node.prefix.element; + if (prefixElement == null) { + return const []; + } + mixinName = node.identifier.name; + } else { + return const []; + } + if (mixinName.isEmpty) { + return const []; + } + return [ + // Lowercase mixin names are valid but not idiomatic so lower the + // priority. + if (mixinName.firstLetterIsLowercase) + _CreateMixin.lowercase( + mixinName, + prefixElement, + withKeyword: withKeyword, + context: context, + ) + else + _CreateMixin.uppercase( + mixinName, + prefixElement, + withKeyword: withKeyword, + context: context, + ), + ]; + } +} + +class _CreateMixin extends ResolvedCorrectionProducer { + final String _mixinName; + final Element? prefixElement; + + @override + final FixKind fixKind; + + _CreateMixin.lowercase( + this._mixinName, + this.prefixElement, { + required bool withKeyword, + required super.context, + }) : fixKind = + withKeyword + ? DartFixKind.CREATE_MIXIN_LOWERCASE_WITH + : DartFixKind.CREATE_MIXIN_LOWERCASE; + + _CreateMixin.uppercase( + this._mixinName, + this.prefixElement, { + required bool withKeyword, + required super.context, + }) : fixKind = + withKeyword + ? DartFixKind.CREATE_MIXIN_UPPERCASE_WITH + : DartFixKind.CREATE_MIXIN_UPPERCASE; + @override CorrectionApplicability get applicability => // TODO(applicability): comment on why. @@ -24,51 +114,8 @@ class CreateMixin extends ResolvedCorrectionProducer { @override List get fixArguments => [_mixinName]; - @override - FixKind get fixKind => DartFixKind.CREATE_MIXIN; - @override Future compute(ChangeBuilder builder) async { - Element? prefixElement; - var node = this.node; - if (node is NamedType) { - var importPrefix = node.importPrefix; - if (importPrefix != null) { - prefixElement = importPrefix.element2; - if (prefixElement == null) { - return; - } - } - _mixinName = node.name.lexeme; - } else if (node is SimpleIdentifier) { - var parent = node.parent; - switch (parent) { - case PrefixedIdentifier(): - if (parent.identifier == node) { - return; - } - case PropertyAccess(): - if (parent.propertyName == node) { - return; - } - case ExpressionFunctionBody(): - if (parent.expression == node) { - return; - } - } - _mixinName = node.name; - } else if (node is PrefixedIdentifier) { - if (node.parent is InstanceCreationExpression) { - return; - } - prefixElement = node.prefix.element; - if (prefixElement == null) { - return; - } - _mixinName = node.identifier.name; - } else { - return; - } // prepare environment LibraryFragment targetUnit; var prefix = ''; diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart index bac13196bb5..786fc41b803 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix.dart @@ -733,11 +733,21 @@ abstract final class DartFixKind { DartFixKindPriority.standard + 2, "Create class '{0}'", ); + static const CREATE_CLASS_UPPERCASE_WITH = FixKind( + 'dart.fix.create.class.uppercase.with', + DartFixKindPriority.standard + 1, + "Create class '{0}'", + ); static const CREATE_CLASS_LOWERCASE = FixKind( 'dart.fix.create.class.lowercase', DartFixKindPriority.standard - 5, "Create class '{0}'", ); + static const CREATE_CLASS_LOWERCASE_WITH = FixKind( + 'dart.fix.create.class.lowercase.with', + DartFixKindPriority.standard - 6, + "Create class '{0}'", + ); static const CREATE_CONSTRUCTOR = FixKind( 'dart.fix.create.constructor', DartFixKindPriority.standard, @@ -820,11 +830,26 @@ abstract final class DartFixKind { DartFixKindPriority.standard + 1, 'Create {0} missing override{1}', ); - static const CREATE_MIXIN = FixKind( - 'dart.fix.create.mixin', + static const CREATE_MIXIN_UPPERCASE = FixKind( + 'dart.fix.create.mixin.uppercase', DartFixKindPriority.standard, "Create mixin '{0}'", ); + static const CREATE_MIXIN_UPPERCASE_WITH = FixKind( + 'dart.fix.create.mixin.uppercase.with', + DartFixKindPriority.standard + 2, + "Create mixin '{0}'", + ); + static const CREATE_MIXIN_LOWERCASE = FixKind( + 'dart.fix.create.mixin.lowercase', + DartFixKindPriority.standard - 6, + "Create mixin '{0}'", + ); + static const CREATE_MIXIN_LOWERCASE_WITH = FixKind( + 'dart.fix.create.mixin.lowercase.with', + DartFixKindPriority.standard - 5, + "Create mixin '{0}'", + ); static const CREATE_NO_SUCH_METHOD = FixKind( 'dart.fix.create.noSuchMethod', DartFixKindPriority.standard - 1, 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 14459d91c9c..3432e630f43 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart @@ -602,10 +602,7 @@ final _builtInNonLintGenerators = >{ ], CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT: [AddAsync.new], CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY: [AddAsync.missingReturn], - CompileTimeErrorCode.CAST_TO_NON_TYPE: [ - ChangeTo.classOrMixin, - CreateMixin.new, - ], + CompileTimeErrorCode.CAST_TO_NON_TYPE: [ChangeTo.classOrMixin], CompileTimeErrorCode.CLASS_INSTANTIATION_ACCESS_TO_STATIC_MEMBER: [ RemoveTypeArguments.new, ], @@ -853,8 +850,7 @@ final _builtInNonLintGenerators = >{ AddMissingSwitchCases.new, ], CompileTimeErrorCode.NON_FINAL_FIELD_IN_ENUM: [MakeFinal.new], - CompileTimeErrorCode.NON_TYPE_AS_TYPE_ARGUMENT: [CreateMixin.new], - CompileTimeErrorCode.NOT_A_TYPE: [ChangeTo.classOrMixin, CreateMixin.new], + CompileTimeErrorCode.NOT_A_TYPE: [ChangeTo.classOrMixin], CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD: [ AddLate.new, ], @@ -909,10 +905,7 @@ final _builtInNonLintGenerators = >{ ], CompileTimeErrorCode.SUPER_INVOCATION_NOT_LAST: [MakeSuperInvocationLast.new], CompileTimeErrorCode.SWITCH_CASE_COMPLETES_NORMALLY: [AddSwitchCaseBreak.new], - CompileTimeErrorCode.TYPE_TEST_WITH_UNDEFINED_NAME: [ - ChangeTo.classOrMixin, - CreateMixin.new, - ], + CompileTimeErrorCode.TYPE_TEST_WITH_UNDEFINED_NAME: [ChangeTo.classOrMixin], CompileTimeErrorCode.UNCHECKED_INVOCATION_OF_NULLABLE_VALUE: [ AddNullCheck.new, ], @@ -947,10 +940,7 @@ final _builtInNonLintGenerators = >{ AddNullCheck.new, ], CompileTimeErrorCode.UNDEFINED_ANNOTATION: [ChangeTo.annotation], - CompileTimeErrorCode.UNDEFINED_CLASS: [ - ChangeTo.classOrMixin, - CreateMixin.new, - ], + CompileTimeErrorCode.UNDEFINED_CLASS: [ChangeTo.classOrMixin], CompileTimeErrorCode.UNDEFINED_CLASS_BOOLEAN: [ReplaceBooleanWithBool.new], CompileTimeErrorCode.UNDEFINED_ENUM_CONSTANT: [ AddEnumConstant.new, @@ -987,7 +977,6 @@ final _builtInNonLintGenerators = >{ CreateGetter.new, CreateLocalVariable.new, CreateMethodOrFunction.new, - CreateMixin.new, ], CompileTimeErrorCode.UNDEFINED_IDENTIFIER: [ ChangeTo.getterOrSetter, @@ -996,7 +985,6 @@ final _builtInNonLintGenerators = >{ CreateLocalVariable.new, CreateParameter.new, CreateMethodOrFunction.new, - CreateMixin.new, CreateSetter.new, CreateExtensionGetter.new, CreateExtensionMethod.new, @@ -1306,6 +1294,7 @@ final _builtInNonLintMultiGenerators = { CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE: [DataDriven.new], CompileTimeErrorCode.CAST_TO_NON_TYPE: [ CreateClass.new, + CreateMixin.new, DataDriven.new, ImportLibrary.forType, ], @@ -1344,6 +1333,7 @@ final _builtInNonLintMultiGenerators = { CompileTimeErrorCode.MISSING_REQUIRED_ARGUMENT: [DataDriven.new], CompileTimeErrorCode.MIXIN_OF_NON_CLASS: [ CreateClass.new, + CreateMixin.new, DataDriven.new, ImportLibrary.forType, ], @@ -1362,10 +1352,15 @@ final _builtInNonLintMultiGenerators = { CompileTimeErrorCode.NON_TYPE_IN_CATCH_CLAUSE: [ImportLibrary.forType], CompileTimeErrorCode.NON_TYPE_AS_TYPE_ARGUMENT: [ CreateClass.new, + CreateMixin.new, DataDriven.new, ImportLibrary.forType, ], - CompileTimeErrorCode.NOT_A_TYPE: [CreateClass.new, ImportLibrary.forType], + CompileTimeErrorCode.NOT_A_TYPE: [ + CreateClass.new, + ImportLibrary.forType, + CreateMixin.new, + ], CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS_NAME_PLURAL: [ DataDriven.new, ], @@ -1378,6 +1373,7 @@ final _builtInNonLintMultiGenerators = { ], CompileTimeErrorCode.TYPE_TEST_WITH_UNDEFINED_NAME: [ CreateClass.new, + CreateMixin.new, ImportLibrary.forType, ], CompileTimeErrorCode.UNDEFINED_ANNOTATION: [ @@ -1389,6 +1385,7 @@ final _builtInNonLintMultiGenerators = { CreateClass.new, DataDriven.new, ImportLibrary.forType, + CreateMixin.new, ], CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT: [ AddSuperConstructorInvocation.new, @@ -1408,6 +1405,7 @@ final _builtInNonLintMultiGenerators = { ImportLibrary.forExtensionMember, ImportLibrary.forTopLevelVariable, ImportLibrary.forType, + CreateMixin.new, ], CompileTimeErrorCode.UNDEFINED_IDENTIFIER: [ CreateClass.new, @@ -1417,6 +1415,7 @@ final _builtInNonLintMultiGenerators = { ImportLibrary.forFunction, ImportLibrary.forTopLevelVariable, ImportLibrary.forType, + CreateMixin.new, ], CompileTimeErrorCode.UNDEFINED_METHOD: [ CreateClass.new, diff --git a/pkg/analysis_server/lib/src/utilities/extensions/string.dart b/pkg/analysis_server/lib/src/utilities/extensions/string.dart index 43d6dc2cd02..9bf08fb4763 100644 --- a/pkg/analysis_server/lib/src/utilities/extensions/string.dart +++ b/pkg/analysis_server/lib/src/utilities/extensions/string.dart @@ -3,6 +3,10 @@ // BSD-style license that can be found in the LICENSE file. extension StringExtension on String { + static final _lowerCaseRegex = RegExp(r'([_\$]||[_\$]+[0-9])*[a-z]'); + + bool get firstLetterIsLowercase => startsWith(_lowerCaseRegex); + /// Returns this string if not empty, otherwise null. String? get nullIfEmpty => isEmpty ? null : this; diff --git a/pkg/analysis_server/test/shared/shared_code_actions_fixes_tests.dart b/pkg/analysis_server/test/shared/shared_code_actions_fixes_tests.dart index 73e807935aa..d3091a2b8e0 100644 --- a/pkg/analysis_server/test/shared/shared_code_actions_fixes_tests.dart +++ b/pkg/analysis_server/test/shared/shared_code_actions_fixes_tests.dart @@ -435,7 +435,7 @@ void main() { // Non-ignore fixes (order doesn't matter here, but this is what // server produces). 'quickfix.create.class.uppercase', - 'quickfix.create.mixin', + 'quickfix.create.mixin.uppercase', 'quickfix.create.localVariable', 'quickfix.remove.unusedLocalVariable', // Ignore fixes last, with line sorted above file. diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_class_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_class_test.dart index ed4f01b02ca..4e34dec5bdc 100644 --- a/pkg/analysis_server/test/src/services/correction/fix/create_class_test.dart +++ b/pkg/analysis_server/test/src/services/correction/fix/create_class_test.dart @@ -13,8 +13,10 @@ import 'fix_processor.dart'; void main() { defineReflectiveSuite(() { defineReflectiveTests(CreateClassLowercaseTest); + defineReflectiveTests(CreateClassLowercaseWithTest); defineReflectiveTests(CreateClassPriorityTest); defineReflectiveTests(CreateClassUppercaseTest); + defineReflectiveTests(CreateClassUppercaseWithTest); }); } @@ -104,6 +106,24 @@ class _newName { } } +@reflectiveTest +class CreateClassLowercaseWithTest extends FixProcessorTest { + @override + FixKind get kind => DartFixKind.CREATE_CLASS_LOWERCASE_WITH; + + Future test_with() async { + await resolveTestCode(''' +class MyClass with baseMixin {} +'''); + await assertHasFix(''' +class MyClass with baseMixin {} + +class baseMixin { +} +'''); + } +} + @reflectiveTest class CreateClassPriorityTest extends FixPriorityTest { Future test_classFirst_function() async { @@ -132,6 +152,26 @@ class A { ]); } + Future test_classFirst_mixin() async { + await resolveTestCode(''' +void f(M m) {} +'''); + await assertFixPriorityOrder([ + DartFixKind.CREATE_CLASS_UPPERCASE, + DartFixKind.CREATE_MIXIN_UPPERCASE, + ]); + } + + Future test_classFirst_mixin_lowercase() async { + await resolveTestCode(''' +void f(newName m) {} +'''); + await assertFixPriorityOrder([ + DartFixKind.CREATE_CLASS_LOWERCASE, + DartFixKind.CREATE_MIXIN_LOWERCASE, + ]); + } + Future test_classLast_function() async { await resolveTestCode(''' void f() { @@ -170,6 +210,26 @@ class A { DartFixKind.CREATE_CLASS_LOWERCASE, ]); } + + Future test_classLast_mixin_lowercaseWith() async { + await resolveTestCode(''' +class Class with myMixin {} +'''); + await assertFixPriorityOrder([ + DartFixKind.CREATE_MIXIN_LOWERCASE_WITH, + DartFixKind.CREATE_CLASS_LOWERCASE_WITH, + ]); + } + + Future test_classLast_mixin_with() async { + await resolveTestCode(''' +class Class with MyMixin {} +'''); + await assertFixPriorityOrder([ + DartFixKind.CREATE_MIXIN_UPPERCASE_WITH, + DartFixKind.CREATE_CLASS_UPPERCASE_WITH, + ]); + } } @reflectiveTest @@ -478,6 +538,31 @@ class _NewName { Future test_with() async { await resolveTestCode(''' class MyClass with BaseClass {} +'''); + await assertNoFix(); + } + + Future test_withStaticName() async { + await resolveTestCode(''' +var a = [Foo.bar]; +'''); + await assertHasFix(''' +var a = [Foo.bar]; + +class Foo { +} +'''); + } +} + +@reflectiveTest +class CreateClassUppercaseWithTest extends FixProcessorTest { + @override + FixKind get kind => DartFixKind.CREATE_CLASS_UPPERCASE_WITH; + + Future test_with() async { + await resolveTestCode(''' +class MyClass with BaseClass {} '''); await assertHasFix(''' class MyClass with BaseClass {} diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_mixin_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_mixin_test.dart index 8d3065dcb3f..e9757e0a287 100644 --- a/pkg/analysis_server/test/src/services/correction/fix/create_mixin_test.dart +++ b/pkg/analysis_server/test/src/services/correction/fix/create_mixin_test.dart @@ -12,14 +12,165 @@ import 'fix_processor.dart'; void main() { defineReflectiveSuite(() { - defineReflectiveTests(CreateMixinTest); + defineReflectiveTests(CreateMixinLowercaseTest); + defineReflectiveTests(CreateMixinLowercaseWithTest); + defineReflectiveTests(CreateMixinPriorityTest); + defineReflectiveTests(CreateMixinUppercaseTest); + defineReflectiveTests(CreateMixinUppercaseWithTest); }); } @reflectiveTest -class CreateMixinTest extends FixProcessorTest { +class CreateMixinLowercaseTest extends FixProcessorTest { @override - FixKind get kind => DartFixKind.CREATE_MIXIN; + FixKind get kind => DartFixKind.CREATE_MIXIN_LOWERCASE; + + Future test_lowercaseAssignment() async { + await resolveTestCode(''' +newName? a; +'''); + await assertHasFix(''' +newName? a; + +mixin newName { +} +'''); + } + + Future test_multiple() async { + await resolveTestCode(r''' +_$_newName? a; +'''); + await assertHasFix(r''' +_$_newName? a; + +mixin _$_newName { +} +'''); + } + + Future test_number() async { + await resolveTestCode(r''' +_0newName? a; +'''); + await assertHasFix(r''' +_0newName? a; + +mixin _0newName { +} +'''); + } + + Future test_startWithDollarSign() async { + await resolveTestCode(r''' +$newName? a; +'''); + await assertHasFix(r''' +$newName? a; + +mixin $newName { +} +'''); + } + + Future test_startWithUnderscore() async { + await resolveTestCode(''' +_newName? a; +'''); + await assertHasFix(''' +_newName? a; + +mixin _newName { +} +'''); + } + + Future test_with() async { + await resolveTestCode(''' +class MyClass with myMixin {} +'''); + await assertNoFix(); + } +} + +@reflectiveTest +class CreateMixinLowercaseWithTest extends FixProcessorTest { + @override + FixKind get kind => DartFixKind.CREATE_MIXIN_LOWERCASE_WITH; + + Future test_with() async { + await resolveTestCode(''' +class MyClass with myMixin {} +'''); + await assertHasFix(''' +class MyClass with myMixin {} + +mixin myMixin { +} +'''); + } +} + +@reflectiveTest +class CreateMixinPriorityTest extends FixPriorityTest { + Future test_mixinFirst_class_lowercaseWith() async { + await resolveTestCode(''' +class Class with myMixin {} +'''); + await assertFixPriorityOrder([ + DartFixKind.CREATE_MIXIN_LOWERCASE_WITH, + DartFixKind.CREATE_CLASS_LOWERCASE_WITH, + ]); + } + + Future test_mixinFirst_class_with() async { + await resolveTestCode(''' +class Class with MyMixin {} +'''); + await assertFixPriorityOrder([ + DartFixKind.CREATE_MIXIN_UPPERCASE_WITH, + DartFixKind.CREATE_CLASS_UPPERCASE_WITH, + ]); + } + + Future test_mixinLast_class() async { + await resolveTestCode(''' +void f(M m) {} +'''); + await assertFixPriorityOrder([ + DartFixKind.CREATE_CLASS_UPPERCASE, + DartFixKind.CREATE_MIXIN_UPPERCASE, + ]); + } + + Future test_mixinLast_class_lowercase() async { + await resolveTestCode(''' +void f(newName m) {} +'''); + await assertFixPriorityOrder([ + DartFixKind.CREATE_CLASS_LOWERCASE, + DartFixKind.CREATE_MIXIN_LOWERCASE, + ]); + } + + Future test_mixinLast_import() async { + newFile('$testPackageLibPath/lib.dart', r''' +class A {} +'''); + await resolveTestCode(''' +A? a; +'''); + await assertFixPriorityOrder([ + DartFixKind.IMPORT_LIBRARY_PROJECT1, + DartFixKind.CREATE_MIXIN_UPPERCASE, + ]); + } +} + +@reflectiveTest +class CreateMixinUppercaseTest extends FixProcessorTest { + @override + FixKind get kind => DartFixKind.CREATE_MIXIN_UPPERCASE; Future test_hasUnresolvedPrefix() async { await resolveTestCode(''' @@ -211,4 +362,41 @@ mixin Test { '''); assertLinkedGroup(change.linkedEditGroups[0], ['Test v =', 'Test {']); } + + Future test_with() async { + await resolveTestCode(''' +class MyClass with MyMixin {} +'''); + await assertNoFix(); + } + + Future test_withStaticName() async { + await resolveTestCode(''' +var a = [Foo.bar]; +'''); + await assertHasFix(''' +var a = [Foo.bar]; + +mixin Foo { +} +'''); + } +} + +@reflectiveTest +class CreateMixinUppercaseWithTest extends FixProcessorTest { + @override + FixKind get kind => DartFixKind.CREATE_MIXIN_UPPERCASE_WITH; + + Future test_with() async { + await resolveTestCode(''' +class MyClass with MyMixin {} +'''); + await assertHasFix(''' +class MyClass with MyMixin {} + +mixin MyMixin { +} +'''); + } }