Skip to content

Commit 16eaa80

Browse files
authored
Merge pull request #16 from Eilodon/claude/rust-support-plan-review-tzxbsc
docs(plan): correct Rust support plan against current codebase state
2 parents 0b40543 + 5310bac commit 16eaa80

1 file changed

Lines changed: 75 additions & 21 deletions

File tree

docs/superskills/plans/2026-07-03-rust-support.md

Lines changed: 75 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ Rust dev), crate `scip` 0.9 (Phase B, feature-gated), rayon.
4646
- `crates/ci-core/src/indexer/mod.rs` — khai báo `pub mod crate_map;` (A2)
4747
- `crates/ci-core/src/indexer/pipeline.rs` — dùng crate map trong resolve import (A3)
4848
- `crates/ci-core/src/indexer/lang_constants.rs` — thêm `function_signature_item` (A4)
49-
- `crates/ci-core/src/indexer/parser.rs` — constructor type inference (A5)
49+
- `crates/ci-core/src/indexer/parser.rs``walk_symbols` per-node-kind class name field cho
50+
`trait_item` (A4) + constructor type inference (A5)
5051
- `crates/ci-core/tests/fixtures/rust_workspace/`**mới**: fixture (Task 0)
5152

5253
**Phase B (module mới):**
@@ -59,7 +60,8 @@ Rust dev), crate `scip` 0.9 (Phase B, feature-gated), rayon.
5960
- `crates/ci-core/src/config.rs` — thêm `RustConfig`/`ScipConfig`
6061
- `crates/ci-core/Cargo.toml` — thêm `scip` dep + feature `scip-overlay`
6162
- `crates/ci-server/src/lib.rs` — gọi overlay sau `phase=ready`
62-
- `benchmarks/rust_precision/`**mới**: harness đo precision/recall vs SCIP oracle (B6)
63+
- `benchmarks/b2_call_graph_quality/`**mới**: harness đo precision/recall vs SCIP oracle,
64+
lấp slot "B2 | Call Graph Resolution Quality | Planned" có sẵn trong `benchmarks/README.md` (B6)
6365

6466
---
6567

@@ -290,6 +292,13 @@ fn parse_rust_import(text: &str) -> Option<ParsedImport> {
290292
}
291293
```
292294

295+
> **Phạm vi Task A1**: vẫn thuần string-manipulation (chỉ thêm bước strip visibility) — **không**
296+
> chuyển sang AST-based traversal của `use_declaration` như `docs/rust-support-research.md` §R0.1
297+
> mô tả ("parse bằng cấu trúc node tree-sitter... sửa luôn nested groups"). Nested groups như
298+
> `use a::{b::{c, d}, e}` vẫn chưa được xử lý đúng sau task này — không phải regression (đã sai
299+
> y hệt trước đó), chỉ là scope Task A1 hẹp hơn cách research doc mô tả. Không chặn merge, chỉ
300+
> ghi rõ để không ai tưởng nested groups đã được sửa xong.
301+
293302
- [ ] **Step 4: Chạy — xác nhận PASS** `cargo test -p ci-core --lib imports::tests``cargo test -p ci-core --test rust_indexing pub_use_reexport_is_indexed` → expected: cả hai PASS
294303
- [ ] **Step 5: fmt + clippy + commit** `cargo fmt --all && cargo clippy --all-targets -- -D warnings && git commit -am "fix(imports): recognize pub use re-exports (Rust R0.1)"`
295304

@@ -487,6 +496,13 @@ fn rel_dir(project_root: &Path, abs_dir: &Path) -> Option<String> {
487496
### Task A3: Rust cross-crate + module resolution (R0.3)
488497

489498
Thay đường strip-prefix sai trong `resolve_module_to_path` bằng resolver Rust dùng `CrateMap`.
499+
500+
> **Lưu ý số dòng**: các trích dẫn `pipeline.rs:NNN` dưới đây đã cập nhật theo commit
501+
> `9d45c63` ("prefer same-file candidate when resolving a call by bare name", đã merge trước
502+
> khi plan này được viết ra nhưng phần phân tích ban đầu không tính tới), commit này chèn thêm
503+
> ~21 dòng vào giữa file (khối "same-file preference" trong `rebuild_graph`, dòng ~440-472).
504+
> Khối đó nằm **trước****tách biệt** với `resolve_import_targets`/`resolve_module_to_path`
505+
> mà Task A3 sửa — không có tương tác chức năng, chỉ cần biết để số dòng khớp bản hiện tại.
490506
Đây là task đóng gap #2 (cross-crate `to_path = NULL`). Scope Tier-0: `crate::`, tên crate
491507
ngoài, `self::` resolve chính xác; `super::` chấp nhận xấp xỉ (climb theo thư mục) và ghi chú
492508
rõ — phần dư để Phase B phủ.
@@ -615,7 +631,7 @@ fn resolve_rust_module(
615631
}
616632
```
617633

618-
- [ ] **Step 4: Rẽ nhánh rust trong `resolve_module_to_path`** — sửa đầu hàm (pipeline.rs:526, ngay sau khối `let m = ...; if m.is_empty()`), thêm tham số `crate_map` và ưu tiên resolver rust cho file `.rs`. Đổi signature:
634+
- [ ] **Step 4: Rẽ nhánh rust trong `resolve_module_to_path`** — sửa đầu hàm (pipeline.rs:547, ngay sau khối `let m = ...; if m.is_empty()`), thêm tham số `crate_map` và ưu tiên resolver rust cho file `.rs`. Đổi signature:
619635
```rust
620636
fn resolve_module_to_path(
621637
from_path: &str,
@@ -638,7 +654,7 @@ fn resolve_module_to_path(
638654
// ... existing generic body unchanged from here ...
639655
```
640656

641-
- [ ] **Step 5: Build & truyền `CrateMap` trong `resolve_import_targets`** — sửa hàm (pipeline.rs:489). Nó cần `project_root` để build map; hiện chỉ nhận `tx`. Thêm tham số. Sửa signature + thân:
657+
- [ ] **Step 5: Build & truyền `CrateMap` trong `resolve_import_targets`** — sửa hàm (pipeline.rs:510). Nó cần `project_root` để build map; hiện chỉ nhận `tx`. Thêm tham số. Sửa signature + thân:
642658
```rust
643659
fn resolve_import_targets(
644660
tx: &rusqlite::Transaction,
@@ -653,7 +669,7 @@ fn resolve_import_targets(
653669
}
654670
```
655671

656-
Và tại call site trong `rebuild_graph` (pipeline.rs:480), đổi `resolve_import_targets(tx)?;` thành nhận map. `rebuild_graph` cũng chưa có `project_root` — thêm tham số vào nó và truyền từ 2 call site (`run_indexing_pipeline` ~712, incremental ~823). Chuỗi thay đổi:
672+
Và tại call site trong `rebuild_graph` (pipeline.rs:501), đổi `resolve_import_targets(tx)?;` thành nhận map. `rebuild_graph` cũng chưa có `project_root` — thêm tham số vào nó và truyền từ 2 call site (`run_indexing_pipeline` ~733, incremental ~844). Chuỗi thay đổi:
657673
```rust
658674
// rebuild_graph signature:
659675
fn rebuild_graph(
@@ -687,6 +703,8 @@ của `node_kind_to_symbol_kind` (đã đọc: parser.rs:54-60) map function-lik
687703

688704
**Files:**
689705
- Modify: `crates/ci-core/src/indexer/lang_constants.rs:26-34` (rust `function_node_types`)
706+
- Modify: `crates/ci-core/src/indexer/parser.rs:294-301` (`walk_symbols` — per-node-kind class
707+
name field cho Rust `trait_item`; xem Step 4)
690708
- Test: `crates/ci-core/tests/rust_indexing.rs`
691709

692710
- [ ] **Step 1: Viết test đỏ**:
@@ -726,15 +744,41 @@ fn trait_method_declaration_is_a_symbol() {
726744
class_name_field: "type",
727745
}),
728746
```
729-
> **Lưu ý:** thêm `trait_item` vào `class_node_types` để method bên trong trait nhận
730-
> `class_context` = tên trait (`class_name_field = "type"` — với `trait_item` field tên là
731-
> `name`, không phải `type`; kiểm tra: nếu `trait_item` dùng field `name`, cần xử lý riêng).
732-
> **Executor phải verify**: chạy Step 4; nếu `Runner::run` ra qualified_name sai (thiếu tên
733-
> trait), thì `class_name_field` cho trait khác impl — khi đó tách logic tên-class theo node
734-
> kind trong `walk_symbols` (impl→field `type`, trait→field `name`). Test ở Step 1 là oracle.
735747

736-
- [ ] **Step 4: Chạy — xác nhận PASS** `cargo test -p ci-core --test rust_indexing` và full `cargo test -p ci-core` → expected: PASS, không regression (đặc biệt các test symbol Rust có sẵn trong `parser.rs`/`pipeline.rs`).
737-
- [ ] **Step 5: fmt + clippy + commit** `cargo fmt --all && cargo clippy --all-targets -- -D warnings && git commit -am "feat(indexer): index Rust trait method declarations (R0.5)"`
748+
- [ ] **Step 4: Sửa `walk_symbols` cho field tên của `trait_item`****đã xác nhận bằng
749+
`node-types.json` thật của `tree-sitter-rust 0.23.3`, không phải giả định**: `impl_item`
750+
field `type` (kiểu Self) và field optional `trait`; `trait_item` có field `name` (kiểu
751+
`type_identifier`) — **không có field `type`**. `class_name_field` là một hằng số dùng chung
752+
cho cả ngôn ngữ (`"type"` cho rust), nên chỉ thêm `trait_item` vào `class_node_types` như
753+
Step 3 là **chưa đủ**: `node.child_by_field_name(lc.class_name_field)` trong `walk_symbols`
754+
(`parser.rs:295-297`) sẽ tra field `"type"` trên `trait_item`, field này không tồn tại →
755+
`None` → rơi về `enclosing_class` (thường là `None` ở top-level) → method trong trait
756+
**không** nhận được `class_context = "Runner"`, và oracle test ở Step 1 (`qualified_name =
757+
'core/src/lib.rs::Runner::run'`) sẽ FAIL dù Step 3 đã áp dụng đúng. Sửa `walk_symbols`
758+
(`parser.rs:294-301`) để chọn field theo node kind thay vì dùng thẳng `lc.class_name_field`:
759+
760+
```rust
761+
// Entering a class/impl sets the context for its descendants. Rust's
762+
// `trait_item` names itself via field `name` (a `type_identifier`) — it
763+
// does not share `impl_item`'s `class_name_field` ("type", the Self
764+
// type) — so the field to read can't come from the single
765+
// per-language `class_name_field` constant alone for this node kind.
766+
let child_class = if lc.class_node_types.contains(&node.kind()) {
767+
let name_field = if node.kind() == "trait_item" {
768+
"name"
769+
} else {
770+
lc.class_name_field
771+
};
772+
node.child_by_field_name(name_field)
773+
.map(|n| source[n.byte_range()].to_string())
774+
.or_else(|| enclosing_class.clone())
775+
} else {
776+
enclosing_class.clone()
777+
};
778+
```
779+
780+
- [ ] **Step 5: Chạy — xác nhận PASS** `cargo test -p ci-core --test rust_indexing` và full `cargo test -p ci-core` → expected: PASS, không regression (đặc biệt các test symbol Rust có sẵn trong `parser.rs`/`pipeline.rs`).
781+
- [ ] **Step 6: fmt + clippy + commit** `cargo fmt --all && cargo clippy --all-targets -- -D warnings && git commit -am "feat(indexer): index Rust trait method declarations (R0.5)"`
738782

739783
---
740784

@@ -773,7 +817,7 @@ fn constructor_binding_infers_receiver_type() {
773817
```
774818

775819
- [ ] **Step 2: Chạy — xác nhận FAIL** `cargo test -p ci-core --test rust_indexing constructor_binding_infers_receiver_type` → expected: FAIL (textual, edge có thể không tồn tại theo target_class)
776-
- [ ] **Step 3: Thêm inference** — trong `extract_type_map_impl` (parser.rs, hàm chứa vòng `while let Some(node) = stack.pop()` ~810), thêm một nhánh rust: khi gặp `let_declaration` không có field `type` nhưng có `value` là constructor. Chèn ngay sau khối `if binding_kinds.contains(...)`:
820+
- [ ] **Step 3: Thêm inference** — trong `extract_type_map_from_tree` (parser.rs, hàm chứa vòng `while let Some(node) = stack.pop()` ~810), thêm một nhánh rust: khi gặp `let_declaration` không có field `type` nhưng có `value` là constructor. Chèn ngay sau khối `if binding_kinds.contains(...)`:
777821
```rust
778822
// Rust constructor inference: `let x = Foo::new(...)`, `Foo::default()`,
779823
// or `Foo { .. }` binds x to type Foo even without a type annotation.
@@ -1416,7 +1460,7 @@ pub fn run_overlay(conn: &Connection, root: &Path, rust: &RustConfig) -> anyhow:
14161460
> `scip-overlay`: `tempfile = { version = "3", optional = true }` và thêm vào
14171461
> `scip-overlay = ["dep:scip", "dep:tempfile"]`; giữ dòng dev-dependency cho test khác).
14181462
1419-
- [ ] **Step 4: Wire vào server** — trong `crates/ci-server/src/lib.rs`, sau khi indexer đặt `phase=Ready` trong `spawn_blocking` (sau block 70-97), thêm (feature-gated):
1463+
- [ ] **Step 4: Wire vào server** — trong `crates/ci-server/src/lib.rs`, sau khi indexer đặt `phase=Ready` trong `spawn_blocking` (khối hiện tại là dòng 53-113; điểm chèn đúng là ngay sau khối `if index_ok { ... }` ở dòng 99-105, cạnh lệnh `bootstrap_embeddings(&conn, ...)` đã có sẵn ở dòng 107-109, trước khi gọi `watcher::run_watch_loop` ở dòng 112), thêm (feature-gated):
14201464
```rust
14211465
#[cfg(feature = "scip-overlay")]
14221466
if index_ok {
@@ -1444,22 +1488,32 @@ pub fn run_overlay(conn: &Connection, root: &Path, rust: &RustConfig) -> anyhow:
14441488

14451489
---
14461490

1447-
### Task B6: Benchmark — SCIP as precision/recall oracle
1491+
### Task B6: Benchmark — SCIP as precision/recall oracle (lấp slot B2 có sẵn)
14481492

14491493
Biến "Rust tốt chưa" thành số: so call edges của Tầng A với SCIP ground truth trên corpus thật.
14501494

1495+
`benchmarks/README.md` đã có sẵn dòng **"B2 | Call Graph Resolution Quality | Tier-1/2/3 vs
1496+
textual | Planned"** — đây chính là lần triển khai đầu tiên của B2 (scope: Rust, dùng
1497+
rust-analyzer làm oracle). Đặt tên/vị trí theo đúng convention `bN_ten/run_benchmark.py` +
1498+
tái dùng `benchmarks/lib/` (`mcp_client.py`) mà `b3_search_quality`/`b4_token_efficiency`/
1499+
`b6_tool_call_efficiency` đã dùng — không tạo thư mục top-level riêng lệch chuẩn như
1500+
`rust_precision/run.py`.
1501+
14511502
**Files:**
1452-
- Create: `benchmarks/rust_precision/run.py`
1453-
- Create: `benchmarks/rust_precision/README.md`
1503+
- Create: `benchmarks/b2_call_graph_quality/run_benchmark.py`
1504+
- Create: `benchmarks/b2_call_graph_quality/README.md`
1505+
- Modify: `benchmarks/README.md` — đổi dòng B2 từ `Planned``Implemented` (ghi rõ scope
1506+
hiện tại: Rust only) kèm link tới `b2_call_graph_quality/`, theo đúng cách B3/B4/B6 đã làm.
14541507

1455-
- [ ] **Step 1: Viết harness** `benchmarks/rust_precision/run.py` — pseudo-flow (executor hoàn thiện theo `benchmarks/lib` sẵn có):
1508+
- [ ] **Step 1: Viết harness** `benchmarks/b2_call_graph_quality/run_benchmark.py` — pseudo-flow (executor hoàn thiện theo `benchmarks/lib/mcp_client.py` sẵn có, cùng cách b3/b4/b6 đã dùng):
14561509
1. Nhận `--repo <path>` (mặc định: chính `ci`).
14571510
2. Chạy `rust-analyzer scip <repo> --output oracle.scip`; decode bằng `scip print --json` (hoặc crate) → tập cạnh `(caller_file:line → callee_def_file:line)` cho các ref không-local.
14581511
3. Chạy `ci index <repo>`; đọc `.codeindex/index.db` `call_edges` (Rust) → tập cạnh tương ứng qua `symbols.line_start`.
14591512
4. Tính precision = |ci ∩ oracle| / |ci|, recall = |ci ∩ oracle| / |oracle|, phân tách theo `edge_confidence`.
14601513
5. In bảng; ghi JSON để track qua thời gian.
1461-
- [ ] **Step 2: Chạy baseline** `python benchmarks/rust_precision/run.py --repo .` → ghi lại số precision/recall hiện tại (sau Phase A). Đây là mốc để mọi thay đổi Rust về sau đối chiếu.
1462-
- [ ] **Step 3: Commit** `git commit -am "bench(rust): precision/recall harness with SCIP oracle (Phase B6)"`
1514+
- [ ] **Step 2: Chạy baseline** `python benchmarks/b2_call_graph_quality/run_benchmark.py --repo .` → ghi lại số precision/recall hiện tại (sau Phase A). Đây là mốc để mọi thay đổi Rust về sau đối chiếu.
1515+
- [ ] **Step 3: Cập nhật `benchmarks/README.md`** — đổi dòng B2 sang `Implemented` kèm link tới `b2_call_graph_quality/`.
1516+
- [ ] **Step 4: Commit** `git commit -am "bench(rust): B2 call-graph precision/recall harness with SCIP oracle"`
14631517

14641518
---
14651519

0 commit comments

Comments
 (0)