1
- use super :: { AttrWrapper , Capturing , Parser , PathStyle } ;
1
+ use super :: { AttrWrapper , Capturing , ForceCollect , Parser , PathStyle } ;
2
2
use rustc_ast as ast;
3
3
use rustc_ast:: attr;
4
4
use rustc_ast:: token:: { self , Nonterminal } ;
5
5
use 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 } ;
8
8
use std:: convert:: TryInto ;
9
9
10
10
use tracing:: debug;
@@ -25,6 +25,12 @@ pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPo
25
25
prev_attr_sp : None ,
26
26
} ;
27
27
28
+ enum OuterAttributeType {
29
+ DocComment ,
30
+ DocBlockComment ,
31
+ Attribute ,
32
+ }
33
+
28
34
impl < ' a > Parser < ' a > {
29
35
/// Parses attributes that appear before an item.
30
36
pub ( super ) fn parse_outer_attributes ( & mut self ) -> PResult < ' a , AttrWrapper > {
@@ -49,18 +55,32 @@ impl<'a> Parser<'a> {
49
55
Some ( self . parse_attribute ( inner_parse_policy) ?)
50
56
} else if let token:: DocComment ( comment_kind, attr_style, data) = self . token . kind {
51
57
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 ( ) ;
64
84
}
65
85
self . bump ( ) ;
66
86
just_parsed_doc_comment = true ;
@@ -97,7 +117,7 @@ impl<'a> Parser<'a> {
97
117
inner_parse_policy, self . token
98
118
) ;
99
119
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]
101
121
self . collect_tokens_no_attrs ( |this| {
102
122
if this. eat ( & token:: Pound ) {
103
123
let style = if this. eat ( & token:: Not ) {
@@ -125,6 +145,75 @@ impl<'a> Parser<'a> {
125
145
} )
126
146
}
127
147
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
+
128
217
pub ( super ) fn error_on_forbidden_inner_attr ( & self , attr_sp : Span , policy : InnerAttrPolicy < ' _ > ) {
129
218
if let InnerAttrPolicy :: Forbidden { reason, saw_doc_comment, prev_attr_sp } = policy {
130
219
let prev_attr_note =
@@ -138,11 +227,20 @@ impl<'a> Parser<'a> {
138
227
}
139
228
140
229
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 ( ) ;
146
244
}
147
245
}
148
246
0 commit comments