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