1- use super :: { AttrWrapper , Capturing , Parser , PathStyle } ;
1+ use super :: { AttrWrapper , Capturing , ForceCollect , Parser , PathStyle } ;
22use rustc_ast as ast;
33use rustc_ast:: attr;
44use rustc_ast:: token:: { self , Nonterminal } ;
55use rustc_ast_pretty:: pprust;
6- use rustc_errors:: { error_code, PResult } ;
7- use rustc_span:: { sym, Span } ;
6+ use rustc_errors:: { error_code, DiagnosticBuilder , PResult } ;
7+ use rustc_span:: { sym, BytePos , Span } ;
88use std:: convert:: TryInto ;
99
1010use tracing:: debug;
@@ -25,6 +25,12 @@ pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPo
2525 prev_attr_sp : None ,
2626} ;
2727
28+ enum OuterAttributeType {
29+ DocComment ,
30+ DocBlockComment ,
31+ Attribute ,
32+ }
33+
2834impl < ' a > Parser < ' a > {
2935 /// Parses attributes that appear before an item.
3036 pub ( super ) fn parse_outer_attributes ( & mut self ) -> PResult < ' a , AttrWrapper > {
@@ -49,18 +55,32 @@ impl<'a> Parser<'a> {
4955 Some ( self . parse_attribute ( inner_parse_policy) ?)
5056 } else if let token:: DocComment ( comment_kind, attr_style, data) = self . token . kind {
5157 if attr_style != ast:: AttrStyle :: Outer {
52- self . sess
53- . span_diagnostic
54- . struct_span_err_with_code (
55- self . token . span ,
56- "expected outer doc comment" ,
57- error_code ! ( E0753 ) ,
58- )
59- . note (
60- "inner doc comments like this (starting with \
61- `//!` or `/*!`) can only appear before items",
62- )
63- . emit ( ) ;
58+ let span = self . token . span ;
59+ let mut err = self . sess . span_diagnostic . struct_span_err_with_code (
60+ span,
61+ "expected outer doc comment" ,
62+ error_code ! ( E0753 ) ,
63+ ) ;
64+ if let Some ( replacement_span) = self . annotate_following_item_if_applicable (
65+ & mut err,
66+ span,
67+ match comment_kind {
68+ token:: CommentKind :: Line => OuterAttributeType :: DocComment ,
69+ token:: CommentKind :: Block => OuterAttributeType :: DocBlockComment ,
70+ } ,
71+ ) {
72+ err. note (
73+ "inner doc comments like this (starting with `//!` or `/*!`) can \
74+ only appear before items",
75+ ) ;
76+ err. span_suggestion_verbose (
77+ replacement_span,
78+ "you might have meant to write a regular comment" ,
79+ String :: new ( ) ,
80+ rustc_errors:: Applicability :: MachineApplicable ,
81+ ) ;
82+ }
83+ err. emit ( ) ;
6484 }
6585 self . bump ( ) ;
6686 just_parsed_doc_comment = true ;
@@ -97,7 +117,7 @@ impl<'a> Parser<'a> {
97117 inner_parse_policy, self . token
98118 ) ;
99119 let lo = self . token . span ;
100- // Attributse can't have attributes of their own
120+ // Attributes can't have attributes of their own [Editor's note: not with that attitude]
101121 self . collect_tokens_no_attrs ( |this| {
102122 if this. eat ( & token:: Pound ) {
103123 let style = if this. eat ( & token:: Not ) {
@@ -125,6 +145,75 @@ impl<'a> Parser<'a> {
125145 } )
126146 }
127147
148+ fn annotate_following_item_if_applicable (
149+ & self ,
150+ err : & mut DiagnosticBuilder < ' _ > ,
151+ span : Span ,
152+ attr_type : OuterAttributeType ,
153+ ) -> Option < Span > {
154+ let mut snapshot = self . clone ( ) ;
155+ let lo = span. lo ( )
156+ + BytePos ( match attr_type {
157+ OuterAttributeType :: Attribute => 1 ,
158+ _ => 2 ,
159+ } ) ;
160+ let hi = lo + BytePos ( 1 ) ;
161+ let replacement_span = span. with_lo ( lo) . with_hi ( hi) ;
162+ if let OuterAttributeType :: DocBlockComment | OuterAttributeType :: DocComment = attr_type {
163+ snapshot. bump ( ) ;
164+ }
165+ loop {
166+ // skip any other attributes, we want the item
167+ if snapshot. token . kind == token:: Pound {
168+ if let Err ( mut err) = snapshot. parse_attribute ( InnerAttrPolicy :: Permitted ) {
169+ err. cancel ( ) ;
170+ return Some ( replacement_span) ;
171+ }
172+ } else {
173+ break ;
174+ }
175+ }
176+ match snapshot. parse_item_common (
177+ AttrWrapper :: empty ( ) ,
178+ true ,
179+ false ,
180+ |_| true ,
181+ ForceCollect :: No ,
182+ ) {
183+ Ok ( Some ( item) ) => {
184+ let attr_name = match attr_type {
185+ OuterAttributeType :: Attribute => "attribute" ,
186+ _ => "doc comment" ,
187+ } ;
188+ err. span_label (
189+ item. span ,
190+ & format ! ( "the inner {} doesn't annotate this {}" , attr_name, item. kind. descr( ) ) ,
191+ ) ;
192+ err. span_suggestion_verbose (
193+ replacement_span,
194+ & format ! (
195+ "to annotate the {}, change the {} from inner to outer style" ,
196+ item. kind. descr( ) ,
197+ attr_name
198+ ) ,
199+ ( match attr_type {
200+ OuterAttributeType :: Attribute => "" ,
201+ OuterAttributeType :: DocBlockComment => "*" ,
202+ OuterAttributeType :: DocComment => "/" ,
203+ } )
204+ . to_string ( ) ,
205+ rustc_errors:: Applicability :: MachineApplicable ,
206+ ) ;
207+ return None ;
208+ }
209+ Err ( mut item_err) => {
210+ item_err. cancel ( ) ;
211+ }
212+ Ok ( None ) => { }
213+ }
214+ Some ( replacement_span)
215+ }
216+
128217 pub ( super ) fn error_on_forbidden_inner_attr ( & self , attr_sp : Span , policy : InnerAttrPolicy < ' _ > ) {
129218 if let InnerAttrPolicy :: Forbidden { reason, saw_doc_comment, prev_attr_sp } = policy {
130219 let prev_attr_note =
@@ -138,11 +227,20 @@ impl<'a> Parser<'a> {
138227 }
139228
140229 diag. note (
141- "inner attributes, like `#![no_std]`, annotate the item enclosing them, \
142- and are usually found at the beginning of source files. \
143- Outer attributes, like `#[test]`, annotate the item following them.",
144- )
145- . emit ( ) ;
230+ "inner attributes, like `#![no_std]`, annotate the item enclosing them, and \
231+ are usually found at the beginning of source files",
232+ ) ;
233+ if self
234+ . annotate_following_item_if_applicable (
235+ & mut diag,
236+ attr_sp,
237+ OuterAttributeType :: Attribute ,
238+ )
239+ . is_some ( )
240+ {
241+ diag. note ( "outer attributes, like `#[test]`, annotate the item following them" ) ;
242+ } ;
243+ diag. emit ( ) ;
146244 }
147245 }
148246
0 commit comments