1use rustc_ast as ast;
2use rustc_ast::token::{self, MetaVarKind};
3use rustc_ast::tokenstream::ParserRange;
4use rustc_ast::{Attribute, attr};
5use rustc_errors::codes::*;
6use rustc_errors::{Diag, PResult};
7use rustc_span::{BytePos, Span};
8use thin_vec::ThinVec;
9use tracing::debug;
10
11use super::{
12 AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle, Trailing, UsePreAttrPos,
13};
14use crate::parser::FnContext;
15use crate::{errors, exp, fluent_generated as fluent};
16
17#[derive(Debug)]
19pub enum InnerAttrPolicy {
20 Permitted,
21 Forbidden(Option<InnerAttrForbiddenReason>),
22}
23
24#[derive(Clone, Copy, Debug)]
25pub enum InnerAttrForbiddenReason {
26 InCodeBlock,
27 AfterOuterDocComment { prev_doc_comment_span: Span },
28 AfterOuterAttribute { prev_outer_attr_sp: Span },
29}
30
31enum OuterAttributeType {
32 DocComment,
33 DocBlockComment,
34 Attribute,
35}
36
37#[derive(Clone, Copy, PartialEq, Eq)]
38pub enum AllowLeadingUnsafe {
39 Yes,
40 No,
41}
42
43impl<'a> Parser<'a> {
44 pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> {
46 let mut outer_attrs = ast::AttrVec::new();
47 let mut just_parsed_doc_comment = false;
48 let start_pos = self.num_bump_calls;
49 loop {
50 let attr = if self.check(exp!(Pound)) {
51 let prev_outer_attr_sp = outer_attrs.last().map(|attr: &Attribute| attr.span);
52
53 let inner_error_reason = if just_parsed_doc_comment {
54 Some(InnerAttrForbiddenReason::AfterOuterDocComment {
55 prev_doc_comment_span: prev_outer_attr_sp.unwrap(),
56 })
57 } else {
58 prev_outer_attr_sp.map(|prev_outer_attr_sp| {
59 InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }
60 })
61 };
62 let inner_parse_policy = InnerAttrPolicy::Forbidden(inner_error_reason);
63 just_parsed_doc_comment = false;
64 Some(self.parse_attribute(inner_parse_policy)?)
65 } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
66 if attr_style != ast::AttrStyle::Outer {
67 let span = self.token.span;
68 let mut err = self
69 .dcx()
70 .struct_span_err(span, fluent::parse_inner_doc_comment_not_permitted);
71 err.code(E0753);
72 if let Some(replacement_span) = self.annotate_following_item_if_applicable(
73 &mut err,
74 span,
75 match comment_kind {
76 token::CommentKind::Line => OuterAttributeType::DocComment,
77 token::CommentKind::Block => OuterAttributeType::DocBlockComment,
78 },
79 true,
80 ) {
81 err.note(fluent::parse_note);
82 err.span_suggestion_verbose(
83 replacement_span,
84 fluent::parse_suggestion,
85 "",
86 rustc_errors::Applicability::MachineApplicable,
87 );
88 }
89 err.emit();
90 }
91 self.bump();
92 just_parsed_doc_comment = true;
93 Some(attr::mk_doc_comment(
96 &self.psess.attr_id_generator,
97 comment_kind,
98 ast::AttrStyle::Outer,
99 data,
100 self.prev_token.span,
101 ))
102 } else {
103 None
104 };
105
106 if let Some(attr) = attr {
107 if attr.style == ast::AttrStyle::Outer {
108 outer_attrs.push(attr);
109 }
110 } else {
111 break;
112 }
113 }
114 Ok(AttrWrapper::new(outer_attrs, start_pos))
115 }
116
117 pub fn parse_attribute(
121 &mut self,
122 inner_parse_policy: InnerAttrPolicy,
123 ) -> PResult<'a, ast::Attribute> {
124 debug!(
125 "parse_attribute: inner_parse_policy={:?} self.token={:?}",
126 inner_parse_policy, self.token
127 );
128 let lo = self.token.span;
129 self.collect_tokens_no_attrs(|this| {
131 let pound_hi = this.token.span.hi();
132 assert!(this.eat(exp!(Pound)), "parse_attribute called in non-attribute position");
133
134 let not_lo = this.token.span.lo();
135 let style =
136 if this.eat(exp!(Bang)) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer };
137
138 let mut bracket_res = this.expect(exp!(OpenBracket));
139 if let Err(err) = &mut bracket_res
141 && style == ast::AttrStyle::Inner
142 && pound_hi == not_lo
143 {
144 err.note(
145 "the token sequence `#!` here looks like the start of \
146 a shebang interpreter directive but it is not",
147 );
148 err.help(
149 "if you meant this to be a shebang interpreter directive, \
150 move it to the very start of the file",
151 );
152 }
153 bracket_res?;
154 let item = this.parse_attr_item(ForceCollect::No)?;
155 this.expect(exp!(CloseBracket))?;
156 let attr_sp = lo.to(this.prev_token.span);
157
158 if style == ast::AttrStyle::Inner {
160 this.error_on_forbidden_inner_attr(
161 attr_sp,
162 inner_parse_policy,
163 item.is_valid_for_outer_style(),
164 );
165 }
166
167 Ok(attr::mk_attr_from_item(&self.psess.attr_id_generator, item, None, style, attr_sp))
168 })
169 }
170
171 fn annotate_following_item_if_applicable(
172 &self,
173 err: &mut Diag<'_>,
174 span: Span,
175 attr_type: OuterAttributeType,
176 suggest_to_outer: bool,
177 ) -> Option<Span> {
178 let mut snapshot = self.create_snapshot_for_diagnostic();
179 let lo = span.lo()
180 + BytePos(match attr_type {
181 OuterAttributeType::Attribute => 1,
182 _ => 2,
183 });
184 let hi = lo + BytePos(1);
185 let replacement_span = span.with_lo(lo).with_hi(hi);
186 if let OuterAttributeType::DocBlockComment | OuterAttributeType::DocComment = attr_type {
187 snapshot.bump();
188 }
189 loop {
190 if snapshot.token == token::Pound {
192 if let Err(err) = snapshot.parse_attribute(InnerAttrPolicy::Permitted) {
193 err.cancel();
194 return Some(replacement_span);
195 }
196 } else {
197 break;
198 }
199 }
200 match snapshot.parse_item_common(
201 AttrWrapper::empty(),
202 true,
203 false,
204 FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true },
205 ForceCollect::No,
206 ) {
207 Ok(Some(item)) => {
208 err.arg("item", item.kind.descr());
210 err.span_label(item.span, fluent::parse_label_does_not_annotate_this);
211 if suggest_to_outer {
212 err.span_suggestion_verbose(
213 replacement_span,
214 fluent::parse_sugg_change_inner_to_outer,
215 match attr_type {
216 OuterAttributeType::Attribute => "",
217 OuterAttributeType::DocBlockComment => "*",
218 OuterAttributeType::DocComment => "/",
219 },
220 rustc_errors::Applicability::MachineApplicable,
221 );
222 }
223 return None;
224 }
225 Err(item_err) => {
226 item_err.cancel();
227 }
228 Ok(None) => {}
229 }
230 Some(replacement_span)
231 }
232
233 pub(super) fn error_on_forbidden_inner_attr(
234 &self,
235 attr_sp: Span,
236 policy: InnerAttrPolicy,
237 suggest_to_outer: bool,
238 ) {
239 if let InnerAttrPolicy::Forbidden(reason) = policy {
240 let mut diag = match reason.as_ref().copied() {
241 Some(InnerAttrForbiddenReason::AfterOuterDocComment { prev_doc_comment_span }) => {
242 self.dcx()
243 .struct_span_err(
244 attr_sp,
245 fluent::parse_inner_attr_not_permitted_after_outer_doc_comment,
246 )
247 .with_span_label(attr_sp, fluent::parse_label_attr)
248 .with_span_label(
249 prev_doc_comment_span,
250 fluent::parse_label_prev_doc_comment,
251 )
252 }
253 Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => self
254 .dcx()
255 .struct_span_err(
256 attr_sp,
257 fluent::parse_inner_attr_not_permitted_after_outer_attr,
258 )
259 .with_span_label(attr_sp, fluent::parse_label_attr)
260 .with_span_label(prev_outer_attr_sp, fluent::parse_label_prev_attr),
261 Some(InnerAttrForbiddenReason::InCodeBlock) | None => {
262 self.dcx().struct_span_err(attr_sp, fluent::parse_inner_attr_not_permitted)
263 }
264 };
265
266 diag.note(fluent::parse_inner_attr_explanation);
267 if self
268 .annotate_following_item_if_applicable(
269 &mut diag,
270 attr_sp,
271 OuterAttributeType::Attribute,
272 suggest_to_outer,
273 )
274 .is_some()
275 {
276 diag.note(fluent::parse_outer_attr_explanation);
277 };
278 diag.emit();
279 }
280 }
281
282 pub fn parse_attr_item(&mut self, force_collect: ForceCollect) -> PResult<'a, ast::AttrItem> {
292 if let Some(item) = self.eat_metavar_seq_with_matcher(
293 |mv_kind| matches!(mv_kind, MetaVarKind::Meta { .. }),
294 |this| this.parse_attr_item(force_collect),
295 ) {
296 return Ok(item);
297 }
298
299 self.collect_tokens(None, AttrWrapper::empty(), force_collect, |this, _empty_attrs| {
301 let is_unsafe = this.eat_keyword(exp!(Unsafe));
302 let unsafety = if is_unsafe {
303 let unsafe_span = this.prev_token.span;
304 this.expect(exp!(OpenParen))?;
305 ast::Safety::Unsafe(unsafe_span)
306 } else {
307 ast::Safety::Default
308 };
309
310 let path = this.parse_path(PathStyle::Mod)?;
311 let args = this.parse_attr_args()?;
312 if is_unsafe {
313 this.expect(exp!(CloseParen))?;
314 }
315 Ok((
316 ast::AttrItem { unsafety, path, args, tokens: None },
317 Trailing::No,
318 UsePreAttrPos::No,
319 ))
320 })
321 }
322
323 pub fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> {
329 let mut attrs = ast::AttrVec::new();
330 loop {
331 let start_pos = self.num_bump_calls;
332 let attr = if self.check(exp!(Pound)) && self.look_ahead(1, |t| t == &token::Bang) {
334 Some(self.parse_attribute(InnerAttrPolicy::Permitted)?)
335 } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
336 if attr_style == ast::AttrStyle::Inner {
337 self.bump();
338 Some(attr::mk_doc_comment(
339 &self.psess.attr_id_generator,
340 comment_kind,
341 attr_style,
342 data,
343 self.prev_token.span,
344 ))
345 } else {
346 None
347 }
348 } else {
349 None
350 };
351 if let Some(attr) = attr {
352 if let Capturing::Yes = self.capture_state.capturing {
356 let end_pos = self.num_bump_calls;
357 let parser_range = ParserRange(start_pos..end_pos);
358 self.capture_state.inner_attr_parser_ranges.insert(attr.id, parser_range);
359 }
360 attrs.push(attr);
361 } else {
362 break;
363 }
364 }
365 Ok(attrs)
366 }
367
368 pub(crate) fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'a, ast::MetaItemLit> {
370 let lit = self.parse_meta_item_lit()?;
371 debug!("checking if {:?} is unsuffixed", lit);
372
373 if !lit.kind.is_unsuffixed() {
374 self.dcx().emit_err(errors::SuffixedLiteralInAttribute { span: lit.span });
375 }
376
377 Ok(lit)
378 }
379
380 pub fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec<ast::MetaItemInner>> {
382 let mut nmis = ThinVec::with_capacity(1);
384 while self.token != token::Eof {
385 nmis.push(self.parse_meta_item_inner()?);
386 if !self.eat(exp!(Comma)) {
387 break;
388 }
389 }
390 Ok(nmis)
391 }
392
393 pub fn parse_meta_item(
400 &mut self,
401 unsafe_allowed: AllowLeadingUnsafe,
402 ) -> PResult<'a, ast::MetaItem> {
403 if let Some(MetaVarKind::Meta { has_meta_form }) = self.token.is_metavar_seq() {
404 return if has_meta_form {
405 let attr_item = self
406 .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
407 this.parse_attr_item(ForceCollect::No)
408 })
409 .unwrap();
410 Ok(attr_item.meta(attr_item.path.span).unwrap())
411 } else {
412 self.unexpected_any()
413 };
414 }
415
416 let lo = self.token.span;
417 let is_unsafe = if unsafe_allowed == AllowLeadingUnsafe::Yes {
418 self.eat_keyword(exp!(Unsafe))
419 } else {
420 false
421 };
422 let unsafety = if is_unsafe {
423 let unsafe_span = self.prev_token.span;
424 self.expect(exp!(OpenParen))?;
425
426 ast::Safety::Unsafe(unsafe_span)
427 } else {
428 ast::Safety::Default
429 };
430
431 let path = self.parse_path(PathStyle::Mod)?;
432 let kind = self.parse_meta_item_kind()?;
433 if is_unsafe {
434 self.expect(exp!(CloseParen))?;
435 }
436 let span = lo.to(self.prev_token.span);
437
438 Ok(ast::MetaItem { unsafety, path, kind, span })
439 }
440
441 pub(crate) fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
442 Ok(if self.eat(exp!(Eq)) {
443 ast::MetaItemKind::NameValue(self.parse_unsuffixed_meta_item_lit()?)
444 } else if self.check(exp!(OpenParen)) {
445 let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?;
446 ast::MetaItemKind::List(list)
447 } else {
448 ast::MetaItemKind::Word
449 })
450 }
451
452 pub fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::MetaItemInner> {
458 match self.parse_unsuffixed_meta_item_lit() {
459 Ok(lit) => return Ok(ast::MetaItemInner::Lit(lit)),
460 Err(err) => err.cancel(), }
462
463 match self.parse_meta_item(AllowLeadingUnsafe::No) {
464 Ok(mi) => return Ok(ast::MetaItemInner::MetaItem(mi)),
465 Err(err) => err.cancel(), }
467
468 let mut err = errors::InvalidMetaItem {
469 span: self.token.span,
470 descr: super::token_descr(&self.token),
471 quote_ident_sugg: None,
472 };
473
474 if self.prev_token == token::Eq
478 && let token::Ident(..) = self.token.kind
479 {
480 let before = self.token.span.shrink_to_lo();
481 while let token::Ident(..) = self.token.kind {
482 self.bump();
483 }
484 err.quote_ident_sugg = Some(errors::InvalidMetaItemQuoteIdentSugg {
485 before,
486 after: self.prev_token.span.shrink_to_hi(),
487 });
488 }
489
490 Err(self.dcx().create_err(err))
491 }
492}