Change:
Other expressions work as expected: it reports just one error, for the whole sequence of doc comment lines. () Errors:
( is a rustc lint.)
True, but I as well since it's a rustdoc-related lint.

Discussion:
// since they will not be detected after macro expansion. fn check_attributes(&mut self, attrs: &[ast::Attribute]) { let features = self.cx.ecfg.features.unwrap(); <del> for attr in attrs.iter() { </del> <ins> let mut attrs = attrs.iter().peekable(); let mut span: Option<Span> = None; while let Some(attr) = attrs.next() { </ins> rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features); validate_attr::check_meta(&self.cx.sess.parse_sess, attr); <ins> let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; span = Some(current_span); if attrs.peek().map_or(false, |next_attr| next_attr.doc_str().is_some()) { continue; } </ins> if attr.doc_str().is_some() { self.cx.sess.parse_sess.buffer_lint_with_diagnostic( &UNUSED_DOC_COMMENTS, <del> attr.span, </del> <ins> current_span, </ins> ast::CRATE_NODE_ID, "unused doc comment", BuiltinLintDiagnostics::UnusedDocComment(attr.span),

Some(sugared_span.map_or(attr.span, |span| span.with_hi(attr.span.hi()))); } <del> if attrs.peek().map(|next_attr| next_attr.is_doc_comment()).unwrap_or_default() { </del> <ins> if attrs.peek().map_or(false, |next_attr| next_attr.is_doc_comment()) { </ins> continue; }

<ins> #![deny(unused_doc_comments)] #![feature(rustc_attrs)] macro_rules! foo { () => {}; } fn main() { /// line1 //~ ERROR: unused doc comment /// line2 /// line3 foo!(); // Ensure we still detect another doc-comment block. /// line1 //~ ERROR: unused doc comment /// line2 /// line3 foo!(); } </ins>

<ins> error: unused doc comment --> $DIR/unused-doc-comments-for-macros.rs:7:5 | LL | /     /// line1 LL | |     /// line2 LL | |     /// line3 | |_____--------^ |       | |       rustdoc does not generate documentation for macro invocations | note: the lint level is defined here --> $DIR/unused-doc-comments-for-macros.rs:1:9 | LL | #![deny(unused_doc_comments)] |         ^^^^^^^^^^^^^^^^^^^ = help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion error: unused doc comment --> $DIR/unused-doc-comments-for-macros.rs:13:5 | LL | /     /// line1 LL | |     /// line2 LL | |     /// line3 | |_____--------^ |       | |       rustdoc does not generate documentation for macro invocations | = help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion error: aborting due to 2 previous errors </ins>

<ins> #![feature(stmt_expr_attributes)] #![deny(unused_doc_comments)] macro_rules! mac { () => {} } /// foo //~ ERROR unused doc comment mac!(); fn foo() { /// a //~ ERROR unused doc comment let x = 12; /// multi-line //~ unused doc comment /// doc comment /// that is unused match x { /// c //~ ERROR unused doc comment 1 => {}, _ => {} } /// foo //~ ERROR unused doc comment unsafe {} #[doc = "foo"] //~ ERROR unused doc comment #[doc = "bar"] //~ ERROR unused doc comment 3; /// bar //~ ERROR unused doc comment mac!(); let x = /** comment */ 47; //~ ERROR unused doc comment /// dox //~ ERROR unused doc comment { } } fn main() { foo(); } </ins>

<ins> error: unused doc comment --> $DIR/useless-comment.rs:9:1 | LL | /// foo | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macro invocations | note: the lint level is defined here --> $DIR/useless-comment.rs:3:9 | LL | #![deny(unused_doc_comments)] |         ^^^^^^^^^^^^^^^^^^^ = help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion error: unused doc comment --> $DIR/useless-comment.rs:32:5 | LL |     /// bar |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macro invocations | = help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion error: unused doc comment --> $DIR/useless-comment.rs:13:5 | LL |     /// a |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL |     let x = 12; |     ----------- rustdoc does not generate documentation for statements error: unused doc comment --> $DIR/useless-comment.rs:16:5 | LL | /     /// multi-line LL | |     /// doc comment LL | |     /// that is unused | |______________________^ LL | /     match x { LL | |         /// c LL | |         1 => {}, LL | |         _ => {} LL | |     } | |_____- rustdoc does not generate documentation for expressions error: unused doc comment --> $DIR/useless-comment.rs:20:9 | LL |         /// c |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL |         1 => {}, |         ------- rustdoc does not generate documentation for match arms error: unused doc comment --> $DIR/useless-comment.rs:25:5 | LL |     /// foo |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL |     unsafe {} |     --------- rustdoc does not generate documentation for expressions error: unused doc comment --> $DIR/useless-comment.rs:28:5 | LL |     #[doc = "foo"] |     ^^^^^^^^^^^^^^ LL |     #[doc = "bar"] LL |     3; |     - rustdoc does not generate documentation for expressions error: unused doc comment --> $DIR/useless-comment.rs:29:5 | LL |     #[doc = "bar"] |     ^^^^^^^^^^^^^^ LL |     3; |     - rustdoc does not generate documentation for expressions error: unused doc comment --> $DIR/useless-comment.rs:35:13 | LL |     let x = /** comment */ 47; |             ^^^^^^^^^^^^^^ -- rustdoc does not generate documentation for expressions error: unused doc comment --> $DIR/useless-comment.rs:37:5 | LL |       /// dox |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | /     { LL | | LL | |     } | |_____- rustdoc does not generate documentation for expressions error: aborting due to 10 previous errors </ins>

<del> #![feature(stmt_expr_attributes)] #![deny(unused_doc_comments)] macro_rules! mac { () => {} } /// foo //~ ERROR unused doc comment mac!(); fn foo() { /// a //~ ERROR unused doc comment let x = 12; /// multi-line //~ unused doc comment /// doc comment /// that is unused match x { /// c //~ ERROR unused doc comment 1 => {}, _ => {} } /// foo //~ ERROR unused doc comment unsafe {} #[doc = "foo"] //~ ERROR unused doc comment #[doc = "bar"] //~ ERROR unused doc comment 3; /// bar //~ ERROR unused doc comment mac!(); let x = /** comment */ 47; //~ ERROR unused doc comment /// dox //~ ERROR unused doc comment { } } fn main() { foo(); } </del>

<del> error: unused doc comment --> $DIR/useless-comment.rs:9:1 | LL | /// foo | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macro invocations | note: the lint level is defined here --> $DIR/useless-comment.rs:3:9 | LL | #![deny(unused_doc_comments)] |         ^^^^^^^^^^^^^^^^^^^ = help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion error: unused doc comment --> $DIR/useless-comment.rs:32:5 | LL |     /// bar |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macro invocations | = help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion error: unused doc comment --> $DIR/useless-comment.rs:13:5 | LL |     /// a |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL |     let x = 12; |     ----------- rustdoc does not generate documentation for statements error: unused doc comment --> $DIR/useless-comment.rs:16:5 | LL | /     /// multi-line LL | |     /// doc comment LL | |     /// that is unused | |______________________^ LL | /     match x { LL | |         /// c LL | |         1 => {}, LL | |         _ => {} LL | |     } | |_____- rustdoc does not generate documentation for expressions error: unused doc comment --> $DIR/useless-comment.rs:20:9 | LL |         /// c |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL |         1 => {}, |         ------- rustdoc does not generate documentation for match arms error: unused doc comment --> $DIR/useless-comment.rs:25:5 | LL |     /// foo |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL |     unsafe {} |     --------- rustdoc does not generate documentation for expressions error: unused doc comment --> $DIR/useless-comment.rs:28:5 | LL |     #[doc = "foo"] |     ^^^^^^^^^^^^^^ LL |     #[doc = "bar"] LL |     3; |     - rustdoc does not generate documentation for expressions error: unused doc comment --> $DIR/useless-comment.rs:29:5 | LL |     #[doc = "bar"] |     ^^^^^^^^^^^^^^ LL |     3; |     - rustdoc does not generate documentation for expressions error: unused doc comment --> $DIR/useless-comment.rs:35:13 | LL |     let x = /** comment */ 47; |             ^^^^^^^^^^^^^^ -- rustdoc does not generate documentation for expressions error: unused doc comment --> $DIR/useless-comment.rs:37:5 | LL |       /// dox |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | /     { LL | | LL | |     } | |_____- rustdoc does not generate documentation for expressions error: aborting due to 10 previous errors </del>