mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
refactor(semantic): checking label in ContinueStatement based on LabelBuilder (#2202)
This commit is contained in:
parent
b694a6a76e
commit
f59e87f9c4
5 changed files with 55 additions and 47 deletions
|
|
@ -1091,6 +1091,19 @@ pub enum Statement<'a> {
|
|||
Declaration(Declaration<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Statement<'a> {
|
||||
pub fn is_iteration_statement(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Statement::DoWhileStatement(_)
|
||||
| Statement::ForInStatement(_)
|
||||
| Statement::ForOfStatement(_)
|
||||
| Statement::ForStatement(_)
|
||||
| Statement::WhileStatement(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Directive Prologue
|
||||
#[derive(Debug, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
|
||||
|
|
|
|||
|
|
@ -594,10 +594,32 @@ struct InvalidLabelJumpTarget(#[label] Span);
|
|||
#[diagnostic()]
|
||||
struct InvalidLabelTarget(#[label("This label is used, but not defined")] Span);
|
||||
|
||||
fn check_label(label: &LabelIdentifier, ctx: &SemanticBuilder) {
|
||||
fn check_label(label: &LabelIdentifier, ctx: &SemanticBuilder, is_continue: bool) {
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[error(
|
||||
"A `{0}` statement can only jump to a label of an enclosing `for`, `while` or `do while` statement."
|
||||
)]
|
||||
#[diagnostic()]
|
||||
struct InvalidLabelNonIteration(
|
||||
&'static str,
|
||||
#[label("This is an non-iteration statement")] Span,
|
||||
#[label("for this label")] Span,
|
||||
);
|
||||
|
||||
if ctx.label_builder.is_inside_labeled_statement() {
|
||||
for labeled in ctx.label_builder.get_accessible_labels() {
|
||||
if label.name == labeled.name {
|
||||
if is_continue
|
||||
&& matches!(ctx.nodes.kind(labeled.id), AstKind::LabeledStatement(stmt) if {
|
||||
let mut body = &stmt.body;
|
||||
while let Statement::LabeledStatement(stmt) = body {
|
||||
body = &stmt.body;
|
||||
}
|
||||
!body.is_iteration_statement()
|
||||
})
|
||||
{
|
||||
ctx.error(InvalidLabelNonIteration("continue", labeled.span, label.span));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -617,7 +639,7 @@ fn check_break_statement<'a>(stmt: &BreakStatement, node: &AstNode<'a>, ctx: &Se
|
|||
struct InvalidBreak(#[label] Span);
|
||||
|
||||
if let Some(label) = &stmt.label {
|
||||
return check_label(label, ctx);
|
||||
return check_label(label, ctx, false);
|
||||
}
|
||||
|
||||
// It is a Syntax Error if this BreakStatement is not nested, directly or indirectly (but not crossing function or static initialization block boundaries), within an IterationStatement or a SwitchStatement.
|
||||
|
|
@ -649,53 +671,16 @@ fn check_continue_statement<'a>(
|
|||
))]
|
||||
struct InvalidContinue(#[label] Span);
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[error(
|
||||
"A `{0}` statement can only jump to a label of an enclosing `for`, `while` or `do while` statement."
|
||||
)]
|
||||
#[diagnostic()]
|
||||
struct InvalidLabelNonIteration(
|
||||
&'static str,
|
||||
#[label("This is an non-iteration statement")] Span,
|
||||
#[label("for this label")] Span,
|
||||
);
|
||||
if let Some(label) = &stmt.label {
|
||||
return check_label(label, ctx, true);
|
||||
}
|
||||
|
||||
// It is a Syntax Error if this ContinueStatement is not nested, directly or indirectly (but not crossing function or static initialization block boundaries), within an IterationStatement.
|
||||
for node_id in ctx.nodes.ancestors(node.id()).skip(1) {
|
||||
match ctx.nodes.kind(node_id) {
|
||||
AstKind::Program(_) => {
|
||||
return stmt.label.as_ref().map_or_else(
|
||||
|| ctx.error(InvalidContinue(stmt.span)),
|
||||
|label| ctx.error(InvalidLabelTarget(label.span)),
|
||||
);
|
||||
AstKind::Program(_) | AstKind::Function(_) | AstKind::StaticBlock(_) => {
|
||||
ctx.error(InvalidContinue(stmt.span));
|
||||
}
|
||||
AstKind::Function(_) | AstKind::StaticBlock(_) => {
|
||||
return stmt.label.as_ref().map_or_else(
|
||||
|| ctx.error(InvalidContinue(stmt.span)),
|
||||
|label| ctx.error(InvalidLabelJumpTarget(label.span)),
|
||||
);
|
||||
}
|
||||
AstKind::LabeledStatement(labeled_statement) => match &stmt.label {
|
||||
Some(label) if label.name == labeled_statement.label.name => {
|
||||
if matches!(
|
||||
labeled_statement.body,
|
||||
Statement::LabeledStatement(_)
|
||||
| Statement::DoWhileStatement(_)
|
||||
| Statement::WhileStatement(_)
|
||||
| Statement::ForStatement(_)
|
||||
| Statement::ForInStatement(_)
|
||||
| Statement::ForOfStatement(_)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
return ctx.error(InvalidLabelNonIteration(
|
||||
"continue",
|
||||
labeled_statement.label.span,
|
||||
label.span,
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
kind if kind.is_iteration_statement() && stmt.label.is_none() => break,
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::AstNodeId;
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Label<'a> {
|
||||
id: AstNodeId,
|
||||
pub id: AstNodeId,
|
||||
pub name: &'a str,
|
||||
pub span: Span,
|
||||
used: bool,
|
||||
|
|
|
|||
|
|
@ -28292,11 +28292,12 @@ Expect Syntax Error: "language/import/import-attributes/json-named-bindings.js"
|
|||
23 │ }
|
||||
╰────
|
||||
|
||||
× Jump target cannot cross function boundary.
|
||||
× Use of undefined label
|
||||
╭─[language/statements/class/static-init-invalid-undefined-continue-target.js:21:1]
|
||||
21 │ x: while (false) {
|
||||
22 │ continue y;
|
||||
· ─
|
||||
· ┬
|
||||
· ╰── This label is used, but not defined
|
||||
23 │ }
|
||||
╰────
|
||||
|
||||
|
|
|
|||
|
|
@ -6688,6 +6688,15 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
|
|||
╰────
|
||||
help: A `continue` statement can only be used within an enclosing `for`, `while` or `do while`
|
||||
|
||||
× Illegal continue statement: no surrounding iteration statement
|
||||
╭─[compiler/invalidContinueInDownlevelAsync.ts:2:1]
|
||||
2 │ if (true) {
|
||||
3 │ continue;
|
||||
· ─────────
|
||||
4 │ }
|
||||
╰────
|
||||
help: A `continue` statement can only be used within an enclosing `for`, `while` or `do while`
|
||||
|
||||
× Expected `;` but found `[`
|
||||
╭─[compiler/invalidLetInForOfAndForIn_ES5.ts:5:1]
|
||||
5 │ var let = 10;
|
||||
|
|
|
|||
Loading…
Reference in a new issue