Skip to content

[Bug] Trailing backslash degrades the whole bash parse to a single ERROR node #3237

Description

@matthew-demidoff

What version of Kimi Code is running?

Reproduced on main at commit d1a46db94 (package @moonshot-ai/tree-sitter-bash).

Which open platform/subscription were you using?

N/A — this is a code-level correctness bug in the @moonshot-ai/tree-sitter-bash parser, found by source review and reproduced with a unit test. It is not tied to a plan or model.

Which model were you using?

N/A

What platform is your computer?

macOS (Darwin arm64). The bug is platform-independent — the parser is pure TypeScript.

What issue are you seeing?

parse() degrades a perfectly valid command whose scan range ends in a lone backslash into a single whole-source ERROR node (hasError: true), losing all structure.

On current main:

  • parse('echo \') returns a root that is a single ERROR [0,6) instead of command(command_name(word "echo"), word "\"). Internally SyntaxNodeBuilder throws RangeError: invalid node range [0, 7) for source of length 6, and parse()'s last-resort catch in src/parse.ts converts that into the whole-source ERROR node.
  • The same happens for \, [[ -f x && \, and case x in a\.

What steps can reproduce the bug?

In packages/tree-sitter-bash:

import { parse } from '#/parse';

const r = parse('echo \\'); // the string: echo, space, one backslash
// expected: ok:true, hasError:false — a command with a trailing word "\"
// actual:   ok:true, hasError:true — root is a single whole-source ERROR node

This is not fuzzer-only: test/fixtures/corpus/statements.txt already contains a real line ending in a continuation backslash, so any consumer that parses a single line before the following newline arrives hits it.

What is the expected behavior?

A lone trailing backslash is kept as word text — which is exactly what scanWord's own comment in src/lexer.ts says should happen. echo \ should parse to a normal command with a trailing word "\" and hasError: false; the test-command and case inputs should error-recover locally rather than collapsing the whole tree.

Additional information

Root cause: four character scanners skip an escaped character with idx += 2. When the backslash is the last character of the scan range, that overshoots to end + 1, so the emitted node endIndex exceeds the source length (or the parent's end) and SyntaxNodeBuilder rejects it. The four sites are scanWord (src/lexer.ts) and scanCasePatternEnd / scanTestToken / scanTestCloser (src/parser.ts).

Proposed fix: clamp the skip to the scan bound, idx = Math.min(idx + 2, end). The clamp is a no-op unless the backslash is the final character, so no other parse changes. I verified locally that the full tree-sitter-bash suite — including the differential comparison against the reference tree-sitter-bash corpus — stays green, and added regression cases that fail before the change and pass after it.

I'm happy to open the PR once this is approved.

Contribution

  • I am willing to submit a PR for this bug fix myself (please wait for maintainer approval in this issue first)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions