Repository navigation
fix(rust): fence a QN on the function's own cfg predicate, balanced - #2391
BobbieBarker wants to merge 2 commits into
Conversation
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
|
Thank you, @BobbieBarker. This is a really careful piece of work, and exactly what we hoped for when we asked you to file it separately. You didn't stop at the
You measured them on ripgrep, tokio and rust-analyzer with node counts unchanged, and you spotted that We checked:
On the index format version, yes please: add the Tiny and optional, and pre-existing: the #495 doc comment above |
d450275 to
f297737
Compare
|
Both in, rebased onto
The |
rust_cfg_qualified_name folds a function's `#[cfg(...)]` predicate into its qualified name so two mutually-exclusive twins get distinct QNs instead of one overwriting the other (DeusData#495). It read the attribute three ways that the QN it minted shows are wrong. The suffix was copied from the `cfg(` it found to the END of the decorator text. extract_decorators stores the bracketed form, so that appended the attribute's own `]` to every QN: `#[cfg(test)]` minted `add#cfg(test)]`. Across ripgrep 14.1.1, tokio 1.40.0 and rust-analyzer 2024-09-30 that is 332 of 335 fenced QNs. On a multi-line attribute the copied text also carried the newline, and cbm_defs_push cuts a qualified name at a line break by design, pinned by defs_push_cuts_multiline_names_and_rejects_js_literal_names. What survived was everything before the newline, so the suffix stopped mid-predicate: `thread_rng_n#cfg(any(`. Two twins whose predicates differ only past that newline then computed ONE qualified name and the second upsert overwrote the first, which is the loss this function exists to prevent. 10 tokio QNs are truncated this way. None of them happens to collide, because the visible prefixes stay distinct there; the collision is in the test below, which drops from two nodes to one without this commit. `strstr(decorators[i], "cfg(")` was also unanchored, and `cfg_attr` carries its own nested `cfg(`. In `#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]` that inner predicate gates DOCUMENTATION, not compilation: the function is compiled unconditionally and has no twin to be told apart from, and it was fenced anyway as `yield_now#cfg(feature=rt)))]`. 7 tokio functions are fenced solely by a cfg_attr. rust_def_is_test, twenty lines above, already anchors on the bracketed path and says why; this was the one Rust decorator inspector that did not. The fix anchors on `#[cfg(` and copies the balanced span, stopping at the paren that closes it and dropping newlines with the other whitespace. A predicate longer than the 256-byte buffer still truncates, as before, and stays distinct over its first 255 characters. Measured by indexing the three corpora above with a binary built from 5df8b04 and with this one: corpus QNs changed stop being fenced nodes added or lost ripgrep 131 0 0 tokio 176 7 0 rust-analyzer 28 0 0 The 7 are the cfg_attr-only functions, which now carry their plain QN. Node counts are identical in all three, and tokio's multi-line predicates are reconstructed in full: `main#cfg(all(tokio_unstable,tokio_taskdump,target_os=linux,any(target_arch=aarch64,target_arch=x86,target_arch=x86_64)))`. This changes node identity for cfg-fenced Rust functions, so CBM_INDEX_FORMAT_VERSION goes 1 -> 2 and the next index rebuilds from scratch. The deciding case is the incremental path rather than the mixed store: incr_capture_inbound_edge snapshots an inbound cross-file edge by its target's qualified_name STRING, and the re-link after the purge resolves that string again. A caller in an unchanged file would therefore lose its edge into a re-extracted fenced function, because the QN it was snapshotted under no longer exists, and nothing would report the loss. The DeusData#495 comment sat above rust_def_is_test while describing this function; it moves down with the code it documents. tests/test_extraction.c: three tests, one per misreading, and the only coverage this function has in the gating suite. tests/repro/repro_issue495.c exercises it today but Makefile.cbm keeps that file out of `make test`, and it asserts only that two QNs differ and that one contains `not(`, never the string minted. Each of the three fails without this commit. Signed-off-by: Chad <4307099+BobbieBarker@users.noreply.github.com>
f297737 to
538ac25
Compare
|
Thanks for adding the requested index-format change and correcting the comment. I have read your follow-up; the revised Rust cfg identity fix remains pending the next maintainer review/merge step. No new change is requested here. |
|
Thank you, @BobbieBarker, and sorry this sat so long. Your analysis here was excellent: you traced all three misreadings in We are closing this one because With no suffix, all three cases you found are gone on current Your index-format concern is covered too: the format has moved from 1 to 4 since then, so any index that still holds the old suffixed names is rebuilt in full on upgrade, and the incremental edge loss you identified cannot happen. Thanks again for the care you put into this, and for #2370 before it. |
This PR was written by an AI agent working on my behalf. I certify the DCO sign-off on the commit and answer for the change.
Fixes #2390
Filed at your request in #2370. Looking at the
cfg_attrfencing turned up two more misreadings in the same function, and one of them re-opens #495, so all three are here as one claim: the fence should be the function's own cfg predicate, balanced, and nothing else. #2390 carries the reproducer and the evidence for each.The search now anchors on
#[cfg(rather than a barecfg(, so acfg_attrcarrying a nesteddoc(cfg(...))stops fencing a function that is compiled unconditionally. The copy then takes the balanced span, stopping at the paren that closescfg(and dropping newlines along with the other whitespace, which keeps the attribute's own]out of the QN and stops a multi-line predicate being cut at its first line break. A predicate longer than the 256-byte buffer still truncates, as before, and stays distinct over its first 255 characters.Measured
ripgrep 14.1.1
4649aa97, tokio 1.40.0ea6d652a, rust-analyzer 2024-09-30822644d9, indexed with a binary from13ce2c0cand with this branch:The 7 are the
cfg_attr-only functions, which now carry their plain QN. tokio's multi-line predicates come back whole:main#cfg(all(tokio_unstable,tokio_taskdump,target_os=linux,any(target_arch=aarch64,target_arch=x86,target_arch=x86_64))).Index format version
CBM_INDEX_FORMAT_VERSIONgoes 1 to 2, so the next index rebuilds from scratch. The deciding case is the incremental path:incr_capture_inbound_edgesnapshots an inbound cross-file edge by its target'squalified_namestring, and the re-link after the purge resolves that string again. A caller in an unchanged file would otherwise lose its edge into a re-extracted fenced function, with nothing reporting the loss.Tests
Three in
tests/test_extraction.c, one per misreading, and the first coverage this function has in the gating suite.tests/repro/repro_issue495.cexercises it today, butMakefile.cbmdeliberately keeps that file out ofmake testand it asserts only that two QNs differ and that one containsnot(, never the string minted. That is why a trailing]on every fenced QN went unnoticed.Each fails without the commit, for the reason it names: on
13ce2c0cthe extraction suite goes387 passed, 3 failedwithextract_defs.creverted to main, and390 passedwith the commit.Full suite on the rebased branch,
13ce2c0cplus this commit: 144 of 144 suites, 8,239 passed, 0 failed, 10 skipped. clang-format clean; the four violations my local clang-format-20 reports intests/test_extraction.care present on a pristine tree too and are not in the lines this PR adds.