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 { +} +'''); + } }