mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(linter/eslint/sort-vars): report every out-of-order declaration (#27767)
ESLint reports both `a` and `b` for `var c, a, b;`, while Oxlint only reported `a`. Keep the largest name seen so far to match ESLint's diagnostic count, and build a fix once per safe declaration run. Addresses #27746, report 20 (`eslint/sort-vars`).
This commit is contained in:
1 parent
01e1c5e37b
commit
9c35461498
2 files changed
+82
-39
No files matched your search
@@ -79,51 +79,38 @@ impl Rule for SortVars {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut previous: Option<&BindingIdentifier> = None;
|
||||
for current in &var_decl.declarations {
|
||||
let mut maximum: Option<&BindingIdentifier> = None;
|
||||
let mut checked_until = 0;
|
||||
for (current_index, current) in var_decl.declarations.iter().enumerate() {
|
||||
let Some(identifier) = current.id.get_binding_identifier() else {
|
||||
continue;
|
||||
};
|
||||
if let Some(previous) = previous {
|
||||
self.check_declaration_sort_with_fix(
|
||||
previous,
|
||||
identifier,
|
||||
current,
|
||||
&var_decl.declarations,
|
||||
ctx,
|
||||
);
|
||||
if let Some(maximum) = maximum
|
||||
&& self.get_sortable_name(maximum).cmp(&self.get_sortable_name(identifier))
|
||||
== Ordering::Greater
|
||||
{
|
||||
let diagnostic = sort_vars_diagnostic(current.span);
|
||||
if current.span.start >= checked_until
|
||||
&& let Some((span, replacement)) = self.build_declarations_fix(
|
||||
current_index,
|
||||
&var_decl.declarations,
|
||||
ctx,
|
||||
&mut checked_until,
|
||||
)
|
||||
{
|
||||
ctx.diagnostic_with_fix(diagnostic, |fixer| fixer.replace(span, replacement));
|
||||
} else {
|
||||
ctx.diagnostic(diagnostic);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
previous = Some(identifier);
|
||||
maximum = Some(identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SortVars {
|
||||
fn check_declaration_sort_with_fix<'a>(
|
||||
&self,
|
||||
previous: &BindingIdentifier<'a>,
|
||||
current: &BindingIdentifier<'a>,
|
||||
declaration: &VariableDeclarator<'a>,
|
||||
declarations: &'a [VariableDeclarator<'a>],
|
||||
ctx: &LintContext<'a>,
|
||||
) {
|
||||
if self.get_sortable_name(previous).cmp(&self.get_sortable_name(current))
|
||||
!= Ordering::Greater
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let diagnostic = sort_vars_diagnostic(declaration.span);
|
||||
if let Some((span, replacement)) =
|
||||
self.build_declarations_fix(declaration, declarations, ctx)
|
||||
{
|
||||
ctx.diagnostic_with_fix(diagnostic, |fixer| fixer.replace(span, replacement));
|
||||
} else {
|
||||
ctx.diagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_sortable_name<'a>(&self, ident: &BindingIdentifier<'a>) -> Cow<'a, str> {
|
||||
if self.ignore_case {
|
||||
return ident.name.as_str().cow_to_ascii_lowercase();
|
||||
@@ -134,18 +121,17 @@ impl SortVars {
|
||||
|
||||
fn build_declarations_fix<'a>(
|
||||
&self,
|
||||
current: &VariableDeclarator<'a>,
|
||||
current_index: usize,
|
||||
declarations: &'a [VariableDeclarator<'a>],
|
||||
ctx: &LintContext<'a>,
|
||||
checked_until: &mut u32,
|
||||
) -> Option<(Span, String)> {
|
||||
// Only reorder a contiguous safe run:
|
||||
// `var {} = obj, b, a` can fix `b, a`, but must not move the destructuring.
|
||||
if !is_fixable_declarator(current) {
|
||||
if !is_fixable_declarator(&declarations[current_index]) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let current_index = declarations.iter().position(|decl| std::ptr::eq(decl, current))?;
|
||||
|
||||
let mut start_index = current_index;
|
||||
while start_index > 0 && is_fixable_declarator(&declarations[start_index - 1]) {
|
||||
start_index -= 1;
|
||||
@@ -155,6 +141,7 @@ impl SortVars {
|
||||
while declarations.get(end_index + 1).is_some_and(is_fixable_declarator) {
|
||||
end_index += 1;
|
||||
}
|
||||
*checked_until = declarations[end_index].span.end;
|
||||
|
||||
if start_index == end_index {
|
||||
return None;
|
||||
@@ -303,6 +290,9 @@ fn test() {
|
||||
("var b = f(), c, d, a;", None),
|
||||
("var b = `${f()}`, c, d, a;", None), // { "ecmaVersion": 6 },
|
||||
("var c, a = b = 0", None),
|
||||
("var c, a, b;", None),
|
||||
("var C, a, b;", Some(serde_json::json!([{ "ignoreCase": true }]))),
|
||||
("var c, a, e, d;", None),
|
||||
];
|
||||
|
||||
let fix = vec![
|
||||
@@ -331,6 +321,10 @@ fn test() {
|
||||
("var {} = 1, b, a", "var {} = 1, a, b", Some(serde_json::json!([{ "ignoreCase": true }]))),
|
||||
("var b = f(), c, d, a;", "var b = f(), a, c, d;", None),
|
||||
("var b, /* comment */ a", "var b, /* comment */ a", None),
|
||||
("var c, a, b;", "var a, b, c;", None),
|
||||
("var C, a, b;", "var a, b, C;", Some(serde_json::json!([{ "ignoreCase": true }]))),
|
||||
("var f, d, e, {} = obj, c, a, b;", "var d, e, f, {} = obj, a, b, c;", None),
|
||||
("var c, /* comment */ a, b;", "var c, /* comment */ a, b;", None),
|
||||
];
|
||||
|
||||
Tester::new(SortVars::NAME, SortVars::PLUGIN, pass, fail).expect_fix(fix).test_and_snapshot();
|
||||
|
||||
@@ -59,6 +59,13 @@ source: crates/oxc_linter/src/tester.rs
|
||||
╰────
|
||||
help: Sort variable declarations in ascending order (case-sensitive by default).
|
||||
|
||||
⚠ eslint(sort-vars): Variable declarations should be sorted
|
||||
╭─[sort_vars.tsx:1:14]
|
||||
1 │ var c, d, a, b
|
||||
· ─
|
||||
╰────
|
||||
help: Sort variable declarations in ascending order (case-sensitive by default).
|
||||
|
||||
⚠ eslint(sort-vars): Variable declarations should be sorted
|
||||
╭─[sort_vars.tsx:1:8]
|
||||
1 │ var a, A;
|
||||
@@ -177,3 +184,45 @@ source: crates/oxc_linter/src/tester.rs
|
||||
· ─────────
|
||||
╰────
|
||||
help: Sort variable declarations in ascending order (case-sensitive by default).
|
||||
|
||||
⚠ eslint(sort-vars): Variable declarations should be sorted
|
||||
╭─[sort_vars.tsx:1:8]
|
||||
1 │ var c, a, b;
|
||||
· ─
|
||||
╰────
|
||||
help: Sort variable declarations in ascending order (case-sensitive by default).
|
||||
|
||||
⚠ eslint(sort-vars): Variable declarations should be sorted
|
||||
╭─[sort_vars.tsx:1:11]
|
||||
1 │ var c, a, b;
|
||||
· ─
|
||||
╰────
|
||||
help: Sort variable declarations in ascending order (case-sensitive by default).
|
||||
|
||||
⚠ eslint(sort-vars): Variable declarations should be sorted
|
||||
╭─[sort_vars.tsx:1:8]
|
||||
1 │ var C, a, b;
|
||||
· ─
|
||||
╰────
|
||||
help: Sort variable declarations in ascending order (case-sensitive by default).
|
||||
|
||||
⚠ eslint(sort-vars): Variable declarations should be sorted
|
||||
╭─[sort_vars.tsx:1:11]
|
||||
1 │ var C, a, b;
|
||||
· ─
|
||||
╰────
|
||||
help: Sort variable declarations in ascending order (case-sensitive by default).
|
||||
|
||||
⚠ eslint(sort-vars): Variable declarations should be sorted
|
||||
╭─[sort_vars.tsx:1:8]
|
||||
1 │ var c, a, e, d;
|
||||
· ─
|
||||
╰────
|
||||
help: Sort variable declarations in ascending order (case-sensitive by default).
|
||||
|
||||
⚠ eslint(sort-vars): Variable declarations should be sorted
|
||||
╭─[sort_vars.tsx:1:14]
|
||||
1 │ var c, a, e, d;
|
||||
· ─
|
||||
╰────
|
||||
help: Sort variable declarations in ascending order (case-sensitive by default).
|
||||
Reference in new issue
Block a user