Fix handling of for-elements with unparenthesized bodies.

The CFE defers the final build of the body of a for-element until
after building the rest of the body.  This caused a bug: if the body
was an assignment expression, flow analysis was not picking up on the
fact that the assignment was inside the loop.

Fixed by introducing a mechanism that allows the CFE to temporarily
pop the innermost structure on the AssignedVariables stack and then
push it again.

Change-Id: I11d526302934e9283807124ebd4a23e9d3e5ec66
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164980
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index 218b4f6..e404a1a 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -155,6 +155,22 @@
     }());
   }
 
+  /// Call this method between calls to [beginNode] and [endNode]/[deferNode],
+  /// if it is necessary to temporarily process some code outside the current
+  /// node.  Returns a data structure that should be passed to [pushNode].
+  ///
+  /// This is used by the front end when building for-elements in lists, maps,
+  /// and sets; their initializers are partially built after building their
+  /// loop conditions but before completely building their bodies.
+  AssignedVariablesNodeInfo<Variable> popNode() {
+    return _stack.removeLast();
+  }
+
+  /// Call this method to un-do the effect of [popNode].
+  void pushNode(AssignedVariablesNodeInfo<Variable> node) {
+    _stack.add(node);
+  }
+
   /// Call this method to register that the node [from] for which information
   /// has been stored is replaced by the node [to].
   // TODO(johnniwinther): Remove this when unified collections are encoded as
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart
index 8259c35..cb3d20b 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart
@@ -7,11 +7,21 @@
   [/*assigned={b, c, d}*/ for (a = 0; (b = 0) != 0; c = 0) (d = 0)];
 }
 
+/*member: cStyle_unparenthesized:declared={a, b, c, d, e}, assigned={a, b, d, e}*/
+cStyle_unparenthesized(int a, bool b, bool c, int d, int e) {
+  [/*assigned={b, d, e}*/ for (a = 0; b = c; d = 0) e = 0];
+}
+
 /*member: cStyleWithDeclaration:declared={a, b, c, d, e}, assigned={a, b, c, d}*/
 cStyleWithDeclaration(int a, int b, int c, int d) {
   [/*assigned={b, c, d}*/ for (int e = (a = 0); (b = 0) != 0; c = 0) (d = 0)];
 }
 
+/*member: cStyleWithDeclaration_unparenthesized:declared={a, b, c, d, e, f}, assigned={a, b, d, e}*/
+cStyleWithDeclaration_unparenthesized(int a, bool b, bool c, int d, int e) {
+  [/*assigned={b, d, e}*/ for (int f = a = 0; b = c; d = 0) e = 0];
+}
+
 /*member: forEach:declared={a, b, c}, assigned={a, b, c}*/
 forEach(int a, int b, int c) {
   [
@@ -19,9 +29,23 @@
   ];
 }
 
+/*member: forEach_unparenthesized:declared={a, b, c}, assigned={a, b, c}*/
+forEach_unparenthesized(int a, int b, int c) {
+  [
+    /*assigned={c}*/ for (a in [b = 0]) c = 0
+  ];
+}
+
 /*member: forEachWithDeclaration:declared={a, b, c}, assigned={a, b}*/
 forEachWithDeclaration(int a, int b) {
   [
     /*assigned={b}*/ for (var c in [a = 0]) (b = 0)
   ];
 }
+
+/*member: forEachWithDeclaration_unparenthesized:declared={a, b, c}, assigned={a, b}*/
+forEachWithDeclaration_unparenthesized(int a, int b) {
+  [
+    /*assigned={b}*/ for (var c in [a = 0]) b = 0
+  ];
+}
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index a07d1a8..de52a7e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -2804,7 +2804,7 @@
     // [handleForInitializerExpressionStatement], and
     // [handleForInitializerLocalVariableDeclaration].
     AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesNodeInfo =
-        typeInferrer?.assignedVariables?.deferNode();
+        typeInferrer?.assignedVariables?.popNode();
 
     Object variableOrExpression = pop();
     exitLocalScope();
@@ -2812,6 +2812,7 @@
     transformCollections = true;
     List<VariableDeclaration> variables =
         _buildForLoopVariableDeclarations(variableOrExpression);
+    typeInferrer?.assignedVariables?.pushNode(assignedVariablesNodeInfo);
     Expression condition;
     if (conditionStatement is ExpressionStatement) {
       condition = conditionStatement.expression;
@@ -2821,14 +2822,12 @@
     if (entry is MapEntry) {
       ForMapEntry result = forest.createForMapEntry(
           offsetForToken(forToken), variables, condition, updates, entry);
-      typeInferrer?.assignedVariables
-          ?.storeInfo(result, assignedVariablesNodeInfo);
+      typeInferrer?.assignedVariables?.endNode(result);
       push(result);
     } else {
       ForElement result = forest.createForElement(offsetForToken(forToken),
           variables, condition, updates, toValue(entry));
-      typeInferrer?.assignedVariables
-          ?.storeInfo(result, assignedVariablesNodeInfo);
+      typeInferrer?.assignedVariables?.endNode(result);
       push(result);
     }
   }
@@ -5039,7 +5038,7 @@
 
     // This is matched by the call to [beginNode] in [handleForInLoopParts].
     AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesNodeInfo =
-        typeInferrer?.assignedVariables?.deferNode();
+        typeInferrer?.assignedVariables?.popNode();
 
     Expression iterable = popForValue();
     Object lvalue = pop(); // lvalue
@@ -5048,6 +5047,7 @@
     transformCollections = true;
     ForInElements elements =
         _computeForInElements(forToken, inToken, lvalue, null);
+    typeInferrer?.assignedVariables?.pushNode(assignedVariablesNodeInfo);
     VariableDeclaration variable = elements.variable;
     Expression problem = elements.expressionProblem;
     if (entry is MapEntry) {
@@ -5060,8 +5060,7 @@
           entry,
           problem,
           isAsync: awaitToken != null);
-      typeInferrer?.assignedVariables
-          ?.storeInfo(result, assignedVariablesNodeInfo);
+      typeInferrer?.assignedVariables?.endNode(result);
       push(result);
     } else {
       ForInElement result = forest.createForInElement(
@@ -5073,8 +5072,7 @@
           toValue(entry),
           problem,
           isAsync: awaitToken != null);
-      typeInferrer?.assignedVariables
-          ?.storeInfo(result, assignedVariablesNodeInfo);
+      typeInferrer?.assignedVariables?.endNode(result);
       push(result);
     }
   }