Skip to content

fix: preserve comments around associated type bounds - #7014

Open
saberoueslati wants to merge 3 commits into
rust-lang:mainfrom
saberoueslati:fix/issue-6761-associated-type-bounds
Open

fix: preserve comments around associated type bounds#7014
saberoueslati wants to merge 3 commits into
rust-lang:mainfrom
saberoueslati:fix/issue-6761-associated-type-bounds

Conversation

@saberoueslati

Copy link
Copy Markdown

Summary

Fixes #6761, where rustfmt could duplicate associated-type bounds when comments appeared inside the bounds and before a where clause.

The rewrite now:

  • Uses the final bound’s span when recovering comments before where.
  • Preserves comments before and after the :.
  • Retains the existing behavior for associated types without bounds.

Added regression coverage for comments inside bounds, around the colon, before where, and on generic associated types.

Testing

  • cargo test --lib system_tests

@rustbot rustbot added the S-waiting-on-review Status: awaiting review from the assignee but also interested parties. label Aug 6, 2026
@ytmimi

ytmimi commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Looks like there's some overlap here with #6980

Comment thread tests/target/issue-6761.rs Outdated
Comment on lines +13 to +19
// Case 2: comment between the bound and `where`.
trait Bar1 {
type B: Iterator<Item = u8>
// trailing
where
Self: Copy;
}

@ytmimi ytmimi Aug 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread tests/source/issue-6761.rs Outdated
Comment on lines +28 to +33
// Case 4: bound + RHS + trailing where clause (associated type default).
trait Bound {}
impl Bound for () {}
trait Qux {
type D: Bound = () where Self: Copy;
}

@ytmimi ytmimi Aug 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this testing? I don't see any comments in the bonds here.

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
}

Comment thread tests/source/issue-6761.rs Outdated
Comment on lines +35 to +42
// 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;
}

@ytmimi ytmimi Aug 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

View changes since the review

@saberoueslati saberoueslati Aug 22, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread tests/source/issue-6761.rs Outdated
Comment on lines +60 to +66
// Case 6: no bounds + comment before `where` (guards the preserved
// generics.span.hi() default).
trait NoBounds {
type F // just a comment
where
Self: Copy;
}

@ytmimi ytmimi Aug 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this just Case 2 from above?

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread tests/source/issue-6761.rs Outdated
Comment on lines +84 to +86
type J /* before colon */: Bound
where
Self: Copy;

@ytmimi ytmimi Aug 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if there's a // line comment here.

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 */ : Bound

The new fixture's P1 section covers all four comment forms in this position: line, block, multi-line line, and multi-line block.

@ytmimi ytmimi Aug 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
 * /

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@ytmimi

ytmimi commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: awaiting review from the assignee but also interested parties. labels Aug 22, 2026
@rustbot

rustbot commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@saberoueslati

Copy link
Copy Markdown
Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: awaiting some action (such as code changes or more information) from the author. labels Aug 23, 2026
@saberoueslati

Copy link
Copy Markdown
Author

@ytmimi tried responding to every review comment, thank you for the review.

@saberoueslati
saberoueslati requested a review from ytmimi August 23, 2026 00:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: awaiting review from the assignee but also interested parties.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rustfmt generates invalid code for associated type bounds with where-clause and comments

3 participants