Repository: rust-lang/rustfmt (rust)

Issue:
Rustfmt removes argument attribute when line is long!
**Describe the bug**

Rustfmt removes argument attribute when line is long!

<!-- A clear and concise description of what the bug is. -->

**To Reproduce**

<!-- Steps to reproduce the behavior. If possible, please provide us with a link to your project or a minimal working example. -->
1. Put this in a file `temp.rs`
```rust
#[macro_export]
macro_rules! main {
	() => {
		#[spirv(fragment)]
		pub fn main_fs(
			mut out_color: ::spirv_std::storage_class::Output<Vec4>,
			#[spirv(frag_coord)] frag_coord: ::spirv_std::storage_class::Input<
				::spirv_std::glam::Vec4,
			>,
			#[spirv(descriptor_set = 1)] iChannel0: ::spirv_std::storage_class::UniformConstant<
				::spirv_std::SampledImage<::spirv_std::Image2d>,
			>,
			#[spirv(descriptor_set = 1)] iChannel1: ::spirv_std::storage_class::UniformConstant<
				::spirv_std::SampledImage<::spirv_std::Image2d>,
			>,
			#[spirv(descriptor_set = 1)] iChannel2: ::spirv_std::storage_class::UniformConstant<
				::spirv_std::SampledImage<::spirv_std::Image2d>,
			>,
			#[spirv(descriptor_set = 1)] iChannel3: ::spirv_std::storage_class::UniformConstant<
				::spirv_std::SampledImage<::spirv_std::Image2d>,
			>,
			#[spirv(descriptor_set = 1)] iResolution: ::spirv_std::storage_class::UniformConstant<
				::spirv_std::glam::Vec3A,
			>,
			#[spirv(descriptor_set = 1)] iTime: ::spirv_std::storage_class::UniformConstant<f32>,
			#[spirv(descriptor_set = 1)] iMouse: ::spirv_std::storage_class::UniformConstant<
				::spirv_std::glam::Vec4,
			>,
			#[spirv(descriptor_set = 1)] iDate: ::spirv_std::storage_class::UniformConstant<
				::spirv_std::glam::Vec4,
			>,
			#[spirv(descriptor_set = 1)] iSampleRate: ::spirv_std::storage_class::UniformConstant<
				f32,
			>,
			#[spirv(descriptor_set = 1)] iFrame: ::spirv_std::storage_class::UniformConstant<i32>,
			#[spirv(descriptor_set = 1)] iTimeDelta: ::spirv_std::storage_class::UniformConstant<
				f32,
			>,
			#[spirv(descriptor_set = 1)] iFrameRate: ::spirv_std::storage_class::UniformConstant<
				f32,
			>,
			#[spirv(descriptor_set = 1)] iChannelTime: ::spirv_std::storage_class::UniformConstant<
				[f32; 4],
			>,
			#[spirv(descriptor_set = 1)] iChannelResolution: ::spirv_std::storage_class::UniformConstant<
				[::spirv_std::glam::Vec3A; 4],
			>,
			#[spirv(descriptor_set = 1)] unViewport: ::spirv_std::storage_class::UniformConstant<
				::spirv_std::glam::Vec4,
			>,
			#[spirv(descriptor_set = 1)] unCorners: ::spirv_std::storage_class::UniformConstant<
				[::spirv_std::glam::Vec3A; 5],
			>,
		) {
		}
	};
}
```
2. Run `rustfmt temp.rs`
3. Notice it removed the `#[spirv(descriptor_set = 1)]` attribute from the `iChannelResolution` arg!
It turns it into this:
```rust
// ...
			#[spirv(descriptor_set = 1)] iChannelTime: ::spirv_std::storage_class::UniformConstant<
				[f32; 4],
			>,
			iChannelResolution: ::spirv_std::storage_class::UniformConstant<
						[::spirv_std::glam::Vec3A; 4],
					>,
			#[spirv(descriptor_set = 1)] unViewport: ::spirv_std::storage_class::UniformConstant<
				::spirv_std::glam::Vec4,
			>,
			#[spirv(descriptor_set = 1)] unCorners: ::spirv_std::storage_class::UniformConstant<
				[::spirv_std::glam::Vec3A; 5],
			>,
		) {
		}
	};
}
```

**Expected behavior**
Don't remove **any** attributes.

<!-- A clear and concise description of what you expected to happen. -->

**Meta**

- rustfmt version: `rustfmt 1.4.27-nightly (580d826 2020-11-16)`
- From where did you install rustfmt?: rustup
- How do you run rustfmt: `rustfmt`

Settings:
```toml
edition = "2018"
version = "Two"
newline_style = "Unix"
use_small_heuristics = "Max"
hard_tabs = true
merge_imports = true
reorder_impl_items = true
use_field_init_shorthand = true
use_try_shorthand = true
spaces_around_ranges = true
overflow_delimited_expr = true
```

Pull request description:
backport fixes for long params with attrs
Closes #4579 

Patch:
diff --git a/src/comment.rs b/src/comment.rs
index 62b624ac..42449d10 100644
--- a/src/comment.rs
+++ b/src/comment.rs
@@ -10,7 +10,8 @@ use crate::rewrite::RewriteContext;
 use crate::shape::{Indent, Shape};
 use crate::string::{rewrite_string, StringFormat};
 use crate::utils::{
-    count_newlines, first_line_width, last_line_width, trim_left_preserve_layout, unicode_str_width,
+    count_newlines, first_line_width, last_line_width, trim_left_preserve_layout,
+    trimmed_last_line_width, unicode_str_width,
 };
 use crate::{ErrorKind, FormattingError};
 
@@ -171,11 +172,12 @@ pub(crate) fn combine_strs_with_missing_comments(
         String::with_capacity(prev_str.len() + next_str.len() + shape.indent.width() + 128);
     result.push_str(prev_str);
     let mut allow_one_line = !prev_str.contains('\n') && !next_str.contains('\n');
-    let first_sep = if prev_str.is_empty() || next_str.is_empty() {
-        ""
-    } else {
-        " "
-    };
+    let first_sep =
+        if prev_str.is_empty() || next_str.is_empty() || trimmed_last_line_width(prev_str) == 0 {
+            ""
+        } else {
+            " "
+        };
     let mut one_line_width =
         last_line_width(prev_str) + first_line_width(next_str) + first_sep.len();
 
diff --git a/src/items.rs b/src/items.rs
index 4fa7190c..6ba83b57 100644
--- a/src/items.rs
+++ b/src/items.rs
@@ -1980,12 +1980,13 @@ impl Rewrite for ast::Param {
                 has_multiple_attr_lines,
             )
         } else if is_named_param(self) {
+            let param_name = &self
+                .pat
+                .rewrite(context, Shape::legacy(shape.width, shape.indent))?;
             let mut result = combine_strs_with_missing_comments(
                 context,
                 &param_attrs_result,
-                &self
-                    .pat
-                    .rewrite(context, Shape::legacy(shape.width, shape.indent))?,
+                param_name,
                 span,
                 shape,
                 !has_multiple_attr_lines,
@@ -1999,10 +2000,30 @@ impl Rewrite for ast::Param {
                 result.push_str(&after_comment);
                 let overhead = last_line_width(&result);
                 let max_width = shape.width.checked_sub(overhead)?;
-                let ty_str = self
+                if let Some(ty_str) = self
                     .ty
-                    .rewrite(context, Shape::legacy(max_width, shape.indent))?;
-                result.push_str(&ty_str);
+                    .rewrite(context, Shape::legacy(max_width, shape.indent))
+                {
+                    result.push_str(&ty_str);
+                } else {
+                    result = combine_strs_with_missing_comments(
+                        context,
+                        &(param_attrs_result + &shape.to_string_with_newline(context.config)),
+                        param_name,
+                        span,
+                        shape,
+                        !has_multiple_attr_lines,
+                    )?;
+                    result.push_str(&before_comment);
+                    result.push_str(colon_spaces(context.config));
+                    result.push_str(&after_comment);
+                    let overhead = last_line_width(&result);
+                    let max_width = shape.width.checked_sub(overhead)?;
+                    let ty_str = self
+                        .ty
+                        .rewrite(context, Shape::legacy(max_width, shape.indent))?;
+                    result.push_str(&ty_str);
+                }
             }
 
             Some(result)
diff --git a/src/spanned.rs b/src/spanned.rs
index 7e3786b7..8e6c75a3 100644
--- a/src/spanned.rs
+++ b/src/spanned.rs
@@ -104,7 +104,7 @@ impl Spanned for ast::Arm {
 impl Spanned for ast::Param {
     fn span(&self) -> Span {
         if crate::items::is_named_param(self) {
-            mk_sp(self.pat.span.lo(), self.ty.span.hi())
+            mk_sp(crate::items::span_lo_for_param(self), self.ty.span.hi())
         } else {
             self.ty.span
         }


Test patch:
diff --git a/tests/source/issue_4032.rs b/tests/source/issue_4032.rs
new file mode 100644
index 00000000..11ded074
--- /dev/null
+++ b/tests/source/issue_4032.rs
@@ -0,0 +1,4 @@
+fn a1(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] a: u8) {}
+fn b1(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] bb: u8) {}
+fn a2(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] a: u8) {}
+fn b2(#[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] bb: u8) {}
diff --git a/tests/source/issue_4579.rs b/tests/source/issue_4579.rs
new file mode 100644
index 00000000..73f34523
--- /dev/null
+++ b/tests/source/issue_4579.rs
@@ -0,0 +1,15 @@
+// rustfmt-hard_tabs: true
+
+#[macro_export]
+macro_rules! main {
+	() => {
+		#[spirv(fragment)]
+		pub fn main_fs(
+			mut out_color: ::spirv_std::storage_class::Output<Vec4>,
+			#[spirv(descriptor_set = 1)]iChannelResolution: ::spirv_std::storage_class::UniformConstant<
+				[::spirv_std::glam::Vec3A; 4],
+			>,
+		) {
+		}
+	};
+}
diff --git a/tests/target/issue_4032.rs b/tests/target/issue_4032.rs
new file mode 100644
index 00000000..2e7e624c
--- /dev/null
+++ b/tests/target/issue_4032.rs
@@ -0,0 +1,18 @@
+fn a1(
+    #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]
+    a: u8,
+) {
+}
+fn b1(
+    #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]
+    bb: u8,
+) {
+}
+fn a2(
+    #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] a: u8,
+) {
+}
+fn b2(
+    #[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] bb: u8,
+) {
+}
diff --git a/tests/target/issue_4579.rs b/tests/target/issue_4579.rs
new file mode 100644
index 00000000..7b0a5f3a
--- /dev/null
+++ b/tests/target/issue_4579.rs
@@ -0,0 +1,16 @@
+// rustfmt-hard_tabs: true
+
+#[macro_export]
+macro_rules! main {
+	() => {
+		#[spirv(fragment)]
+		pub fn main_fs(
+			mut out_color: ::spirv_std::storage_class::Output<Vec4>,
+			#[spirv(descriptor_set = 1)]
+			iChannelResolution: ::spirv_std::storage_class::UniformConstant<
+				[::spirv_std::glam::Vec3A; 4],
+			>,
+		) {
+		}
+	};
+}