mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(linter/eslint/one-var): keep declare when splitting declarations (#27081)
## Root cause With `"never"`, `report_split` (`crates/oxc_linter/src/rules/eslint/one_var.rs:524-551`) inserts a new declaration prefix before every declarator after the first. The prefix (`:537-538`) is only `export` plus the keyword, so `declare` is dropped and the split-off declarators become runtime declarations. With oxlint 1.85.0, `--fix` gives: | input | output | | --- | --- | | `declare var a: string, b: number;` | `declare var a: string; var b: number;` | | `export declare let a: string, b: number;` | `export declare let a: string; export let b: number;` | | `declare const a: string, b: number;` | `declare const a: string; const b: number;` | The first two still parse, but add a runtime `var b` / `export let b` that the source did not have. The last one no longer parses (`Missing initializer in const declaration`); in a debug build it trips the fixer's syntax check. ## Fix Add `declare ` to the prefix when `declaration.declare` is set, after `export`, so each split-off declaration keeps the modifiers of the original one. The join path already handles `declare` (`report_join` reports without a fix since #25314); the split path only has to keep the modifier. Declarations inside `declare namespace` / `declare global` don't carry the flag, so they are split as before (TypeScript rejects `declare` in an already ambient context). Alternative: report without a fix when `declare` is set, like the join path. I kept the fix because re-emitting the modifier is enough to make the output equivalent to the input. ESLint's fixer builds the same `export` + keyword prefix and has no TypeScript handling, so there is no upstream behavior to match; this is a correctness fix. ## Notes AI-assisted: I used Claude Code while investigating; I reviewed, ran and verified every change myself. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cec6571f03
commit
42dfbb5010
2 files changed
+68
-1
No files matched your search
@@ -535,7 +535,9 @@ fn report_split(
|
||||
ctx.diagnostic_with_fix(diagnostic, |fixer| {
|
||||
let keyword = declaration.kind.as_str();
|
||||
let exported = matches!(ctx.nodes().parent_kind(node.id()), AstKind::ExportDeclaration(_));
|
||||
let prefix = if exported { format!("export {keyword} ") } else { format!("{keyword} ") };
|
||||
let export = if exported { "export " } else { "" };
|
||||
let declare = if declaration.declare { "declare " } else { "" };
|
||||
let prefix = format!("{export}{declare}{keyword} ");
|
||||
let mut fixes = fixer.new_fix_with_capacity((declaration.declarations.len() - 1) * 2);
|
||||
for [left, right] in declaration.declarations.array_windows() {
|
||||
let offset = ctx
|
||||
@@ -1331,6 +1333,11 @@ fn test() {
|
||||
Some(serde_json::json!(["always"])),
|
||||
),
|
||||
("declare const foo: number; const bar = 2;", Some(serde_json::json!(["always"]))),
|
||||
("declare var a: string, b: number;", Some(serde_json::json!(["never"]))),
|
||||
("declare const a: string, b: number;", Some(serde_json::json!(["never"]))),
|
||||
("export declare let a: string, b: number;", Some(serde_json::json!(["never"]))),
|
||||
("declare namespace N { var a, b; }", Some(serde_json::json!(["never"]))),
|
||||
("declare global { var a: string, b: number; }", Some(serde_json::json!(["never"]))),
|
||||
("if (foo) var x, y;", Some(serde_json::json!(["never"]))),
|
||||
("if (foo) var x, y;", Some(serde_json::json!([{ "var": "never" }]))),
|
||||
("if (foo) var x, y;", Some(serde_json::json!([{ "uninitialized": "never" }]))),
|
||||
@@ -1899,6 +1906,31 @@ fn test() {
|
||||
"declare const foo: number; const bar = 2;",
|
||||
Some(serde_json::json!(["always"])),
|
||||
),
|
||||
(
|
||||
"declare var a: string, b: number;",
|
||||
"declare var a: string; declare var b: number;",
|
||||
Some(serde_json::json!(["never"])),
|
||||
),
|
||||
(
|
||||
"declare const a: string, b: number;",
|
||||
"declare const a: string; declare const b: number;",
|
||||
Some(serde_json::json!(["never"])),
|
||||
),
|
||||
(
|
||||
"export declare let a: string, b: number;",
|
||||
"export declare let a: string; export declare let b: number;",
|
||||
Some(serde_json::json!(["never"])),
|
||||
),
|
||||
(
|
||||
"declare namespace N { var a, b; }",
|
||||
"declare namespace N { var a; var b; }",
|
||||
Some(serde_json::json!(["never"])),
|
||||
),
|
||||
(
|
||||
"declare global { var a: string, b: number; }",
|
||||
"declare global { var a: string; var b: number; }",
|
||||
Some(serde_json::json!(["never"])),
|
||||
),
|
||||
(
|
||||
"class C { static { let x, y; } }",
|
||||
"class C { static { let x; let y; } }",
|
||||
|
||||
@@ -972,6 +972,41 @@ source: crates/oxc_linter/src/tester.rs
|
||||
· ──────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(one-var): Split 'var' declarations into multiple statements.
|
||||
╭─[one_var.tsx:1:1]
|
||||
1 │ declare var a: string, b: number;
|
||||
· ─────────────────────────────────
|
||||
╰────
|
||||
help: Split variable declarations
|
||||
|
||||
⚠ eslint(one-var): Split 'const' declarations into multiple statements.
|
||||
╭─[one_var.tsx:1:1]
|
||||
1 │ declare const a: string, b: number;
|
||||
· ───────────────────────────────────
|
||||
╰────
|
||||
help: Split variable declarations
|
||||
|
||||
⚠ eslint(one-var): Split 'let' declarations into multiple statements.
|
||||
╭─[one_var.tsx:1:8]
|
||||
1 │ export declare let a: string, b: number;
|
||||
· ─────────────────────────────────
|
||||
╰────
|
||||
help: Split variable declarations
|
||||
|
||||
⚠ eslint(one-var): Split 'var' declarations into multiple statements.
|
||||
╭─[one_var.tsx:1:23]
|
||||
1 │ declare namespace N { var a, b; }
|
||||
· ─────────
|
||||
╰────
|
||||
help: Split variable declarations
|
||||
|
||||
⚠ eslint(one-var): Split 'var' declarations into multiple statements.
|
||||
╭─[one_var.tsx:1:18]
|
||||
1 │ declare global { var a: string, b: number; }
|
||||
· ─────────────────────────
|
||||
╰────
|
||||
help: Split variable declarations
|
||||
|
||||
⚠ eslint(one-var): Split 'var' declarations into multiple statements.
|
||||
╭─[one_var.tsx:1:10]
|
||||
1 │ if (foo) var x, y;
|
||||
|
||||
Reference in new issue
Block a user