Support for override completions in mixins.
Also moves tests for override implementations into a separate class.
New tests:
test_mixin_method_of_interface
test_mixin_method_of_superclassConstraint
Changes for deciding whether to generate `super` invocation.
R=brianwilkerson@google.com
Change-Id: I7f875695ce81f0212863fbd2f9fe1f1b9d7aaa3f
Reviewed-on: https://dart-review.googlesource.com/c/90784
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
index 99eb8b5..22692c1 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
@@ -9,9 +9,7 @@
import 'package:analysis_server/src/protocol_server.dart' as protocol
hide CompletionSuggestion, CompletionSuggestionKind;
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
-import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/inheritance_manager2.dart';
import 'package:analyzer/src/generated/source.dart';
@@ -33,30 +31,29 @@
if (targetId == null) {
return const <CompletionSuggestion>[];
}
- ClassDeclaration classDecl =
- targetId.thisOrAncestorOfType<ClassDeclaration>();
+ var classDecl = targetId.thisOrAncestorOfType<ClassOrMixinDeclaration>();
if (classDecl == null) {
return const <CompletionSuggestion>[];
}
- // TODO(brianwilkerson) Consider making the type system visible from the
- // request.result.
- var inheritance = new InheritanceManager2(
- await request.result.libraryElement.session.typeSystem);
+ var inheritance = new InheritanceManager2(request.result.typeSystem);
// Generate a collection of inherited members
- ClassElement classElem = classDecl.declaredElement;
- var interface = inheritance.getInterface(classElem.type).map;
- var namesToOverride = _namesToOverride(classElem, interface.keys);
+ var classElem = classDecl.declaredElement;
+ var interface = inheritance.getInterface(classElem.type);
+ var interfaceMap = interface.map;
+ var namesToOverride =
+ _namesToOverride(classElem.librarySource.uri, interface);
// Build suggestions
List<CompletionSuggestion> suggestions = <CompletionSuggestion>[];
for (Name name in namesToOverride) {
- FunctionType signature = interface[name];
+ FunctionType signature = interfaceMap[name];
// Gracefully degrade if the overridden element has not been resolved.
if (signature.returnType != null) {
- CompletionSuggestion suggestion =
- await _buildSuggestion(request, targetId, signature);
+ var invokeSuper = interface.isSuperImplemented(name);
+ var suggestion =
+ await _buildSuggestion(request, targetId, signature, invokeSuper);
if (suggestion != null) {
suggestions.add(suggestion);
}
@@ -66,41 +63,26 @@
}
/**
- * Return a template for an override of the given [signature]. If selected,
- * the template will replace [targetId].
+ * Build a suggestion to replace [targetId] in the given [request] with an
+ * override of the given [signature].
*/
- Future<DartChangeBuilder> _buildReplacementText(
- ResolvedUnitResult result,
+ Future<CompletionSuggestion> _buildSuggestion(
+ DartCompletionRequest request,
SimpleIdentifier targetId,
FunctionType signature,
- StringBuffer displayTextBuffer) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
- DartChangeBuilder builder = new DartChangeBuilder(result.session);
- await builder.addFileEdit(result.path, (DartFileEditBuilder builder) {
- builder.addReplacement(range.node(targetId), (DartEditBuilder builder) {
- ExecutableElement element = signature.element;
+ bool invokeSuper) async {
+ var displayTextBuffer = new StringBuffer();
+ var builder = new DartChangeBuilder(request.result.session);
+ await builder.addFileEdit(request.result.path, (builder) {
+ builder.addReplacement(range.node(targetId), (builder) {
builder.writeOverride(
signature,
displayTextBuffer: displayTextBuffer,
- invokeSuper: !element.isAbstract,
+ invokeSuper: invokeSuper,
);
});
});
- return builder;
- }
- /**
- * Build a suggestion to replace [targetId] in the given [unit]
- * with an override of the given [signature].
- */
- Future<CompletionSuggestion> _buildSuggestion(DartCompletionRequest request,
- SimpleIdentifier targetId, FunctionType signature) async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
- StringBuffer displayTextBuffer = new StringBuffer();
- DartChangeBuilder builder = await _buildReplacementText(
- request.result, targetId, signature, displayTextBuffer);
String replacement = builder.sourceChange.edits[0].edits[0].replacement;
String completion = replacement.trim();
String overrideAnnotation = '@override';
@@ -139,7 +121,7 @@
*/
SimpleIdentifier _getTargetId(CompletionTarget target) {
AstNode node = target.containingNode;
- if (node is ClassDeclaration) {
+ if (node is ClassOrMixinDeclaration) {
Object entity = target.entity;
if (entity is FieldDeclaration) {
return _getTargetIdFromVarList(entity.fields);
@@ -175,17 +157,6 @@
}
/**
- * Return `true` if the given [classElement] directly declares a member with
- * the given [memberName].
- */
- bool _hasMember(ClassElement classElement, String memberName) {
- return classElement.getField(memberName) != null ||
- classElement.getGetter(memberName) != null ||
- classElement.getMethod(memberName) != null ||
- classElement.getSetter(memberName) != null;
- }
-
- /**
* Return `true` if the given [node] has an `override` annotation.
*/
bool _hasOverride(AstNode node) {
@@ -202,16 +173,14 @@
}
/**
- * Return a list containing the subset of [interfaceNames] that are not
- * defined yet in the given [classElement].
+ * Return the list of names that belong to the [interface] of a class, but
+ * are not yet declared in the class.
*/
- List<Name> _namesToOverride(
- ClassElement classElement, Iterable<Name> interfaceNames) {
- var libraryUri = classElement.library.source.uri;
+ List<Name> _namesToOverride(Uri libraryUri, Interface interface) {
var namesToOverride = <Name>[];
- for (var name in interfaceNames) {
+ for (var name in interface.map.keys) {
if (name.isAccessibleFor(libraryUri)) {
- if (!_hasMember(classElement, name.name)) {
+ if (!interface.declared.containsKey(name)) {
namesToOverride.add(name);
}
}
diff --git a/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
index 49a5e34..076ae44 100644
--- a/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
@@ -21,6 +21,22 @@
return new OverrideContributor();
}
+ test_alreadyOverridden() async {
+ addTestSource('''
+class A {
+ void foo() {}
+ void bar() {}
+}
+
+class B implements A {
+ void bar() {}
+ f^
+}
+''');
+ await computeSuggestions();
+ _assertNoOverrideContaining('bar');
+ }
+
test_fromMultipleSuperclasses() async {
addTestSource(r'''
class A {
@@ -152,6 +168,61 @@
selectionLength: 27);
}
+ test_inClass_of_interface() async {
+ addTestSource('''
+class A {
+ void foo() {}
+}
+
+class B implements A {
+ f^
+}
+''');
+ await computeSuggestions();
+ _assertOverride('''
+@override
+ void foo() {
+ // TODO: implement foo
+ }''', displayText: 'foo() { … }', selectionOffset: 51, selectionLength: 0);
+ }
+
+ test_inMixin_of_interface() async {
+ addTestSource('''
+class A {
+ void foo() {}
+}
+
+mixin M implements A {
+ f^
+}
+''');
+ await computeSuggestions();
+ _assertOverride('''
+@override
+ void foo() {
+ // TODO: implement foo
+ }''', displayText: 'foo() { … }', selectionOffset: 51, selectionLength: 0);
+ }
+
+ test_inMixin_of_superclassConstraint() async {
+ addTestSource('''
+class A {
+ void foo() {}
+}
+
+mixin M on A {
+ f^
+}
+''');
+ await computeSuggestions();
+ _assertOverride('''
+@override
+ void foo() {
+ // TODO: implement foo
+ super.foo();
+ }''', displayText: 'foo() { … }', selectionOffset: 56, selectionLength: 12);
+ }
+
@failingTest
test_insideBareClass() async {
addTestSource('''
@@ -278,6 +349,14 @@
selectionLength: 22);
}
+ void _assertNoOverrideContaining(String search) {
+ expect(
+ suggestions.where((c) =>
+ c.kind == CompletionSuggestionKind.OVERRIDE &&
+ c.completion.contains(search)),
+ isEmpty);
+ }
+
CompletionSuggestion _assertOverride(String completion,
{String displayText, int selectionOffset, int selectionLength}) {
CompletionSuggestion cs = getSuggest(
diff --git a/pkg/analyzer/lib/src/dart/element/inheritance_manager2.dart b/pkg/analyzer/lib/src/dart/element/inheritance_manager2.dart
index 40f988c..8acdc92 100644
--- a/pkg/analyzer/lib/src/dart/element/inheritance_manager2.dart
+++ b/pkg/analyzer/lib/src/dart/element/inheritance_manager2.dart
@@ -450,6 +450,11 @@
this._superImplemented,
this.conflicts,
);
+
+ /// Return `true` if the [name] is implemented in the supertype.
+ bool isSuperImplemented(Name name) {
+ return _superImplemented.last.containsKey(name);
+ }
}
/// A public name, or a private name qualified by a library URI.
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
index b9d641f..3cd8e09 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
@@ -29,6 +29,7 @@
defineReflectiveTests(DartFileEditBuilderImplTest);
defineReflectiveTests(DartLinkedEditBuilderImplTest);
defineReflectiveTests(ImportLibraryTest);
+ defineReflectiveTests(WriteOverrideTest);
});
}
@@ -871,464 +872,6 @@
expect(edit.replacement, equalsIgnoringWhitespace('mixin M on A { }'));
}
- test_writeOverride_getter_abstract() async {
- await _assertWriteOverride(
- content: '''
-abstract class A {
- int get zero;
-}
-class B extends A {
-}
-''',
- nameToOverride: 'zero',
- expected: '''
- @override
- // TODO: implement zero
- int get zero => null;
-''',
- displayText: 'zero => …',
- selection: new SourceRange(111, 4),
- );
- }
-
- test_writeOverride_getter_concrete() async {
- await _assertWriteOverride(
- content: '''
-class A {
- int get zero => 0;
-}
-class B extends A {
-}
-''',
- nameToOverride: 'zero',
- expected: '''
- @override
- // TODO: implement zero
- int get zero => super.zero;
-''',
- displayText: 'zero => …',
- selection: new SourceRange(107, 10),
- );
- }
-
- test_writeOverride_method_abstract() async {
- await _assertWriteOverride(
- content: '''
-abstract class A {
- A add(A a);
-}
-class B extends A {
-}
-''',
- nameToOverride: 'add',
- expected: '''
- @override
- A add(A a) {
- // TODO: implement add
- return null;
- }
-''',
- displayText: 'add(A a) { … }',
- selection: new SourceRange(111, 12),
- );
- }
-
- test_writeOverride_method_concrete() async {
- await _assertWriteOverride(
- content: '''
-class A {
- A add(A a) => null;
-}
-class B extends A {
-}
-''',
- nameToOverride: 'add',
- expected: '''
- @override
- A add(A a) {
- // TODO: implement add
- return super.add(a);
- }
-''',
- displayText: 'add(A a) { … }',
- selection: new SourceRange(110, 20),
- );
- }
-
- test_writeOverride_method_functionTypeAlias_abstract() async {
- await _assertWriteOverride(
- content: '''
-typedef int F(int left, int right);
-abstract class A {
- void perform(F f);
-}
-class B extends A {
-}
-''',
- nameToOverride: 'perform',
- expected: '''
- @override
- void perform(F f) {
- // TODO: implement perform
- }
-''',
- displayText: 'perform(F f) { … }',
- );
- }
-
- test_writeOverride_method_functionTypeAlias_concrete() async {
- await _assertWriteOverride(
- content: '''
-typedef int F(int left, int right);
-class A {
- void perform(F f) {}
-}
-class B extends A {
-}
-''',
- nameToOverride: 'perform',
- expected: '''
- @override
- void perform(F f) {
- // TODO: implement perform
- super.perform(f);
- }
-''',
- displayText: 'perform(F f) { … }',
- selection: new SourceRange(158, 17),
- );
- }
-
- test_writeOverride_method_functionTypedParameter_abstract() async {
- await _assertWriteOverride(
- content: '''
-abstract class A {
- forEach(int f(double p1, String p2));
-}
-class B extends A {
-}
-''',
- nameToOverride: 'forEach',
- expected: '''
- @override
- forEach(int Function(double p1, String p2) f) {
- // TODO: implement forEach
- return null;
- }
-''',
- displayText: 'forEach(int Function(double p1, String p2) f) { … }',
- selection: new SourceRange(176, 12),
- );
- }
-
- test_writeOverride_method_functionTypedParameter_concrete() async {
- await _assertWriteOverride(
- content: '''
-class A {
- forEach(int f(double p1, String p2)) {}
-}
-class B extends A {
-}
-''',
- nameToOverride: 'forEach',
- expected: '''
- @override
- forEach(int Function(double p1, String p2) f) {
- // TODO: implement forEach
- return super.forEach(f);
- }
-''',
- displayText: 'forEach(int Function(double p1, String p2) f) { … }',
- selection: new SourceRange(169, 24),
- );
- }
-
- test_writeOverride_method_generic_noBounds_abstract() async {
- await _assertWriteOverride(
- content: '''
-abstract class A {
- List<T> get<T>(T key);
-}
-class B implements A {
-}
-''',
- nameToOverride: 'get',
- expected: '''
- @override
- List<T> get<T>(T key) {
- // TODO: implement get
- return null;
- }
-''',
- displayText: 'get<T>(T key) { … }',
- selection: new SourceRange(136, 12),
- );
- }
-
- test_writeOverride_method_generic_noBounds_concrete() async {
- await _assertWriteOverride(
- content: '''
-class A {
- List<T> get<T>(T key) {}
-}
-class B implements A {
-}
-''',
- nameToOverride: 'get',
- expected: '''
- @override
- List<T> get<T>(T key) {
- // TODO: implement get
- return super.get(key);
- }
-''',
- displayText: 'get<T>(T key) { … }',
- selection: new SourceRange(129, 22),
- );
- }
-
- test_writeOverride_method_generic_withBounds_abstract() async {
- await _assertWriteOverride(
- content: '''
-abstract class A<K1, V1> {
- List<T> get<T extends V1>(K1 key);
-}
-class B<K2, V2> implements A<K2, V2> {
-}
-''',
- nameToOverride: 'get',
- expected: '''
- @override
- List<T> get<T extends V2>(K2 key) {
- // TODO: implement get
- return null;
- }
-''',
- displayText: 'get<T extends V2>(K2 key) { … }',
- selection: new SourceRange(184, 12),
- );
- }
-
- test_writeOverride_method_generic_withBounds_concrete() async {
- await _assertWriteOverride(
- content: '''
-class A<K1, V1> {
- List<T> get<T extends V1>(K1 key) {
- return null;
- }
-}
-class B<K2, V2> implements A<K2, V2> {
-}
-''',
- nameToOverride: 'get',
- expected: '''
- @override
- List<T> get<T extends V2>(K2 key) {
- // TODO: implement get
- return super.get(key);
- }
-''',
- displayText: 'get<T extends V2>(K2 key) { … }',
- selection: new SourceRange(197, 22),
- );
- }
-
- test_writeOverride_method_genericFunctionTypedParameter_abstract() async {
- await _assertWriteOverride(
- content: '''
-abstract class A {
- int foo(T Function<T>() fn);
-}
-class B extends A {
-}
-''',
- nameToOverride: 'foo',
- expected: '''
- @override
- int foo(T Function<T>() fn) {
- // TODO: implement foo
- return null;
- }
-''',
- displayText: 'foo(T Function<T>() fn) { … }',
- selection: new SourceRange(145, 12),
- );
- }
-
- test_writeOverride_method_genericFunctionTypedParameter_concrete() async {
- await _assertWriteOverride(
- content: '''
-class A {
- int foo(T Function<T>() fn) => 0;
-}
-class B extends A {
-}
-''',
- nameToOverride: 'foo',
- expected: '''
- @override
- int foo(T Function<T>() fn) {
- // TODO: implement foo
- return super.foo(fn);
- }
-''',
- displayText: 'foo(T Function<T>() fn) { … }',
- selection: new SourceRange(141, 21),
- );
- }
-
- test_writeOverride_method_nullAsTypeArgument_abstract() async {
- await _assertWriteOverride(
- content: '''
-abstract class A {
- List<Null> foo();
-}
-class B extends A {
-}
-''',
- nameToOverride: 'foo',
- expected: '''
- @override
- List<Null> foo() {
- // TODO: implement foo
- return null;
- }
-''',
- displayText: 'foo() { … }',
- selection: new SourceRange(123, 12),
- );
- }
-
- test_writeOverride_method_nullAsTypeArgument_concrete() async {
- await _assertWriteOverride(
- content: '''
-class A {
- List<Null> foo() => null
-}
-class B extends A {
-}
-''',
- nameToOverride: 'foo',
- expected: '''
- @override
- List<Null> foo() {
- // TODO: implement foo
- return super.foo();
- }
-''',
- displayText: 'foo() { … }',
- selection: new SourceRange(121, 19),
- );
- }
-
- test_writeOverride_method_returnVoid_abstract() async {
- await _assertWriteOverride(
- content: '''
-abstract class A {
- void test();
-}
-class B extends A {
-}
-''',
- nameToOverride: 'test',
- expected: '''
- @override
- void test() {
- // TODO: implement test
- }
-''',
- displayText: 'test() { … }',
- selection: new SourceRange(109, 0),
- );
- }
-
- test_writeOverride_method_voidAsTypeArgument_abstract() async {
- await _assertWriteOverride(
- content: '''
-abstract class A {
- List<void> foo();
-}
-class B extends A {
-}
-''',
- nameToOverride: 'foo',
- expected: '''
- @override
- List<void> foo() {
- // TODO: implement foo
- return null;
- }
-''',
- displayText: 'foo() { … }',
- selection: new SourceRange(123, 12),
- );
- }
-
- test_writeOverride_method_voidAsTypeArgument_concrete() async {
- await _assertWriteOverride(
- content: '''
-class A {
- List<void> foo() => null;
-}
-class B extends A {
-}
-''',
- nameToOverride: 'foo',
- expected: '''
- @override
- List<void> foo() {
- // TODO: implement foo
- return super.foo();
- }
-''',
- displayText: 'foo() { … }',
- selection: new SourceRange(122, 19),
- );
- }
-
- test_writeOverride_setter_abstract() async {
- await _assertWriteOverride(
- content: '''
-abstract class A {
- set value(int value);
-}
-class B extends A {
-}
-''',
- nameToOverride: 'value=',
- expected: '''
- @override
- void set value(int value) {
- // TODO: implement value
- }
-''',
- displayText: 'value(int value) { … }',
- selection: new SourceRange(133, 0),
- );
- }
-
- test_writeOverride_setter_concrete() async {
- await _assertWriteOverride(
- content: '''
-class A {
- set value(int value) {}
-}
-class B extends A {
-}
-''',
- nameToOverride: 'value=',
- expected: '''
- @override
- void set value(int value) {
- // TODO: implement value
- super.value = value;
- }
-''',
- displayText: 'value(int value) { … }',
- selection: new SourceRange(131, 20),
- );
- }
-
test_writeParameter() async {
String path = convertPath('/home/test/lib/test.dart');
String content = 'class A {}';
@@ -1948,54 +1491,6 @@
expect(edit.replacement, equalsIgnoringWhitespace('implements A, B'));
}
- /**
- * Assuming that the [content] being edited defines a class named `A` whose
- * member with the given [nameToOverride] to be overridden and has
- * `class B extends A {...}` to which an inherited method is to be added,
- * assert that the text of the overridden member matches the [expected] text
- * (modulo white space). Assert that the generated display text matches the
- * given [displayText]. If a [selection] is provided, assert that the
- * generated selection range matches it.
- */
- _assertWriteOverride({
- String content,
- String nameToOverride,
- String expected,
- String displayText,
- SourceRange selection,
- }) async {
- String path = convertPath('/home/test/lib/test.dart');
- addSource(path, content);
-
- TypeSystem typeSystem = await session.typeSystem;
- var b = await _getClassElement(path, 'B');
- var inherited = new InheritanceManager2(typeSystem).getInherited(
- b.type,
- new Name(null, nameToOverride),
- );
-
- StringBuffer displayBuffer =
- displayText != null ? new StringBuffer() : null;
-
- DartChangeBuilderImpl builder = newBuilder();
- await builder.addFileEdit(path, (FileEditBuilder builder) {
- builder.addInsertion(content.length - 2, (EditBuilder builder) {
- ExecutableElement element = inherited.element as ExecutableElement;
- (builder as DartEditBuilder).writeOverride(
- inherited,
- displayTextBuffer: displayBuffer,
- invokeSuper: !element.isAbstract,
- );
- });
- });
- SourceEdit edit = getEdit(builder);
- expect(edit.replacement, equalsIgnoringWhitespace(expected));
- expect(displayBuffer?.toString(), displayText);
- if (selection != null) {
- expect(builder.selectionRange, selection);
- }
- }
-
Future<void> _assertWriteType(String typeCode, {String declarations}) async {
String path = convertPath('/home/test/lib/test.dart');
String content = (declarations ?? '') + '$typeCode v;';
@@ -2562,3 +2057,583 @@
expect(resultCode, expectedCode);
}
}
+
+@reflectiveTest
+class WriteOverrideTest extends AbstractContextTest with BuilderTestMixin {
+ test_getter_abstract() async {
+ await _assertWriteOverride(
+ content: '''
+abstract class A {
+ int get zero;
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'zero',
+ expected: '''
+ @override
+ // TODO: implement zero
+ int get zero => null;
+''',
+ displayText: 'zero => …',
+ selection: new SourceRange(111, 4),
+ );
+ }
+
+ test_getter_concrete() async {
+ await _assertWriteOverride(
+ content: '''
+class A {
+ int get zero => 0;
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'zero',
+ invokeSuper: true,
+ expected: '''
+ @override
+ // TODO: implement zero
+ int get zero => super.zero;
+''',
+ displayText: 'zero => …',
+ selection: new SourceRange(107, 10),
+ );
+ }
+
+ test_method_abstract() async {
+ await _assertWriteOverride(
+ content: '''
+abstract class A {
+ A add(A a);
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'add',
+ expected: '''
+ @override
+ A add(A a) {
+ // TODO: implement add
+ return null;
+ }
+''',
+ displayText: 'add(A a) { … }',
+ selection: new SourceRange(111, 12),
+ );
+ }
+
+ test_method_concrete() async {
+ await _assertWriteOverride(
+ content: '''
+class A {
+ A add(A a) => null;
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'add',
+ invokeSuper: true,
+ expected: '''
+ @override
+ A add(A a) {
+ // TODO: implement add
+ return super.add(a);
+ }
+''',
+ displayText: 'add(A a) { … }',
+ selection: new SourceRange(110, 20),
+ );
+ }
+
+ test_method_functionTypeAlias_abstract() async {
+ await _assertWriteOverride(
+ content: '''
+typedef int F(int left, int right);
+abstract class A {
+ void perform(F f);
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'perform',
+ expected: '''
+ @override
+ void perform(F f) {
+ // TODO: implement perform
+ }
+''',
+ displayText: 'perform(F f) { … }',
+ );
+ }
+
+ test_method_functionTypeAlias_concrete() async {
+ await _assertWriteOverride(
+ content: '''
+typedef int F(int left, int right);
+class A {
+ void perform(F f) {}
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'perform',
+ invokeSuper: true,
+ expected: '''
+ @override
+ void perform(F f) {
+ // TODO: implement perform
+ super.perform(f);
+ }
+''',
+ displayText: 'perform(F f) { … }',
+ selection: new SourceRange(158, 17),
+ );
+ }
+
+ test_method_functionTypedParameter_abstract() async {
+ await _assertWriteOverride(
+ content: '''
+abstract class A {
+ forEach(int f(double p1, String p2));
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'forEach',
+ expected: '''
+ @override
+ forEach(int Function(double p1, String p2) f) {
+ // TODO: implement forEach
+ return null;
+ }
+''',
+ displayText: 'forEach(int Function(double p1, String p2) f) { … }',
+ selection: new SourceRange(176, 12),
+ );
+ }
+
+ test_method_functionTypedParameter_concrete() async {
+ await _assertWriteOverride(
+ content: '''
+class A {
+ forEach(int f(double p1, String p2)) {}
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'forEach',
+ invokeSuper: true,
+ expected: '''
+ @override
+ forEach(int Function(double p1, String p2) f) {
+ // TODO: implement forEach
+ return super.forEach(f);
+ }
+''',
+ displayText: 'forEach(int Function(double p1, String p2) f) { … }',
+ selection: new SourceRange(169, 24),
+ );
+ }
+
+ test_method_generic_noBounds_abstract() async {
+ await _assertWriteOverride(
+ content: '''
+abstract class A {
+ List<T> get<T>(T key);
+}
+class B implements A {
+}
+''',
+ nameToOverride: 'get',
+ expected: '''
+ @override
+ List<T> get<T>(T key) {
+ // TODO: implement get
+ return null;
+ }
+''',
+ displayText: 'get<T>(T key) { … }',
+ selection: new SourceRange(136, 12),
+ );
+ }
+
+ test_method_generic_noBounds_concrete() async {
+ await _assertWriteOverride(
+ content: '''
+class A {
+ List<T> get<T>(T key) {}
+}
+class B implements A {
+}
+''',
+ nameToOverride: 'get',
+ invokeSuper: true,
+ expected: '''
+ @override
+ List<T> get<T>(T key) {
+ // TODO: implement get
+ return super.get(key);
+ }
+''',
+ displayText: 'get<T>(T key) { … }',
+ selection: new SourceRange(129, 22),
+ );
+ }
+
+ test_method_generic_withBounds_abstract() async {
+ await _assertWriteOverride(
+ content: '''
+abstract class A<K1, V1> {
+ List<T> get<T extends V1>(K1 key);
+}
+class B<K2, V2> implements A<K2, V2> {
+}
+''',
+ nameToOverride: 'get',
+ expected: '''
+ @override
+ List<T> get<T extends V2>(K2 key) {
+ // TODO: implement get
+ return null;
+ }
+''',
+ displayText: 'get<T extends V2>(K2 key) { … }',
+ selection: new SourceRange(184, 12),
+ );
+ }
+
+ test_method_generic_withBounds_concrete() async {
+ await _assertWriteOverride(
+ content: '''
+class A<K1, V1> {
+ List<T> get<T extends V1>(K1 key) {
+ return null;
+ }
+}
+class B<K2, V2> implements A<K2, V2> {
+}
+''',
+ nameToOverride: 'get',
+ invokeSuper: true,
+ expected: '''
+ @override
+ List<T> get<T extends V2>(K2 key) {
+ // TODO: implement get
+ return super.get(key);
+ }
+''',
+ displayText: 'get<T extends V2>(K2 key) { … }',
+ selection: new SourceRange(197, 22),
+ );
+ }
+
+ test_method_genericFunctionTypedParameter_abstract() async {
+ await _assertWriteOverride(
+ content: '''
+abstract class A {
+ int foo(T Function<T>() fn);
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'foo',
+ expected: '''
+ @override
+ int foo(T Function<T>() fn) {
+ // TODO: implement foo
+ return null;
+ }
+''',
+ displayText: 'foo(T Function<T>() fn) { … }',
+ selection: new SourceRange(145, 12),
+ );
+ }
+
+ test_method_genericFunctionTypedParameter_concrete() async {
+ await _assertWriteOverride(
+ content: '''
+class A {
+ int foo(T Function<T>() fn) => 0;
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'foo',
+ invokeSuper: true,
+ expected: '''
+ @override
+ int foo(T Function<T>() fn) {
+ // TODO: implement foo
+ return super.foo(fn);
+ }
+''',
+ displayText: 'foo(T Function<T>() fn) { … }',
+ selection: new SourceRange(141, 21),
+ );
+ }
+
+ test_method_nullAsTypeArgument_abstract() async {
+ await _assertWriteOverride(
+ content: '''
+abstract class A {
+ List<Null> foo();
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'foo',
+ expected: '''
+ @override
+ List<Null> foo() {
+ // TODO: implement foo
+ return null;
+ }
+''',
+ displayText: 'foo() { … }',
+ selection: new SourceRange(123, 12),
+ );
+ }
+
+ test_method_nullAsTypeArgument_concrete() async {
+ await _assertWriteOverride(
+ content: '''
+class A {
+ List<Null> foo() => null
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'foo',
+ invokeSuper: true,
+ expected: '''
+ @override
+ List<Null> foo() {
+ // TODO: implement foo
+ return super.foo();
+ }
+''',
+ displayText: 'foo() { … }',
+ selection: new SourceRange(121, 19),
+ );
+ }
+
+ test_method_returnVoid_abstract() async {
+ await _assertWriteOverride(
+ content: '''
+abstract class A {
+ void test();
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'test',
+ expected: '''
+ @override
+ void test() {
+ // TODO: implement test
+ }
+''',
+ displayText: 'test() { … }',
+ selection: new SourceRange(109, 0),
+ );
+ }
+
+ test_method_voidAsTypeArgument_abstract() async {
+ await _assertWriteOverride(
+ content: '''
+abstract class A {
+ List<void> foo();
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'foo',
+ expected: '''
+ @override
+ List<void> foo() {
+ // TODO: implement foo
+ return null;
+ }
+''',
+ displayText: 'foo() { … }',
+ selection: new SourceRange(123, 12),
+ );
+ }
+
+ test_method_voidAsTypeArgument_concrete() async {
+ await _assertWriteOverride(
+ content: '''
+class A {
+ List<void> foo() => null;
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'foo',
+ invokeSuper: true,
+ expected: '''
+ @override
+ List<void> foo() {
+ // TODO: implement foo
+ return super.foo();
+ }
+''',
+ displayText: 'foo() { … }',
+ selection: new SourceRange(122, 19),
+ );
+ }
+
+ test_mixin_method_of_interface() async {
+ await _assertWriteOverride(
+ content: '''
+class A {
+ void foo(int a) {}
+}
+
+mixin M implements A {
+}
+''',
+ nameToOverride: 'foo',
+ targetMixinName: 'M',
+ expected: '''
+ @override
+ void foo(int a) {
+ // TODO: implement foo
+ }
+''',
+ displayText: 'foo(int a) { … }',
+ selection: new SourceRange(113, 0),
+ );
+ }
+
+ test_mixin_method_of_superclassConstraint() async {
+ await _assertWriteOverride(
+ content: '''
+class A {
+ void foo(int a) {}
+}
+
+mixin M on A {
+}
+''',
+ nameToOverride: 'foo',
+ targetMixinName: 'M',
+ invokeSuper: true,
+ expected: '''
+ @override
+ void foo(int a) {
+ // TODO: implement foo
+ super.foo(a);
+ }
+''',
+ displayText: 'foo(int a) { … }',
+ selection: new SourceRange(110, 13),
+ );
+ }
+
+ test_setter_abstract() async {
+ await _assertWriteOverride(
+ content: '''
+abstract class A {
+ set value(int value);
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'value=',
+ expected: '''
+ @override
+ void set value(int value) {
+ // TODO: implement value
+ }
+''',
+ displayText: 'value(int value) { … }',
+ selection: new SourceRange(133, 0),
+ );
+ }
+
+ test_setter_concrete() async {
+ await _assertWriteOverride(
+ content: '''
+class A {
+ set value(int value) {}
+}
+class B extends A {
+}
+''',
+ nameToOverride: 'value=',
+ invokeSuper: true,
+ expected: '''
+ @override
+ void set value(int value) {
+ // TODO: implement value
+ super.value = value;
+ }
+''',
+ displayText: 'value(int value) { … }',
+ selection: new SourceRange(131, 20),
+ );
+ }
+
+ /**
+ * Assuming that the [content] being edited defines a class named `A` whose
+ * member with the given [nameToOverride] to be overridden and has
+ * `class B extends A {...}` to which an inherited method is to be added,
+ * assert that the text of the overridden member matches the [expected] text
+ * (modulo white space). Assert that the generated display text matches the
+ * given [displayText]. If a [selection] is provided, assert that the
+ * generated selection range matches it.
+ */
+ _assertWriteOverride({
+ String content,
+ String nameToOverride,
+ String expected,
+ String displayText,
+ SourceRange selection,
+ String targetClassName = 'B',
+ String targetMixinName,
+ bool invokeSuper = false,
+ }) async {
+ String path = convertPath('/home/test/lib/test.dart');
+ addSource(path, content);
+
+ ClassElement targetElement;
+ {
+ var unitResult = await driver.getUnitElement(path);
+ if (targetMixinName != null) {
+ targetElement = unitResult.element.mixins
+ .firstWhere((e) => e.name == targetMixinName);
+ } else {
+ targetElement = unitResult.element.types
+ .firstWhere((e) => e.name == targetClassName);
+ }
+ }
+
+ TypeSystem typeSystem = await session.typeSystem;
+ var inherited = new InheritanceManager2(typeSystem).getInherited(
+ targetElement.type,
+ new Name(null, nameToOverride),
+ );
+
+ StringBuffer displayBuffer =
+ displayText != null ? new StringBuffer() : null;
+
+ DartChangeBuilderImpl builder = newBuilder();
+ await builder.addFileEdit(path, (FileEditBuilder builder) {
+ builder.addInsertion(content.length - 2, (EditBuilder builder) {
+ (builder as DartEditBuilder).writeOverride(
+ inherited,
+ displayTextBuffer: displayBuffer,
+ invokeSuper: invokeSuper,
+ );
+ });
+ });
+ SourceEdit edit = getEdit(builder);
+ expect(edit.replacement, equalsIgnoringWhitespace(expected));
+ expect(displayBuffer?.toString(), displayText);
+ if (selection != null) {
+ expect(builder.selectionRange, selection);
+ }
+ }
+}