Issue 36733. Completion in spread, if, and for elements.
R=brianwilkerson@google.com
Bug: https://github.com/dart-lang/sdk/issues/36733
Change-Id: I019741086495eaaf1f2dc078d801f61a0919cc78
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100523
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
index 93c1024..6e035ce 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
@@ -622,6 +621,22 @@
}
@override
+ visitForElement(ForElement node) {
+ // for (^) {}
+ // for (Str^ str = null;) {}
+ // In theory it is possible to specify any expression in initializer,
+ // but for any practical use we need only types.
+ if (entity == node.forLoopParts) {
+ optype.includeTypeNameSuggestions = true;
+ }
+
+ if (entity == node.body) {
+ optype.includeReturnValueSuggestions = true;
+ optype.includeTypeNameSuggestions = true;
+ }
+ }
+
+ @override
void visitFormalParameterList(FormalParameterList node) {
dynamic entity = this.entity;
if (entity is Token) {
@@ -688,22 +703,6 @@
@override
void visitForStatement(ForStatement node) {
- var entity = this.entity;
- var entity2 = entity; // Work around limitations of type promotion
- if (entity2 is SyntacticEntity &&
- entity2.offset >= node.forLoopParts.offset &&
- entity2.end <= node.forLoopParts.end) {
- // Older versions of the analyzer yield elements of `node.forLoopParts`
- // when iterating through children of `ForEachStatement`. Handle this
- // situation by simulating the behavior of newer versions of the analyzer.
- // TODO(paulberry): remove this case once we require a version of analyzer
- // containing a1349ac52972a4c69e1b05079ed1662b3b0f8c3f
- if (entity2.offset == node.forLoopParts.offset) {
- entity = node.forLoopParts;
- } else {
- return node.forLoopParts.accept(this);
- }
- }
// for (^) {}
// for (Str^ str = null;) {}
// In theory it is possible to specify any expression in initializer,
@@ -736,6 +735,19 @@
}
@override
+ visitIfElement(IfElement node) {
+ if (identical(entity, node.condition)) {
+ optype.includeReturnValueSuggestions = true;
+ optype.includeTypeNameSuggestions = true;
+ } else if (identical(entity, node.thenElement) ||
+ identical(entity, node.elseElement)) {
+ optype.includeReturnValueSuggestions = true;
+ optype.includeTypeNameSuggestions = true;
+ optype.includeVoidReturnSuggestions = true;
+ }
+ }
+
+ @override
void visitIfStatement(IfStatement node) {
if (_isEntityPrevTokenSynthetic()) {
// Actual: if (var v i^)
@@ -970,6 +982,14 @@
}
@override
+ visitSpreadElement(SpreadElement node) {
+ if (identical(entity, node.expression)) {
+ optype.includeReturnValueSuggestions = true;
+ optype.includeTypeNameSuggestions = true;
+ }
+ }
+
+ @override
void visitStringLiteral(StringLiteral node) {
// no suggestions
}
diff --git a/pkg/analyzer_plugin/test/src/utilities/completion/optype_test.dart b/pkg/analyzer_plugin/test/src/utilities/completion/optype_test.dart
index f4c2eb8..caf7f39 100644
--- a/pkg/analyzer_plugin/test/src/utilities/completion/optype_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/completion/optype_test.dart
@@ -715,6 +715,29 @@
functionBody: true);
}
+ test_ForElement_body() async {
+ addTestSource('main(args) {[for (var foo in [0]) ^];}');
+ await assertOpType(
+ constructors: true,
+ returnValue: true,
+ typeNames: true,
+ functionBody: true);
+ }
+
+ test_ForElement_forEachParts_iterable() async {
+ addTestSource('main(args) {[for (var foo in ^) foo];}');
+ await assertOpType(
+ constructors: true,
+ returnValue: true,
+ typeNames: true,
+ functionBody: true);
+ }
+
+ test_ForElement_forEachParts_type() async {
+ addTestSource('main(args) {[for (i^ foo in [0]) foo];}');
+ await assertOpType(typeNames: true, functionBody: true);
+ }
+
test_FormalParameter_partialType() async {
// FormalParameterList MethodDeclaration
addTestSource('class A {a(b.^ f) { }}');
@@ -780,6 +803,47 @@
functionBody: true);
}
+ test_IfElement_condition() async {
+ addTestSource('''
+main() {
+ [if (^)];
+}
+''');
+ await assertOpType(
+ constructors: true,
+ returnValue: true,
+ typeNames: true,
+ functionBody: true);
+ }
+
+ test_IfElement_else() async {
+ addTestSource('''
+main() {
+ [if (true) 0 else ^];
+}
+''');
+ await assertOpType(
+ constructors: true,
+ returnValue: true,
+ typeNames: true,
+ functionBody: true,
+ voidReturn: true);
+ }
+
+ test_IfElement_then() async {
+ addTestSource('''
+main() {
+ [if (true) ^];
+}
+''');
+ await assertOpType(
+ constructors: true,
+ returnValue: true,
+ typeNames: true,
+ functionBody: true,
+ voidReturn: true);
+ }
+
test_IfStatement() async {
// EmptyStatement IfStatement Block BlockFunctionBody
addTestSource('main(){var a; if (true) ^}');
@@ -1043,6 +1107,19 @@
functionBody: true);
}
+ test_SpreadElement() async {
+ addTestSource(r'''
+main() {
+ [...^];
+}
+''');
+ await assertOpType(
+ constructors: true,
+ returnValue: true,
+ typeNames: true,
+ functionBody: true);
+ }
+
test_SwitchCase_between() async {
// SwitchCase SwitchStatement Block
addTestSource('main() {switch(k) {case 1: ^ case 2: return}}');
@@ -1234,7 +1311,7 @@
// ArgumentList InstanceCreationExpression ExpressionStatement Block
addTestSource(
'main() { new A.b(^); }'
- 'class A{ A.b({one, two}) {} }',
+ 'class A{ A.b({one, two}) {} }',
);
await assertOpType(namedArgs: true, functionBody: true);
}
@@ -1243,7 +1320,7 @@
// ArgumentList InstanceCreationExpression ExpressionStatement Block
addTestSource(
'main() { new A.b(o^); }'
- 'class A { A.b({one, two}) {} }',
+ 'class A { A.b({one, two}) {} }',
);
await assertOpType(namedArgs: true, functionBody: true);
}
@@ -1252,7 +1329,7 @@
// ArgumentList InstanceCreationExpression ExpressionStatement Block
addTestSource(
'main() { new A(^); }'
- 'class A{ A({one, two}) {} }',
+ 'class A{ A({one, two}) {} }',
);
await assertOpType(namedArgs: true, functionBody: true);
}
@@ -1261,7 +1338,7 @@
// ArgumentList InstanceCreationExpression ExpressionStatement Block
addTestSource(
'main() { new A(o^); }'
- 'class A { A({one, two}) {} }',
+ 'class A { A({one, two}) {} }',
);
await assertOpType(namedArgs: true, functionBody: true);
}
@@ -1270,7 +1347,7 @@
// ArgumentList InstanceCreationExpression ExpressionStatement Block
addTestSource(
'main() { new A.b(^); }'
- 'class A{ factory A.b({one, two}) {} }',
+ 'class A{ factory A.b({one, two}) {} }',
);
await assertOpType(namedArgs: true, functionBody: true);
}
@@ -1279,7 +1356,7 @@
// ArgumentList InstanceCreationExpression ExpressionStatement Block
addTestSource(
'main() { new A.b(o^); }'
- 'class A { factory A.b({one, two}) {} }',
+ 'class A { factory A.b({one, two}) {} }',
);
await assertOpType(namedArgs: true, functionBody: true);
}
@@ -1288,7 +1365,7 @@
// ArgumentList InstanceCreationExpression ExpressionStatement Block
addTestSource(
'main() { new A(^); }'
- 'class A{ factory A({one, two}) {} }',
+ 'class A{ factory A({one, two}) {} }',
);
await assertOpType(namedArgs: true, functionBody: true);
}
@@ -1297,7 +1374,7 @@
// ArgumentList InstanceCreationExpression ExpressionStatement Block
addTestSource(
'main() { new A(o^); }'
- 'class A { factory A({one, two}) {} }',
+ 'class A { factory A({one, two}) {} }',
);
await assertOpType(namedArgs: true, functionBody: true);
}