Skip to content

Commit

Permalink
Support replacing similar names in extensions
Browse files Browse the repository at this point in the history
Change-Id: I1a3dd56dba6ac245ea7176488ea50f629ed8d174
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/115774
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
  • Loading branch information
bwilkerson authored and commit-bot@chromium.org committed Sep 6, 2019
1 parent 4d0fa1e commit d8f1661
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 21 deletions.
26 changes: 23 additions & 3 deletions pkg/analysis_server/lib/src/services/correction/fix_internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ class FixProcessor extends BaseProcessor {
await _addFix_importLibrary_withType();
}
if (errorCode == CompileTimeErrorCode.UNDEFINED_EXTENSION_GETTER) {
await _addFix_undefinedClassAccessor_useSimilar();
await _addFix_createGetter();
}
if (errorCode == StaticTypeWarningCode.UNDEFINED_METHOD) {
Expand All @@ -524,10 +525,16 @@ class FixProcessor extends BaseProcessor {
await _addFix_undefinedMethod_create();
await _addFix_undefinedFunction_create();
}
if (errorCode == CompileTimeErrorCode.UNDEFINED_EXTENSION_METHOD) {
await _addFix_undefinedMethod_useSimilar();
}
if (errorCode == StaticTypeWarningCode.UNDEFINED_SETTER) {
await _addFix_undefinedClassAccessor_useSimilar();
await _addFix_createField();
}
if (errorCode == CompileTimeErrorCode.UNDEFINED_EXTENSION_SETTER) {
await _addFix_undefinedClassAccessor_useSimilar();
}
if (errorCode == CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER) {
await _addFix_convertFlutterChild();
await _addFix_convertFlutterChildren();
Expand Down Expand Up @@ -3578,8 +3585,9 @@ class FixProcessor extends BaseProcessor {
// prepare target
Expression target = null;
if (node.parent is PrefixedIdentifier) {
PrefixedIdentifier invocation = node.parent as PrefixedIdentifier;
target = invocation.prefix;
target = (node.parent as PrefixedIdentifier).prefix;
} else if (node.parent is PropertyAccess) {
target = (node.parent as PropertyAccess).target;
}
// find getter
if (node.inGetterContext()) {
Expand Down Expand Up @@ -3613,6 +3621,11 @@ class FixProcessor extends BaseProcessor {
ClassElement classElement = clazz.declaredElement;
_updateFinderWithClassMembers(finder, classElement);
}
} else if (target is ExtensionOverride) {
_updateFinderWithExtensionMembers(finder, target.staticElement);
} else if (target is Identifier &&
target.staticElement is ExtensionElement) {
_updateFinderWithExtensionMembers(finder, target.staticElement);
} else {
DartType type = target.staticType;
if (type is InterfaceType) {
Expand All @@ -3622,7 +3635,7 @@ class FixProcessor extends BaseProcessor {
}
// if we have close enough element, suggest to use it
if (finder._element != null) {
String closestName = finder._element.name;
String closestName = finder._element.displayName;
var changeBuilder = _newDartChangeBuilder();
await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
builder.addSimpleReplacement(range.node(node), closestName);
Expand Down Expand Up @@ -4453,6 +4466,13 @@ class FixProcessor extends BaseProcessor {
}
}

void _updateFinderWithExtensionMembers(
_ClosestElementFinder finder, ExtensionElement element) {
if (element != null) {
finder._updateList(getExtensionMembers(element));
}
}

static bool _isNameOfType(String name) {
if (name.isEmpty) {
return false;
Expand Down
22 changes: 22 additions & 0 deletions pkg/analysis_server/lib/src/services/search/hierarchy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,28 @@ Future<Set<ClassElement>> getDirectSubClasses(
return matches.map((match) => match.element).cast<ClassElement>().toSet();
}

/// Return the non-synthetic children of the given [extension]. This includes
/// fields, accessors and methods, but excludes synthetic elements.
List<Element> getExtensionMembers(ExtensionElement extension, [String name]) {
List<Element> members = <Element>[];
visitChildren(extension, (Element element) {
if (element.isSynthetic) {
return false;
}
if (name != null && element.displayName != name) {
return false;
}
if (element is ExecutableElement) {
members.add(element);
}
if (element is FieldElement) {
members.add(element);
}
return false;
});
return members;
}

/**
* @return all implementations of the given {@link ClassMemberElement} is its superclasses and
* their subclasses.
Expand Down
162 changes: 144 additions & 18 deletions pkg/analysis_server/test/src/services/correction/fix/change_to_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'fix_processor.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ChangeToTest);
defineReflectiveTests(ChangeToWithExtensionMethodsTest);
});
}

Expand All @@ -19,7 +20,7 @@ class ChangeToTest extends FixProcessorTest {
@override
FixKind get kind => DartFixKind.CHANGE_TO;

test_undefinedClass_fromImport() async {
test_class_fromImport() async {
await resolveTestUnit('''
main() {
Stirng s = 'abc';
Expand All @@ -34,7 +35,7 @@ main() {
''');
}

test_undefinedClass_fromThisLibrary() async {
test_class_fromThisLibrary() async {
await resolveTestUnit('''
class MyClass {}
main() {
Expand All @@ -51,7 +52,7 @@ main() {
''');
}

test_undefinedClass_prefixed() async {
test_class_prefixed() async {
await resolveTestUnit('''
import 'dart:async' as c;
main() {
Expand All @@ -68,7 +69,7 @@ main() {
''');
}

test_undefinedFunction_fromImport() async {
test_function_fromImport() async {
await resolveTestUnit('''
main() {
pritn(0);
Expand All @@ -81,7 +82,7 @@ main() {
''');
}

test_undefinedFunction_prefixed_fromImport() async {
test_function_prefixed_fromImport() async {
await resolveTestUnit('''
import 'dart:core' as c;
main() {
Expand All @@ -96,7 +97,7 @@ main() {
''');
}

test_undefinedFunction_prefixed_ignoreLocal() async {
test_function_prefixed_ignoreLocal() async {
await resolveTestUnit('''
import 'dart:async' as c;
main() {
Expand All @@ -106,7 +107,7 @@ main() {
await assertNoFix();
}

test_undefinedFunction_thisLibrary() async {
test_function_thisLibrary() async {
await resolveTestUnit('''
myFunction() {}
main() {
Expand All @@ -121,7 +122,7 @@ main() {
''');
}

test_undefinedGetter_hint() async {
test_getter_hint() async {
await resolveTestUnit('''
class A {
int myField;
Expand All @@ -142,7 +143,7 @@ main(A a) {
''');
}

test_undefinedGetter_qualified() async {
test_getter_qualified() async {
await resolveTestUnit('''
class A {
int myField;
Expand All @@ -161,7 +162,7 @@ main(A a) {
''');
}

test_undefinedGetter_qualified_static() async {
test_getter_qualified_static() async {
await resolveTestUnit('''
class A {
static int MY_NAME = 1;
Expand All @@ -180,7 +181,7 @@ main() {
''');
}

test_undefinedGetter_unqualified() async {
test_getter_unqualified() async {
await resolveTestUnit('''
class A {
int myField;
Expand All @@ -199,7 +200,7 @@ class A {
''');
}

test_undefinedMethod_ignoreOperators() async {
test_method_ignoreOperators() async {
await resolveTestUnit('''
main(Object object) {
object.then();
Expand All @@ -208,7 +209,7 @@ main(Object object) {
await assertNoFix();
}

test_undefinedMethod_qualified() async {
test_method_qualified() async {
await resolveTestUnit('''
class A {
myMethod() {}
Expand All @@ -229,7 +230,7 @@ main() {
''');
}

test_undefinedMethod_unqualified_superClass() async {
test_method_unqualified_superClass() async {
await resolveTestUnit('''
class A {
myMethod() {}
Expand All @@ -252,7 +253,7 @@ class B extends A {
''');
}

test_undefinedMethod_unqualified_thisClass() async {
test_method_unqualified_thisClass() async {
await resolveTestUnit('''
class A {
myMethod() {}
Expand All @@ -271,7 +272,7 @@ class A {
''');
}

test_undefinedSetter_hint() async {
test_setter_hint() async {
await resolveTestUnit('''
class A {
int myField;
Expand All @@ -292,7 +293,7 @@ main(A a) {
''');
}

test_undefinedSetter_qualified() async {
test_setter_qualified() async {
await resolveTestUnit('''
class A {
int myField;
Expand All @@ -311,7 +312,7 @@ main(A a) {
''');
}

test_undefinedSetter_unqualified() async {
test_setter_unqualified() async {
await resolveTestUnit('''
class A {
int myField;
Expand All @@ -330,3 +331,128 @@ class A {
''');
}
}

@reflectiveTest
class ChangeToWithExtensionMethodsTest extends FixProcessorTest {
@override
FixKind get kind => DartFixKind.CHANGE_TO;

void setUp() {
createAnalysisOptionsFile(experiments: ['extension-methods']);
super.setUp();
}

test_getter_override() async {
await resolveTestUnit('''
extension E on int {
int get myGetter => 0;
}
void f() {
E(1).myGeter;
}
''');
await assertHasFix('''
extension E on int {
int get myGetter => 0;
}
void f() {
E(1).myGetter;
}
''');
}

test_getter_static() async {
await resolveTestUnit('''
extension E on int {
static int get myGetter => 0;
}
void f() {
E.myGeter;
}
''');
await assertHasFix('''
extension E on int {
static int get myGetter => 0;
}
void f() {
E.myGetter;
}
''');
}

test_method_override() async {
await resolveTestUnit('''
extension E on int {
int myMethod() => 0;
}
void f() {
E(1).myMetod();
}
''');
await assertHasFix('''
extension E on int {
int myMethod() => 0;
}
void f() {
E(1).myMethod();
}
''');
}

test_method_static() async {
await resolveTestUnit('''
extension E on int {
static int myMethod() => 0;
}
void f() {
E.myMetod();
}
''');
await assertHasFix('''
extension E on int {
static int myMethod() => 0;
}
void f() {
E.myMethod();
}
''');
}

test_setter_override() async {
await resolveTestUnit('''
extension E on int {
void set mySetter(int i) {}
}
void f() {
E(1).mySeter = 0;
}
''');
await assertHasFix('''
extension E on int {
void set mySetter(int i) {}
}
void f() {
E(1).mySetter = 0;
}
''');
}

test_setter_static() async {
await resolveTestUnit('''
extension E on int {
static void set mySetter(int i) {}
}
void f() {
E.mySeter = 0;
}
''');
await assertHasFix('''
extension E on int {
static void set mySetter(int i) {}
}
void f() {
E.mySetter = 0;
}
''');
}
}

0 comments on commit d8f1661

Please sign in to comment.