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);
   }