mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 20:41:24 +00:00
Code completion for super-formal parameters.
Change-Id: I52b0786f7db8e8ec0181b5e4d7eb352b157856b0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/226606 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
9bfb74e681
commit
794a42074d
|
@ -28,6 +28,7 @@ import 'package:analysis_server/src/services/completion/dart/redirecting_contrib
|
|||
import 'package:analysis_server/src/services/completion/dart/relevance_tables.g.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/static_member_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/super_formal_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/type_member_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/uri_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/variable_name_contributor.dart';
|
||||
|
@ -155,6 +156,7 @@ class DartCompletionManager {
|
|||
if (enableOverrideContributor) OverrideContributor(request, builder),
|
||||
RedirectingContributor(request, builder),
|
||||
StaticMemberContributor(request, builder),
|
||||
SuperFormalContributor(request, builder),
|
||||
TypeMemberContributor(request, builder),
|
||||
if (enableUriContributor) UriContributor(request, builder),
|
||||
VariableNameContributor(request, builder),
|
||||
|
|
|
@ -877,6 +877,17 @@ class SuggestionBuilder {
|
|||
relevance: relevance));
|
||||
}
|
||||
|
||||
/// Add a suggestion to reference a [parameter] in a super formal parameter.
|
||||
void suggestSuperFormalParameter(ParameterElement parameter) {
|
||||
_add(
|
||||
_createSuggestion(
|
||||
parameter,
|
||||
kind: CompletionSuggestionKind.IDENTIFIER,
|
||||
relevance: Relevance.superFormalParameter,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Add a suggestion for a top-level [function]. If a [kind] is provided it
|
||||
/// will be used as the kind for the suggestion. If the function can only be
|
||||
/// referenced using a prefix, then the [prefix] should be provided.
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
/// A contributor that produces suggestions for super formal parameters that
|
||||
/// are based on the parameters declared by the invoked super-constructor.
|
||||
/// The enclosing declaration is expected to be a constructor.
|
||||
class SuperFormalContributor extends DartCompletionContributor {
|
||||
SuperFormalContributor(
|
||||
DartCompletionRequest request,
|
||||
SuggestionBuilder builder,
|
||||
) : super(request, builder);
|
||||
|
||||
@override
|
||||
Future<void> computeSuggestions() async {
|
||||
var node = request.target.containingNode;
|
||||
if (node is! SuperFormalParameter) {
|
||||
return;
|
||||
}
|
||||
|
||||
var element = node.declaredElement as SuperFormalParameterElementImpl;
|
||||
|
||||
var constructor = node.thisOrAncestorOfType<ConstructorDeclaration>();
|
||||
if (constructor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var constructorElement = constructor.declaredElement;
|
||||
constructorElement as ConstructorElementImpl;
|
||||
|
||||
var superConstructor = constructorElement.superConstructor;
|
||||
if (superConstructor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.isNamed) {
|
||||
var superConstructorInvocation = constructor.initializers
|
||||
.whereType<SuperConstructorInvocation>()
|
||||
.singleOrNull;
|
||||
var specified = <String>{
|
||||
...constructorElement.parameters.map((e) => e.name),
|
||||
...?superConstructorInvocation?.argumentList.arguments
|
||||
.whereType<NamedExpression>()
|
||||
.map((e) => e.name.label.name),
|
||||
};
|
||||
for (var superParameter in superConstructor.parameters) {
|
||||
if (superParameter.isNamed &&
|
||||
!specified.contains(superParameter.name)) {
|
||||
builder.suggestSuperFormalParameter(superParameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node.isPositional) {
|
||||
var indexOfThis = element.indexIn(constructorElement);
|
||||
var superPositionalList = superConstructor.parameters
|
||||
.where((parameter) => parameter.isPositional)
|
||||
.toList();
|
||||
if (indexOfThis >= 0 && indexOfThis < superPositionalList.length) {
|
||||
var superPositional = superPositionalList[indexOfThis];
|
||||
builder.suggestSuperFormalParameter(superPositional);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ main() {
|
|||
..docSummary.isEqualTo('aaa')
|
||||
..hasSelection(offset: 5)
|
||||
..element.isNotNull.which((e) => e
|
||||
..isParameter
|
||||
..kind.isParameter
|
||||
..name.isEqualTo('fff'))
|
||||
]);
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ main() {
|
|||
..docSummary.isNull
|
||||
..hasSelection(offset: 5)
|
||||
..element.isNotNull.which((e) => e
|
||||
..isParameter
|
||||
..kind.isParameter
|
||||
..name.isEqualTo('fff'))
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -55,6 +55,20 @@ class CompletionSuggestionForTesting {
|
|||
|
||||
extension CompletionResponseExtension
|
||||
on CheckTarget<CompletionResponseForTesting> {
|
||||
CheckTarget<int> get replacementLength {
|
||||
return nest(
|
||||
value.replacementLength,
|
||||
(selected) => 'has replacementLength ${valueStr(selected)}',
|
||||
);
|
||||
}
|
||||
|
||||
CheckTarget<int> get replacementOffset {
|
||||
return nest(
|
||||
value.replacementOffset,
|
||||
(selected) => 'has replacementOffset ${valueStr(selected)}',
|
||||
);
|
||||
}
|
||||
|
||||
CheckTarget<List<CompletionSuggestionForTesting>> get suggestions {
|
||||
var suggestions = value.suggestions.map((e) {
|
||||
return CompletionSuggestionForTesting(
|
||||
|
@ -67,6 +81,19 @@ extension CompletionResponseExtension
|
|||
(selected) => 'suggestions ${valueStr(selected)}',
|
||||
);
|
||||
}
|
||||
|
||||
/// Check that the replacement offset is the completion request offset,
|
||||
/// and the length of the replacement is zero.
|
||||
void hasEmptyReplacement() {
|
||||
hasReplacement(left: 0, right: 0);
|
||||
}
|
||||
|
||||
/// Check that the replacement offset is the completion request offset
|
||||
/// minus [left], and the length of the replacement is `left + right`.
|
||||
void hasReplacement({int left = 0, int right = 0}) {
|
||||
replacementOffset.isEqualTo(value.requestOffset - left);
|
||||
replacementLength.isEqualTo(left + right);
|
||||
}
|
||||
}
|
||||
|
||||
extension CompletionSuggestionExtension
|
||||
|
@ -113,6 +140,23 @@ extension CompletionSuggestionExtension
|
|||
);
|
||||
}
|
||||
|
||||
void get isField {
|
||||
kind.isIdentifier;
|
||||
element.isNotNull.kind.isField;
|
||||
}
|
||||
|
||||
void get isParameter {
|
||||
kind.isIdentifier;
|
||||
element.isNotNull.kind.isParameter;
|
||||
}
|
||||
|
||||
CheckTarget<CompletionSuggestionKind> get kind {
|
||||
return nest(
|
||||
value.suggestion.kind,
|
||||
(selected) => 'has kind ${valueStr(selected)}',
|
||||
);
|
||||
}
|
||||
|
||||
CheckTarget<String?> get parameterType {
|
||||
return nest(
|
||||
value.suggestion.parameterType,
|
||||
|
@ -136,6 +180,13 @@ extension CompletionSuggestionExtension
|
|||
);
|
||||
}
|
||||
|
||||
CheckTarget<String?> get returnType {
|
||||
return nest(
|
||||
value.suggestion.returnType,
|
||||
(selected) => 'has returnType ${valueStr(selected)}',
|
||||
);
|
||||
}
|
||||
|
||||
CheckTarget<int> get selectionLength {
|
||||
return nest(
|
||||
value.suggestion.selectionLength,
|
||||
|
@ -169,8 +220,22 @@ extension CompletionSuggestionExtension
|
|||
}
|
||||
}
|
||||
|
||||
extension CompletionSuggestionKindExtension
|
||||
on CheckTarget<CompletionSuggestionKind> {
|
||||
void get isIdentifier {
|
||||
isEqualTo(CompletionSuggestionKind.IDENTIFIER);
|
||||
}
|
||||
}
|
||||
|
||||
extension CompletionSuggestionsExtension
|
||||
on CheckTarget<Iterable<CompletionSuggestionForTesting>> {
|
||||
CheckTarget<List<String>> get completions {
|
||||
return nest(
|
||||
value.map((e) => e.suggestion.completion).toList(),
|
||||
(selected) => 'completions ${valueStr(selected)}',
|
||||
);
|
||||
}
|
||||
|
||||
CheckTarget<Iterable<CompletionSuggestionForTesting>> get namedArguments {
|
||||
var result = value
|
||||
.where((suggestion) =>
|
||||
|
@ -185,10 +250,6 @@ extension CompletionSuggestionsExtension
|
|||
}
|
||||
|
||||
extension ElementExtension on CheckTarget<Element> {
|
||||
void get isParameter {
|
||||
kind.isEqualTo(ElementKind.PARAMETER);
|
||||
}
|
||||
|
||||
CheckTarget<ElementKind> get kind {
|
||||
return nest(
|
||||
value.kind,
|
||||
|
@ -203,3 +264,13 @@ extension ElementExtension on CheckTarget<Element> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension ElementKindExtension on CheckTarget<ElementKind> {
|
||||
void get isField {
|
||||
isEqualTo(ElementKind.FIELD);
|
||||
}
|
||||
|
||||
void get isParameter {
|
||||
isEqualTo(ElementKind.PARAMETER);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,10 @@ import 'package:analysis_server/src/provisional/completion/dart/completion_dart.
|
|||
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/field_formal_contributor.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:analyzer_utilities/check/check.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import 'completion_check.dart';
|
||||
import 'completion_contributor_util.dart';
|
||||
|
||||
void main() {
|
||||
|
@ -31,183 +32,120 @@ class FieldFormalContributorTest extends DartCompletionContributorTest {
|
|||
Future<void> test_mixin_constructor() async {
|
||||
addTestSource('''
|
||||
mixin M {
|
||||
var field = 0;
|
||||
M(this.^);
|
||||
}
|
||||
''');
|
||||
await computeSuggestions();
|
||||
expect(suggestions, isEmpty);
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response).suggestions.isEmpty;
|
||||
}
|
||||
|
||||
Future<void> test_ThisExpression_constructor_param() async {
|
||||
// SimpleIdentifier FieldFormalParameter FormalParameterList
|
||||
Future<void> test_replacement_left() async {
|
||||
addTestSource('''
|
||||
main() { }
|
||||
class I {X get f => new A();get _g => new A();}
|
||||
class A implements I {
|
||||
A(this.^) {}
|
||||
A.z() {}
|
||||
var b; X _c; static sb;
|
||||
X get d => new A();get _e => new A();
|
||||
// no semicolon between completion point and next statement
|
||||
set s1(I x) {} set _s2(I x) {m(null);}
|
||||
m(X x) {} I _n(X x) {}}
|
||||
class X{}''');
|
||||
await computeSuggestions();
|
||||
expect(replacementOffset, completionOffset);
|
||||
expect(replacementLength, 0);
|
||||
assertSuggestField('b', null);
|
||||
assertSuggestField('_c', 'X');
|
||||
assertNotSuggested('sb');
|
||||
assertNotSuggested('d');
|
||||
assertNotSuggested('_e');
|
||||
assertNotSuggested('f');
|
||||
assertNotSuggested('_g');
|
||||
assertNotSuggested('m');
|
||||
assertNotSuggested('_n');
|
||||
assertNotSuggested('s1');
|
||||
assertNotSuggested('_s2');
|
||||
assertNotSuggested('z');
|
||||
assertNotSuggested('I');
|
||||
assertNotSuggested('A');
|
||||
assertNotSuggested('X');
|
||||
assertNotSuggested('Object');
|
||||
assertNotSuggested('==');
|
||||
class A {
|
||||
var field = 0;
|
||||
A(this.f^);
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasReplacement(left: 1)
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('field')
|
||||
..isField
|
||||
..returnType.isEqualTo('int'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_ThisExpression_constructor_param2() async {
|
||||
// SimpleIdentifier FieldFormalParameter FormalParameterList
|
||||
Future<void> test_replacement_right() async {
|
||||
addTestSource('''
|
||||
main() { }
|
||||
class I {X get f => new A();get _g => new A();}
|
||||
class A implements I {
|
||||
A(this.b^) {}
|
||||
A.z() {}
|
||||
var b; X _c;
|
||||
X get d => new A();get _e => new A();
|
||||
// no semicolon between completion point and next statement
|
||||
set s1(I x) {} set _s2(I x) {m(null);}
|
||||
m(X x) {} I _n(X x) {}}
|
||||
class X{}''');
|
||||
await computeSuggestions();
|
||||
expect(replacementOffset, completionOffset - 1);
|
||||
expect(replacementLength, 1);
|
||||
assertSuggestField('b', null);
|
||||
assertSuggestField('_c', 'X');
|
||||
assertNotSuggested('d');
|
||||
assertNotSuggested('_e');
|
||||
assertNotSuggested('f');
|
||||
assertNotSuggested('_g');
|
||||
assertNotSuggested('m');
|
||||
assertNotSuggested('_n');
|
||||
assertNotSuggested('s1');
|
||||
assertNotSuggested('_s2');
|
||||
assertNotSuggested('z');
|
||||
assertNotSuggested('I');
|
||||
assertNotSuggested('A');
|
||||
assertNotSuggested('X');
|
||||
assertNotSuggested('Object');
|
||||
assertNotSuggested('==');
|
||||
class A {
|
||||
var field = 0;
|
||||
A(this.^f);
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasReplacement(right: 1)
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('field')
|
||||
..isField
|
||||
..returnType.isEqualTo('int'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_ThisExpression_constructor_param3() async {
|
||||
// SimpleIdentifier FieldFormalParameter FormalParameterList
|
||||
Future<void> test_suggestions_onlyLocal() async {
|
||||
addTestSource('''
|
||||
main() { }
|
||||
class I {X get f => new A();get _g => new A();}
|
||||
class A implements I {
|
||||
A(this.^b) {}
|
||||
A.z() {}
|
||||
var b; X _c;
|
||||
X get d => new A();get _e => new A();
|
||||
// no semicolon between completion point and next statement
|
||||
set s1(I x) {} set _s2(I x) {m(null);}
|
||||
m(X x) {} I _n(X x) {}}
|
||||
class X{}''');
|
||||
await computeSuggestions();
|
||||
expect(replacementOffset, completionOffset);
|
||||
expect(replacementLength, 1);
|
||||
assertSuggestField('b', null);
|
||||
assertSuggestField('_c', 'X');
|
||||
assertNotSuggested('d');
|
||||
assertNotSuggested('_e');
|
||||
assertNotSuggested('f');
|
||||
assertNotSuggested('_g');
|
||||
assertNotSuggested('m');
|
||||
assertNotSuggested('_n');
|
||||
assertNotSuggested('s1');
|
||||
assertNotSuggested('_s2');
|
||||
assertNotSuggested('z');
|
||||
assertNotSuggested('I');
|
||||
assertNotSuggested('A');
|
||||
assertNotSuggested('X');
|
||||
assertNotSuggested('Object');
|
||||
assertNotSuggested('==');
|
||||
class A {
|
||||
var inherited = 0;
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
var first = 0;
|
||||
var second = 1.2;
|
||||
B(this.^);
|
||||
B.constructor() {}
|
||||
void method() {}
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('first')
|
||||
..isField
|
||||
..returnType.isEqualTo('int'),
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isField
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_ThisExpression_constructor_param4() async {
|
||||
// SimpleIdentifier FieldFormalParameter FormalParameterList
|
||||
Future<void> test_suggestions_onlyNotSpecified_optionalNamed() async {
|
||||
addTestSource('''
|
||||
main() { }
|
||||
class I {X get f => new A();get _g => new A();}
|
||||
class A implements I {
|
||||
A(this.b, this.^) {}
|
||||
A.z() {}
|
||||
var b; X _c;
|
||||
X get d => new A();get _e => new A();
|
||||
// no semicolon between completion point and next statement
|
||||
set s1(I x) {} set _s2(I x) {m(null);}
|
||||
m(X x) {} I _n(X x) {}}
|
||||
class X{}''');
|
||||
await computeSuggestions();
|
||||
expect(replacementOffset, completionOffset);
|
||||
expect(replacementLength, 0);
|
||||
assertNotSuggested('b');
|
||||
assertSuggestField('_c', 'X');
|
||||
assertNotSuggested('d');
|
||||
assertNotSuggested('_e');
|
||||
assertNotSuggested('f');
|
||||
assertNotSuggested('_g');
|
||||
assertNotSuggested('m');
|
||||
assertNotSuggested('_n');
|
||||
assertNotSuggested('s1');
|
||||
assertNotSuggested('_s2');
|
||||
assertNotSuggested('z');
|
||||
assertNotSuggested('I');
|
||||
assertNotSuggested('A');
|
||||
assertNotSuggested('X');
|
||||
assertNotSuggested('Object');
|
||||
assertNotSuggested('==');
|
||||
class Point {
|
||||
final int x;
|
||||
final int y;
|
||||
Point({this.x, this.^});
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('y')
|
||||
..isField
|
||||
..returnType.isEqualTo('int'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_ThisExpression_constructor_param_optional() async {
|
||||
// SimpleIdentifier FieldFormalParameter FormalParameterList
|
||||
Future<void> test_suggestions_onlyNotSpecified_requiredPositional() async {
|
||||
addTestSource('''
|
||||
main() { }
|
||||
class Point {
|
||||
int x;
|
||||
int y;
|
||||
Point({this.x, this.^}) {}
|
||||
''');
|
||||
await computeSuggestions();
|
||||
expect(replacementOffset, completionOffset);
|
||||
expect(replacementLength, 0);
|
||||
assertSuggestField('y', 'int');
|
||||
assertNotSuggested('x');
|
||||
}
|
||||
class Point {
|
||||
final int x;
|
||||
final int y;
|
||||
Point(this.x, this.^);
|
||||
}
|
||||
''');
|
||||
|
||||
Future<void> test_ThisExpression_constructor_param_positional() async {
|
||||
// SimpleIdentifier FieldFormalParameter FormalParameterList
|
||||
addTestSource('''
|
||||
main() { }
|
||||
class Point {
|
||||
int x;
|
||||
int y;
|
||||
Point({this.x, this.^}) {}
|
||||
''');
|
||||
await computeSuggestions();
|
||||
expect(replacementOffset, completionOffset);
|
||||
expect(replacementLength, 0);
|
||||
assertSuggestField('y', 'int');
|
||||
assertNotSuggested('x');
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('y')
|
||||
..isField
|
||||
..returnType.isEqualTo('int'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,471 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
|
||||
import 'package:analysis_server/src/services/completion/dart/super_formal_contributor.dart';
|
||||
import 'package:analyzer_utilities/check/check.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import 'completion_check.dart';
|
||||
import 'completion_contributor_util.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(SuperFormalContributorTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class SuperFormalContributorTest extends DartCompletionContributorTest {
|
||||
@override
|
||||
DartCompletionContributor createContributor(
|
||||
DartCompletionRequest request,
|
||||
SuggestionBuilder builder,
|
||||
) {
|
||||
return SuperFormalContributor(request, builder);
|
||||
}
|
||||
|
||||
Future<void> test_explicit_optionalNamed_hasArgument_named() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A({int first, double second});
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B({super.^}) : super(first: 0);
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_explicit_optionalNamed_hasArgument_positional() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A({int first, double second});
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B({super.^}) : super(0);
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('first')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('int'),
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
/// It is an error, but the user already typed `super.`, so maybe do it.
|
||||
Future<void> test_explicit_requiredPositional_hasArgument_positional() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A(int first, double second);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B(super.^) : super(0);
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('first')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('int'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_explicitNamed_noOther() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A.named(int first, double second);
|
||||
A(int third)
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B(super.^) : super.named();
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('first')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('int'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_optionalNamed_hasNamed_notSuper() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A({int first, double second});
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B({int a, super.^});
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('first')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('int'),
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_optionalNamed_hasNamed_notSuper2() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A({int first, double second});
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B({int first, super.^});
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_optionalNamed_hasNamed_super() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A({int first, double second});
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B({super.first, super.^});
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_optionalNamed_hasNamed_super2() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A({int first, double second});
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B({super.second, super.^});
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('first')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('int'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_optionalNamed_hasPositional_notSuper() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A({int first, double second});
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B(int a, {super.^});
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('first')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('int'),
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_optionalNamed_hasPositional_super() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A({int first, double second});
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B(super.first, {super.^});
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_optionalNamed_noOther() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A(bool first, {int second, double third});
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B({super.^});
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('int'),
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('third')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_optionalPositional_hasPositional_notSuper() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A([int first, double second]);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B([int one, super.^]);
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('first')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('int'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_optionalPositional_hasPositional_super() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A([int first, double second, bool third]);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B([super.one, super.^]);
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_optionalPositional_hasPositional_super2() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A([int first, double second, bool third]);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B([super.second, super.^]);
|
||||
}
|
||||
''');
|
||||
|
||||
// It does not matter what is the name of the positional parameter.
|
||||
// Here `super.second` consumes `int first`.
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_optionalPositional_noOther() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A([int first, double second]);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B(super.^);
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('first')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('int'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_requiredPositional_hasPositional_notSuper() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A(int first, double second);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B(int one, super.^);
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('first')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('int'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_requiredPositional_hasPositional_super() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A(int first, double second, bool third);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B(super.one, super.^);
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_requiredPositional_hasPositional_super2() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A(int first, double second, bool third);
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B(super.second, super.^);
|
||||
}
|
||||
''');
|
||||
|
||||
// It does not matter what is the name of the positional parameter.
|
||||
// Here `super.second` consumes `int first`.
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('second')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('double'),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> test_implicit_requiredPositional_noOther() async {
|
||||
addTestSource('''
|
||||
class A {
|
||||
A(int first, double second);
|
||||
A.named(int third)
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
B(super.^);
|
||||
}
|
||||
''');
|
||||
|
||||
var response = await computeSuggestions2();
|
||||
check(response)
|
||||
..hasEmptyReplacement()
|
||||
..suggestions.matchesInAnyOrder([
|
||||
(suggestion) => suggestion
|
||||
..completion.isEqualTo('first')
|
||||
..isParameter
|
||||
..returnType.isEqualTo('int'),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import 'named_constructor_contributor_test.dart' as named_contributor_test;
|
|||
import 'override_contributor_test.dart' as override_contributor_test;
|
||||
import 'relevance/test_all.dart' as relevance_tests;
|
||||
import 'static_member_contributor_test.dart' as static_contributor_test;
|
||||
import 'super_formal_contributor_test.dart' as super_formal_contributor;
|
||||
import 'type_member_contributor_test.dart' as type_member_contributor_test;
|
||||
import 'uri_contributor_test.dart' as uri_contributor_test;
|
||||
import 'variable_name_contributor_test.dart' as variable_name_contributor_test;
|
||||
|
@ -44,6 +45,7 @@ void main() {
|
|||
override_contributor_test.main();
|
||||
relevance_tests.main();
|
||||
static_contributor_test.main();
|
||||
super_formal_contributor.main();
|
||||
type_member_contributor_test.main();
|
||||
uri_contributor_test.main();
|
||||
variable_name_contributor_test.main();
|
||||
|
|
|
@ -5511,12 +5511,9 @@ class SuperFormalParameterElementImpl extends ParameterElementImpl
|
|||
return superParameters
|
||||
.firstWhereOrNull((e) => e.isNamed && e.name == name);
|
||||
} else {
|
||||
var index = indexIn(enclosingElement);
|
||||
var positionalSuperParameters =
|
||||
superParameters.where((e) => e.isPositional).toList();
|
||||
var index = enclosingElement.parameters
|
||||
.whereType<SuperFormalParameterElementImpl>()
|
||||
.toList()
|
||||
.indexOf(this);
|
||||
if (index >= 0 && index < positionalSuperParameters.length) {
|
||||
return positionalSuperParameters[index];
|
||||
}
|
||||
|
@ -5529,6 +5526,14 @@ class SuperFormalParameterElementImpl extends ParameterElementImpl
|
|||
@override
|
||||
T? accept<T>(ElementVisitor<T> visitor) =>
|
||||
visitor.visitSuperFormalParameterElement(this);
|
||||
|
||||
/// Return the index of this super-formal parameter among other super-formals.
|
||||
int indexIn(ConstructorElementImpl enclosingElement) {
|
||||
return enclosingElement.parameters
|
||||
.whereType<SuperFormalParameterElementImpl>()
|
||||
.toList()
|
||||
.indexOf(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// A concrete implementation of a [TopLevelVariableElement].
|
||||
|
|
|
@ -54,6 +54,10 @@ abstract class Relevance {
|
|||
/// The relevance used when suggesting a named argument corresponding to a
|
||||
/// named parameter that is required.
|
||||
static const int requiredNamedArgument = 950;
|
||||
|
||||
/// The relevance used when suggesting a super-constructor parameter as
|
||||
/// a super formal parameter.
|
||||
static const int superFormalParameter = 1000;
|
||||
}
|
||||
|
||||
/// A name scope for constants that are related to the relevance of completion
|
||||
|
|
Loading…
Reference in a new issue