Skip to content

refactor(mcp): tree-sitter masked source for text scanners#391

Merged
ScriptedAlchemy merged 1 commit into
masterfrom
codex/treesitter-source-mask
Jul 9, 2026
Merged

refactor(mcp): tree-sitter masked source for text scanners#391
ScriptedAlchemy merged 1 commit into
masterfrom
codex/treesitter-source-mask

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Jul 9, 2026

Copy link
Copy Markdown
Owner

What

Replaces the hand-rolled Rust lexer in src/mcp/tools/handlers/analysis.rs (the MaskState machine, mask_rust_noise, try_open_raw_or_byte_string, is_char_literal_start, raw_string_closes, is_direct_format_literal_start, format_capture_identifier_end) with a shared tree-sitter-driven masking facility: src/extraction/source_mask.rs.

The facility parses with the existing Rust grammar and blanks the byte ranges of line_comment/block_comment/string_literal/raw_string_literal/char_literal nodes, preserving newlines and total byte length so line/byte indexing stays valid.

API

  • masked_rust_source(&str) -> String — comments + strings, format captures preserved (the unused_imports behavior).
  • masked_rust_source_with(&str, MaskOptions) -> StringMaskOptions { mask_comments, mask_strings, preserve_format_captures }, with MaskOptions::UNUSED_IMPORTS and MaskOptions::CODE_SCAN presets. Node sets are opt-in per caller, so tracedecay_todos (which deliberately scans comments) is untouched.

Per-scanner migration

  • handle_unused_imports — swaps mask_rust_noise for masked_rust_source (UNUSED_IMPORTS preset); per-file caching in file_cache retained (each file parsed once per call).
  • has_bare_call (recursion self-call probe) — cached_lines now caches the CODE_SCAN-masked source, so a name( appearing only in a comment/string no longer counts as a self-call.
  • contains_unsafe_block_start (via handle_unsafe_patterns) — the handler masks each Rust source (CODE_SCAN) and runs detection on the masked copy while emitting the original line as the snippet; non-Rust files are scanned raw (a new path_is_rust helper gates masking).

Format-capture decision

Honored via tree-sitter spans plus a tiny linear delimiter/comma walk over the remaining code bytes — no second lexer. Tree-sitter does the only hard job the old lexer did (finding comment/string boundaries); the delimiter walk reproduces the format-arg logic. This matches the expanded semantics of merged PR #383 (codex/format-capture-completeness), not the old masker's: panic/todo/unimplemented/unreachable at arg 0; assert/debug_assert/write/writeln at arg 1; assert_eq/assert_ne/debug_assert_eq/debug_assert_ne at arg 2; position-aware first-literal selection; comments between the macro name and ( tolerated. The branch is rebased after #383 and retains its expanded contract (both converge on the same format_macro_argument_index / take_format_literal_context logic and the same expanded contract test).

Tests

All original mask_rust_noise unit tests (comment/string/char/raw-string/byte-string/format-capture) are carried over as contract tests in source_mask::tests, along with #383's implicit_captures_survive_supported_format_macro_positions. The hand-rolled lexer and its helpers are deleted.

Line counts

  • analysis.rs: 2982 → 2635 (net −347; hand-rolled lexer + old test module removed).
  • source_mask.rs: new, 500 lines (facility + docs + contract tests).

Gates (CI-exact)

  • cargo fmt -- --check → clean (EXIT 0)
  • cargo clippy --workspace --all-targets --locked -- -D warnings → clean (EXIT 0)
  • cargo nextest run --test mcp_suite --no-fail-fast364 passed, 0 failed (incl. unused_imports_*, unsafe_patterns_reports_unsafe_block_in_markdown_and_json)
  • cargo nextest run --lib (touched modules) → 17 passed (10 source_mask, 3 unsafe_pattern_detection_tests, render/unused)

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: c827eea

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 590818df32

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +217 to +220
b',' => {
if let Some(context) = stack.last_mut() {
context.top_level_commas += 1;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track angle-bracket nesting before counting macro commas

When a formatting macro argument before the message contains turbofish/type arguments with commas, e.g. assert_eq!(make::<A, B>(), "{HashMap}"), this comma is counted as a top-level macro separator because only ()[]{} nesting is tracked. That makes the second argument look like assert_eq's format-string slot, so restore_format_captures preserves HashMap even though it is just literal string data; tracedecay_unused_imports will then miss a truly unused HashMap import in otherwise valid Rust code.

Useful? React with 👍 / 👎.

@ScriptedAlchemy ScriptedAlchemy force-pushed the codex/treesitter-source-mask branch from 590818d to 999bbcb Compare July 9, 2026 22:25
Replace the hand-rolled Rust lexer in analysis.rs (MaskState machine,
mask_rust_noise, try_open_raw_or_byte_string, is_char_literal_start,
raw_string_closes) with a shared tree-sitter-driven masking facility in
src/extraction/source_mask.rs. It blanks comment and string/char literal
node ranges reported by the existing Rust grammar, preserving newlines and
byte offsets, with opt-in node sets (mask_comments / mask_strings) so
tracedecay_todos is never affected.

All three text scanners now share it: handle_unused_imports uses the
UNUSED_IMPORTS preset (captures preserved); has_bare_call (via cached_lines)
and contains_unsafe_block_start (via handle_unsafe_patterns) use the
CODE_SCAN preset, fixing the comment/string false-positive class that was
previously fixed for imports only. unsafe_patterns detects on the masked
copy but still emits the original line as the snippet; non-Rust files are
scanned raw.

Format-capture carry-over is honored via tree-sitter spans plus a tiny
delimiter/comma walk over the remaining code bytes, matching the EXPANDED
semantics of PR #383 (codex/format-capture-completeness): panic/todo/
unimplemented/unreachable at arg 0, assert/write/writeln at arg 1,
assert_eq/ne at arg 2, position-aware first-literal selection. The old
mask_rust_noise unit tests plus #383's expanded capture test are carried
over as contract tests against the new facility; the hand-rolled lexer and
its helpers are deleted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ScriptedAlchemy ScriptedAlchemy force-pushed the codex/treesitter-source-mask branch from 999bbcb to c827eea Compare July 9, 2026 22:31
@ScriptedAlchemy ScriptedAlchemy merged commit 76dc0db into master Jul 9, 2026
16 of 17 checks passed
This was referenced Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant