| Index: frog/leg/resolver.dart
|
| diff --git a/frog/leg/resolver.dart b/frog/leg/resolver.dart
|
| index 545af307ab37063b7993a520a3fddc42d7909ef2..e0ad4f1402dc82ba0d87e06728c98ba41aa43241 100644
|
| --- a/frog/leg/resolver.dart
|
| +++ b/frog/leg/resolver.dart
|
| @@ -478,15 +478,36 @@ class LabeledStatementLabelScope implements LabelScope {
|
| final LabelScope outer;
|
| final LabelElement label;
|
| LabeledStatementLabelScope(this.outer, this.label);
|
| - LabelElement lookup(String labelName) {
|
| + LabelElement lookupBreak(String labelName) {
|
| if (this.label.labelName == labelName) return label;
|
| - return outer.lookup(labelName);
|
| + return outer.lookupBreak(labelName);
|
| + }
|
| + LabelElement lookupContinue(String labelName) {
|
| + if (this.label.labelName == labelName) return label;
|
| + return outer.lookupContinue(labelName);
|
| + }
|
| +}
|
| +
|
| +
|
| +class SwitchLabelScope implements LabelScope {
|
| + final LabelScope outer;
|
| + final Map<String, LabelElement> caseLabels;
|
| + SwitchLabelScope(this.outer, this.caseLabels);
|
| + LabelElement lookupBreak(String labelName) {
|
| + return outer.lookupBreak(labelName);
|
| + }
|
| + LabelElement lookupContinue(String labelName) {
|
| + LabelElement result = caseLabels[labelName];
|
| + if (result !== null) return result;
|
| + return outer.lookupContinue(labelName);
|
| }
|
| }
|
|
|
| +
|
| class EmptyLabelScope implements LabelScope {
|
| const EmptyLabelScope();
|
| - LabelElement lookup(String label) => null;
|
| + LabelElement lookupBreak(String label) => null;
|
| + LabelElement lookupContinue(String label) => null;
|
| LabelScope get outer() {
|
| throw 'internal error: empty label scope has no outer';
|
| }
|
| @@ -502,26 +523,46 @@ class StatementScope {
|
| breakTargetStack = const EmptyLink<StatementElement>(),
|
| continueTargetStack = const EmptyLink<StatementElement>();
|
|
|
| - LabelElement lookupLabel(String label) => labels.lookup(label);
|
| + LabelElement lookupBreakLabel(String label) =>
|
| + labels.lookupBreak(label);
|
| +
|
| + LabelElement lookupContinueLabel(String label) =>
|
| + labels.lookupContinue(label);
|
| +
|
| StatementElement currentBreakTarget() =>
|
| breakTargetStack.isEmpty() ? null : breakTargetStack.head;
|
| +
|
| StatementElement currentContinueTarget() =>
|
| continueTargetStack.isEmpty() ? null : continueTargetStack.head;
|
|
|
| void enterLabelScope(LabelElement element) {
|
| labels = new LabeledStatementLabelScope(labels, element);
|
| }
|
| +
|
| void exitLabelScope() {
|
| labels = labels.outer;
|
| }
|
| +
|
| void enterLoop(StatementElement element) {
|
| breakTargetStack = breakTargetStack.prepend(element);
|
| continueTargetStack = continueTargetStack.prepend(element);
|
| }
|
| +
|
| void exitLoop() {
|
| breakTargetStack = breakTargetStack.tail;
|
| continueTargetStack = continueTargetStack.tail;
|
| }
|
| +
|
| + void enterSwitch(StatementElement breakElement,
|
| + Map<String, LabelElement> continueElements) {
|
| + breakTargetStack = breakTargetStack.prepend(breakElement);
|
| + labels = new SwitchLabelScope(labels, continueElements);
|
| + }
|
| +
|
| + void exitSwitch() {
|
| + breakTargetStack = breakTargetStack.tail;
|
| + labels = labels.outer;
|
| + }
|
| }
|
|
|
|
|
| @@ -1049,7 +1090,7 @@ class ResolverVisitor extends CommonResolverVisitor<Element> {
|
| target.isBreakTarget = true;
|
| } else {
|
| String labelName = node.target.source.slowToString();
|
| - LabelElement label = statementScope.lookupLabel(labelName);
|
| + LabelElement label = statementScope.lookupBreakLabel(labelName);
|
| if (label === null) {
|
| error(node.target, MessageKind.UNBOUND_LABEL, [labelName]);
|
| return;
|
| @@ -1071,7 +1112,7 @@ class ResolverVisitor extends CommonResolverVisitor<Element> {
|
| }
|
| } else {
|
| String labelName = node.target.source.slowToString();
|
| - LabelElement label = statementScope.lookupLabel(labelName);
|
| + LabelElement label = statementScope.lookupContinueLabel(labelName);
|
| if (label === null) {
|
| error(node.target, MessageKind.UNBOUND_LABEL, [labelName]);
|
| return;
|
| @@ -1105,7 +1146,7 @@ class ResolverVisitor extends CommonResolverVisitor<Element> {
|
|
|
| visitLabelledStatement(LabelledStatement node) {
|
| String labelName = node.label.source.slowToString();
|
| - LabelElement existingElement = statementScope.lookupLabel(labelName);
|
| + LabelElement existingElement = statementScope.lookupBreakLabel(labelName);
|
| if (existingElement !== null) {
|
| warning(node.label, MessageKind.DUPLICATE_LABEL, [labelName]);
|
| warning(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]);
|
| @@ -1143,20 +1184,60 @@ class ResolverVisitor extends CommonResolverVisitor<Element> {
|
|
|
| visitSwitchStatement(SwitchStatement node) {
|
| node.expression.accept(this);
|
| - StatementElement element = getOrCreateStatementElement(node);
|
| - statementScope.enterLoop(element);
|
| +
|
| + StatementElement breakElement = getOrCreateStatementElement(node);
|
| + Map<String, LabelElement> continueLabels = <LabelElement>{};
|
| + for (SwitchCase switchCase in node.cases) {
|
| + if (switchCase.label !== null) {
|
| + Identifier labelIdentifier = switchCase.label;
|
| + String labelName = labelIdentifier.source.slowToString();
|
| +
|
| + LabelElement existingElement = continueLabels[labelName];
|
| + if (existingElement !== null) {
|
| + // It's an error if the same label occurs twice in the same switch.
|
| + warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]);
|
| + error(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]);
|
| + } else {
|
| + // It's only a warning if it shadows another label.
|
| + existingElement = statementScope.lookupContinue(labelName);
|
| + if (existingElement !== null) {
|
| + warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]);
|
| + warning(existingElement.label,
|
| + MessageKind.EXISTING_LABEL, [labelName]);
|
| + }
|
| + }
|
| +
|
| + StatementElement statementElement =
|
| + new StatementElement(switchCase, enclosingElement);
|
| + elements[switchCase] = statementElement;
|
| +
|
| + LabelElement label =
|
| + new LabelElement(currentLabel, labelName,
|
| + statementElement, enclosingElement);
|
| + elements[labelIdentifier] = label;
|
| + continueLabels[labelName] = label;
|
| +
|
| + currentLabel = null;
|
| + }
|
| + }
|
| + statementScope.enterSwitch(breakElement, continueLabels);
|
| node.cases.accept(this);
|
| - statementScope.exitLoop();
|
| + statementScope.exitSwitch();
|
| +
|
| + // Clean-up unused labels
|
| + continueLabels.forEach((String key, LabelElement label) {
|
| + StatementElement statementElement = label.target;
|
| + SwitchCase switchCase = statementElement.statement;
|
| + if (!label.isContinueTarget) {
|
| + elements.remove(switchCase);
|
| + elements.remove(label.identifier);
|
| + }
|
| + });
|
| }
|
|
|
| visitSwitchCase(SwitchCase node) {
|
| - // TODO(ahe): What about the label?
|
| - node.expression.accept(this);
|
| - node.statements.accept(this);
|
| - }
|
| -
|
| - visitDefaultCase(DefaultCase node) {
|
| - // TODO(ahe): What about the label?
|
| + // The label was handled in [visitSwitchStatement(SwitchStatement)].
|
| + node.expressions.accept(this);
|
| node.statements.accept(this);
|
| }
|
|
|
|
|