Skip to content

Commit 6c712f1

Browse files
bwilkersoncommit-bot@chromium.org
authored andcommitted
Add a fix to create a mixin
Change-Id: Id0f31949a5639a00e884abc61b2cfcb5bc552b44 Reviewed-on: https://dart-review.googlesource.com/75130 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent cc08d0e commit 6c712f1

File tree

7 files changed

+362
-1
lines changed

7 files changed

+362
-1
lines changed

pkg/analysis_server/lib/src/services/correction/fix.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ class DartFixKind {
148148
const FixKind('CREATE_METHOD', 50, "Create method '{0}'");
149149
static const CREATE_MISSING_OVERRIDES = const FixKind(
150150
'CREATE_MISSING_OVERRIDES', 49, "Create {0} missing override(s)");
151+
static const CREATE_MIXIN =
152+
const FixKind('CREATE_MIXIN', 50, "Create mixin '{0}'");
151153
static const CREATE_NO_SUCH_METHOD = const FixKind(
152154
'CREATE_NO_SUCH_METHOD', 51, "Create 'noSuchMethod' method");
153155
static const CONVERT_TO_NAMED_ARGUMENTS = const FixKind(

pkg/analysis_server/lib/src/services/correction/fix_internal.dart

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ class FixProcessor {
438438
errorCode == StaticWarningCode.UNDEFINED_CLASS) {
439439
await _addFix_importLibrary_withType();
440440
await _addFix_createClass();
441+
await _addFix_createMixin();
441442
await _addFix_undefinedClass_useSimilar();
442443
}
443444
if (errorCode ==
@@ -461,6 +462,7 @@ class FixProcessor {
461462
await _addFix_createField();
462463
await _addFix_createGetter();
463464
await _addFix_createFunction_forFunctionType();
465+
await _addFix_createMixin();
464466
await _addFix_importLibrary_withType();
465467
await _addFix_importLibrary_withTopLevelVariable();
466468
await _addFix_createLocalVariable();
@@ -492,6 +494,7 @@ class FixProcessor {
492494
if (errorCode == StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT) {
493495
await _addFix_importLibrary_withType();
494496
await _addFix_createClass();
497+
await _addFix_createMixin();
495498
}
496499
if (errorCode == StaticTypeWarningCode.UNDEFINED_FUNCTION) {
497500
await _addFix_createClass();
@@ -508,6 +511,7 @@ class FixProcessor {
508511
// TODO(brianwilkerson) The following were added because fasta produces
509512
// UNDEFINED_GETTER in places where analyzer produced UNDEFINED_IDENTIFIER
510513
await _addFix_createClass();
514+
await _addFix_createMixin();
511515
await _addFix_createLocalVariable();
512516
await _addFix_importLibrary_withTopLevelVariable();
513517
await _addFix_importLibrary_withType();
@@ -2069,6 +2073,92 @@ class FixProcessor {
20692073
utils.targetExecutableElement = null;
20702074
}
20712075

2076+
Future<void> _addFix_createMixin() async {
2077+
Element prefixElement = null;
2078+
String name = null;
2079+
SimpleIdentifier nameNode;
2080+
if (node is SimpleIdentifier) {
2081+
AstNode parent = node.parent;
2082+
if (parent is PrefixedIdentifier) {
2083+
if (parent.parent is InstanceCreationExpression) {
2084+
return;
2085+
}
2086+
PrefixedIdentifier prefixedIdentifier = parent;
2087+
prefixElement = prefixedIdentifier.prefix.staticElement;
2088+
if (prefixElement == null) {
2089+
return;
2090+
}
2091+
parent = prefixedIdentifier.parent;
2092+
nameNode = prefixedIdentifier.identifier;
2093+
name = prefixedIdentifier.identifier.name;
2094+
} else if (parent is TypeName &&
2095+
parent.parent is ConstructorName &&
2096+
parent.parent.parent is InstanceCreationExpression) {
2097+
return;
2098+
} else {
2099+
nameNode = node;
2100+
name = nameNode.name;
2101+
}
2102+
if (!_mayBeTypeIdentifier(nameNode)) {
2103+
return;
2104+
}
2105+
} else {
2106+
return;
2107+
}
2108+
// prepare environment
2109+
Element targetUnit;
2110+
String prefix = '';
2111+
String suffix = '';
2112+
int offset = -1;
2113+
String filePath;
2114+
if (prefixElement == null) {
2115+
targetUnit = unitElement;
2116+
CompilationUnitMember enclosingMember = node.getAncestor((node) =>
2117+
node is CompilationUnitMember && node.parent is CompilationUnit);
2118+
if (enclosingMember == null) {
2119+
return;
2120+
}
2121+
offset = enclosingMember.end;
2122+
filePath = file;
2123+
prefix = '$eol$eol';
2124+
} else {
2125+
for (ImportElement import in unitLibraryElement.imports) {
2126+
if (prefixElement is PrefixElement && import.prefix == prefixElement) {
2127+
LibraryElement library = import.importedLibrary;
2128+
if (library != null) {
2129+
targetUnit = library.definingCompilationUnit;
2130+
Source targetSource = targetUnit.source;
2131+
try {
2132+
offset = targetSource.contents.data.length;
2133+
filePath = targetSource.fullName;
2134+
prefix = '$eol';
2135+
suffix = '$eol';
2136+
} on FileSystemException {
2137+
// If we can't read the file to get the offset, then we can't
2138+
// create a fix.
2139+
}
2140+
break;
2141+
}
2142+
}
2143+
}
2144+
}
2145+
if (offset < 0) {
2146+
return;
2147+
}
2148+
DartChangeBuilder changeBuilder = new DartChangeBuilder(session);
2149+
await changeBuilder.addFileEdit(filePath, (DartFileEditBuilder builder) {
2150+
builder.addInsertion(offset, (DartEditBuilder builder) {
2151+
builder.write(prefix);
2152+
builder.writeMixinDeclaration(name, nameGroupName: 'NAME');
2153+
builder.write(suffix);
2154+
});
2155+
if (prefixElement == null) {
2156+
builder.addLinkedPosition(range.node(node), 'NAME');
2157+
}
2158+
});
2159+
_addFixFromBuilder(changeBuilder, DartFixKind.CREATE_MIXIN, args: [name]);
2160+
}
2161+
20722162
Future<void> _addFix_createNoSuchMethod() async {
20732163
// TODO(brianwilkerson) Determine whether this await is necessary.
20742164
await null;

pkg/analysis_server/test/edit/fixes_test.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ main() {
4545
expect(error.severity, AnalysisErrorSeverity.WARNING);
4646
expect(error.type, AnalysisErrorType.STATIC_WARNING);
4747
List<SourceChange> fixes = errorFixes[0].fixes;
48-
expect(fixes, hasLength(2));
48+
expect(fixes, hasLength(3));
4949
expect(fixes[0].message, matches('Import library'));
5050
expect(fixes[1].message, matches('Create class'));
51+
expect(fixes[2].message, matches('Create mixin'));
5152
}
5253

5354
test_fromPlugins() async {

pkg/analysis_server/test/services/correction/fix_test.dart

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4129,6 +4129,146 @@ class B extends A {
41294129
''');
41304130
}
41314131

4132+
test_createMixin() async {
4133+
await resolveTestUnit('''
4134+
main() {
4135+
Test v = null;
4136+
}
4137+
''');
4138+
await assertHasFix(DartFixKind.CREATE_MIXIN, '''
4139+
main() {
4140+
Test v = null;
4141+
}
4142+
4143+
mixin Test {
4144+
}
4145+
''');
4146+
_assertLinkedGroup(change.linkedEditGroups[0], ['Test v =', 'Test {']);
4147+
}
4148+
4149+
test_createMixin_BAD_hasUnresolvedPrefix() async {
4150+
await resolveTestUnit('''
4151+
main() {
4152+
prefix.Test v = null;
4153+
}
4154+
''');
4155+
await assertNoFix(DartFixKind.CREATE_MIXIN);
4156+
}
4157+
4158+
test_createMixin_BAD_instanceCreation_withNew() async {
4159+
await resolveTestUnit('''
4160+
main() {
4161+
new Test();
4162+
}
4163+
''');
4164+
await assertNoFix(DartFixKind.CREATE_MIXIN);
4165+
}
4166+
4167+
test_createMixin_BAD_instanceCreation_withoutNew() async {
4168+
await resolveTestUnit('''
4169+
main() {
4170+
Test();
4171+
}
4172+
''');
4173+
await assertNoFix(DartFixKind.CREATE_MIXIN);
4174+
}
4175+
4176+
test_createMixin_inLibraryOfPrefix() async {
4177+
String libCode = r'''
4178+
library my.lib;
4179+
4180+
class A {}
4181+
''';
4182+
addSource('/project/lib.dart', libCode);
4183+
await resolveTestUnit('''
4184+
import 'lib.dart' as lib;
4185+
4186+
main() {
4187+
lib.A a = null;
4188+
lib.Test t = null;
4189+
}
4190+
''');
4191+
AnalysisError error = await _findErrorToFix();
4192+
fix = await _assertHasFix(DartFixKind.CREATE_MIXIN, error);
4193+
change = fix.change;
4194+
// apply to "lib.dart"
4195+
List<SourceFileEdit> fileEdits = change.edits;
4196+
expect(fileEdits, hasLength(1));
4197+
SourceFileEdit fileEdit = change.edits[0];
4198+
expect(fileEdit.file, convertPath('/project/lib.dart'));
4199+
expect(SourceEdit.applySequence(libCode, fileEdit.edits), r'''
4200+
library my.lib;
4201+
4202+
class A {}
4203+
4204+
mixin Test {
4205+
}
4206+
''');
4207+
expect(change.linkedEditGroups, hasLength(1));
4208+
}
4209+
4210+
test_createMixin_innerLocalFunction() async {
4211+
await resolveTestUnit('''
4212+
f() {
4213+
g() {
4214+
Test v = null;
4215+
}
4216+
}
4217+
''');
4218+
await assertHasFix(DartFixKind.CREATE_MIXIN, '''
4219+
f() {
4220+
g() {
4221+
Test v = null;
4222+
}
4223+
}
4224+
4225+
mixin Test {
4226+
}
4227+
''');
4228+
_assertLinkedGroup(change.linkedEditGroups[0], ['Test v =', 'Test {']);
4229+
}
4230+
4231+
test_createMixin_itemOfList() async {
4232+
await resolveTestUnit('''
4233+
main() {
4234+
var a = [Test];
4235+
}
4236+
''');
4237+
await assertHasFix(DartFixKind.CREATE_MIXIN, '''
4238+
main() {
4239+
var a = [Test];
4240+
}
4241+
4242+
mixin Test {
4243+
}
4244+
''');
4245+
_assertLinkedGroup(change.linkedEditGroups[0], ['Test];', 'Test {']);
4246+
}
4247+
4248+
test_createMixin_itemOfList_inAnnotation() async {
4249+
errorFilter = (AnalysisError error) {
4250+
return error.errorCode == StaticWarningCode.UNDEFINED_IDENTIFIER;
4251+
};
4252+
await resolveTestUnit('''
4253+
class MyAnnotation {
4254+
const MyAnnotation(a, b);
4255+
}
4256+
@MyAnnotation(int, const [Test])
4257+
main() {}
4258+
''');
4259+
await assertHasFix(DartFixKind.CREATE_MIXIN, '''
4260+
class MyAnnotation {
4261+
const MyAnnotation(a, b);
4262+
}
4263+
@MyAnnotation(int, const [Test])
4264+
main() {}
4265+
4266+
mixin Test {
4267+
}
4268+
''');
4269+
_assertLinkedGroup(change.linkedEditGroups[0], ['Test])', 'Test {']);
4270+
}
4271+
41324272
test_createNoSuchMethod_BAD_classTypeAlias() async {
41334273
await resolveTestUnit('''
41344274
abstract class A {

pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
150150
write(' extends ');
151151
writeType(superclass, groupName: superclassGroupName);
152152
} else if (mixins != null && mixins.isNotEmpty) {
153+
// TODO(brianwilkerson) Remove this branch when 2.1 semantics are
154+
// supported everywhere.
153155
write(' extends Object ');
154156
}
155157
writeTypes(mixins, prefix: ' with ');
@@ -362,6 +364,29 @@ class DartEditBuilderImpl extends EditBuilderImpl implements DartEditBuilder {
362364
write(';');
363365
}
364366

367+
@override
368+
void writeMixinDeclaration(String name,
369+
{Iterable<DartType> interfaces,
370+
void membersWriter(),
371+
String nameGroupName,
372+
Iterable<DartType> superclassConstraints}) {
373+
// TODO(brianwilkerson) Add support for type parameters, probably as a
374+
// parameterWriter parameter.
375+
write('mixin ');
376+
if (nameGroupName == null) {
377+
write(name);
378+
} else {
379+
addSimpleLinkedEdit(nameGroupName, name);
380+
}
381+
writeTypes(superclassConstraints, prefix: ' on ');
382+
writeTypes(interfaces, prefix: ' implements ');
383+
writeln(' {');
384+
if (membersWriter != null) {
385+
membersWriter();
386+
}
387+
write('}');
388+
}
389+
365390
@override
366391
void writeOverrideOfInheritedMember(ExecutableElement member,
367392
{StringBuffer displayTextBuffer, String returnTypeGroupName}) {

pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,19 @@ abstract class DartEditBuilder implements EditBuilder {
189189
DartType type,
190190
String typeGroupName});
191191

192+
/**
193+
* Write the code for a declaration of a mixin with the given [name]. If a
194+
* list of [interfaces] is provided, then the mixin will implement those
195+
* interfaces. If a [membersWriter] is provided, then it will be invoked to
196+
* allow members to be generated. If a [nameGroupName] is provided, then the
197+
* name of the class will be included in the linked edit group with that name.
198+
*/
199+
void writeMixinDeclaration(String name,
200+
{Iterable<DartType> interfaces,
201+
void membersWriter(),
202+
String nameGroupName,
203+
Iterable<DartType> superclassConstraints});
204+
192205
/**
193206
* Append a placeholder for an override of the specified inherited [member].
194207
* If provided, write a string value suitable for display (e.g., in a

0 commit comments

Comments
 (0)