Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-trailing-backslash-bash-parse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Fix bash commands ending in a lone trailing backslash being misread as unparseable, which could affect command safety checks.
2 changes: 1 addition & 1 deletion packages/tree-sitter-bash/src/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ export class Lexer {
// A line continuation ends the run (it acts as whitespace); a lone
// trailing backslash at end of range is consumed as word text.
if (this.source[i + 1] === '\n') break;
i += 2;
i = Math.min(i + 2, this.end);
continue;
}
if (ch === '"') {
Expand Down
6 changes: 3 additions & 3 deletions packages/tree-sitter-bash/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ export class Parser {
const ch = this.source[j]!;
if (ch === '\n' || ch === ';') break; // defensive: malformed item
if (ch === '\\') {
j += 2;
j = Math.min(j + 2, end);
continue;
}
if (ch === '"') {
Expand Down Expand Up @@ -3482,7 +3482,7 @@ export class Parser {
continue;
}
if (c === '\\') {
j += 2;
j = Math.min(j + 2, end);
continue;
}
j++;
Expand Down Expand Up @@ -3652,7 +3652,7 @@ export class Parser {
const ch = this.source[j]!;
if (ch === '\n') break;
if (ch === '\\') {
j += 2;
j = Math.min(j + 2, end);
continue;
}
if (ch === '"') {
Expand Down
21 changes: 21 additions & 0 deletions packages/tree-sitter-bash/test/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,27 @@ describe('statement lists', () => {
expectTree('ls |', `(program (pipeline (command (command_name (word "ls"))) "|"))`, true);
});

it('keeps a lone trailing backslash as word text instead of degrading the whole parse', () => {
expectTree('echo \\', `(program (command (command_name (word "echo")) (word "\\\\")))`);
expectTree('\\', `(program (command (command_name (word "\\\\"))))`);
});

it('recovers a test command whose expression ends in a lone trailing backslash', () => {
expectTree(
'[[ -f x && \\',
`(program (test_command "[[" (binary_expression (unary_expression (test_operator "-f") (word "x")) "&&" (word "\\\\")) "]]"))`,
true,
);
});

it('recovers a case item pattern that ends in a lone trailing backslash', () => {
expectTree(
'case x in a\\',
`(program (case_statement "case" (word "x") "in" (case_item (word "a\\\\"))))`,
true,
);
});

it('continues a list across a newline after &&', () => {
expectTree(
'a &&\nb',
Expand Down