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