Skip to content

Code Analysis Go Rust Investigation Batch

Tu Shaokun edited this page Nov 8, 2026 · 7 revisions

Go/Rust real-change investigation batch (2026-11-09)

Draft, not merged: #1290, head 021a3898a, base codex/go-rust-batch-baseline (70bdf0227, merge of Go #1285 and Rust #1288). Review order: #1289 merges both baselines; this PR targets that merged branch. Started from the reviewed head ac36a4ab0. No merge, no labs/code-analysis update, no live installation.

This stage reproduces and fixes a wrong Rust target, extends generic field receivers and defaulted owner methods, keeps Go interface evidence for external embedded methods, and re-runs the same 20 real questions on both revisions. Every number below comes from the same query set, sample roots and call positions.

Question

On real Go and Rust changes, does the normal change query answer four things: which definition changed, who directly calls it, which calls only prove an interface/trait declaration, and where can the reader verify the call in source?

Fixed baselines

Line Commit Meaning
Batch base 70bdf0227 merge of Go #1285 and Rust #1288
Reviewed head / stage baseline ac36a4ab0 Rust v7 / adapter 7, Go v3 / adapter 6
Candidate 021a3898a Rust v8 / adapter 8, Go v4 / adapter 6

Remote state was re-checked at the end of the stage: #1290 Draft, base #1289 still at 70bdf0227 and unmerged; origin/labs/code-analysis still bf0a29e76 (2026-11-04); origin/main advanced only with DSH docs (#1291/#1294) and has no archify/modules/code-analysis change since branch point 654da74e0.

Sample trees are independent git worktree checkouts at fixed base/head commits. No project build or test was executed. Go used Go 1.27.2, default GOOS/GOARCH, cgo disabled. clap, tracing, ripgrep and bytes were read with a generated Cargo.lock because the extractor uses --locked.

Cases and change groups

Twenty cases and 86 expected call positions: the earlier 12 (still exactly 73 positions, unchanged) plus 8 new/frozen cases (13 positions). New repositories relative to the 12-case batch: spf13/cobra, labstack/echo, urfave/cli, BurntSushi/ripgrep, tokio-rs/bytes. Thirteen new base/head groups plus two regression groups; two questions from one pair count once.

Group Cases
certmagic 16c9db14→1a2275d5 certmagic-load, certmagic-store
certmagic 2d114193→9a56fcd4 certmagic-interface, certmagic-interface-load (reserved Go)
gin 8d0468f7→d75fcd4c gin-close, gin-hijack
clap b55ebc9f→67202405 clap-any-value-parser, clap-typed-value-parser (reserved Rust)
tracing d3eb109e→f42e7e6d tracing-layer-event-enabled, tracing-subscriber-event-enabled
raft d3c91249→a2e87783 raft-stage-commit-index
cobra f2878bab→746ef071 cobra-get-completions
echo 4f7bea5d→29682401 echo-remove-custom-method (reserved Go)
cli d854bb40→ca8f45a9 cli-wrap-line
ripgrep 4c2362d3→7155f640 ripgrep-file-name
ripgrep 1752fb78→6892bc2b ripgrep-parent-key
bytes 002df10b→d5c8ad32 bytes-truncate-zero (reserved Rust)
bytes d5c8ad32→7930d93d bytes-try-get-int
http c6af28b→3dd9f5e http-insert-occupied-mult (regression)
rust-base64 a8a60f43→9e9c7abe base64-inner-decode (regression)

Reserved answers were frozen by reviewers reading only upstream source/diff, before any candidate run. echo-remove-custom-method and bytes-truncate-zero matched other frozen keys.

Whole-batch result

Scope Definitions Baseline found/wrong/missed Candidate found/wrong/missed/extras
All 20 cases 20/20 66/1/20 78/0/8/2
Go (10) 10/10 57/0/5 62/0/0/0
Rust (10) 10/10 9/1/15 16/0/8/2
New (14) 14/14 47/1/18 58/0/7/1
Regression (2) 2/2 4/0/1 5/0/0/1
Reserved Go (2) 2/2 14/0/0 14/0/0/0
Reserved Rust (2) 2/2 1/0/1 1/0/1/0

+13 correct call positions, 1 wrong target removed, missed positions 20 → 8, no previously correct hit lost. Target display moved from 15 determined / 51 candidate / 0 unknown to 16 / 57 / 5; every candidate still carries runtimeTarget: "not-determined". The two extras are source-verified real calls omitted from the frozen keys, not false targets: base64 src/read/decoder.rs:146 calls through E: Engine; tracing tracing-subscriber/src/layer/layered.rs:295 calls through B: Layer<S>.

Remaining misses: clap type-erased parser and ok! argument (2), bytes default-method and &mut B forwarding (2), tracing macro body/closure/try_lock! (3), tracing feature!-gated fmt/mod.rs (1). None points at a wrong target.

Wrong-target fix

fixtures/rust-local-asref is a cargo-checkable pair with a local trait AsRef<T> whose as_ref returns &Other and Wrap: AsRef<First>. The test proves rustc dispatches self.as_ref().enabled() to Other::enabled.

Query ac36a4ab0 (v7) Candidate (v8)
change Other::enabled 0 references 2: src/lib.rs:32:9, 37:5 → line 11
change First::enabled 1 false reference: 32:9 → First::enabled:5 0 references

The extractor now records trait method return hints, substitutes impl arguments, prefers a captured inherent accessor, accepts Deref::Target, and trusts std/core/alloc pointer semantics or aliases only when the path is proven. Otherwise the receiver stays unknown. Tests cover local same-name traits, std paths, aliases, inherent shadowing, Borrow/Deref, unresolved evidence, and renamed/swapped parameters.

Other product fixes

  1. Defaulted and unsupported generic owners. Impl where-clause bounds and default type parameters are recorded. Structs whose declarations cannot be modelled (defaults, where clauses, lifetimes) expose fields through Binding::Fields, so HeaderMap<T = u8> and Layered<L, I, S = I> field receivers resolve. Tuple-variant bindings and inherent methods on defaulted owners resolve; direct-caller kind distinguishes function, method and trait-default-method.
  2. Go external embedded interfaces. interfaceComplete no longer rejects an interface because it embeds an external named interface. go/types still proves the implementation relation; the edge names the external package and method, and the query emits a candidate at each unresolved call site with no target and an explicit reason. Evidence model captured-interface-implementations-v2; go-source-direct-v4 / revision 6.
  3. Coverage gates. v4/v8 models are accepted by the shared semantics, call-question and change-question gates; the schema accepts external interface edges and the go-interface-external-method reason.

Reader paths

  • Go, echo: *routeMethods.set at router.go:226 → direct call router.go:860:2 in (*node).setHandler (line 859), determined go-direct-method. The nil-producing (*DefaultRouter).Remove call at router.go:446 is one indirect hop away; the tool does not expand transitive callers.
  • Rust, bytes: Bytes::truncate at src/bytes.rs:569 → self.truncate(0) at src/bytes.rs:599:9 in Bytes::clear (line 598), rust-inherent-source-candidate.
  • Go, gin interface: *responseWriter.Hijack at response_writer.go:111 → test calls at response_writer_test.go:120:20, 190:23, 245:23; three interface candidates with no concrete target because the static method is external net/http.Hijacker.Hijack.
  • Rust, AsRef: Other::enabled at the fixture src/lib.rs:11 ← 32:9, 37:5; the baseline path pointed at First::enabled:5.

Reserved and reader evidence

  • echo-remove-custom-method (reserved Go) matched exactly: definition router.go:226, call 860:2, caller setHandler:859 method.
  • bytes-truncate-zero (reserved Rust) matched exactly: definition src/bytes.rs:569, call 599:9, caller clear:598 method.
  • Two readers who had not seen answer keys reproduced both keys from CLI output and source alone. Their reported gaps were display limits (no transitive callers, no argument analysis, tests outside the capture profile), not coordinate errors.
  • A third reviewer, with no CLI and no answer key, re-froze the bytes reserved case from the two source trees alone: same definition src/bytes.rs:569, same production call 599:9, caller clear:598. Reports: evidence/readers.

Verification

  • node --test modules/code-analysis/test/*.test.mjs: 444 pass, 0 fail, 0 skipped.
  • Counterexamples: baseline 102/104 (two non-discriminative preservation checks on an already-wrong item); candidate 136/136 with all eight kinds covered. Of the candidate checks, 29 were not applicable because the mutation had no record to act on (8 delete-target, 9 promote-candidate, 10 omit-target-file, 2 wrong-declaration); the baseline had 20.
  • Canonical ZIP /tmp/archify-stage2.zip, SHA256 5b3ee9f516b19c20004b36f32313f3650a820a446e6ca9d9907f89cb7e18e58e, built with official Node 22 / zlib 1.3.1-e00f703, committed as archify.zip in c10ea384c.
  • Fresh unpack + npm ci replayed the AsRef fixture, gin-hijack, echo-remove-custom-method and tracing-subscriber-event-enabled. Identities rust-syn@2.0.119 rev 8 / rust-source-direct-v8 and go-parser@1.27.2 rev 6 / go-source-direct-v4; positions matched the batch runs. fmt/mod.rs:402 remains the documented miss.
  • Evidence: evidence/stage2-baseline, stage2-final, comparison, asref-local, stage2-replay, readers; README has per-scope tables and commands.
  • CI run 42502359214 for head 021a3898a completed successfully; only deploy jobs were skipped. PR stays Draft and unmerged.

Most worthwhile next step

Extend Rust source capture into macro bodies and arguments for calls whose receiver is otherwise identifiable. Four of the eight remaining misses come from macro_rules! layer_impl_body (layer/mod.rs:1609), try_lock! (reload.rs:139), ok!(...) (value_parser.rs:658) and the feature!-gated fmt/mod.rs:402. Source-level token scanning can recover them without a full macro-expansion or type-checking backend; the bytes forwarding misses remain a separate, smaller rule.

Clone this wiki locally