Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
74ea280 to
05f2a57
Compare
Xceptional! 🛠️ Minor Suggestions & NitsThese are mostly stylistic or minor optimizations. 1. Simplify Iterator Chaining in let group_docs = d.group.iter().flat_map(|g| &g.docs);
let doc_has = |needle: &str| {
d.docs.iter().chain(group_docs.clone()).any(|x| x.contains(needle))
};You can make this cleaner and slightly more efficient by chaining directly. This avoids the need to clone the iterator state and is more idiomatic Rust: let doc_has = |needle: &str| {
d.docs
.iter()
.chain(d.group.iter().flat_map(|g| &g.docs))
.any(|x| x.contains(needle))
};2. Idiomatic let doc_start = comments
.first()
.map(|c| c.range().start)
.unwrap_or(node.range().start);This is perfectly fine, but let doc_start = comments.first().map_or(node.range().start, |c| c.range().start);3. Hardcoded Tree-Sitter Grammar Strings matches!(c.kind().as_ref(), "(" | "const_spec_list" | "var_spec_list" | "type_spec_list")While this is correct for the current 4. CRLF Handling in Rendering for line in d.trim_end_matches(&['\r', '\n'][..]).split('\n') {
out.push(format!("{}{} {}", prefix, "[group]".dimmed(), line.trim_end_matches('\r')));
}This correctly handles both |
Maintainer review on PR aeroxy#53: chain the group's docs inline in `_deprecated` rather than cloning a captured iterator, `map_or` in `_spec_docs`, and lift the tree-sitter-go node kinds that spell a group into `GROUP_KINDS`, where a grammar rename has one place to land. `var_spec_list` is a constant of its own because that name has two readers: the group tell and the traversal that reaches a var block's members. Spelling it twice is how a grammar rename empties every parenthesised `var` block silently rather than failing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
All three addressed in 0d1e9e4:
Nit 4 needs no change, as you said. Want these squashed into the one commit before merge, or is a follow-up commit fine? |
|
@vlsi Ran a deeper pass over the branch and turned up three things, one of which lands directly on the constant from nit 3.
Worth noting Trailing comments are dropped rather than re-attributed. The Verification behind all of the above, for what it's worth: I diffed On your squash question: separate commits are fine, and I'd rather have the review trail while this is still moving. I'll squash at merge. |
…t all Go attaches doc comments in two places the mapper had no model for. `const ( … )`, `var ( … )` and `type ( … )` blocks take a comment of their own, which `go doc` renders as the documentation of the whole block. The adapter handed it to whichever member came first, or dropped it when that member had a comment already, and nothing marked the result as inherited: asked to review the doc comment of `MaxBytes`, an agent was handed a sentence written about the group. A member's own comment lost to the group's, too — `// Circle is round.` inside a `type ( … )` block. Struct fields and interface methods had the opposite failure: their comments were never read, so `docs: null` was indistinguishable from a genuinely undocumented field. For a Kubernetes operator that is the whole API surface — `controller-gen` builds every CRD description from those comments — so a package of documented types mapped as undocumented. A block's comment now travels as `group` on every member of the block and as no member's `docs`. `DeclarationGroup` carries the block's line range beside the comment: the comment above `const (` matches no declaration's line, so a consumer deciding by position whether a comment is a doc comment had nothing to anchor on, and a renderer comparing comment text alone cannot tell two adjacent blocks apart when both are headed `// Deprecated: …`. `map` prints the comment once per block, marked `[group]` on every line, because printed bare above the first member it reads as that member's own. Struct fields and interface methods report their comments the way types and functions already do. A comment sharing a line with the declaration before it is that declaration's trailing comment: without that rule every field below `Alpha int // in bytes` would inherit it, trading one misattribution for a denser one. Reporting the block as a declaration whose children are its members, the issue's other suggestion, would match `go doc` more literally at the cost of every Go consumer: `digest`'s flatteners would hide the members, the header counts would shift, and `_walk_top` resolves receiver types by their index among the package's direct children, so types declared inside a `type ( … )` block would stop collecting their methods. A parenthesised block holding one spec is still a block. Measured with go1.26.4, `go doc pkg.Solo` on a one-member block and `go doc pkg.Red` on a two-member one both print the whole block followed by the block's comment, so `go doc` draws no distinction by arity either. Fixes aeroxy#46 Fixes aeroxy#47 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Maintainer review on PR aeroxy#53: chain the group's docs inline in `_deprecated` rather than cloning a captured iterator, `map_or` in `_spec_docs`, and lift the tree-sitter-go node kinds that spell a group into `GROUP_KINDS`, where a grammar rename has one place to land. `var_spec_list` is a constant of its own because that name has two readers: the group tell and the traversal that reaches a var block's members. Spelling it twice is how a grammar rename empties every parenthesised `var` block silently rather than failing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ther Cross-review over the branch, four rounds across two runs. `surface` builds its own `SurfaceEntry` and copied only `docs`, so moving a block's comment out of the first member's `docs` left it unreachable: `surface --json` reported every member of every documented Go block as undocumented, which is issue aeroxy#46's failure one surface over. `SurfaceEntry` now carries `group`, and all five resolvers copy it beside `docs` so the next one to be written sees the pair. A `type ( … )` group's members each push a trailing blank line, which separated every member after the first from the `[group]` line documenting it — `Square` read as an undocumented top-level type. Members of one block now render as one block. That gap closes only when the marker is printed: closing it unconditionally told the reader by spacing alone which block carried the comment they had asked `--no-docs` to hide, and left a consumer segmenting on blank lines merging a documented block's members into one record. The marker was documented only in README and SKILL.md, neither of which reaches an MCP client. The `map` tool description carries it too, and a test fails if the two stop stating the same facts — which JSON field holds a block's comment, and that a member's own stays in `docs`. `GROUP_KINDS` listed `const_spec_list` and `type_spec_list` as insurance against a grammar that starts interposing a spec list for those two. It was not insurance: the traversal reaches a block's members through `VAR_SPEC_LIST` alone, so such a block would have been classified as a group and then yielded nothing, silently and at exit 0. What covers that upgrade is the fixture, whose blocks fail the suite the moment their members stop being found. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e8b589b to
56d41f2
Compare
|
Review fixes pushed, and the branch is rebased onto Commit id mapping:
Also: the Rebase conflict was one file, |
|
@vlsi Thanks for picking these up rather than handing them back — and for the two things you caught that I didn't. The And your read on On Rebuilt and re-ran everything on the new base — suite green, and I re-checked the gap-close under default, Three nits left, all prose or test rather than behaviour:
The test that would catch that omits the deciding member. The Two more I'm not asking you to change: That leaves the trailing-comment question from my last comment unanswered, which is the only thing between this and merge. |
Maintainer review on PR aeroxy#53. `SurfaceEntry.group`'s comment described a conditional the code does not implement — carried "when its own `docs` are empty" — while every resolver copies it unconditionally. Read literally it is the spec for whoever writes the sixth resolver, and it prescribes the answer that erases a block's comment from exactly the members that also document themselves. It states the invariant now. The test that would have caught that hand-kept a list of 8 members while the fixture has 9; the missing one was `Circle`, the only member with both its own `docs` and a `group`, and so the only one whose result differs between the two readings. The set comes from the fixture now, so a member the fixture gains cannot go uncovered. `group` is documentation and sheds with `docs` under the docs projection, which the README, the skill and the MCP tool description all promised around rather than stating; the drift test pins the caveat on the two agent-facing ones. Doc comments reviewed under rustdoc-authoring: seven summaries carrying a second sentence into the item table are split, `the traversal below` and `both sites` name the items they mean, and the test module comment states what holds rather than what used to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cross-review of 244111a. `VAR_SPEC_LIST`'s comment claimed a stale copy would leave "detection working through the `(` child". A `var ( … )` keeps its parens inside the spec list, which the paragraph above says in as many words, so detection finds no direct child, calls the block ungrouped, and hands its comment to the first member — issue aeroxy#46 again, not an emptied block. Both failure modes are named by which copy goes stale. `GROUP_KINDS` attributed the member walk to `_const_var_to_decls`, which never sees a `type_declaration`; the `type_spec_list` half of the sentence it justifies is `_type_declaration_to_decls`. Both are named. `_close_group_gap`'s summary split turned "would separate" into "separates", stating as current output the exact thing the function prevents. The surface test derived its expected set through `map`'s visibility and compared it against `surface`, which drops unexported symbols: the first unexported member the fixture gained would have failed the test on correct resolver behaviour. It derives through `--no-private` now, verified both ways — an unexported documented block no longer fails it, and the `docs`-empty conditional still does. `wiki/architecture.md` was the fourth text stating the group contract and the only one already reasoning about projections; it gets the same caveat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cross-review round 2 refuted my round-1 rejection, with a reproduction.
I argued a name collision could only weaken the test, on the grounds that
Go rejects duplicate top-level names. That covers one collision order and
not the other: a struct field may share a name with a top-level symbol and
compiles fine, and the fixture already has three such shapes. With
`type Board struct { Square int }` above the `type ( … )` block, the
grouped type keeps the name in the expected set while the loop's bare-name
match reaches the field entry, and the test fails `surface` for correctly
reporting a field that belongs to no block.
Both sides key on `(name, line)` now. `map` and `surface` agree on that
line — measured: field `Square` at 40, type `Square` at 50 on both.
Three axes checked by reverting each: a colliding field passes, an
unexported documented block passes, and carrying `group` only when `docs`
is empty still fails.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cross-review of e62fe8e. The collision narrowed rather than closed: a struct and its own field share a name and a line, so `Square struct{ Square int }` inside the documented block puts one key in the expected set and lets the field entry answer for the type. Keyed by kind, name and line now, with the surface side narrowed to the fixture file — `map` reads one file while `surface` walks the directory, so a second `.go` file there reintroduced the same collision one level up. `kind` agrees across the two by construction: `surface::fallback` copies it off the same `Declaration` that `map` prints. The walk also uses the `docs` helper the surface side already used, instead of inlining a second reading of the same JSON shape. Five axes checked by reverting each: name-and-line collision, name-only collision, unexported documented block and a second file in the directory all pass, and carrying `group` only when `docs` is empty still fails. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cross-review round 2. The basename filter closed the sibling-file case and not the nested one: `surface` is handed a directory and walks it recursively, so `sub/docs.go` carries the fixture's basename, passes the filter, and joins as if it were the fixture. Demonstrated with a nested `const Red` placed on line 8 to collide with the fixture's grouped `Red`. `source_path` is the walk's own spelling of `FIXTURE` and matches it byte for byte, so the filter compares the whole path. `key` also stopped defaulting. The two payloads spell the same fields differently — `name` / `start_line` against `source_name` / `source_line` — and `unwrap_or_default` turned a rename on either side into empty keys, which fail under "surface must report every member" — the one assertion here that cannot name its cause. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
All three nits addressed, plus the two test observations you weren't asking about. Five commits on top of
The test that would have caught it derives its expected set from the fixture instead of hand-keeping a list, so The Both test observations: Comments were also swept under our rustdoc rubric: seven summaries were carrying a second sentence into the item table, and two references named a position rather than an item. Worth flagging honestly, since it is most of the diff above: cross-review spent four rounds not on the fix but on the test that guards it. The join key was wrong three times — bare name, then name and line (a struct and its own field share both: Two things I found while doing that, neither touched here:
On the trailing-comment question: I'd rather it were its own PR. Not because it's hard — because every defect in this branch for four rounds has hidden in the checks around a change rather than in the change, and reading a trailing comment onto the declaration it trails adds an axis in every spec list. Its own fixture and its own rounds will do better by it than a fifth round here. This PR closes #46 and #47 as scoped; happy to be overruled if you'd rather land it together. |
|
@vlsi All three landed, and the two corrections in And thank you for writing the four-rounds-on-the-test paragraph rather than letting the commit list imply a smooth ride. That's the more useful artifact than the fix. Three left, all in the test, and one of them is a direct correction to a rationale in that paragraph: The The Put the three collision cases in the fixture rather than in the comment. I had this down as speculative hardening until your note; reverting each one in is real verification and better than I'd assumed. But the comment states them present-tense as fixture contents ( Yes to the multi-name spec issue, please open it: On trailing comments — agreed, and your reason is better than my instinct to land it here. Every defect in this branch for four rounds hid in the checks rather than the change, and adding an axis to every spec list under that track record earns its own fixture and its own rounds. Filing it; this PR closes #46 and #47 as scoped. None of the three block anything, so leave them where they are — they'll keep until you're back, and they're yours rather than something I patch over the top of your branch while you're out. The third one is the only one with any shape to it: making those collision cases real means new fixture lines, which shifts the hardcoded Have a good week off — and thanks for four rounds of this, the sharpest findings in it being the ones you turned back on your own previous commits. |
Why
Go attaches doc comments in two places the mapper had no model for, and both failures land on an agent reading
sb mapoutput.const ( … ),var ( … )andtype ( … )take a comment of their own, whichgo docrenders as the documentation of the whole block. The adapter handed that comment to whichever member came first, or dropped it when that member had a comment already — and nothing marked the result as inherited. Asked to review the doc comment ofMaxBytes, an agent was handed a sentence written about the group. (#46)Struct fields and interface methods had the opposite failure: their comments were never read at all, so
docs: nullwas indistinguishable from a genuinely undocumented field. For a Kubernetes operator that is the whole API surface —controller-genbuilds every CRD description from those comments — so a package of documented types mapped as undocumented. (#47)What
groupon every member of the block and as no member'sdocs.DeclarationGroupis{docs, start_line, end_line}; the range is the block's own,const (through), with the comment ending on the line above it — the anchor issue Go: a grouped declaration's doc comment is dropped, or silently attributed to its first member #46 asked for, since the comment aboveconst (matches no declaration's line.mapprints the comment once per block, marked[group]on every line. Printed bare above the first member it reads as that member's own, which is the bug. Two adjacent blocks are told apart by the block's range, not by its prose:// Deprecated: …and generator banners repeat verbatim.// Circle is round.inside atype ( … )block used to lose to the group's.docsanddoc_start_bytethe way types and functions already do.Alpha int // in byteswould have inherited it, which would have traded one misattribution for a denser one.--no-docssheds the group in both JSON projection layers;--no-lineskeeps its range, which is the only thing a block can be identified by.group: None,additions across the other adapters are the new IR field reaching everyDeclarationliteral.I took the issue's second suggested fix rather than the first. Emitting the group as a declaration whose children are its members matches
go docmore literally, but it changes the tree shape for every Go consumer:digest's flatteners would hide the members, the header counts would shift, and_walk_topresolves receiver types by their index among the package's direct children, so types declared inside atype ( … )block would stop collecting their methods.groupgets the same information to consumers without that blast radius.One thing the issues suggest that this deliberately does not do: a parenthesised block holding a single spec is still a block. Measured with go1.26.4,
go doc pkg.Soloon a one-member block andgo doc pkg.Redon a two-member one both print the whole block followed by the block's comment —go docdraws no distinction by arity, so neither does this.How to verify
cargo test --test go_adaptertests/go_adapter.rscovers each case againsttests/fixtures/go_adapter/docs.go: the group comment reaching no member'sdocs, a member keeping its own, struct-field and interface-method docs, undocumented members staying undocumented, trailing comments, two adjacent blocks sharing a comment, block comments, and both projections. Full suite green.Fixes #46
Fixes #47
🤖 Generated with Claude Code