Skip to content

Commit e560cc1

Browse files
committed
fix(language_server): fix panic when "disable rule for this line" position is after error span (#14597)
The problem is, that `get_section_insert_position` get the offset after the line break. But the error offset is before the insert offset. So it gets a range of `[4..3]`. This rule makes no sense to ignore, because the span will always be 0 with a length of 0. Ignored the case for adding "ignore fixes" and fixed the panic to make sure, it will not happen for other rules. closes #14565
1 parent 12a9934 commit e560cc1

File tree

5 files changed

+70
-0
lines changed

5 files changed

+70
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"rules": {
3+
"no-debugger": "off",
4+
"unicorn/filename-case": ["error", {
5+
"cases": {
6+
"kebabCase": false,
7+
"camelCase": false,
8+
"pascalCase": true,
9+
"snakeCase": true
10+
}
11+
}]
12+
}
13+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
debugger
3+
---

crates/oxc_language_server/src/linter/error_with_position.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@ pub fn message_to_lsp_diagnostic(
122122
// Add ignore fixes
123123
let error_offset = message.span().start;
124124
let section_offset = message.section_offset;
125+
126+
// If the error is exactly at the section offset and has 0 span length, it means that the file is the problem
127+
// and attaching a ignore comment would not ignore the error.
128+
// This is because the ignore comment would need to be placed before the error offset, which is not possible.
129+
if error_offset == section_offset && message.span().end == section_offset {
130+
return DiagnosticReport { diagnostic, fixed_content };
131+
}
132+
125133
let fixed_content = add_ignore_fixes(
126134
fixed_content,
127135
&message.error.code,
@@ -265,6 +273,9 @@ fn disable_for_this_line(
265273
let whitespace_range = {
266274
let start = insert_offset as usize;
267275
let end = error_offset as usize;
276+
277+
// make sure that end is at least start to avoid panic
278+
let end = end.max(start);
268279
let slice = &bytes[start..end];
269280
let whitespace_len = slice.iter().take_while(|c| matches!(c, b' ' | b'\t')).count();
270281
&slice[..whitespace_len]
@@ -608,6 +619,21 @@ mod test {
608619
assert_eq!(fix.range.start.character, 0);
609620
}
610621

622+
#[test]
623+
fn disable_for_this_line_section_offset_start() {
624+
// Test framework file where error is exactly at section offset
625+
let source = "<script>\nconsole.log('hello');\n</script>";
626+
let rope = Rope::from_str(source);
627+
let section_offset = 8; // At the \n after "<script>"
628+
let error_offset = 8; // Error exactly at section offset
629+
let fix =
630+
super::disable_for_this_line("no-console", error_offset, section_offset, &rope, source);
631+
632+
assert_eq!(fix.code, "// oxlint-disable-next-line no-console\n");
633+
assert_eq!(fix.range.start.line, 1);
634+
assert_eq!(fix.range.start.character, 0);
635+
}
636+
611637
fn assert_position(source: &str, offset: u32, expected: (u32, u32)) {
612638
let position = offset_to_position(&Rope::from_str(source), offset, source);
613639
assert_eq!(position.line, expected.0);

crates/oxc_language_server/src/linter/server_linter.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,4 +646,14 @@ mod test {
646646
);
647647
tester.test_and_snapshot_single_file("index.js");
648648
}
649+
650+
// https://github.com/oxc-project/oxc/issues/14565
651+
#[test]
652+
fn test_issue_14565() {
653+
let tester = Tester::new(
654+
"fixtures/linter/issue_14565",
655+
Some(LintOptions { run: Run::OnSave, ..Default::default() }),
656+
);
657+
tester.test_and_snapshot_single_file("foo-bar.astro");
658+
}
649659
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
source: crates/oxc_language_server/src/tester.rs
3+
---
4+
##########
5+
file: fixtures/linter/issue_14565/foo-bar.astro
6+
----------
7+
8+
code: "eslint-plugin-unicorn(filename-case)"
9+
code_description.href: "https://oxc.rs/docs/guide/usage/linter/rules/unicorn/filename-case.html"
10+
message: "Filename should be in snake case, or pascal case\nhelp: Rename the file to 'foo_bar.astro', or 'FooBar.astro'"
11+
range: Range { start: Position { line: 0, character: 3 }, end: Position { line: 0, character: 3 } }
12+
related_information[0].message: ""
13+
related_information[0].location.uri: "file://<variable>/fixtures/linter/issue_14565/foo-bar.astro"
14+
related_information[0].location.range: Range { start: Position { line: 0, character: 3 }, end: Position { line: 0, character: 3 } }
15+
severity: Some(Error)
16+
source: Some("oxc")
17+
tags: None
18+
fixed: None

0 commit comments

Comments
 (0)