diff --git a/src/imports.rs b/src/imports.rs index 2f26791639a..1415fb6034e 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -344,13 +344,17 @@ impl UseTree { let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| { crate::utils::format_visibility(context, vis) }); + let vis_separator = if vis.is_empty() { "" } else { " " }; let use_str = self - .rewrite_result(context, shape.offset_left(vis.len(), self.span())?) + .rewrite_result( + context, + shape.offset_left(vis.len() + vis_separator.len(), self.span())?, + ) .map(|s| { if s.is_empty() { s } else { - format!("{}use {};", vis, s) + format!("{vis}{vis_separator}use {s};") } })?; match self.attrs { diff --git a/src/items.rs b/src/items.rs index a2c0e8e0f50..ab412610ff8 100644 --- a/src/items.rs +++ b/src/items.rs @@ -353,7 +353,11 @@ impl<'a> FnSig<'a> { fn to_str(&self, context: &RewriteContext<'_>) -> String { let mut result = String::with_capacity(128); // Vis defaultness constness unsafety abi. - result.push_str(&*format_visibility(context, self.visibility)); + let vis = format_visibility(context, self.visibility); + result.push_str(&*vis); + if !vis.is_empty() { + result.push(' '); + } result.push_str(format_defaultness(self.defaultness)); result.push_str(format_constness(self.constness)); self.coroutine_kind @@ -956,7 +960,11 @@ fn format_impl_ref_and_type( } = iimpl; let mut result = String::with_capacity(128); - result.push_str(&format_visibility(context, &item.vis)); + let vis = format_visibility(context, &item.vis); + result.push_str(&vis); + if !vis.is_empty() { + result.push(' '); + } if let Some(of_trait) = of_trait.as_deref() { result.push_str(format_defaultness(of_trait.defaultness)); @@ -1165,9 +1173,10 @@ pub(crate) fn format_trait( } = *trait_; let mut result = String::with_capacity(128); + let vis = format_visibility(context, &item.vis); + let vis_separator = if vis.is_empty() { "" } else { " " }; let header = format!( - "{}{}{}{}trait ", - format_visibility(context, &item.vis), + "{vis}{vis_separator}{}{}{}trait ", format_constness(constness), format_safety(safety), format_auto(is_auto), @@ -1376,8 +1385,9 @@ pub(crate) fn format_trait_alias( let g_shape = shape.offset_left(6, span)?.sub_width(2, span)?; let generics_str = rewrite_generics(context, alias, &ta.generics, g_shape)?; let vis_str = format_visibility(context, vis); + let vis_separator = if vis_str.is_empty() { "" } else { " " }; let constness = format_constness(ta.constness); - let lhs = format!("{vis_str}{constness}trait {generics_str} ="); + let lhs = format!("{vis_str}{vis_separator}{constness}trait {generics_str} ="); // 1 = ";" let trait_alias_bounds = TraitAliasBounds { generic_bounds: &ta.bounds, @@ -1749,7 +1759,12 @@ fn rewrite_ty( ) -> RewriteResult { let mut result = String::with_capacity(128); let TyAliasRewriteInfo(context, indent, generics, after_where_clause, ident, span) = *rw_info; - result.push_str(&format!("{}type ", format_visibility(context, vis))); + let vis = format_visibility(context, vis); + if !vis.is_empty() { + result.push_str(&format!("{vis} type ")); + } else { + result.push_str("type "); + } let ident_str = rewrite_ident(context, ident); if generics.params.is_empty() { @@ -1887,15 +1902,18 @@ pub(crate) fn rewrite_struct_field_prefix( field: &ast::FieldDef, ) -> RewriteResult { let vis = format_visibility(context, &field.vis); + let vis_separator = if vis.is_empty() { "" } else { " " }; let safety = format_safety(field.safety); let type_annotation_spacing = type_annotation_spacing(context.config); Ok(match field.ident { Some(name) => format!( - "{vis}{safety}{}{}:", + "{vis}{vis_separator}{safety}{}{}:", rewrite_ident(context, name), type_annotation_spacing.0 ), - None => format!("{vis}{safety}"), + None => format!("{vis}{vis_separator}{safety}") + .trim_end() + .to_string(), }) } @@ -1936,6 +1954,8 @@ pub(crate) fn rewrite_struct_field( }; let mut spacing = String::from(if field.ident.is_some() { type_annotation_spacing.1 + } else if !prefix.is_empty() { + " " } else { "" }); @@ -1970,6 +1990,13 @@ pub(crate) fn rewrite_struct_field( let is_prefix_empty = prefix.is_empty(); // We must use multiline. We are going to put attributes and a field on different lines. + // Trim trailing whitespace from the prefix for tuple struct fields to avoid leaving it + // behind when the type wraps to the next line (e.g. "pub(crate) " -> "pub(crate)"). + let prefix = if field.ident.is_none() { + prefix.trim_end().to_string() + } else { + prefix + }; let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, &RhsAssignKind::Ty, shape)?; // Remove a leading white-space from `rewrite_assign_rhs()` when rewriting a tuple struct. let field_str = if is_prefix_empty { @@ -2116,9 +2143,10 @@ fn rewrite_static( } let colon = colon_spaces(context.config); + let vis = format_visibility(context, static_parts.vis); + let vis_separator = if vis.is_empty() { "" } else { " " }; let mut prefix = format!( - "{}{}{}{} {}{}{}", - format_visibility(context, static_parts.vis), + "{vis}{vis_separator}{}{}{} {}{}{}", static_parts.defaultness.map_or("", format_defaultness), format_safety(static_parts.safety), static_parts.prefix, @@ -3307,7 +3335,7 @@ fn format_header( let mut result = String::with_capacity(128); let shape = Shape::indented(offset, context.config); - result.push_str(format_visibility(context, vis).trim()); + result.push_str(&format_visibility(context, vis)); // Check for a missing comment between the visibility and the item name. let after_vis = vis.span.hi(); @@ -3493,11 +3521,11 @@ impl Rewrite for ast::ForeignItem { // FIXME(#21): we're dropping potential comments in between the // function kw here. let vis = format_visibility(context, &self.vis); + let vis_separator = if vis.is_empty() { "" } else { " " }; let safety = format_safety(static_foreign_item.safety); let mut_str = format_mutability(static_foreign_item.mutability); let prefix = format!( - "{}{}static {}{}:", - vis, + "{vis}{vis_separator}{}static {}{}:", safety, mut_str, rewrite_ident(context, static_foreign_item.ident) @@ -3580,7 +3608,11 @@ pub(crate) fn rewrite_mod( attrs_shape: Shape, ) -> RewriteResult { let mut result = String::with_capacity(32); - result.push_str(&*format_visibility(context, &item.vis)); + let vis = format_visibility(context, &item.vis); + result.push_str(&*vis); + if !vis.is_empty() { + result.push(' '); + } result.push_str("mod "); result.push_str(rewrite_ident(context, ident)); result.push(';'); diff --git a/src/macros.rs b/src/macros.rs index 2d56021069c..5ab347fb8b1 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1454,10 +1454,10 @@ fn format_lazy_static( for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() { // Rewrite as a static item. let vis = crate::utils::format_visibility(context, vis); + let vis_separator = if vis.is_empty() { "" } else { " " }; let mut stmt = String::with_capacity(128); stmt.push_str(&format!( - "{}static ref {}: {} =", - vis, + "{vis}{vis_separator}static ref {}: {} =", id, ty.rewrite_result(context, nested_shape)? )); diff --git a/src/utils.rs b/src/utils.rs index b676803379f..7e7101dc059 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -55,7 +55,7 @@ pub(crate) fn format_visibility( vis: &Visibility, ) -> Cow<'static, str> { match vis.kind { - VisibilityKind::Public => Cow::from("pub "), + VisibilityKind::Public => Cow::from("pub"), VisibilityKind::Inherited => Cow::from(""), VisibilityKind::Restricted { ref path, .. } => { let Path { ref segments, .. } = **path; @@ -69,7 +69,7 @@ pub(crate) fn format_visibility( let path = segments_iter.collect::>().join("::"); let in_str = if is_keyword(&path) { "" } else { "in " }; - Cow::from(format!("pub({in_str}{path}) ")) + Cow::from(format!("pub({in_str}{path})")) } } } diff --git a/src/visitor.rs b/src/visitor.rs index 4072a1d8697..84cdbf2e161 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -983,6 +983,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ) { let vis_str = utils::format_visibility(&self.get_context(), vis); self.push_str(&*vis_str); + if !vis_str.is_empty() { + self.push_str(" "); + } self.push_str(format_safety(safety)); self.push_str("mod "); // Calling `to_owned()` to work around borrow checker. diff --git a/tests/source/issue-5525.rs b/tests/source/issue-5525.rs new file mode 100644 index 00000000000..ab56ed43f91 --- /dev/null +++ b/tests/source/issue-5525.rs @@ -0,0 +1,7 @@ +pub struct SomeCallback( + pub extern "C" fn( + long_argument_name_to_avoid_wrap: u32, + second_long_argument_name: u32, + third_long_argument_name: u32, + ), +); diff --git a/tests/source/issue-5703.rs b/tests/source/issue-5703.rs new file mode 100644 index 00000000000..df7dafe846f --- /dev/null +++ b/tests/source/issue-5703.rs @@ -0,0 +1,21 @@ +struct PubCrate( + pub(crate) std::collections::HashMap, +); + +struct PubSuper( + pub(super) std::collections::HashMap, +); + +struct PubInPath( + pub(in some::module) std::collections::HashMap, +); + +struct PubPlain( + pub std::collections::HashMap, +); + +struct NamedPubCrate { + pub(crate) field: std::collections::HashMap, +} + +struct ShortPubCrate(pub(crate) u32); diff --git a/tests/source/issue-5997.rs b/tests/source/issue-5997.rs new file mode 100644 index 00000000000..d2b547bf196 --- /dev/null +++ b/tests/source/issue-5997.rs @@ -0,0 +1,11 @@ +pub struct Newtype( + /// Doc + #[doc()] // + pub Vec, +); + +pub struct Newtype2( + /// Doc + #[doc()] + pub Vec, +); diff --git a/tests/target/issue-5525.rs b/tests/target/issue-5525.rs new file mode 100644 index 00000000000..ab56ed43f91 --- /dev/null +++ b/tests/target/issue-5525.rs @@ -0,0 +1,7 @@ +pub struct SomeCallback( + pub extern "C" fn( + long_argument_name_to_avoid_wrap: u32, + second_long_argument_name: u32, + third_long_argument_name: u32, + ), +); diff --git a/tests/target/issue-5703.rs b/tests/target/issue-5703.rs new file mode 100644 index 00000000000..83b43985541 --- /dev/null +++ b/tests/target/issue-5703.rs @@ -0,0 +1,28 @@ +struct PubCrate( + pub(crate) + std::collections::HashMap, +); + +struct PubSuper( + pub(super) + std::collections::HashMap, +); + +struct PubInPath( + pub(in some::module) + std::collections::HashMap, +); + +struct PubPlain( + pub std::collections::HashMap< + some::module::path::TypeNameAaaaaaaaaaa, + some::module::path::TypeNameB, + >, +); + +struct NamedPubCrate { + pub(crate) field: + std::collections::HashMap, +} + +struct ShortPubCrate(pub(crate) u32); diff --git a/tests/target/issue-5997.rs b/tests/target/issue-5997.rs new file mode 100644 index 00000000000..0ba9acf28d7 --- /dev/null +++ b/tests/target/issue-5997.rs @@ -0,0 +1,11 @@ +pub struct Newtype( + /// Doc + #[doc()] // + pub Vec, +); + +pub struct Newtype2( + /// Doc + #[doc()] + pub Vec, +); diff --git a/tests/target/struct_field_doc_comment.rs b/tests/target/struct_field_doc_comment.rs index ebb01a668f4..0bc074cf08e 100644 --- a/tests/target/struct_field_doc_comment.rs +++ b/tests/target/struct_field_doc_comment.rs @@ -25,17 +25,17 @@ struct MyTuple( struct MyTuple( #[cfg(unix)] // some comment - pub u64, - #[cfg(not(unix))] /*block comment */ pub(crate) u32, + pub u64, + #[cfg(not(unix))] /*block comment */ pub(crate) u32, ); struct MyTuple( /// Doc Comments /* TODO note to add more to Doc Comments */ - pub u32, + pub u32, /// Doc Comments // TODO note - pub(crate) u64, + pub(crate) u64, ); struct MyStruct {