Repository navigation
fix(rust-lsp): Rust calls never bind definitions of other languages - #2681
Merged
Merged
Conversation
Both Rust registries were built from the pipeline's mixed-language def set. The shared Tier-2 build (cbm_rust_build_cross_registry) and the per-file conversion (pxc_lspdefs_to_rust) copied every def, so a Perl `sub new` or any C function became a Rust free function. The lsp_short_name_unique fallback then bound `Store::new()` to the Perl constructor at confidence 0.90 on both venues: its "crate scope" check compares only the first module-QN segment, which is the project name. (Found by the generic-engine lane on a 12-language fixture.) Register Rust defs only, at both conversion points, exactly as the Go builder filters to Go. Rust impl-relation records already carry CBM_LANG_RUST. As a side effect, the shared Rust build no longer registers the C defs of a mixed C+Rust tree. Tests: pipeline_rust_call_never_binds_another_language (sequential and parallel): no CALLS from the Rust caller into the Perl file, and the Rust associated function still binds. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Formatting only, with the repo's clang-format (Homebrew LLVM). Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…t-defs-only Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…llback `Opts::write(true)` is an associated function. When the Rust LSP cannot place it, the registry can (import_map or qualified_suffix onto fsx.Opts.write). The two free-function short-name fallbacks (the cross-crate member lookup and the project-wide lsp_short_name_unique) ran first, though. They bound the call to the project's only free `write`, and that LSP row pre-empted the right answer. While the Rust registry also held other languages' definitions, a same-named JS or Perl function often made the free name ambiguous and hid this. With Rust-only registries the hijack replaced 516 qualified_suffix and 84 import_map edges on 7 Rust repos. A call whose qualifying segment names a type now skips both fallbacks. That covers an ASCII-uppercase initial, `Self`, and a qualified `<T as Tr>::f` path. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The shared Tier-2 build now registers Rust definitions only. Two fixtures that feed cbm_rust_build_cross_registry left CBMLSPDef.lang zero, which the pipeline never does (pass_lsp_cross.c tags every def with its file's language). The other shared-registry fixtures in this file already set CBM_LANG_RUST. Set it on these two as well. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
4 tasks done
…Smart CI selection fix #2684) Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This was referenced Oct 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
The Rust LSP built its registries from the pipeline's mixed-language definition set. The shared Tier-2 build (
cbm_rust_build_cross_registry) and the per-file conversion (pxc_lspdefs_to_rust) copied every def, so a Perlsub new, a TypeScript function or a C function became a Rust free function.The
lsp_short_name_uniquefallback then bound Rust calls to them at confidence 0.90. Its "crate scope" check compares only the first module-QN segment, which is the project name, so it accepts anything in the project. Example:use crate::store::Store; Store::new()bound toperlsrc.Lib.newon both resolvers.The fix registers only Rust defs at both conversion points, exactly as the Go builder filters to Go. Rust impl-relation records already carry
CBM_LANG_RUST. As a side effect, the shared Rust build no longer registers the C defs of a mixed C+Rust tree.The filter keys on
CBMLSPDef.lang, so every production source of those defs must set it. All of them do:cbm_rust_build_cross_registryis the lazy getter inpass_parallel.c:3984, which readsrc->all_defs. Those defs come from one of two places:cbm_pxc_collect_all_defs(pipeline.c:2592).cbm_pxc_collect_all_defsfor the re-parsed files (pipeline_incremental.c:1867,:1893-1903).pxc_lspdefs_to_rust(pass_lsp_cross.c:1596) receives defs from the same collector: the sequential path (pass_lsp_cross.c:1982), the incremental legacy path (pipeline_incremental.c:1950) and the surface probe (:2278).cbm_pxc_collect_all_defspasses each file'sfiles[fi].languagetopxc_build_lsp_def(pass_lsp_cross.c:819), which setsdst->lang(:476). Rust impl relations setCBM_LANG_RUST(:715).langunder the"lg"key (lsp_surface.c:389encode,:664decode). The key has been in the codec since its first version.cbm_batch_rust_lsp_crosshas no caller.A second commit, which the corpus proof forced
The first version (2ac9c33 alone) regressed true edges. Without the cross-language namesakes, many short names became unique among Rust free functions, and the same fallback bound type-qualified calls to them. Examples:
OpenOptions::write(..)landed on a freecache_file::write, andVector4::zeros()on a freeresponse_body::zeros. That LSP row pre-empts the registry, which had the right answer.On 7 Rust repos, this replaced 516 correct
qualified_suffixedges and 84 correctimport_mapedges. 1,137 of the 1,606 addedlsp_short_name_uniqueedges were type-qualified. The bug already exists on main: in a one-crate fixture,Opts::write(true)binds a freecache::writethere too. The namesakes only hid it more often.ec4f0ea: a call whose qualifying segment names a type (ASCII-uppercase initial,
Self, or a<T as Tr>::path) never takes either free-function fallback. Module segments are snake_case, so a lowercasecache::writekeeps its fallback.Evidence
Base
9f85f104vs this branch, measured atec4f0eaawith currentmainmerged in; the head adds only the test-fixture commit. Setup: same machine, production binaries, 7 pinned Rust repos (qdrant, tauri, helix, typst, deno, swc, wasmtime). Every repo was indexed twice with the parallel resolver, and both runs were identical for every edge type.lsp_short_name_uniqueOf the 835 remaining non-Rust targets, 834 come from the pipeline registry's
import_mapstrategy (deno 615, swc 129, wasmtime 78), not from the Rust LSP. They are outside this change and are tracked separately as a cross-language veto for the registry.CALLS diff, base -> this PR:
lsp_short_name_uniquequalified_suffiximport_mapsuffix_matchlsp_trait_dispatchsame_moduleI hand-judged a stratified sample (4 per stratum, with source snippets):
TranspileOptions::default()-> a JSdefault,Func::wrap(..)-> a free test functionwrap,std::cmp::min->BaseHistogram.min.qualified_suffix,import_mapandlsp_trait_dispatch: 12 of 12 were true. Examples:Transaction::change,LocalShard::load,self.realpath_sync()->DenoRtSys.suffix_match, all at confidence <= 0.28: 4 of 4 were false. Example:fs::write->FileLock.write. These are the generic registry fallback reaching call sites whose 0.90 LSP edge was also false. They are a separate follow-up, as arestd::/dependency-crate paths such asstd::ptr::write.The accounting, weighted by stratum size:
lsp_short_name_uniquedrops from 2,274 to 0. The second commit alone removes 2,998 type-qualified short-name hijacks.qualified_suffix,import_map, trait dispatch; 12 of 12 judged true).suffix_matchat confidence <= 0.28 (4 of 4 judged false). Each sits at a call site whose old 0.90 LSP edge was also false. That makes it the same wrong answer at much lower confidence, not a new wrong edge.Against an independent oracle (rust-analyzer SCIP, covering 4 of these repos), precision and recall both rise on every repo:
Follow-ups, each a separate PR:
std::/core::/dependency-crate paths, such asstd::ptr::write(about 150 of the 1,650).mod x;modules (cache::write,self::cache::write,Opts::writeafteruse crate::fsx::Opts). Those sites fall back tosuffix_match, which is 44% of Rust CALLS on main.Tests
pipeline_rust_call_never_binds_another_language(sequential and parallel): no CALLS from the Rust caller into the Perl file, and the Rust associated function still binds.pipeline_rust_assoc_call_never_binds_free_namesake(sequential and parallel):Opts::write(true)bindsfsx.Opts.write, never the freecache.write. The module-qualifiedcache::write("x")still binds the free function.A third commit tags two existing
test_rust_lsp.cfixtures withlang = CBM_LANG_RUST. They fed the shared build withlangleft at 0, which the pipeline never does. The other shared-registry fixtures in that file already set it.All four new tests fail with the production change reverted, for the asserted reason, and pass with it. Each half was also shown separately with production binaries on the fixtures. With only the first commit,
Opts::writestill lands oncache.write. Onmain,Store::new()lands on the Perlnew.Checklist
git commit -s)pass_lsp_cross.c; memory-core linter passes