[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 <brianwilkerson@google.com>
Auto-Submit: Felipe Morschel <git@fmorschel.dev>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
FMorschel
2025-06-09 15:10:50 -07:00
committed by Commit Queue
parent 6f076f90e1
commit bc4e201375
8 changed files with 438 additions and 74 deletions
@@ -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 =>
@@ -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<List<ResolvedCorrectionProducer>> 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<String> get fixArguments => [_mixinName];
@override
FixKind get fixKind => DartFixKind.CREATE_MIXIN;
@override
Future<void> 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 = '';
@@ -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,
@@ -602,10 +602,7 @@ final _builtInNonLintGenerators = <DiagnosticCode, List<ProducerGenerator>>{
],
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 = <DiagnosticCode, List<ProducerGenerator>>{
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 = <DiagnosticCode, List<ProducerGenerator>>{
],
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 = <DiagnosticCode, List<ProducerGenerator>>{
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 = <DiagnosticCode, List<ProducerGenerator>>{
CreateGetter.new,
CreateLocalVariable.new,
CreateMethodOrFunction.new,
CreateMixin.new,
],
CompileTimeErrorCode.UNDEFINED_IDENTIFIER: [
ChangeTo.getterOrSetter,
@@ -996,7 +985,6 @@ final _builtInNonLintGenerators = <DiagnosticCode, List<ProducerGenerator>>{
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,
@@ -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;
@@ -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.
@@ -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<void> test_with() async {
await resolveTestCode('''
class MyClass with baseMixin {}
''');
await assertHasFix('''
class MyClass with baseMixin {}
class baseMixin {
}
''');
}
}
@reflectiveTest
class CreateClassPriorityTest extends FixPriorityTest {
Future<void> test_classFirst_function() async {
@@ -132,6 +152,26 @@ class A {
]);
}
Future<void> test_classFirst_mixin() async {
await resolveTestCode('''
void f(M m) {}
''');
await assertFixPriorityOrder([
DartFixKind.CREATE_CLASS_UPPERCASE,
DartFixKind.CREATE_MIXIN_UPPERCASE,
]);
}
Future<void> test_classFirst_mixin_lowercase() async {
await resolveTestCode('''
void f(newName m) {}
''');
await assertFixPriorityOrder([
DartFixKind.CREATE_CLASS_LOWERCASE,
DartFixKind.CREATE_MIXIN_LOWERCASE,
]);
}
Future<void> test_classLast_function() async {
await resolveTestCode('''
void f() {
@@ -170,6 +210,26 @@ class A {
DartFixKind.CREATE_CLASS_LOWERCASE,
]);
}
Future<void> test_classLast_mixin_lowercaseWith() async {
await resolveTestCode('''
class Class with myMixin {}
''');
await assertFixPriorityOrder([
DartFixKind.CREATE_MIXIN_LOWERCASE_WITH,
DartFixKind.CREATE_CLASS_LOWERCASE_WITH,
]);
}
Future<void> 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<void> test_with() async {
await resolveTestCode('''
class MyClass with BaseClass {}
''');
await assertNoFix();
}
Future<void> 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<void> test_with() async {
await resolveTestCode('''
class MyClass with BaseClass {}
''');
await assertHasFix('''
class MyClass with BaseClass {}
@@ -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<void> test_lowercaseAssignment() async {
await resolveTestCode('''
newName? a;
''');
await assertHasFix('''
newName? a;
mixin newName {
}
''');
}
Future<void> test_multiple() async {
await resolveTestCode(r'''
_$_newName? a;
''');
await assertHasFix(r'''
_$_newName? a;
mixin _$_newName {
}
''');
}
Future<void> test_number() async {
await resolveTestCode(r'''
_0newName? a;
''');
await assertHasFix(r'''
_0newName? a;
mixin _0newName {
}
''');
}
Future<void> test_startWithDollarSign() async {
await resolveTestCode(r'''
$newName? a;
''');
await assertHasFix(r'''
$newName? a;
mixin $newName {
}
''');
}
Future<void> test_startWithUnderscore() async {
await resolveTestCode('''
_newName? a;
''');
await assertHasFix('''
_newName? a;
mixin _newName {
}
''');
}
Future<void> 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<void> test_with() async {
await resolveTestCode('''
class MyClass with myMixin {}
''');
await assertHasFix('''
class MyClass with myMixin {}
mixin myMixin {
}
''');
}
}
@reflectiveTest
class CreateMixinPriorityTest extends FixPriorityTest {
Future<void> test_mixinFirst_class_lowercaseWith() async {
await resolveTestCode('''
class Class with myMixin {}
''');
await assertFixPriorityOrder([
DartFixKind.CREATE_MIXIN_LOWERCASE_WITH,
DartFixKind.CREATE_CLASS_LOWERCASE_WITH,
]);
}
Future<void> test_mixinFirst_class_with() async {
await resolveTestCode('''
class Class with MyMixin {}
''');
await assertFixPriorityOrder([
DartFixKind.CREATE_MIXIN_UPPERCASE_WITH,
DartFixKind.CREATE_CLASS_UPPERCASE_WITH,
]);
}
Future<void> test_mixinLast_class() async {
await resolveTestCode('''
void f(M m) {}
''');
await assertFixPriorityOrder([
DartFixKind.CREATE_CLASS_UPPERCASE,
DartFixKind.CREATE_MIXIN_UPPERCASE,
]);
}
Future<void> test_mixinLast_class_lowercase() async {
await resolveTestCode('''
void f(newName m) {}
''');
await assertFixPriorityOrder([
DartFixKind.CREATE_CLASS_LOWERCASE,
DartFixKind.CREATE_MIXIN_LOWERCASE,
]);
}
Future<void> 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<void> test_hasUnresolvedPrefix() async {
await resolveTestCode('''
@@ -211,4 +362,41 @@ mixin Test {
''');
assertLinkedGroup(change.linkedEditGroups[0], ['Test v =', 'Test {']);
}
Future<void> test_with() async {
await resolveTestCode('''
class MyClass with MyMixin {}
''');
await assertNoFix();
}
Future<void> 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<void> test_with() async {
await resolveTestCode('''
class MyClass with MyMixin {}
''');
await assertHasFix('''
class MyClass with MyMixin {}
mixin MyMixin {
}
''');
}
}