This issue and its PR were written by an AI agent working on my behalf. I certify the DCO sign-off on every commit and answer for the change.
A Rust function carrying #[cfg(test)] is absent from the by-name index, so no bare callee resolves to it.
cbm_registry_add takes the symbol's name and discards it ((void)name;), deriving the bare-name lookup key from the QN's last dot segment instead. For most grammars the two agree. For this one they do not: rust_cfg_qualified_name mints the twin as proj.lib.add#cfg(test), and simple_name() splits on . and :: with no # handling, so the derived key is the whole string add#cfg(test).
Nothing is ever looked up under that string. Every by-name read keys through simple_name() of the callee as written, and a callee is written add. So the function is registered, occupies a bucket, and is unreachable.
Reproducer, at the registry's own level:
cbm_registry_t *r = cbm_registry_new();
cbm_registry_add(r, "add", "proj.lib.add#cfg(test)", "Function");
cbm_resolution_t res = cbm_registry_resolve(r, "add", "proj.lib.caller", NULL, NULL, 0);
/* res.qualified_name is NULL; expected "proj.lib.add#cfg(test)" */
The visible effect is on a Rust crate's test code: a #[cfg(test)] helper called by name from another test module gets no inbound CALLS edge, and fan_in on it is 0 whatever calls it.
The fix I have keys the index on the derived segment exactly as today, and takes a second key from the passed name only when the derived segment carries a #. Every grammar that mints no # stays byte-identical, and you can check that from the keys themselves: a passed name differing from the derived key by anything other than a fence is a key no simple_name()-based lookup can reach, so adding it would only widen the bucket the scorer walks.
Two things change what the fix has to cover.
cbm_label_is_registry_symbol gates all three production callers and admits neither Module nor Section, so a file's Module node and a Markdown heading never reach this function. Among the dotted-name shapes only HCL, TOML and INI do, under label Class, and those are why the derived key stays unconditional: instance = aws_instance.web.id reaches an HCL block through the tail web, so re-keying on the passed name alone would drop that edge.
The second key puts the cfg twin into the same bare-name bucket as the real function. candidate_score applies REG_TEST_PENALTY to a candidate whose QN spells test, so the real add outranks add#cfg(test). That protection is a substring test on the QN, so it does not reach a #[test] function whose cfg predicate says something else, and Rust's inline #[cfg(test)] mod tests puts no test segment in the path either.
Both costs are now measured, on ripgrep 14.1.1, tokio 1.40.0 and rust-analyzer 2024-09-30. Bucket width holds: the largest bucket is unchanged on all three, no bucket crosses REG_MAX_CANDIDATES, and the count of lookups that bail on the cap is identical. Resolution moves at 54 call sites: 47 onto the function the source actually calls, 6 onto a cfg-fenced #[test] function that outscores the real callee, and 1 onto nothing. Figures and the six regressions are in #2370.
Raised separately from #2312 at the maintainer's request, as its own defect.
This issue and its PR were written by an AI agent working on my behalf. I certify the DCO sign-off on every commit and answer for the change.
A Rust function carrying
#[cfg(test)]is absent from the by-name index, so no bare callee resolves to it.cbm_registry_addtakes the symbol'snameand discards it ((void)name;), deriving the bare-name lookup key from the QN's last dot segment instead. For most grammars the two agree. For this one they do not:rust_cfg_qualified_namemints the twin asproj.lib.add#cfg(test), andsimple_name()splits on.and::with no#handling, so the derived key is the whole stringadd#cfg(test).Nothing is ever looked up under that string. Every by-name read keys through
simple_name()of the callee as written, and a callee is writtenadd. So the function is registered, occupies a bucket, and is unreachable.Reproducer, at the registry's own level:
The visible effect is on a Rust crate's test code: a
#[cfg(test)]helper called by name from another test module gets no inbound CALLS edge, andfan_inon it is 0 whatever calls it.The fix I have keys the index on the derived segment exactly as today, and takes a second key from the passed
nameonly when the derived segment carries a#. Every grammar that mints no#stays byte-identical, and you can check that from the keys themselves: a passed name differing from the derived key by anything other than a fence is a key nosimple_name()-based lookup can reach, so adding it would only widen the bucket the scorer walks.Two things change what the fix has to cover.
cbm_label_is_registry_symbolgates all three production callers and admits neitherModulenorSection, so a file's Module node and a Markdown heading never reach this function. Among the dotted-name shapes only HCL, TOML and INI do, under labelClass, and those are why the derived key stays unconditional:instance = aws_instance.web.idreaches an HCL block through the tailweb, so re-keying on the passed name alone would drop that edge.The second key puts the cfg twin into the same bare-name bucket as the real function.
candidate_scoreappliesREG_TEST_PENALTYto a candidate whose QN spellstest, so the realaddoutranksadd#cfg(test). That protection is a substring test on the QN, so it does not reach a#[test]function whose cfg predicate says something else, and Rust's inline#[cfg(test)] mod testsputs notestsegment in the path either.Both costs are now measured, on ripgrep 14.1.1, tokio 1.40.0 and rust-analyzer 2024-09-30. Bucket width holds: the largest bucket is unchanged on all three, no bucket crosses
REG_MAX_CANDIDATES, and the count of lookups that bail on the cap is identical. Resolution moves at 54 call sites: 47 onto the function the source actually calls, 6 onto a cfg-fenced#[test]function that outscores the real callee, and 1 onto nothing. Figures and the six regressions are in #2370.Raised separately from #2312 at the maintainer's request, as its own defect.