fix: preserve comments around associated type bounds - #7014
fix: preserve comments around associated type bounds#7014saberoueslati wants to merge 3 commits into
Conversation
|
Looks like there's some overlap here with #6980 |
| // Case 2: comment between the bound and `where`. | ||
| trait Bar1 { | ||
| type B: Iterator<Item = u8> | ||
| // trailing | ||
| where | ||
| Self: Copy; | ||
| } |
There was a problem hiding this comment.
Same question I had on #6980. Can you help me understand why this is wrapping. Should we try to keep it on the same line?
There was a problem hiding this comment.
This wrapping is existing rewrite_where_keyword behavior: when rustfmt recovers a comment immediately before a where clause, it emits that comment as its own block before where.
This is shared by other item kinds, including structs, enums, and type aliases, so this PR does not introduce the behavior. Its scope is to avoid recovering the bounds a second time along with the comment.
I added coverage for this exact same-line trailing-comment input in the new fixture to make the preserved behavior explicit. Keeping the comment on the bounds line would be a broader formatting change, so I think it should be handled separately if we want to change that behavior.
| // Case 4: bound + RHS + trailing where clause (associated type default). | ||
| trait Bound {} | ||
| impl Bound for () {} | ||
| trait Qux { | ||
| type D: Bound = () where Self: Copy; | ||
| } |
There was a problem hiding this comment.
What is this testing? I don't see any comments in the bonds here.
There was a problem hiding this comment.
It was checking that bounds, an RHS default, and a where clause still compose correctly after the span_end_before_where change. But you're right: without comments, it was not testing this PR's behavior.
I kept that case and added comments to it in the new associated-type-bounds-with-comments.rs fixture:
trait Rhs {
type A: Bound = () // line
where
Self: Copy;
type B: Bound /* inline */ = ()
where
Self: Copy;
}| // Case 5a: associated type in an impl block, no bounds (bounds have no | ||
| // effect on impl assoc types and are rejected by rustc), still exercises | ||
| // the shared rewrite_ty/where-clause path. | ||
| trait Gat { | ||
| type I<T> | ||
| where | ||
| T: Copy; | ||
| } |
There was a problem hiding this comment.
Same question here. There aren't any comments and my understanding is that this PR is meant to preserve comments around associated type bounds.
You don't need to define Gat just so you can impl Gat for S below. fustfmt will still be able to format the code as long as it parses. It doesn't need to be a valid program that compiles.
There was a problem hiding this comment.
I replaced it with an impl associated-type case containing a comment before the where clause. That exercises the shared rewrite_ty path when there are no bounds, which is the fallback that must continue to start comment recovery at generics.span.hi().
I removed the unrelated trait definition, since rustfmt only needs the snippet to parse.
| // Case 6: no bounds + comment before `where` (guards the preserved | ||
| // generics.span.hi() default). | ||
| trait NoBounds { | ||
| type F // just a comment | ||
| where | ||
| Self: Copy; | ||
| } |
There was a problem hiding this comment.
Isn't this just Case 2 from above?
There was a problem hiding this comment.
They look similar, but they cover different branches.
Case 2 has bounds, so span_end_before_where is updated to the end of the final bound. This case has no bounds, so it verifies that the existing generics.span.hi() fallback still recovers comments before where.
Both now live side by side in the new fixture: P5 for the with-bounds branch and P5NoBounds for the fallback. The distinction is spelled out in the section comments, and the original type F // just a comment input is carried over verbatim.
| type J /* before colon */: Bound | ||
| where | ||
| Self: Copy; |
There was a problem hiding this comment.
What happens if there's a // line comment here.
There was a problem hiding this comment.
A // line comment in that position necessarily puts the : on the next line:
type A // line
: Bound
where
Self: Copy;That parses and is idempotent, though it is visually unusual. Block comments can remain inline by comparison:
type B /* inline */ : BoundThe new fixture's P1 section covers all four comment forms in this position: line, block, multi-line line, and multi-line block.
There was a problem hiding this comment.
These test cases are a good start, but I'd like them to be more exhaustive. We should make sure that we're testing both // line comments and /* inline comments */ in all of these positions. And we should make sure that we're testing comments that span multiple lines.
For example,
// multi-
// line
// comments
/* multi-
* line
* comments
* /
There was a problem hiding this comment.
I expanded the coverage into a new associated-type-bounds-with-comments.rs fixture.
For each position around the associated-type bounds and where clause, it now covers:
//line comments/* inline */block comments- multi-line
//comments - multi-line
/* ... */comments
The covered positions are before :, after :, inside generic bounds, between bounds, after the final bound before where, and after where. The fixture also retains targeted cases for the no-bounds fallback, GATs, impl/free-alias paths, and associated-type defaults with an RHS.
|
@rustbot author |
|
Reminder, once the PR becomes ready for a review, use |
|
@rustbot ready |
|
@ytmimi tried responding to every review comment, thank you for the review. |
Summary
Fixes #6761, where rustfmt could duplicate associated-type bounds when comments appeared inside the bounds and before a
whereclause.The rewrite now:
where.:.Added regression coverage for comments inside bounds, around the colon, before
where, and on generic associated types.Testing
cargo test --lib system_tests