Add Forest support for while statements

Change-Id: Iffec7799642a44fc6ab2b5f8b7493135d2d05ac5
Reviewed-on: https://dart-review.googlesource.com/55906
Reviewed-by: Dan Rubel <danrubel@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/fasta/ast_building_factory.dart b/pkg/analyzer/lib/src/fasta/ast_building_factory.dart
index 5980a25..ae79499 100644
--- a/pkg/analyzer/lib/src/fasta/ast_building_factory.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_building_factory.dart
@@ -239,6 +239,21 @@
   int readOffset(AstNode node) => node.offset;
 
   @override
+  void resolveBreak(Statement target, BreakStatement user) {
+    user.target = target;
+  }
+
+  @override
+  void resolveContinue(Statement target, ContinueStatement user) {
+    user.target = target;
+  }
+
+  @override
+  void resolveContinueInSwitch(SwitchStatement target, ContinueStatement user) {
+    user.target = target;
+  }
+
+  @override
   Statement rethrowStatement(Token rethrowKeyword, Token semicolon) =>
       astFactory.expressionStatement(
           astFactory.rethrowExpression(rethrowKeyword), semicolon);
@@ -249,6 +264,9 @@
       astFactory.adjacentStrings(strings.cast<StringLiteral>());
 
   @override
+  Statement syntheticLabeledStatement(Statement statement) => statement;
+
+  @override
   Expression thisExpression(Token thisKeyword) =>
       astFactory.thisExpression(thisKeyword);
 
@@ -280,6 +298,12 @@
   }
 
   @override
+  Statement whileStatement(Token whileKeyword,
+          ParenthesizedExpression condition, Statement body) =>
+      astFactory.whileStatement(whileKeyword, condition.leftParenthesis,
+          condition.expression, condition.rightParenthesis, body);
+
+  @override
   Statement wrapVariables(Statement statement) => statement;
 
   @override
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 2ca4888..64a8c0a 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -12,6 +12,8 @@
 
 import '../fasta_codes.dart' show LocatedMessage, Message, noLength, Template;
 
+import 'forest.dart' show Forest;
+
 import '../messages.dart' as messages show getLocationFromUri;
 
 import '../modifier.dart' show Modifier, constMask, covariantMask, finalMask;
@@ -408,7 +410,8 @@
 
   @override
   JumpTarget createJumpTarget(JumpTargetKind kind, int charOffset) {
-    return new JumpTarget(kind, functionNestingLevel, member, charOffset);
+    return new JumpTarget<Statement>(
+        kind, functionNestingLevel, member, charOffset);
   }
 
   @override
@@ -1810,7 +1813,7 @@
     }
   }
 
-  void exitLoopOrSwitch(kernel.Statement statement) {
+  void exitLoopOrSwitch(Statement statement) {
     if (compileTimeErrorInLoopOrSwitch != null) {
       push(compileTimeErrorInLoopOrSwitch);
       compileTimeErrorInLoopOrSwitch = null;
@@ -1876,7 +1879,7 @@
     kernel.Statement kernelBody = toKernelStatement(body);
     if (continueTarget.hasUsers) {
       kernelBody = new ShadowLabeledStatement(kernelBody);
-      continueTarget.resolveContinues(kernelBody);
+      continueTarget.resolveContinues(forest, kernelBody);
     }
     kernel.Statement result = new ShadowForStatement(
         variables,
@@ -1886,9 +1889,9 @@
       ..fileOffset = forKeyword.charOffset;
     if (breakTarget.hasUsers) {
       result = new ShadowLabeledStatement(result);
-      breakTarget.resolveBreaks(result);
+      breakTarget.resolveBreaks(forest, result);
     }
-    exitLoopOrSwitch(result);
+    exitLoopOrSwitch(toStatement(result));
   }
 
   @override
@@ -3152,16 +3155,16 @@
     kernel.Statement kernelBody = toKernelStatement(body);
     if (continueTarget.hasUsers) {
       kernelBody = new ShadowLabeledStatement(kernelBody);
-      continueTarget.resolveContinues(kernelBody);
+      continueTarget.resolveContinues(forest, kernelBody);
     }
     kernel.Statement result =
         new ShadowDoStatement(kernelBody, toKernelExpression(condition))
           ..fileOffset = doKeyword.charOffset;
     if (breakTarget.hasUsers) {
       result = new ShadowLabeledStatement(result);
-      breakTarget.resolveBreaks(result);
+      breakTarget.resolveBreaks(forest, result);
     }
-    exitLoopOrSwitch(result);
+    exitLoopOrSwitch(toStatement(result));
   }
 
   @override
@@ -3190,7 +3193,7 @@
     kernel.Statement kernelBody = toKernelStatement(body);
     if (continueTarget.hasUsers) {
       kernelBody = new ShadowLabeledStatement(kernelBody);
-      continueTarget.resolveContinues(kernelBody);
+      continueTarget.resolveContinues(forest, kernelBody);
     }
     VariableDeclaration variable;
     bool declaresVariable = false;
@@ -3243,9 +3246,9 @@
       ..bodyOffset = kernelBody.fileOffset;
     if (breakTarget.hasUsers) {
       result = new ShadowLabeledStatement(result);
-      breakTarget.resolveBreaks(result);
+      breakTarget.resolveBreaks(forest, result);
     }
-    exitLoopOrSwitch(result);
+    exitLoopOrSwitch(toStatement(result));
   }
 
   @override
@@ -3260,8 +3263,8 @@
     debugEvent("beginLabeledStatement");
     List<Label> labels = popList(labelCount);
     enterLocalScope(null, scope.createNestedLabelScope());
-    LabelTarget target =
-        new LabelTarget(member, functionNestingLevel, token.charOffset);
+    LabelTarget target = new LabelTarget<Statement>(
+        member, functionNestingLevel, token.charOffset);
     for (Label label in labels) {
       scope.declareLabel(label.name, target);
     }
@@ -3279,13 +3282,13 @@
       if (kernelStatement is! LabeledStatement) {
         kernelStatement = new ShadowLabeledStatement(kernelStatement);
       }
-      target.breakTarget.resolveBreaks(kernelStatement);
+      target.breakTarget.resolveBreaks(forest, kernelStatement);
     }
     if (target.continueTarget.hasUsers) {
       if (kernelStatement is! LabeledStatement) {
         kernelStatement = new ShadowLabeledStatement(kernelStatement);
       }
-      target.continueTarget.resolveContinues(kernelStatement);
+      target.continueTarget.resolveContinues(forest, kernelStatement);
     }
     push(kernelStatement);
   }
@@ -3315,17 +3318,14 @@
     Expression condition = popForValue();
     JumpTarget continueTarget = exitContinueTarget();
     JumpTarget breakTarget = exitBreakTarget();
-    kernel.Statement kernelBody = toKernelStatement(body);
     if (continueTarget.hasUsers) {
-      kernelBody = new ShadowLabeledStatement(kernelBody);
-      continueTarget.resolveContinues(kernelBody);
+      body = forest.syntheticLabeledStatement(body);
+      continueTarget.resolveContinues(forest, body);
     }
-    kernel.Statement result =
-        new ShadowWhileStatement(toKernelExpression(condition), kernelBody)
-          ..fileOffset = whileKeyword.charOffset;
+    Statement result = forest.whileStatement(whileKeyword, condition, body);
     if (breakTarget.hasUsers) {
-      result = new ShadowLabeledStatement(result);
-      breakTarget.resolveBreaks(result);
+      result = forest.syntheticLabeledStatement(result);
+      breakTarget.resolveBreaks(forest, result);
     }
     exitLoopOrSwitch(result);
   }
@@ -3500,9 +3500,9 @@
           ..fileOffset = switchKeyword.charOffset;
     if (target.hasUsers) {
       result = new ShadowLabeledStatement(result);
-      target.resolveBreaks(result);
+      target.resolveBreaks(forest, result);
     }
-    exitLoopOrSwitch(result);
+    exitLoopOrSwitch(toStatement(result));
   }
 
   @override
@@ -3516,7 +3516,7 @@
       for (Label label in labels) {
         JumpTarget target = switchScope.lookupLabel(label.name);
         if (target != null) {
-          target.resolveGotos(current);
+          target.resolveGotos(forest, current);
         }
       }
     }
@@ -4442,8 +4442,8 @@
   }
 }
 
-class JumpTarget extends Builder {
-  final List<kernel.Statement> users = <kernel.Statement>[];
+class JumpTarget<Statement> extends Builder {
+  final List<Statement> users = <Statement>[];
 
   final JumpTargetKind kind;
 
@@ -4461,41 +4461,44 @@
 
   bool get hasUsers => users.isNotEmpty;
 
-  void addBreak(BreakStatement statement) {
+  void addBreak(Statement statement) {
     assert(isBreakTarget);
     users.add(statement);
   }
 
-  void addContinue(BreakStatement statement) {
+  void addContinue(Statement statement) {
     assert(isContinueTarget);
     users.add(statement);
   }
 
-  void addGoto(ContinueSwitchStatement statement) {
+  void addGoto(Statement statement) {
     assert(isGotoTarget);
     users.add(statement);
   }
 
-  void resolveBreaks(LabeledStatement target) {
+  void resolveBreaks(
+      Forest<dynamic, Statement, dynamic, dynamic> forest, Statement target) {
     assert(isBreakTarget);
-    for (BreakStatement user in users) {
-      user.target = target;
+    for (Statement user in users) {
+      forest.resolveBreak(target, user);
     }
     users.clear();
   }
 
-  void resolveContinues(LabeledStatement target) {
+  void resolveContinues(
+      Forest<dynamic, Statement, dynamic, dynamic> forest, Statement target) {
     assert(isContinueTarget);
-    for (BreakStatement user in users) {
-      user.target = target;
+    for (Statement user in users) {
+      forest.resolveContinue(target, user);
     }
     users.clear();
   }
 
-  void resolveGotos(SwitchCase target) {
+  void resolveGotos(
+      Forest<dynamic, Statement, dynamic, dynamic> forest, Object target) {
     assert(isGotoTarget);
-    for (ContinueSwitchStatement user in users) {
-      user.target = target;
+    for (Statement user in users) {
+      forest.resolveContinueInSwitch(target, user);
     }
     users.clear();
   }
@@ -4504,7 +4507,7 @@
   String get fullNameForErrors => "<jump-target>";
 }
 
-class LabelTarget extends Builder implements JumpTarget {
+class LabelTarget<Statement> extends Builder implements JumpTarget<Statement> {
   final JumpTarget breakTarget;
 
   final JumpTarget continueTarget;
@@ -4512,15 +4515,15 @@
   final int functionNestingLevel;
 
   LabelTarget(MemberBuilder member, this.functionNestingLevel, int charOffset)
-      : breakTarget = new JumpTarget(
+      : breakTarget = new JumpTarget<Statement>(
             JumpTargetKind.Break, functionNestingLevel, member, charOffset),
-        continueTarget = new JumpTarget(
+        continueTarget = new JumpTarget<Statement>(
             JumpTargetKind.Continue, functionNestingLevel, member, charOffset),
         super(member, charOffset, member.fileUri);
 
   bool get hasUsers => breakTarget.hasUsers || continueTarget.hasUsers;
 
-  List<kernel.Statement> get users => unsupported("users", charOffset, fileUri);
+  List<Statement> get users => unsupported("users", charOffset, fileUri);
 
   JumpTargetKind get kind => unsupported("kind", charOffset, fileUri);
 
@@ -4530,27 +4533,30 @@
 
   bool get isGotoTarget => false;
 
-  void addBreak(BreakStatement statement) {
+  void addBreak(Statement statement) {
     breakTarget.addBreak(statement);
   }
 
-  void addContinue(BreakStatement statement) {
+  void addContinue(Statement statement) {
     continueTarget.addContinue(statement);
   }
 
-  void addGoto(ContinueSwitchStatement statement) {
+  void addGoto(Statement statement) {
     unsupported("addGoto", charOffset, fileUri);
   }
 
-  void resolveBreaks(LabeledStatement target) {
-    breakTarget.resolveBreaks(target);
+  void resolveBreaks(
+      Forest<dynamic, Statement, dynamic, dynamic> forest, Statement target) {
+    breakTarget.resolveBreaks(forest, target);
   }
 
-  void resolveContinues(LabeledStatement target) {
-    continueTarget.resolveContinues(target);
+  void resolveContinues(
+      Forest<dynamic, Statement, dynamic, dynamic> forest, Statement target) {
+    continueTarget.resolveContinues(forest, target);
   }
 
-  void resolveGotos(SwitchCase target) {
+  void resolveGotos(
+      Forest<dynamic, Statement, dynamic, dynamic> forest, Object target) {
     unsupported("resolveGotos", charOffset, fileUri);
   }
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/fangorn.dart b/pkg/front_end/lib/src/fasta/kernel/fangorn.dart
index aff0f1b..aea6f0c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/fangorn.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/fangorn.dart
@@ -9,18 +9,22 @@
 import 'package:kernel/ast.dart'
     show
         Arguments,
+        BreakStatement,
         Block,
         Catch,
+        ContinueSwitchStatement,
         DartType,
         EmptyStatement,
         Expression,
         ExpressionStatement,
         InvalidExpression,
+        LabeledStatement,
         Let,
         LibraryDependency,
         MapEntry,
         NamedExpression,
         Statement,
+        SwitchCase,
         ThisExpression,
         TreeNode,
         VariableDeclaration,
@@ -47,6 +51,7 @@
         ShadowIntLiteral,
         ShadowIsExpression,
         ShadowIsNotExpression,
+        ShadowLabeledStatement,
         ShadowListLiteral,
         ShadowLoadLibrary,
         ShadowMapLiteral,
@@ -62,6 +67,7 @@
         ShadowTryCatch,
         ShadowTryFinally,
         ShadowTypeLiteral,
+        ShadowWhileStatement,
         ShadowYieldStatement;
 
 import 'forest.dart' show Forest;
@@ -289,6 +295,11 @@
   }
 
   @override
+  Statement syntheticLabeledStatement(Statement statement) {
+    return new ShadowLabeledStatement(statement);
+  }
+
+  @override
   Expression thisExpression(Token token) {
     return new ShadowThisExpression()..fileOffset = offsetForToken(token);
   }
@@ -338,6 +349,13 @@
   }
 
   @override
+  Statement whileStatement(
+      Token whileKeyword, Expression condition, Statement body) {
+    return new ShadowWhileStatement(condition, body)
+      ..fileOffset = whileKeyword.charOffset;
+  }
+
+  @override
   Statement yieldStatement(
       Token yieldKeyword, Token star, Expression expression, Token semicolon) {
     return new ShadowYieldStatement(expression, isYieldStar: star != null)
@@ -373,6 +391,22 @@
 
   @override
   bool isVariablesDeclaration(Object node) => node is _VariablesDeclaration;
+
+  @override
+  void resolveBreak(LabeledStatement target, BreakStatement user) {
+    user.target = target;
+  }
+
+  @override
+  void resolveContinue(LabeledStatement target, BreakStatement user) {
+    user.target = target;
+  }
+
+  @override
+  void resolveContinueInSwitch(
+      SwitchCase target, ContinueSwitchStatement user) {
+    user.target = target;
+  }
 }
 
 class _VariablesDeclaration extends Statement {
diff --git a/pkg/front_end/lib/src/fasta/kernel/forest.dart b/pkg/front_end/lib/src/fasta/kernel/forest.dart
index 07e10b2..1547c7f 100644
--- a/pkg/front_end/lib/src/fasta/kernel/forest.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/forest.dart
@@ -4,10 +4,11 @@
 
 library fasta.forest;
 
-import 'body_builder.dart' show Identifier;
 // TODO(ahe): Remove this import.
 import 'package:kernel/ast.dart' as kernel show Arguments, DartType;
 
+import 'body_builder.dart' show Identifier;
+
 /// A tree factory.
 ///
 /// For now, the [Location] is always a token.
@@ -175,6 +176,11 @@
   Expression stringConcatenationExpression(
       List<Expression> expressions, Location location);
 
+  /// The given [statement] is being used as the target of either a break or
+  /// continue statement. Return the statement that should be used as the actual
+  /// target.
+  Statement syntheticLabeledStatement(Statement statement);
+
   Expression thisExpression(Location location);
 
   /// Return a representation of a throw expression consisting of the
@@ -199,6 +205,11 @@
 
   Statement wrapVariables(Statement statement);
 
+  /// Return a representation of a while statement introduced by the
+  /// [whileKeyword] and consisting of the given [condition] and [body].
+  Statement whileStatement(
+      Location whileKeyword, covariant Expression condition, Statement body);
+
   /// Return a representation of a yield statement consisting of the
   /// [yieldKeyword], [star], [expression], and [semicolon]. The [star] is null
   /// when no star was included in the source code.
@@ -213,6 +224,19 @@
 
   bool isVariablesDeclaration(Object node);
 
+  /// Record that the [user] (a break statement) is associated with the [target]
+  /// statement.
+  void resolveBreak(covariant Statement target, covariant Statement user);
+
+  /// Record that the [user] (a continue statement) is associated with the
+  /// [target] statement.
+  void resolveContinue(covariant Statement target, covariant Statement user);
+
+  /// Record that the [user] (a continue statement inside a switch case) is
+  /// associated with the [target] statement.
+  void resolveContinueInSwitch(
+      covariant Object target, covariant Statement user);
+
   // TODO(ahe): Remove this method when all users are moved here.
   kernel.Arguments castArguments(Arguments arguments) {
     dynamic a = arguments;
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_ast_api.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_ast_api.dart
index 0599d3d..a6d0c8c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_ast_api.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_ast_api.dart
@@ -107,5 +107,4 @@
         ShadowVariableAssignment,
         ShadowVariableDeclaration,
         ShadowVariableGet,
-        ShadowWhileStatement,
         ShadowYieldStatement;