Repository: rust-lang/rustfmt (rust)

Issue:
Syntax error after formatting a derive attribute that contains a block comment
Formatting this code:

```rust
#[derive(/*Debug, */Clone)]
struct Foo;
```

generates this syntax error:

```rust
#[derive(#[derive(/*Debug, */ Clone)]
struct Foo;
```

I tested `rustfmt 1.4.37` stable and nightly.

Pull request:
Handle block comment before Traits in derive attribute (issue 4984)
Resolves #4984

When a block comment comes before the traits of a derive attribute
the string "#[derive(" is included as part of the the pre snippet, and as
a result gets duplicated.

For example:

    #[derive(/*Debug, */Clone)] -> #[derive(#[derive(/*Debug, */ Clone)]

Now the string "#[derive(" is trimmed from the start of the pre snippet
before looking for the pre comment.

Patch:
diff --git a/src/attr.rs b/src/attr.rs
index 315eb10a..a5982820 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -13,6 +13,7 @@ use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting,
 use crate::overflow;
 use crate::rewrite::{Rewrite, RewriteContext};
 use crate::shape::Shape;
+use crate::source_map::SpanUtils;
 use crate::types::{rewrite_path, PathContext};
 use crate::utils::{count_newlines, mk_sp};
 
@@ -116,7 +117,9 @@ fn format_derive(
                 |span| span.lo(),
                 |span| span.hi(),
                 |span| Some(context.snippet(*span).to_owned()),
-                attr.span.lo(),
+                // We update derive attribute spans to start after the opening '('
+                // This helps us focus parsing to just what's inside #[derive(...)]
+                context.snippet_provider.span_after(attr.span, "("),
                 attr.span.hi(),
                 false,
             );


Test patch:
diff --git a/tests/source/issue-4984/minimum_example.rs b/tests/source/issue-4984/minimum_example.rs
new file mode 100644
index 00000000..677f8737
--- /dev/null
+++ b/tests/source/issue-4984/minimum_example.rs
@@ -0,0 +1,2 @@
+#[derive(/*Debug, */Clone)]
+struct Foo;
diff --git a/tests/source/issue-4984/multi_line_derive.rs b/tests/source/issue-4984/multi_line_derive.rs
new file mode 100644
index 00000000..73921dd1
--- /dev/null
+++ b/tests/source/issue-4984/multi_line_derive.rs
@@ -0,0 +1,20 @@
+#[derive(
+/* ---------- Some really important comment that just had to go inside the derive --------- */
+Debug, Clone, Eq, PartialEq,
+)]
+struct Foo {
+    a: i32,
+    b: T,
+}
+
+#[derive(
+/*
+    Some really important comment that just had to go inside the derive.
+    Also had to be put over multiple lines
+*/
+Debug, Clone, Eq, PartialEq,
+)]
+struct Bar {
+    a: i32,
+    b: T,
+}
diff --git a/tests/source/issue-4984/multiple_comments_within.rs b/tests/source/issue-4984/multiple_comments_within.rs
new file mode 100644
index 00000000..eb474a72
--- /dev/null
+++ b/tests/source/issue-4984/multiple_comments_within.rs
@@ -0,0 +1,8 @@
+#[derive(
+/* ---------- Some really important comment that just had to go inside the derive --------- */
+Debug, Clone,/* Another comment */Eq, PartialEq,
+)]
+struct Foo {
+    a: i32,
+    b: T,
+}
diff --git a/tests/target/issue-4984/minimum_example.rs b/tests/target/issue-4984/minimum_example.rs
new file mode 100644
index 00000000..f0599c5d
--- /dev/null
+++ b/tests/target/issue-4984/minimum_example.rs
@@ -0,0 +1,2 @@
+#[derive(/*Debug, */ Clone)]
+struct Foo;
diff --git a/tests/target/issue-4984/multi_line_derive.rs b/tests/target/issue-4984/multi_line_derive.rs
new file mode 100644
index 00000000..5fbd9784
--- /dev/null
+++ b/tests/target/issue-4984/multi_line_derive.rs
@@ -0,0 +1,26 @@
+#[derive(
+    /* ---------- Some really important comment that just had to go inside the derive --------- */
+    Debug,
+    Clone,
+    Eq,
+    PartialEq,
+)]
+struct Foo {
+    a: i32,
+    b: T,
+}
+
+#[derive(
+    /*
+        Some really important comment that just had to go inside the derive.
+        Also had to be put over multiple lines
+    */
+    Debug,
+    Clone,
+    Eq,
+    PartialEq,
+)]
+struct Bar {
+    a: i32,
+    b: T,
+}
diff --git a/tests/target/issue-4984/multiple_comments_within.rs b/tests/target/issue-4984/multiple_comments_within.rs
new file mode 100644
index 00000000..d2924f0d
--- /dev/null
+++ b/tests/target/issue-4984/multiple_comments_within.rs
@@ -0,0 +1,11 @@
+#[derive(
+    /* ---------- Some really important comment that just had to go inside the derive --------- */
+    Debug,
+    Clone,
+    /* Another comment */ Eq,
+    PartialEq,
+)]
+struct Foo {
+    a: i32,
+    b: T,
+}
diff --git a/tests/target/issue-4984/should_not_change.rs b/tests/target/issue-4984/should_not_change.rs
new file mode 100644
index 00000000..e46ee511
--- /dev/null
+++ b/tests/target/issue-4984/should_not_change.rs
@@ -0,0 +1,5 @@
+#[derive(Clone, Debug, Eq, PartialEq)]
+struct Foo;
+
+#[derive(Clone)]
+struct Bar;