about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs140
-rw-r--r--src/test/ui/parser/attr-stmt-expr-attr-bad.stderr96
-rw-r--r--src/test/ui/parser/attr.stderr10
-rw-r--r--src/test/ui/parser/doc-comment-in-if-statement.stderr5
-rw-r--r--src/test/ui/parser/inner-attr-after-doc-comment.stderr10
-rw-r--r--src/test/ui/parser/inner-attr.stderr9
-rw-r--r--src/test/ui/parser/issue-30318.fixed27
-rw-r--r--src/test/ui/parser/issue-30318.rs22
-rw-r--r--src/test/ui/parser/issue-30318.stderr74
-rw-r--r--src/test/ui/parser/issue-45296.rs1
-rw-r--r--src/test/ui/parser/issue-45296.stderr9
-rw-r--r--src/test/ui/parser/stmt_expr_attrs_placement.stderr21
-rw-r--r--src/test/ui/proc-macro/issue-86781-bad-inner-doc.fixed12
-rw-r--r--src/test/ui/proc-macro/issue-86781-bad-inner-doc.rs3
-rw-r--r--src/test/ui/proc-macro/issue-86781-bad-inner-doc.stderr10
15 files changed, 382 insertions, 67 deletions
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index e9f0038b2d6..b402b8ba53a 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -1,10 +1,10 @@
-use super::{AttrWrapper, Capturing, Parser, PathStyle};
+use super::{AttrWrapper, Capturing, ForceCollect, Parser, PathStyle};
 use rustc_ast as ast;
 use rustc_ast::attr;
 use rustc_ast::token::{self, Nonterminal};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{error_code, PResult};
-use rustc_span::{sym, Span};
+use rustc_errors::{error_code, DiagnosticBuilder, PResult};
+use rustc_span::{sym, BytePos, Span};
 use std::convert::TryInto;
 
 use tracing::debug;
@@ -25,6 +25,12 @@ pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPo
     prev_attr_sp: None,
 };
 
+enum OuterAttributeType {
+    DocComment,
+    DocBlockComment,
+    Attribute,
+}
+
 impl<'a> Parser<'a> {
     /// Parses attributes that appear before an item.
     pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> {
@@ -49,18 +55,32 @@ impl<'a> Parser<'a> {
                 Some(self.parse_attribute(inner_parse_policy)?)
             } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
                 if attr_style != ast::AttrStyle::Outer {
-                    self.sess
-                        .span_diagnostic
-                        .struct_span_err_with_code(
-                            self.token.span,
-                            "expected outer doc comment",
-                            error_code!(E0753),
-                        )
-                        .note(
-                            "inner doc comments like this (starting with \
-                         `//!` or `/*!`) can only appear before items",
-                        )
-                        .emit();
+                    let span = self.token.span;
+                    let mut err = self.sess.span_diagnostic.struct_span_err_with_code(
+                        span,
+                        "expected outer doc comment",
+                        error_code!(E0753),
+                    );
+                    if let Some(replacement_span) = self.annotate_following_item_if_applicable(
+                        &mut err,
+                        span,
+                        match comment_kind {
+                            token::CommentKind::Line => OuterAttributeType::DocComment,
+                            token::CommentKind::Block => OuterAttributeType::DocBlockComment,
+                        },
+                    ) {
+                        err.note(
+                            "inner doc comments like this (starting with `//!` or `/*!`) can \
+                            only appear before items",
+                        );
+                        err.span_suggestion_verbose(
+                            replacement_span,
+                            "you might have meant to write a regular comment",
+                            String::new(),
+                            rustc_errors::Applicability::MachineApplicable,
+                        );
+                    }
+                    err.emit();
                 }
                 self.bump();
                 just_parsed_doc_comment = true;
@@ -97,7 +117,7 @@ impl<'a> Parser<'a> {
             inner_parse_policy, self.token
         );
         let lo = self.token.span;
-        // Attributse can't have attributes of their own
+        // Attributes can't have attributes of their own [Editor's note: not with that attitude]
         self.collect_tokens_no_attrs(|this| {
             if this.eat(&token::Pound) {
                 let style = if this.eat(&token::Not) {
@@ -125,6 +145,75 @@ impl<'a> Parser<'a> {
         })
     }
 
+    fn annotate_following_item_if_applicable(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        span: Span,
+        attr_type: OuterAttributeType,
+    ) -> Option<Span> {
+        let mut snapshot = self.clone();
+        let lo = span.lo()
+            + BytePos(match attr_type {
+                OuterAttributeType::Attribute => 1,
+                _ => 2,
+            });
+        let hi = lo + BytePos(1);
+        let replacement_span = span.with_lo(lo).with_hi(hi);
+        if let OuterAttributeType::DocBlockComment | OuterAttributeType::DocComment = attr_type {
+            snapshot.bump();
+        }
+        loop {
+            // skip any other attributes, we want the item
+            if snapshot.token.kind == token::Pound {
+                if let Err(mut err) = snapshot.parse_attribute(InnerAttrPolicy::Permitted) {
+                    err.cancel();
+                    return Some(replacement_span);
+                }
+            } else {
+                break;
+            }
+        }
+        match snapshot.parse_item_common(
+            AttrWrapper::empty(),
+            true,
+            false,
+            |_| true,
+            ForceCollect::No,
+        ) {
+            Ok(Some(item)) => {
+                let attr_name = match attr_type {
+                    OuterAttributeType::Attribute => "attribute",
+                    _ => "doc comment",
+                };
+                err.span_label(
+                    item.span,
+                    &format!("the inner {} doesn't annotate this {}", attr_name, item.kind.descr()),
+                );
+                err.span_suggestion_verbose(
+                    replacement_span,
+                    &format!(
+                        "to annotate the {}, change the {} from inner to outer style",
+                        item.kind.descr(),
+                        attr_name
+                    ),
+                    (match attr_type {
+                        OuterAttributeType::Attribute => "",
+                        OuterAttributeType::DocBlockComment => "*",
+                        OuterAttributeType::DocComment => "/",
+                    })
+                    .to_string(),
+                    rustc_errors::Applicability::MachineApplicable,
+                );
+                return None;
+            }
+            Err(mut item_err) => {
+                item_err.cancel();
+            }
+            Ok(None) => {}
+        }
+        Some(replacement_span)
+    }
+
     pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy<'_>) {
         if let InnerAttrPolicy::Forbidden { reason, saw_doc_comment, prev_attr_sp } = policy {
             let prev_attr_note =
@@ -138,11 +227,20 @@ impl<'a> Parser<'a> {
             }
 
             diag.note(
-                "inner attributes, like `#![no_std]`, annotate the item enclosing them, \
-                and are usually found at the beginning of source files. \
-                Outer attributes, like `#[test]`, annotate the item following them.",
-            )
-            .emit();
+                "inner attributes, like `#![no_std]`, annotate the item enclosing them, and \
+                are usually found at the beginning of source files",
+            );
+            if self
+                .annotate_following_item_if_applicable(
+                    &mut diag,
+                    attr_sp,
+                    OuterAttributeType::Attribute,
+                )
+                .is_some()
+            {
+                diag.note("outer attributes, like `#[test]`, annotate the item following them");
+            };
+            diag.emit();
         }
     }
 
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
index cec6980c008..d38b98a1901 100644
--- a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
@@ -4,7 +4,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = box #![attr] 0; }
    |                                    ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: expected expression, found `]`
   --> $DIR/attr-stmt-expr-attr-bad.rs:7:40
@@ -24,7 +25,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); }
    |                                    ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: expected expression, found `)`
   --> $DIR/attr-stmt-expr-attr-bad.rs:11:44
@@ -38,7 +40,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); }
    |                                      ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: expected expression, found `)`
   --> $DIR/attr-stmt-expr-attr-bad.rs:14:46
@@ -52,7 +55,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; }
    |                                    ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:19:33
@@ -60,7 +64,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = !#![attr] 0; }
    |                                 ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:21:33
@@ -68,7 +73,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = -#![attr] 0; }
    |                                 ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `#`
   --> $DIR/attr-stmt-expr-attr-bad.rs:23:34
@@ -82,7 +88,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] foo; }
    |                                   ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:27:40
@@ -90,7 +97,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; }
    |                                        ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:29:35
@@ -98,7 +106,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; }
    |                                   ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:31:40
@@ -106,7 +115,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; }
    |                                        ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: expected expression, found `..`
   --> $DIR/attr-stmt-expr-attr-bad.rs:33:40
@@ -126,7 +136,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; }
    |                                         ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:39:45
@@ -134,7 +145,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; }
    |                                             ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:41:37
@@ -151,7 +163,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; }
    |                                      ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
   --> $DIR/attr-stmt-expr-attr-bad.rs:45:40
@@ -174,7 +187,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; }
    |                                              ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:51:45
@@ -200,7 +214,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; }
    |                                                   ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:57:45
@@ -217,7 +232,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; }
    |                                              ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
   --> $DIR/attr-stmt-expr-attr-bad.rs:61:48
@@ -240,7 +256,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; }
    |                                                      ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/attr-stmt-expr-attr-bad.rs:67:53
@@ -266,7 +283,8 @@ error: an inner attribute is not permitted in this context
 LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; }
    |                                                                   ^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted following an outer attribute
   --> $DIR/attr-stmt-expr-attr-bad.rs:74:32
@@ -276,7 +294,8 @@ LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; }
    |                        |
    |                        previous outer attribute
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted following an outer attribute
   --> $DIR/attr-stmt-expr-attr-bad.rs:76:32
@@ -286,37 +305,56 @@ LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] 0; }
    |                        |
    |                        previous outer attribute
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted following an outer attribute
   --> $DIR/attr-stmt-expr-attr-bad.rs:78:32
    |
 LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); }
-   |                        ------- ^^^^^^^^ not permitted following an outer attribute
-   |                        |
+   |                        ------- ^^^^^^^^ ------- the inner attribute doesn't annotate this item macro invocation
+   |                        |       |
+   |                        |       not permitted following an outer attribute
    |                        previous outer attribute
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+help: to annotate the item macro invocation, change the attribute from inner to outer style
+   |
+LL - #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); }
+LL + #[cfg(FALSE)] fn s() { #[attr] #[attr] foo!(); }
+   | 
 
 error: an inner attribute is not permitted following an outer attribute
   --> $DIR/attr-stmt-expr-attr-bad.rs:80:32
    |
 LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; }
-   |                        ------- ^^^^^^^^ not permitted following an outer attribute
-   |                        |
+   |                        ------- ^^^^^^^^ ------- the inner attribute doesn't annotate this item macro invocation
+   |                        |       |
+   |                        |       not permitted following an outer attribute
    |                        previous outer attribute
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+help: to annotate the item macro invocation, change the attribute from inner to outer style
+   |
+LL - #[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; }
+LL + #[cfg(FALSE)] fn s() { #[attr] #[attr] foo![]; }
+   | 
 
 error: an inner attribute is not permitted following an outer attribute
   --> $DIR/attr-stmt-expr-attr-bad.rs:82:32
    |
 LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; }
-   |                        ------- ^^^^^^^^ not permitted following an outer attribute
-   |                        |
+   |                        ------- ^^^^^^^^ ------ the inner attribute doesn't annotate this item macro invocation
+   |                        |       |
+   |                        |       not permitted following an outer attribute
    |                        previous outer attribute
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+help: to annotate the item macro invocation, change the attribute from inner to outer style
+   |
+LL - #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; }
+LL + #[cfg(FALSE)] fn s() { #[attr] #[attr] foo!{}; }
+   | 
 
 error[E0586]: inclusive range with no end
   --> $DIR/attr-stmt-expr-attr-bad.rs:88:35
diff --git a/src/test/ui/parser/attr.stderr b/src/test/ui/parser/attr.stderr
index 400a0276b3b..3cec61fe41e 100644
--- a/src/test/ui/parser/attr.stderr
+++ b/src/test/ui/parser/attr.stderr
@@ -3,8 +3,16 @@ error: an inner attribute is not permitted in this context
    |
 LL | #![lang = "foo"]
    | ^^^^^^^^^^^^^^^^
+LL |
+LL | fn foo() {}
+   | ----------- the inner attribute doesn't annotate this function
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+help: to annotate the function, change the attribute from inner to outer style
+   |
+LL - #![lang = "foo"]
+LL + #[lang = "foo"]
+   | 
 
 error[E0522]: definition of an unknown language item: `foo`
   --> $DIR/attr.rs:5:1
diff --git a/src/test/ui/parser/doc-comment-in-if-statement.stderr b/src/test/ui/parser/doc-comment-in-if-statement.stderr
index be52a0afd46..b7c1847fc7c 100644
--- a/src/test/ui/parser/doc-comment-in-if-statement.stderr
+++ b/src/test/ui/parser/doc-comment-in-if-statement.stderr
@@ -5,6 +5,11 @@ LL |     if true /*!*/ {}
    |             ^^^^^
    |
    = note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
+help: you might have meant to write a regular comment
+   |
+LL -     if true /*!*/ {}
+LL +     if true /**/ {}
+   | 
 
 error: outer attributes are not allowed on `if` and `else` branches
   --> $DIR/doc-comment-in-if-statement.rs:2:13
diff --git a/src/test/ui/parser/inner-attr-after-doc-comment.stderr b/src/test/ui/parser/inner-attr-after-doc-comment.stderr
index c1e9e7a427f..404800ee15b 100644
--- a/src/test/ui/parser/inner-attr-after-doc-comment.stderr
+++ b/src/test/ui/parser/inner-attr-after-doc-comment.stderr
@@ -8,8 +8,16 @@ LL | |  */
 LL | 
 LL |   #![recursion_limit="100"]
    |   ^^^^^^^^^^^^^^^^^^^^^^^^^ not permitted following an outer attribute
+LL |
+LL |   fn main() {}
+   |   ------------ the inner attribute doesn't annotate this function
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+help: to annotate the function, change the attribute from inner to outer style
+   |
+LL - #![recursion_limit="100"]
+LL + #[recursion_limit="100"]
+   | 
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/inner-attr.stderr b/src/test/ui/parser/inner-attr.stderr
index e1bf2cca1c9..1adac745908 100644
--- a/src/test/ui/parser/inner-attr.stderr
+++ b/src/test/ui/parser/inner-attr.stderr
@@ -6,8 +6,15 @@ LL | #[feature(lang_items)]
 LL | 
 LL | #![recursion_limit="100"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^ not permitted following an outer attribute
+LL | fn main() {}
+   | ------------ the inner attribute doesn't annotate this function
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+help: to annotate the function, change the attribute from inner to outer style
+   |
+LL - #![recursion_limit="100"]
+LL + #[recursion_limit="100"]
+   | 
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issue-30318.fixed b/src/test/ui/parser/issue-30318.fixed
new file mode 100644
index 00000000000..71fc82172a5
--- /dev/null
+++ b/src/test/ui/parser/issue-30318.fixed
@@ -0,0 +1,27 @@
+// run-rustfix
+#![allow(unused)]
+fn foo() { }
+
+/// Misplaced comment...
+//~^ ERROR expected outer doc comment
+fn bar() { } //~ NOTE the inner doc comment doesn't annotate this function
+
+#[test] //~ ERROR an inner attribute is not permitted in this context
+fn baz() { } //~ NOTE the inner attribute doesn't annotate this function
+//~^^ NOTE inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually
+
+/** Misplaced comment... */
+//~^ ERROR expected outer doc comment
+fn bat() { } //~ NOTE the inner doc comment doesn't annotate this function
+
+fn main() { }
+
+// Misplaced comment...
+//~^ ERROR expected outer doc comment
+//~| NOTE inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
+//~| NOTE other attributes here
+/* Misplaced comment... */
+//~^ ERROR expected outer doc comment
+//~| NOTE this doc comment doesn't document anything
+//~| ERROR expected item after doc comment
+//~| NOTE inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
diff --git a/src/test/ui/parser/issue-30318.rs b/src/test/ui/parser/issue-30318.rs
index 38e30de716d..465dca2ff82 100644
--- a/src/test/ui/parser/issue-30318.rs
+++ b/src/test/ui/parser/issue-30318.rs
@@ -1,7 +1,27 @@
+// run-rustfix
+#![allow(unused)]
 fn foo() { }
 
 //! Misplaced comment...
 //~^ ERROR expected outer doc comment
-//~| NOTE inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
+fn bar() { } //~ NOTE the inner doc comment doesn't annotate this function
+
+#![test] //~ ERROR an inner attribute is not permitted in this context
+fn baz() { } //~ NOTE the inner attribute doesn't annotate this function
+//~^^ NOTE inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually
+
+/*! Misplaced comment... */
+//~^ ERROR expected outer doc comment
+fn bat() { } //~ NOTE the inner doc comment doesn't annotate this function
 
 fn main() { }
+
+//! Misplaced comment...
+//~^ ERROR expected outer doc comment
+//~| NOTE inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
+//~| NOTE other attributes here
+/*! Misplaced comment... */
+//~^ ERROR expected outer doc comment
+//~| NOTE this doc comment doesn't document anything
+//~| ERROR expected item after doc comment
+//~| NOTE inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
diff --git a/src/test/ui/parser/issue-30318.stderr b/src/test/ui/parser/issue-30318.stderr
index b3a27f19851..7e710884554 100644
--- a/src/test/ui/parser/issue-30318.stderr
+++ b/src/test/ui/parser/issue-30318.stderr
@@ -1,11 +1,81 @@
 error[E0753]: expected outer doc comment
-  --> $DIR/issue-30318.rs:3:1
+  --> $DIR/issue-30318.rs:5:1
+   |
+LL | //! Misplaced comment...
+   | ^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | fn bar() { }
+   | ------------ the inner doc comment doesn't annotate this function
+   |
+help: to annotate the function, change the doc comment from inner to outer style
+   |
+LL | /// Misplaced comment...
+   |   ~
+
+error: an inner attribute is not permitted in this context
+  --> $DIR/issue-30318.rs:9:1
+   |
+LL | #![test]
+   | ^^^^^^^^
+LL | fn baz() { }
+   | ------------ the inner attribute doesn't annotate this function
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+help: to annotate the function, change the attribute from inner to outer style
+   |
+LL - #![test]
+LL + #[test]
+   | 
+
+error[E0753]: expected outer doc comment
+  --> $DIR/issue-30318.rs:13:1
+   |
+LL | /*! Misplaced comment... */
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | fn bat() { }
+   | ------------ the inner doc comment doesn't annotate this function
+   |
+help: to annotate the function, change the doc comment from inner to outer style
+   |
+LL | /** Misplaced comment... */
+   |   ~
+
+error[E0753]: expected outer doc comment
+  --> $DIR/issue-30318.rs:19:1
    |
 LL | //! Misplaced comment...
    | ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
+help: you might have meant to write a regular comment
+   |
+LL - //! Misplaced comment...
+LL + // Misplaced comment...
+   | 
+
+error[E0753]: expected outer doc comment
+  --> $DIR/issue-30318.rs:23:1
+   |
+LL | /*! Misplaced comment... */
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
+help: you might have meant to write a regular comment
+   |
+LL - /*! Misplaced comment... */
+LL + /* Misplaced comment... */
+   | 
+
+error: expected item after doc comment
+  --> $DIR/issue-30318.rs:23:1
+   |
+LL | //! Misplaced comment...
+   | ------------------------ other attributes here
+...
+LL | /*! Misplaced comment... */
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment doesn't document anything
 
-error: aborting due to previous error
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0753`.
diff --git a/src/test/ui/parser/issue-45296.rs b/src/test/ui/parser/issue-45296.rs
index f242c1d2937..d3a97e89f9a 100644
--- a/src/test/ui/parser/issue-45296.rs
+++ b/src/test/ui/parser/issue-45296.rs
@@ -2,4 +2,5 @@ fn main() {
     let unused = ();
 
     #![allow(unused_variables)] //~ ERROR not permitted in this context
+    fn foo() {}
 }
diff --git a/src/test/ui/parser/issue-45296.stderr b/src/test/ui/parser/issue-45296.stderr
index c0d4ce1243e..6abe266d4e9 100644
--- a/src/test/ui/parser/issue-45296.stderr
+++ b/src/test/ui/parser/issue-45296.stderr
@@ -3,8 +3,15 @@ error: an inner attribute is not permitted in this context
    |
 LL |     #![allow(unused_variables)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     fn foo() {}
+   |     ----------- the inner attribute doesn't annotate this function
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+help: to annotate the function, change the attribute from inner to outer style
+   |
+LL -     #![allow(unused_variables)]
+LL +     #[allow(unused_variables)]
+   | 
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/stmt_expr_attrs_placement.stderr b/src/test/ui/parser/stmt_expr_attrs_placement.stderr
index 808903d9c62..bf4005698a3 100644
--- a/src/test/ui/parser/stmt_expr_attrs_placement.stderr
+++ b/src/test/ui/parser/stmt_expr_attrs_placement.stderr
@@ -4,7 +4,8 @@ error: an inner attribute is not permitted in this context
 LL |     let a = #![allow(warnings)] (1, 2);
    |             ^^^^^^^^^^^^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/stmt_expr_attrs_placement.rs:10:14
@@ -12,7 +13,8 @@ error: an inner attribute is not permitted in this context
 LL |     let b = (#![allow(warnings)] 1, 2);
    |              ^^^^^^^^^^^^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/stmt_expr_attrs_placement.rs:15:10
@@ -20,7 +22,8 @@ error: an inner attribute is not permitted in this context
 LL |         (#![allow(warnings)] 1, 2)
    |          ^^^^^^^^^^^^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/stmt_expr_attrs_placement.rs:21:18
@@ -28,7 +31,8 @@ error: an inner attribute is not permitted in this context
 LL |         let e = (#![allow(warnings)] 1, 2);
    |                  ^^^^^^^^^^^^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/stmt_expr_attrs_placement.rs:26:14
@@ -36,7 +40,8 @@ error: an inner attribute is not permitted in this context
 LL |     let e = [#![allow(warnings)] 1, 2];
    |              ^^^^^^^^^^^^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/stmt_expr_attrs_placement.rs:29:14
@@ -44,7 +49,8 @@ error: an inner attribute is not permitted in this context
 LL |     let f = [#![allow(warnings)] 1; 0];
    |              ^^^^^^^^^^^^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: an inner attribute is not permitted in this context
   --> $DIR/stmt_expr_attrs_placement.rs:36:24
@@ -52,7 +58,8 @@ error: an inner attribute is not permitted in this context
 LL |     let h = MyStruct { #![allow(warnings)] field: 0 };
    |                        ^^^^^^^^^^^^^^^^^^^
    |
-   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
 
 error: aborting due to 7 previous errors
 
diff --git a/src/test/ui/proc-macro/issue-86781-bad-inner-doc.fixed b/src/test/ui/proc-macro/issue-86781-bad-inner-doc.fixed
new file mode 100644
index 00000000000..426a5fa723f
--- /dev/null
+++ b/src/test/ui/proc-macro/issue-86781-bad-inner-doc.fixed
@@ -0,0 +1,12 @@
+// aux-build:test-macros.rs
+// run-rustfix
+
+#[macro_use]
+extern crate test_macros;
+
+/// Inner doc comment
+//~^ ERROR expected outer doc comment
+#[derive(Empty)]
+pub struct Foo; //~ NOTE the inner doc comment doesn't annotate this struct
+
+fn main() {}
diff --git a/src/test/ui/proc-macro/issue-86781-bad-inner-doc.rs b/src/test/ui/proc-macro/issue-86781-bad-inner-doc.rs
index 8be1ae77738..31e3f3c8592 100644
--- a/src/test/ui/proc-macro/issue-86781-bad-inner-doc.rs
+++ b/src/test/ui/proc-macro/issue-86781-bad-inner-doc.rs
@@ -1,4 +1,5 @@
 // aux-build:test-macros.rs
+// run-rustfix
 
 #[macro_use]
 extern crate test_macros;
@@ -6,6 +7,6 @@ extern crate test_macros;
 //! Inner doc comment
 //~^ ERROR expected outer doc comment
 #[derive(Empty)]
-pub struct Foo;
+pub struct Foo; //~ NOTE the inner doc comment doesn't annotate this struct
 
 fn main() {}
diff --git a/src/test/ui/proc-macro/issue-86781-bad-inner-doc.stderr b/src/test/ui/proc-macro/issue-86781-bad-inner-doc.stderr
index 0b2e612ee5b..a92f07522e5 100644
--- a/src/test/ui/proc-macro/issue-86781-bad-inner-doc.stderr
+++ b/src/test/ui/proc-macro/issue-86781-bad-inner-doc.stderr
@@ -1,10 +1,16 @@
 error[E0753]: expected outer doc comment
-  --> $DIR/issue-86781-bad-inner-doc.rs:6:1
+  --> $DIR/issue-86781-bad-inner-doc.rs:7:1
    |
 LL | //! Inner doc comment
    | ^^^^^^^^^^^^^^^^^^^^^
+...
+LL | pub struct Foo;
+   | --------------- the inner doc comment doesn't annotate this struct
    |
-   = note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
+help: to annotate the struct, change the doc comment from inner to outer style
+   |
+LL | /// Inner doc comment
+   |   ~
 
 error: aborting due to previous error