diff --git a/src/expr.rs b/src/expr.rs index 8266f95fd70..4c7c0e6ba7f 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -29,11 +29,7 @@ use crate::spanned::Spanned; use crate::stmt; use crate::string::{rewrite_string, StringFormat}; use crate::types::{rewrite_path, PathContext}; -use crate::utils::{ - colon_spaces, contains_skip, count_newlines, filtered_str_fits, first_line_ends_with, - inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes, - semicolon_for_expr, unicode_str_width, wrap_str, -}; +use crate::utils::*; use crate::vertical::rewrite_with_alignment; use crate::visitor::FmtVisitor; @@ -425,7 +421,20 @@ pub(crate) fn format_expr( attrs.last().map_or(expr.span.lo(), |attr| attr.span.hi()), expr.span.lo(), ); - combine_strs_with_missing_comments(context, &attrs_str, &expr_str, span, shape, false) + + // +1 = ";" + let allow_extend = + extend_inline_attr(&expr.attrs, shape, &attrs_str, expr_str.len() + 1, context) + && expr_type == ExprType::Statement; + + combine_strs_with_missing_comments( + context, + &attrs_str, + &expr_str, + span, + shape, + allow_extend, + ) }) } diff --git a/src/imports.rs b/src/imports.rs index 12c178e136f..ac444f16b6b 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -23,7 +23,7 @@ use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::spanned::Spanned; -use crate::utils::{is_same_visibility, mk_sp, rewrite_ident}; +use crate::utils::{extend_inline_attr, is_same_visibility, mk_sp, rewrite_ident}; use crate::visitor::FmtVisitor; /// Returns a name imported by a `use` declaration. @@ -351,13 +351,8 @@ impl UseTree { let hi = self.span.lo(); let span = mk_sp(lo, hi); - let allow_extend = if attrs.len() == 1 { - let line_len = attr_str.len() + 1 + use_str.len(); - !attrs.first().unwrap().is_doc_comment() - && context.config.inline_attribute_width() >= line_len - } else { - false - }; + let allow_extend = + extend_inline_attr(attrs, shape, &attr_str, use_str.len(), context); combine_strs_with_missing_comments( context, diff --git a/src/items.rs b/src/items.rs index 48f5c36ac07..954469e8628 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1884,7 +1884,19 @@ pub(crate) fn rewrite_struct_field( let prefix = rewrite_struct_field_prefix(context, field)?; let attrs_str = field.attrs.rewrite_result(context, shape)?; - let attrs_extendable = field.ident.is_none() && is_attributes_extendable(&attrs_str); + let ty_str = field.ty.rewrite_result(context, shape)?; + + let allow_extend = extend_inline_attr( + &field.attrs, + shape, + &attrs_str, + // +1 = " ", +1 = "," + prefix.len() + 1 + ty_str.len() + 1, + context, + ); + + let attrs_extendable = + (field.ident.is_none() && is_attributes_extendable(&attrs_str)) || allow_extend; let missing_span = if field.attrs.is_empty() { mk_sp(field.span.lo(), field.span.lo()) } else { @@ -3434,13 +3446,17 @@ impl Rewrite for ast::ForeignItem { } else { mk_sp(self.attrs[self.attrs.len() - 1].span.hi(), self.span.lo()) }; + + let allow_extend = + extend_inline_attr(&self.attrs, shape, &attrs_str, item_str.len(), context); + combine_strs_with_missing_comments( context, &attrs_str, &item_str, missing_span, shape, - false, + allow_extend, ) } } @@ -3461,13 +3477,7 @@ fn rewrite_attrs( mk_sp(attrs[attrs.len() - 1].span.hi(), item.span.lo()) }; - let allow_extend = if attrs.len() == 1 { - let line_len = attrs_str.len() + 1 + item_str.len(); - !attrs.first().unwrap().is_doc_comment() - && context.config.inline_attribute_width() >= line_len - } else { - false - }; + let allow_extend = extend_inline_attr(&item.attrs, shape, &attrs_str, item_str.len(), context); combine_strs_with_missing_comments( context, diff --git a/src/utils.rs b/src/utils.rs index eaf24f3f204..080834173a9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -701,6 +701,25 @@ pub(crate) fn unicode_str_width(s: &str) -> usize { s.width() } +/// Determine if we could place a single attribute on the same line as the rewritten AST node. +pub(crate) fn extend_inline_attr( + attrs: &[ast::Attribute], + shape: Shape, + attrs_str: &str, + rewrite_len: usize, + context: &RewriteContext<'_>, +) -> bool { + if attrs.len() > 1 { + return false; + } + + attrs.first().is_some_and(|attr| { + // +1 = " " + let line_len = shape.indent.width() + attrs_str.len() + 1 + rewrite_len; + !attr.is_doc_comment() && context.config.inline_attribute_width() >= line_len + }) +} + #[cfg(test)] mod test { use super::*; diff --git a/tests/source/configs/inline_attribute_width/50.rs b/tests/source/configs/inline_attribute_width/50.rs new file mode 100644 index 00000000000..d07d0f56d79 --- /dev/null +++ b/tests/source/configs/inline_attribute_width/50.rs @@ -0,0 +1,332 @@ +// rustfmt-inline_attribute_width: 50 +// rustfmt-edition: 2021 + +#[cfg(feature = "alloc")] +use core::slice; + +#[cfg(feature = "alloc")] +use total_len_is::_50__; + +#[cfg(feature = "alloc")] +use total_len_is::_51___; + +#[cfg(feature = "alloc")] +// c +use d; + +#[cfg(feature = "alloc")] +// comment +use total_len_is::_50__; + +#[cfg(feature = "alloc")] +// comment +use total_len_is::_51___; + +fn foo() { + #[cfg(feature = "alloc")] + use total_len::_50_; + + #[cfg(feature = "alloc")] + use total_len::_51__; +} + +#[cfg(feature = "alloc")] +extern crate len_is_50_; + +#[cfg(feature = "alloc")] +extern crate len_is_51__; + +// https://github.com/rust-lang/rustfmt/issues/3343#issuecomment-589945611 +extern "C" { + #[no_mangle] + fn foo(); +} + +extern "C" { + #[no_mangle] + fn total_len_is_49___________(); +} + +extern "C" { + #[no_mangle] + fn total_len_is_50____________(); +} + +extern "C" { + #[no_mangle] + fn total_len_is_51_____________(); +} + +fn main() { + #[cfg(feature = "alloc")] + ["total_len_is_50"]; + #[cfg(feature = "alloc")] + ["total_len_is_51_"]; + #[cfg(feature = "alloc")] + total_len_is_50__(); + #[cfg(feature = "alloc")] + total_len_is_51___(); + #[cfg(feature = "alloc")] + total_len_is_52____(); + #[cfg(feature = "alloc")] + { + foo(); + } + { + #[cfg(feature = "alloc")] + foo(); + } +} + +// https://github.com/rust-lang/rustfmt/pull/5538#issuecomment-1272367684 +struct EventConfigWidget { + #[widget] + menu_delay: Spinner, +} + +struct foo { + #[x] + #[y] + z: bool, +} + +struct foo { + #[widget] + len_is_50____________________: bool, +} + +struct foo { + #[widget] + len_is_51_____________________: bool, +} + +/// this is a comment to test is_sugared_doc property +use core::convert; + +#[fooooo] +#[barrrrr] +use total_len_is_::_51______; + +#[cfg(not(all( + feature = "std", + any( + target_os = "linux", + target_os = "android", + target_os = "netbsd", + target_os = "dragonfly", + target_os = "haiku", + target_os = "emscripten", + target_os = "solaris", + target_os = "cloudabi", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "redox", + target_os = "fuchsia", + windows, + all(target_arch = "wasm32", feature = "stdweb"), + all(target_arch = "wasm32", feature = "wasm-bindgen"), + ) +)))] +use core::slice; + +fn foo() { + // Literal expression + #[cfg(feature = "len_is_50_____________")] + 42; + #[cfg(feature = "len_is_51______________")] + 42; + + // Path expression + #[cfg(feature = "len_is_50__")] + some_variable; + #[cfg(feature = "len_is_51___")] + some_variable; + + // Block expression + #[cfg(feature = "len_is_50_______________")] + { + let x = 2; + }; + #[cfg(feature = "len_is_51________________")] + { + let x = 2; + }; + + // Operator expression + #[cfg(feature = "len_is_50__________")] + 1 + 2; + #[cfg(feature = "len_is_51___________")] + 1 + 2; + + // Tuple expression + #[cfg(feature = "len_is_50")] + (1, "two", 3.0); + #[cfg(feature = "len_is_51_")] + (1, "two", 3.0); + + // Array expression + #[cfg(feature = "len_is_50______")] + [1, 2, 3]; + #[cfg(feature = "len_is_51_______")] + [1, 2, 3]; + + // Struct expression + #[cfg(feature = "len_is_50_____")] + S { f: 1 }; + #[cfg(feature = "len_is_51______")] + S { f: 1 }; + + #[cfg(feature = "len_is_50______")] + MyStruct { + field1: 1, + field2: 1, + }; + #[cfg(feature = "len_is_51_______")] + MyStruct { + field1: 1, + field2: 1, + }; + + // Enum variant expression + #[cfg(feature = "len_is_50")] + MyEnum::Variant; + #[cfg(feature = "len_is_51_")] + MyEnum::Variant; + + // Call expression + #[cfg(feature = "len_is_50")] + some_function(); + #[cfg(feature = "len_is_51_")] + some_function(); + + // Method call expression + #[cfg(feature = "len_is_50")] + object.method(); + #[cfg(feature = "len_is_51_")] + object.method(); + + // Field access expression + #[cfg(feature = "len_is_50")] + my_struct.field; + #[cfg(feature = "len_is_51_")] + my_struct.field; + + // Tuple indexing expression + #[cfg(feature = "len_is_50_____")] + my_tuple.0; + #[cfg(feature = "len_is_51______")] + my_tuple.0; + + // Indexing expression + #[cfg(feature = "len_is_50____")] + my_array[0]; + #[cfg(feature = "len_is_51_____")] + my_array[0]; + + // Range expression + #[cfg(feature = "len_is_50___________")] + 1..5; + #[cfg(feature = "len_is_51____________")] + 1..5; + + // If expression + #[cfg(feature = "len_is_50__")] + if condition { + 1 + }; + #[cfg(feature = "len_is_51___")] + if condition { + 1 + }; + + // Loop expression + #[cfg(feature = "len_is_50__________")] + loop { + break; + } + #[cfg(feature = "len_is_51___________")] + loop { + break; + } + + // While expression + #[cfg(feature = "len_is_50____")] + while cond { + break; + } + #[cfg(feature = "len_is_51_____")] + while cond { + break; + } + + // For expression + #[cfg(feature = "len_is_50")] + for i in 0..10 { + break; + } + #[cfg(feature = "len_is_51_")] + for i in 0..10 { + break; + } + + // Match expression + #[cfg(feature = "len_is_50___")] + match value { + Pattern1 => 1, + _ => 2, + }; + #[cfg(feature = "len_is_51____")] + match value { + Pattern1 => 1, + _ => 2, + }; + + // Return expression + #[cfg(feature = "len_is_50_________")] + return; + #[cfg(feature = "len_is_51__________")] + return; + + // Break expression + #[cfg(feature = "len_is_50__________")] + break; + #[cfg(feature = "len_is_51___________")] + break; + + // Continue expression + #[cfg(feature = "len_is_50_______")] + continue; + #[cfg(feature = "len_is_51________")] + continue; + + // Closure expression + #[cfg(feature = "len_is_50______")] + |x| x + 1; + #[cfg(feature = "len_is_51_______")] + |x| x + 1; + + // Async block expression + #[cfg(feature = "len_is_50_________")] + async { + #[cfg(feature = "len_50__")] + future.await; + #[cfg(feature = "len_51___")] + future.await; + }; + + #[cfg(feature = "len_is_51__________")] + async { + #[cfg(feature = "len_50__")] + future.await; + #[cfg(feature = "len_51___")] + future.await; + }; + + // Try expression + #[cfg(feature = "len_is_50___")] + some_result?; + #[cfg(feature = "len_is_51____")] + some_result?; +} diff --git a/tests/source/issue-3343.rs b/tests/source/issue-3343.rs deleted file mode 100644 index 5670b04f5d6..00000000000 --- a/tests/source/issue-3343.rs +++ /dev/null @@ -1,47 +0,0 @@ -// rustfmt-inline_attribute_width: 50 - -#[cfg(feature = "alloc")] -use core::slice; - -#[cfg(feature = "alloc")] -use total_len_is::_50__; - -#[cfg(feature = "alloc")] -use total_len_is::_51___; - -#[cfg(feature = "alloc")] -extern crate len_is_50_; - -#[cfg(feature = "alloc")] -extern crate len_is_51__; - -/// this is a comment to test is_sugared_doc property -use core::convert; - -#[fooooo] -#[barrrrr] -use total_len_is_::_51______; - -#[cfg(not(all( - feature = "std", - any( - target_os = "linux", - target_os = "android", - target_os = "netbsd", - target_os = "dragonfly", - target_os = "haiku", - target_os = "emscripten", - target_os = "solaris", - target_os = "cloudabi", - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "openbsd", - target_os = "redox", - target_os = "fuchsia", - windows, - all(target_arch = "wasm32", feature = "stdweb"), - all(target_arch = "wasm32", feature = "wasm-bindgen"), - ) -)))] -use core::slice; diff --git a/tests/target/configs/inline_attribute_width/50.rs b/tests/target/configs/inline_attribute_width/50.rs new file mode 100644 index 00000000000..f69f99172ff --- /dev/null +++ b/tests/target/configs/inline_attribute_width/50.rs @@ -0,0 +1,302 @@ +// rustfmt-inline_attribute_width: 50 +// rustfmt-edition: 2021 + +#[cfg(feature = "alloc")] use core::slice; + +#[cfg(feature = "alloc")] use total_len_is::_50__; + +#[cfg(feature = "alloc")] +use total_len_is::_51___; + +#[cfg(feature = "alloc")] +// c +use d; + +#[cfg(feature = "alloc")] +// comment +use total_len_is::_50__; + +#[cfg(feature = "alloc")] +// comment +use total_len_is::_51___; + +fn foo() { + #[cfg(feature = "alloc")] use total_len::_50_; + + #[cfg(feature = "alloc")] + use total_len::_51__; +} + +#[cfg(feature = "alloc")] extern crate len_is_50_; + +#[cfg(feature = "alloc")] +extern crate len_is_51__; + +// https://github.com/rust-lang/rustfmt/issues/3343#issuecomment-589945611 +extern "C" { + #[no_mangle] fn foo(); +} + +extern "C" { + #[no_mangle] fn total_len_is_49___________(); +} + +extern "C" { + #[no_mangle] fn total_len_is_50____________(); +} + +extern "C" { + #[no_mangle] + fn total_len_is_51_____________(); +} + +fn main() { + #[cfg(feature = "alloc")] ["total_len_is_50"]; + #[cfg(feature = "alloc")] + ["total_len_is_51_"]; + #[cfg(feature = "alloc")] total_len_is_50__(); + #[cfg(feature = "alloc")] + total_len_is_51___(); + #[cfg(feature = "alloc")] + total_len_is_52____(); + #[cfg(feature = "alloc")] + { + foo(); + } + { + #[cfg(feature = "alloc")] foo(); + } +} + +// https://github.com/rust-lang/rustfmt/pull/5538#issuecomment-1272367684 +struct EventConfigWidget { + #[widget] menu_delay: Spinner, +} + +struct foo { + #[x] + #[y] + z: bool, +} + +struct foo { + #[widget] len_is_50____________________: bool, +} + +struct foo { + #[widget] + len_is_51_____________________: bool, +} + +/// this is a comment to test is_sugared_doc property +use core::convert; + +#[fooooo] +#[barrrrr] +use total_len_is_::_51______; + +#[cfg(not(all( + feature = "std", + any( + target_os = "linux", + target_os = "android", + target_os = "netbsd", + target_os = "dragonfly", + target_os = "haiku", + target_os = "emscripten", + target_os = "solaris", + target_os = "cloudabi", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "redox", + target_os = "fuchsia", + windows, + all(target_arch = "wasm32", feature = "stdweb"), + all(target_arch = "wasm32", feature = "wasm-bindgen"), + ) +)))] +use core::slice; + +fn foo() { + // Literal expression + #[cfg(feature = "len_is_50_____________")] 42; + #[cfg(feature = "len_is_51______________")] + 42; + + // Path expression + #[cfg(feature = "len_is_50__")] some_variable; + #[cfg(feature = "len_is_51___")] + some_variable; + + // Block expression + #[cfg(feature = "len_is_50_______________")] + { + let x = 2; + }; + #[cfg(feature = "len_is_51________________")] + { + let x = 2; + }; + + // Operator expression + #[cfg(feature = "len_is_50__________")] + 1 + 2; + #[cfg(feature = "len_is_51___________")] + 1 + 2; + + // Tuple expression + #[cfg(feature = "len_is_50")] (1, "two", 3.0); + #[cfg(feature = "len_is_51_")] + (1, "two", 3.0); + + // Array expression + #[cfg(feature = "len_is_50______")] [1, 2, 3]; + #[cfg(feature = "len_is_51_______")] + [1, 2, 3]; + + // Struct expression + #[cfg(feature = "len_is_50_____")] S { f: 1 }; + #[cfg(feature = "len_is_51______")] + S { f: 1 }; + + #[cfg(feature = "len_is_50______")] + MyStruct { + field1: 1, + field2: 1, + }; + #[cfg(feature = "len_is_51_______")] + MyStruct { + field1: 1, + field2: 1, + }; + + // Enum variant expression + #[cfg(feature = "len_is_50")] MyEnum::Variant; + #[cfg(feature = "len_is_51_")] + MyEnum::Variant; + + // Call expression + #[cfg(feature = "len_is_50")] some_function(); + #[cfg(feature = "len_is_51_")] + some_function(); + + // Method call expression + #[cfg(feature = "len_is_50")] object.method(); + #[cfg(feature = "len_is_51_")] + object.method(); + + // Field access expression + #[cfg(feature = "len_is_50")] my_struct.field; + #[cfg(feature = "len_is_51_")] + my_struct.field; + + // Tuple indexing expression + #[cfg(feature = "len_is_50_____")] my_tuple.0; + #[cfg(feature = "len_is_51______")] + my_tuple.0; + + // Indexing expression + #[cfg(feature = "len_is_50____")] my_array[0]; + #[cfg(feature = "len_is_51_____")] + my_array[0]; + + // Range expression + #[cfg(feature = "len_is_50___________")] + 1..5; + #[cfg(feature = "len_is_51____________")] + 1..5; + + // If expression + #[cfg(feature = "len_is_50__")] + if condition { + 1 + }; + #[cfg(feature = "len_is_51___")] + if condition { + 1 + }; + + // Loop expression + #[cfg(feature = "len_is_50__________")] + loop { + break; + } + #[cfg(feature = "len_is_51___________")] + loop { + break; + } + + // While expression + #[cfg(feature = "len_is_50____")] + while cond { + break; + } + #[cfg(feature = "len_is_51_____")] + while cond { + break; + } + + // For expression + #[cfg(feature = "len_is_50")] + for i in 0..10 { + break; + } + #[cfg(feature = "len_is_51_")] + for i in 0..10 { + break; + } + + // Match expression + #[cfg(feature = "len_is_50___")] + match value { + Pattern1 => 1, + _ => 2, + }; + #[cfg(feature = "len_is_51____")] + match value { + Pattern1 => 1, + _ => 2, + }; + + // Return expression + #[cfg(feature = "len_is_50_________")] return; + #[cfg(feature = "len_is_51__________")] + return; + + // Break expression + #[cfg(feature = "len_is_50__________")] break; + #[cfg(feature = "len_is_51___________")] + break; + + // Continue expression + #[cfg(feature = "len_is_50_______")] continue; + #[cfg(feature = "len_is_51________")] + continue; + + // Closure expression + #[cfg(feature = "len_is_50______")] |x| x + 1; + #[cfg(feature = "len_is_51_______")] + |x| x + 1; + + // Async block expression + #[cfg(feature = "len_is_50_________")] + async { + #[cfg(feature = "len_50__")] future.await; + #[cfg(feature = "len_51___")] + future.await; + }; + + #[cfg(feature = "len_is_51__________")] + async { + #[cfg(feature = "len_50__")] future.await; + #[cfg(feature = "len_51___")] + future.await; + }; + + // Try expression + #[cfg(feature = "len_is_50___")] some_result?; + #[cfg(feature = "len_is_51____")] + some_result?; +} diff --git a/tests/target/issue-3343.rs b/tests/target/issue-3343.rs deleted file mode 100644 index d0497758e66..00000000000 --- a/tests/target/issue-3343.rs +++ /dev/null @@ -1,44 +0,0 @@ -// rustfmt-inline_attribute_width: 50 - -#[cfg(feature = "alloc")] use core::slice; - -#[cfg(feature = "alloc")] use total_len_is::_50__; - -#[cfg(feature = "alloc")] -use total_len_is::_51___; - -#[cfg(feature = "alloc")] extern crate len_is_50_; - -#[cfg(feature = "alloc")] -extern crate len_is_51__; - -/// this is a comment to test is_sugared_doc property -use core::convert; - -#[fooooo] -#[barrrrr] -use total_len_is_::_51______; - -#[cfg(not(all( - feature = "std", - any( - target_os = "linux", - target_os = "android", - target_os = "netbsd", - target_os = "dragonfly", - target_os = "haiku", - target_os = "emscripten", - target_os = "solaris", - target_os = "cloudabi", - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "openbsd", - target_os = "redox", - target_os = "fuchsia", - windows, - all(target_arch = "wasm32", feature = "stdweb"), - all(target_arch = "wasm32", feature = "wasm-bindgen"), - ) -)))] -use core::slice;