diff --git a/src/items.rs b/src/items.rs index a63fd874487..1f4f97cd733 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1762,15 +1762,53 @@ fn rewrite_ty( result.push_str(&generics_str); } + // Default keeps today's behavior when there are no bounds, so comments between + // the ident and `where` are still recovered. + let mut span_end_before_where = generics.span.hi(); + if let Some(bounds) = generic_bounds_opt { if !bounds.is_empty() { // 2 = `: ` let shape = Shape::indented(indent, context.config); let shape = shape.offset_left(result.len() + 2, span)?; - let type_bounds = bounds - .rewrite_result(context, shape) - .map(|s| format!(": {}", s))?; - result.push_str(&type_bounds); + let type_bounds = bounds.rewrite_result(context, shape)?; + + // The bounds rewrite only covers the bounds themselves, so comments + // around the `:` would otherwise be dropped. Recover them on either + // side of the colon, keeping the exact `: ` layout when there are none. + let bounds_lo = bounds[0].span().lo(); + let colon_pos = context + .snippet_provider + .span_after(mk_sp(generics.span.hi(), bounds_lo), ":"); + let before_colon = mk_sp(generics.span.hi(), colon_pos - BytePos(1)); + let after_colon = mk_sp(colon_pos, bounds_lo); + + if contains_comment(context.snippet(before_colon)) { + result = combine_strs_with_missing_comments( + context, + &result, + ":", + before_colon, + shape, + true, + )?; + } else { + result.push(':'); + } + + if contains_comment(context.snippet(after_colon)) { + result = combine_strs_with_missing_comments( + context, + &result, + &type_bounds, + after_colon, + shape, + true, + )?; + } else { + result.push_str(&format!(" {}", type_bounds)); + } + span_end_before_where = bounds[bounds.len() - 1].span().hi(); } } @@ -1787,7 +1825,7 @@ fn rewrite_ty( false, "=", None, - generics.span.hi(), + span_end_before_where, option, )?; result.push_str(&before_where_clause_str); diff --git a/tests/source/issue-6761.rs b/tests/source/issue-6761.rs new file mode 100644 index 00000000000..26734f707fc --- /dev/null +++ b/tests/source/issue-6761.rs @@ -0,0 +1,94 @@ +#![feature(associated_type_defaults)] + +// Case 1: exact issue repro (comment inside bounds + before-where clause). +pub trait Trait { + type I: Iterator< + // This is an item + Item = Self, + > + where + Self: Copy; +} + +// Case 2: comment between the bound and `where`. +trait Bar1 { + type B: Iterator // trailing + where + Self: Copy; +} + +// Case 3: a `/` appears in the bounds without being a comment. +trait Foo {} +trait Baz { + type C: Foo<{ 6 / 2 }> + where + Self: Copy; +} + +// Case 4: bound + RHS + trailing where clause (associated type default). +trait Bound {} +impl Bound for () {} +trait Qux { + type D: Bound = () 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 + where + T: Copy; +} +struct S; +impl Gat for S { + type I + // comment before where + where + T: Copy, + = Vec; +} + +// Case 5b: free type alias, no bounds (bounds have no effect outside trait +// definitions), still exercises the shared rewrite_ty/where-clause path. +type E +// a free comment +where + T: Copy, += Vec; + +// Case 6: no bounds + comment before `where` (guards the preserved +// generics.span.hi() default). +trait NoBounds { + type F // just a comment + where + Self: Copy; +} + +// Case 7: comments around the `:` must not be dropped. The bounds rewrite +// covers only the bounds themselves, so these are recovered separately. +trait AroundColon { + type G: /* after colon */ Bound + where + Self: Copy; + + type H: /* c1 */ Bound + Bound + where + Self: Copy; + + type I: // line comment after colon + Bound + where + Self: Copy; + + type J /* before colon */: Bound + where + Self: Copy; +} + +// Case 8: comment around the `:` on a generic associated type. +trait AroundColonGat { + type K: /* generics too */ Bound + where + U: Copy; +} diff --git a/tests/target/issue-6761.rs b/tests/target/issue-6761.rs new file mode 100644 index 00000000000..9e2d412a4a4 --- /dev/null +++ b/tests/target/issue-6761.rs @@ -0,0 +1,99 @@ +#![feature(associated_type_defaults)] + +// Case 1: exact issue repro (comment inside bounds + before-where clause). +pub trait Trait { + type I: Iterator< + // This is an item + Item = Self, + > + where + Self: Copy; +} + +// Case 2: comment between the bound and `where`. +trait Bar1 { + type B: Iterator + // trailing + where + Self: Copy; +} + +// Case 3: a `/` appears in the bounds without being a comment. +trait Foo {} +trait Baz { + type C: Foo<{ 6 / 2 }> + where + Self: Copy; +} + +// Case 4: bound + RHS + trailing where clause (associated type default). +trait Bound {} +impl Bound for () {} +trait Qux { + type D: Bound + = () + 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 + where + T: Copy; +} +struct S; +impl Gat for S { + type I + // comment before where + where + T: Copy, + = Vec; +} + +// Case 5b: free type alias, no bounds (bounds have no effect outside trait +// definitions), still exercises the shared rewrite_ty/where-clause path. +type E +// a free comment +where + T: Copy, += Vec; + +// Case 6: no bounds + comment before `where` (guards the preserved +// generics.span.hi() default). +trait NoBounds { + type F + // just a comment + where + Self: Copy; +} + +// Case 7: comments around the `:` must not be dropped. The bounds rewrite +// covers only the bounds themselves, so these are recovered separately. +trait AroundColon { + type G: /* after colon */ Bound + where + Self: Copy; + + type H: /* c1 */ Bound + Bound + where + Self: Copy; + + type I: // line comment after colon + Bound + where + Self: Copy; + + type J /* before colon */ : Bound + where + Self: Copy; +} + +// Case 8: comment around the `:` on a generic associated type. +trait AroundColonGat { + type K: /* generics too */ Bound + where + U: Copy; +}