From 0fb8887d3c20af06817600bd420ace8f81d6eff3 Mon Sep 17 00:00:00 2001 From: Boshen Date: Thu, 9 Mar 2023 13:50:10 +0800 Subject: [PATCH] feat(linter): check `LabeledStatement` in javascript --- .../src/rules/early_error/javascript.rs | 32 +++++++++++++++++-- tasks/coverage/babel.snap | 29 ++++++++++++++--- tasks/coverage/test262.snap | 26 +++++++++++++-- tasks/coverage/typescript.snap | 27 ++++++++++++++-- 4 files changed, 102 insertions(+), 12 deletions(-) diff --git a/crates/oxc_linter/src/rules/early_error/javascript.rs b/crates/oxc_linter/src/rules/early_error/javascript.rs index f63f22755..fa87b39ab 100644 --- a/crates/oxc_linter/src/rules/early_error/javascript.rs +++ b/crates/oxc_linter/src/rules/early_error/javascript.rs @@ -18,13 +18,23 @@ impl Rule for EarlyErrorJavaScript { AstKind::NumberLiteral(lit) => check_number_literal(lit, node, ctx), AstKind::StringLiteral(lit) => check_string_literal(lit, node, ctx), AstKind::RegExpLiteral(lit) => check_regexp_literal(lit, ctx), - AstKind::BreakStatement(lit) => check_break_statement(lit, node, ctx), - AstKind::ContinueStatement(lit) => check_continue_statement(lit, node, ctx), + AstKind::BreakStatement(stmt) => check_break_statement(stmt, node, ctx), + AstKind::ContinueStatement(stmt) => check_continue_statement(stmt, node, ctx), + AstKind::LabeledStatement(stmt) => check_labeled_statement(stmt, node, ctx), _ => {} } } } +#[derive(Debug, Error, Diagnostic)] +#[error("Identifier `{0:?}` has already been declared")] +#[diagnostic()] +struct Redeclaration( + Atom, + #[label("`{0}` has already been declared here")] Span, + #[label("It can not be redeclared here")] Span, +); + fn check_private_identifier<'a>( ident: &PrivateIdentifier, node: &AstNode<'a>, @@ -282,3 +292,21 @@ fn check_continue_statement<'a>( } } } + +fn check_labeled_statement<'a>(stmt: &LabeledStatement, node: &AstNode<'a>, ctx: &LintContext<'a>) { + for node_id in ctx.ancestors(node).skip(1) { + match ctx.kind(node_id) { + // label cannot cross boundary on function or static block + AstKind::Function(_) | AstKind::StaticBlock(_) | AstKind::Program(_) => break, + // check label name redeclaration + AstKind::LabeledStatement(label_stmt) if stmt.label.name == label_stmt.label.name => { + return ctx.diagnostic(Redeclaration( + stmt.label.name.clone(), + label_stmt.label.span, + stmt.label.span, + )); + } + _ => {} + } + } +} diff --git a/tasks/coverage/babel.snap b/tasks/coverage/babel.snap index f7d5baa45..9f267d7da 100644 --- a/tasks/coverage/babel.snap +++ b/tasks/coverage/babel.snap @@ -1,7 +1,7 @@ Babel Summary: AST Parsed : 2056/2069 (99.37%) Positive Passed: 2056/2069 (99.37%) -Negative Passed: 908/1502 (60.45%) +Negative Passed: 911/1502 (60.65%) Expect Syntax Error: "annex-b/disabled/1.1-html-comments-close/input.js" Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-if-body/input.js" Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-multiple-labels/input.js" @@ -61,7 +61,6 @@ Expect Syntax Error: "core/scope/undecl-export/input.js" Expect Syntax Error: "core/uncategorised/415/input.js" Expect Syntax Error: "core/uncategorised/427/input.js" Expect Syntax Error: "core/uncategorised/454/input.js" -Expect Syntax Error: "core/uncategorised/465/input.js" Expect Syntax Error: "core/uncategorised/466/input.js" Expect Syntax Error: "core/uncategorised/467/input.js" Expect Syntax Error: "core/uncategorised/468/input.js" @@ -389,7 +388,6 @@ Expect Syntax Error: "esprima/invalid-syntax/migrated_0101/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0123/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0143/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0171/input.js" -Expect Syntax Error: "esprima/invalid-syntax/migrated_0182/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0183/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0184/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0185/input.js" @@ -453,7 +451,6 @@ Expect Syntax Error: "esprima/invalid-syntax/migrated_0244/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0245/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0246/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0247/input.js" -Expect Syntax Error: "esprima/invalid-syntax/migrated_0248/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0249/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0250/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0260/input.js" @@ -1765,6 +1762,14 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts" ╰──── help: A `continue` statement can only be used within an enclosing `for`, `while` or `do while` + × Identifier `"x"` has already been declared + ╭─[core/uncategorised/465/input.js:1:1] + 1 │ x: while (true) { x: while (true) { } } + · ┬ ┬ + · │ ╰── It can not be redeclared here + · ╰── `x` has already been declared here + ╰──── + × Unexpected token ╭─[core/uncategorised/523/input.js:1:1] 1 │ var this = 10; @@ -6856,6 +6861,22 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts" ╰──── help: A `continue` statement can only be used within an enclosing `for`, `while` or `do while` + × Identifier `"x"` has already been declared + ╭─[esprima/invalid-syntax/migrated_0182/input.js:1:1] + 1 │ x: while (true) { x: while (true) { } } + · ┬ ┬ + · │ ╰── It can not be redeclared here + · ╰── `x` has already been declared here + ╰──── + + × Identifier `"__proto__"` has already been declared + ╭─[esprima/invalid-syntax/migrated_0248/input.js:1:1] + 1 │ __proto__: __proto__: 42; + · ────┬──── ────┬──── + · │ ╰── It can not be redeclared here + · ╰── `__proto__` has already been declared here + ╰──── + × Unexpected token ╭─[esprima/invalid-syntax/migrated_0252/input.js:1:1] 1 │ var diff --git a/tasks/coverage/test262.snap b/tasks/coverage/test262.snap index ec19d7708..858e4cc75 100644 --- a/tasks/coverage/test262.snap +++ b/tasks/coverage/test262.snap @@ -1,7 +1,7 @@ Test262 Summary: AST Parsed : 44000/44009 (99.98%) Positive Passed: 44000/44009 (99.98%) -Negative Passed: 2223/3917 (56.75%) +Negative Passed: 2225/3917 (56.80%) Expect Syntax Error: "annexB/language/statements/for-in/const-initializer.js" Expect Syntax Error: "annexB/language/statements/for-in/let-initializer.js" Expect Syntax Error: "annexB/language/statements/for-in/strict-initializer.js" @@ -851,7 +851,6 @@ Expect Syntax Error: "language/module-code/early-dup-export-dflt-id.js" Expect Syntax Error: "language/module-code/early-dup-export-id-as.js" Expect Syntax Error: "language/module-code/early-dup-export-id.js" Expect Syntax Error: "language/module-code/early-dup-export-star-as-dflt.js" -Expect Syntax Error: "language/module-code/early-dup-lables.js" Expect Syntax Error: "language/module-code/early-dup-lex.js" Expect Syntax Error: "language/module-code/early-dup-top-function-async-generator.js" Expect Syntax Error: "language/module-code/early-dup-top-function-async.js" @@ -1317,7 +1316,6 @@ Expect Syntax Error: "language/statements/class/static-gen-method-param-dflt-yie Expect Syntax Error: "language/statements/class/static-init-await-binding-invalid.js" Expect Syntax Error: "language/statements/class/static-init-invalid-arguments.js" Expect Syntax Error: "language/statements/class/static-init-invalid-await.js" -Expect Syntax Error: "language/statements/class/static-init-invalid-label-dup.js" Expect Syntax Error: "language/statements/class/static-init-invalid-lex-dup.js" Expect Syntax Error: "language/statements/class/static-init-invalid-lex-var.js" Expect Syntax Error: "language/statements/class/static-init-invalid-return.js" @@ -14092,6 +14090,18 @@ Expect to Parse: "language/statements/class/decorator/syntax/class-valid/decorat 17 │ export default var x = null; ╰──── + × Identifier `"label"` has already been declared + ╭─[language/module-code/early-dup-lables.js:15:1] + 15 │ + 16 │ label: { + · ──┬── + · ╰── `label` has already been declared here + 17 │ label: 0; + · ──┬── + · ╰── It can not be redeclared here + 18 │ } + ╰──── + × An export name cannot include a unicode lone surrogate ╭─[language/module-code/early-export-ill-formed-string.js:20:1] 20 │ // 🌙 is '\uD83C\uDF19' @@ -18425,6 +18435,16 @@ Expect to Parse: "language/statements/class/decorator/syntax/class-valid/decorat 18 │ } ╰──── + × Identifier `"x"` has already been declared + ╭─[language/statements/class/static-init-invalid-label-dup.js:20:1] + 20 │ static { + 21 │ x: x: 0; + · ┬ ┬ + · │ ╰── It can not be redeclared here + · ╰── `x` has already been declared here + 22 │ } + ╰──── + × Jump target cannot cross function boundary. ╭─[language/statements/class/static-init-invalid-undefined-break-target.js:21:1] 21 │ x: while (false) { diff --git a/tasks/coverage/typescript.snap b/tasks/coverage/typescript.snap index 8b1470906..bee6ff695 100644 --- a/tasks/coverage/typescript.snap +++ b/tasks/coverage/typescript.snap @@ -1,7 +1,7 @@ TypeScript Summary: AST Parsed : 2310/2338 (98.80%) Positive Passed: 2310/2338 (98.80%) -Negative Passed: 558/2531 (22.05%) +Negative Passed: 560/2531 (22.13%) Expect Syntax Error: "Symbols/ES5SymbolProperty2.ts" Expect Syntax Error: "Symbols/ES5SymbolProperty6.ts" Expect Syntax Error: "additionalChecks/noPropertyAccessFromIndexSignature1.ts" @@ -1337,8 +1337,6 @@ Expect Syntax Error: "parser/ecmascript5/RegularExpressions/parserRegularExpress Expect Syntax Error: "parser/ecmascript5/Statements/BreakStatements/parser_breakInIterationOrSwitchStatement4.ts" Expect Syntax Error: "parser/ecmascript5/Statements/ContinueStatements/parser_continueInIterationStatement4.ts" Expect Syntax Error: "parser/ecmascript5/Statements/ContinueStatements/parser_continueNotInIterationStatement4.ts" -Expect Syntax Error: "parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel1.ts" -Expect Syntax Error: "parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel2.ts" Expect Syntax Error: "parser/ecmascript5/Statements/ReturnStatements/parserReturnStatement1.ts" Expect Syntax Error: "parser/ecmascript5/Statements/ReturnStatements/parserReturnStatement2.ts" Expect Syntax Error: "parser/ecmascript5/Statements/parserBlockStatement1.d.ts" @@ -7445,6 +7443,29 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts" 3 │ } ╰──── + × Identifier `"target"` has already been declared + ╭─[parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel1.ts:1:1] + 1 │ target: + · ───┬── + · ╰── `target` has already been declared here + 2 │ target: + · ───┬── + · ╰── It can not be redeclared here + 3 │ while (true) { + ╰──── + + × Identifier `"target"` has already been declared + ╭─[parser/ecmascript5/Statements/LabeledStatements/parser_duplicateLabel2.ts:1:1] + 1 │ target: + · ───┬── + · ╰── `target` has already been declared here + 2 │ while (true) { + 3 │ target: + · ───┬── + · ╰── It can not be redeclared here + 4 │ while (true) { + ╰──── + × Illegal break statement ╭─[parser/ecmascript5/Statements/parserBreakStatement1.d.ts:1:1] 1 │ break;