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_cfg_attr(
382 &mut self,
383 ) -> PResult<'a, (ast::MetaItemInner, Vec<(ast::AttrItem, Span)>)> {
384 let cfg_predicate = self.parse_meta_item_inner()?;
385 self.expect(exp!(Comma))?;
386
387 let mut expanded_attrs = Vec::with_capacity(1);
389 while self.token != token::Eof {
390 let lo = self.token.span;
391 let item = self.parse_attr_item(ForceCollect::Yes)?;
392 expanded_attrs.push((item, lo.to(self.prev_token.span)));
393 if !self.eat(exp!(Comma)) {
394 break;
395 }
396 }
397
398 Ok((cfg_predicate, expanded_attrs))
399 }
400
401 pub fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec<ast::MetaItemInner>> {
403 let mut nmis = ThinVec::with_capacity(1);
405 while self.token != token::Eof {
406 nmis.push(self.parse_meta_item_inner()?);
407 if !self.eat(exp!(Comma)) {
408 break;
409 }
410 }
411 Ok(nmis)
412 }
413
414 pub fn parse_meta_item(
421 &mut self,
422 unsafe_allowed: AllowLeadingUnsafe,
423 ) -> PResult<'a, ast::MetaItem> {
424 if let Some(MetaVarKind::Meta { has_meta_form }) = self.token.is_metavar_seq() {
425 return if has_meta_form {
426 let attr_item = self
427 .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
428 this.parse_attr_item(ForceCollect::No)
429 })
430 .unwrap();
431 Ok(attr_item.meta(attr_item.path.span).unwrap())
432 } else {
433 self.unexpected_any()
434 };
435 }
436
437 let lo = self.token.span;
438 let is_unsafe = if unsafe_allowed == AllowLeadingUnsafe::Yes {
439 self.eat_keyword(exp!(Unsafe))
440 } else {
441 false
442 };
443 let unsafety = if is_unsafe {
444 let unsafe_span = self.prev_token.span;
445 self.expect(exp!(OpenParen))?;
446
447 ast::Safety::Unsafe(unsafe_span)
448 } else {
449 ast::Safety::Default
450 };
451
452 let path = self.parse_path(PathStyle::Mod)?;
453 let kind = self.parse_meta_item_kind()?;
454 if is_unsafe {
455 self.expect(exp!(CloseParen))?;
456 }
457 let span = lo.to(self.prev_token.span);
458
459 Ok(ast::MetaItem { unsafety, path, kind, span })
460 }
461
462 pub(crate) fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
463 Ok(if self.eat(exp!(Eq)) {
464 ast::MetaItemKind::NameValue(self.parse_unsuffixed_meta_item_lit()?)
465 } else if self.check(exp!(OpenParen)) {
466 let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?;
467 ast::MetaItemKind::List(list)
468 } else {
469 ast::MetaItemKind::Word
470 })
471 }
472
473 pub fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::MetaItemInner> {
479 match self.parse_unsuffixed_meta_item_lit() {
480 Ok(lit) => return Ok(ast::MetaItemInner::Lit(lit)),
481 Err(err) => err.cancel(), }
483
484 match self.parse_meta_item(AllowLeadingUnsafe::No) {
485 Ok(mi) => return Ok(ast::MetaItemInner::MetaItem(mi)),
486 Err(err) => err.cancel(), }
488
489 let mut err = errors::InvalidMetaItem {
490 span: self.token.span,
491 descr: super::token_descr(&self.token),
492 quote_ident_sugg: None,
493 };
494
495 if self.prev_token == token::Eq
499 && let token::Ident(..) = self.token.kind
500 {
501 let before = self.token.span.shrink_to_lo();
502 while let token::Ident(..) = self.token.kind {
503 self.bump();
504 }
505 err.quote_ident_sugg = Some(errors::InvalidMetaItemQuoteIdentSugg {
506 before,
507 after: self.prev_token.span.shrink_to_hi(),
508 });
509 }
510
511 Err(self.dcx().create_err(err))
512 }
513}