mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(linter/eslint/prefer-const): skip fixers if any declarator is reassigned (#28008)
When a `let` declaration had multiple declarators and an earlier declarator was reassigned while the last declarator was not (for example, `let a = 1, b = 2; a++;`), `eslint/prefer-const` passed the last declarator's `all_const` flag to `fix_let_to_const` and rewrote the entire declaration to `const`. Check that every binding identifier across all declarators in the `VariableDeclaration` can be `const` before applying the `let` to `const` fix, while continuing to report diagnostics on the individual declarators that are never reassigned. Closes #28003 --------- Co-authored-by: Cameron Clark <cameron.clark@hey.com>
This commit is contained in:
1 parent
71ef6a7ff7
commit
f3705a976b
1 file changed
+16
-8
@@ -131,26 +131,22 @@ impl Rule for PreferConst {
|
||||
let is_for_in_of_init =
|
||||
matches!(parent.kind(), AstKind::ForInStatement(_) | AstKind::ForOfStatement(_));
|
||||
|
||||
// For regular for loops (for (let i = 0, j = 1; ...)), we need special handling
|
||||
// If ANY variable in the declaration is reassigned, we can't fix the whole declaration
|
||||
let is_for_statement_init = matches!(parent.kind(), AstKind::ForStatement(_));
|
||||
|
||||
if is_for_statement_init && decl.declarations.len() > 1 {
|
||||
// Check if any declarator is reassigned
|
||||
let any_reassigned = decl.declarations.iter().any(|declarator| {
|
||||
let has_init = declarator.init.is_some();
|
||||
declarator.id.get_binding_identifiers().iter().any(|ident| {
|
||||
let symbol_id = ident.symbol_id();
|
||||
!self.should_be_const(symbol_id, has_init, is_for_in_of_init, ctx)
|
||||
!self.should_be_const(ident.symbol_id(), has_init, is_for_in_of_init, ctx)
|
||||
})
|
||||
});
|
||||
|
||||
// If any variable is reassigned, we can't convert any of them
|
||||
if any_reassigned {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut all_declarators_const = true;
|
||||
for (declarator_index, declarator) in decl.declarations.iter().enumerate() {
|
||||
let binding_identifiers = declarator.id.get_binding_identifiers();
|
||||
let has_init = declarator.init.is_some();
|
||||
@@ -160,6 +156,7 @@ impl Rule for PreferConst {
|
||||
let symbol_id = ident.symbol_id();
|
||||
self.should_be_const(symbol_id, has_init, is_for_in_of_init, ctx)
|
||||
});
|
||||
all_declarators_const &= all_const;
|
||||
|
||||
// For destructuring patterns with "all" mode, check if ALL variables should be const
|
||||
if matches!(self.0.destructuring, Destructuring::All) && is_destructuring {
|
||||
@@ -173,7 +170,7 @@ impl Rule for PreferConst {
|
||||
fixer,
|
||||
decl,
|
||||
declarator_index,
|
||||
true,
|
||||
all_declarators_const,
|
||||
is_for_in_of_init,
|
||||
ctx,
|
||||
)
|
||||
@@ -193,7 +190,7 @@ impl Rule for PreferConst {
|
||||
fixer,
|
||||
decl,
|
||||
declarator_index,
|
||||
all_const,
|
||||
all_declarators_const,
|
||||
is_for_in_of_init,
|
||||
ctx,
|
||||
)
|
||||
@@ -1651,6 +1648,17 @@ fn test_oxc() {
|
||||
("let [x = -1, y] = [1,2]; y = 0;", "let [x = -1, y] = [1,2]; y = 0;", None),
|
||||
("let {a, b} = c, d;", "let {a, b} = c, d;", None),
|
||||
("let {a, b, c} = {}, e, f;", "let {a, b, c} = {}, e, f;", None),
|
||||
("let x = 'x', y = 'y'; x = 1", "let x = 'x', y = 'y'; x = 1", None),
|
||||
(
|
||||
"let a = 1, b = 2; a++; console.log(a + b);",
|
||||
"let a = 1, b = 2; a++; console.log(a + b);",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"let { a, b } = obj, c = 1; a = 2; console.log(a, b, c);",
|
||||
"let { a, b } = obj, c = 1; a = 2; console.log(a, b, c);",
|
||||
Some(serde_json::json!([{ "destructuring": "all" }])),
|
||||
),
|
||||
];
|
||||
|
||||
Tester::new(PreferConst::NAME, PreferConst::PLUGIN, pass, fail)
|
||||
|
||||
Reference in new issue
Block a user