1use std::convert::identity;
2
3use rustc_ast as ast;
4use rustc_ast::token::DocFragmentKind;
5use rustc_ast::{AttrItemKind, AttrStyle, NodeId, Safety};
6use rustc_errors::DiagCtxtHandle;
7use rustc_feature::{AttributeTemplate, Features};
8use rustc_hir::attrs::AttributeKind;
9use rustc_hir::lints::AttributeLintKind;
10use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
11use rustc_session::Session;
12use rustc_session::lint::{BuiltinLintDiag, LintId};
13use rustc_span::{DUMMY_SP, Span, Symbol, sym};
14
15use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
16use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
17use crate::parser::{ArgParser, PathParser, RefPathParser};
18use crate::session_diagnostics::ParsedDescription;
19use crate::{Early, Late, OmitDoc, ShouldEmit};
20
21pub struct AttributeParser<'sess, S: Stage = Late> {
24 pub(crate) tools: Vec<Symbol>,
25 pub(crate) features: Option<&'sess Features>,
26 pub(crate) sess: &'sess Session,
27 pub(crate) stage: S,
28
29 parse_only: Option<Symbol>,
33}
34
35impl<'sess> AttributeParser<'sess, Early> {
36 pub fn parse_limited(
51 sess: &'sess Session,
52 attrs: &[ast::Attribute],
53 sym: Symbol,
54 target_span: Span,
55 target_node_id: NodeId,
56 features: Option<&'sess Features>,
57 ) -> Option<Attribute> {
58 Self::parse_limited_should_emit(
59 sess,
60 attrs,
61 sym,
62 target_span,
63 target_node_id,
64 features,
65 ShouldEmit::Nothing,
66 )
67 }
68
69 pub fn parse_limited_should_emit(
72 sess: &'sess Session,
73 attrs: &[ast::Attribute],
74 sym: Symbol,
75 target_span: Span,
76 target_node_id: NodeId,
77 features: Option<&'sess Features>,
78 should_emit: ShouldEmit,
79 ) -> Option<Attribute> {
80 let mut parsed = Self::parse_limited_all(
81 sess,
82 attrs,
83 Some(sym),
84 Target::Crate, target_span,
86 target_node_id,
87 features,
88 should_emit,
89 );
90 if !(parsed.len() <= 1) {
::core::panicking::panic("assertion failed: parsed.len() <= 1")
};assert!(parsed.len() <= 1);
91 parsed.pop()
92 }
93
94 pub fn parse_limited_all(
102 sess: &'sess Session,
103 attrs: &[ast::Attribute],
104 parse_only: Option<Symbol>,
105 target: Target,
106 target_span: Span,
107 target_node_id: NodeId,
108 features: Option<&'sess Features>,
109 emit_errors: ShouldEmit,
110 ) -> Vec<Attribute> {
111 let mut p =
112 Self { features, tools: Vec::new(), parse_only, sess, stage: Early { emit_errors } };
113 p.parse_attribute_list(
114 attrs,
115 target_span,
116 target,
117 OmitDoc::Skip,
118 std::convert::identity,
119 |lint_id, span, kind| {
120 sess.psess.buffer_lint(
121 lint_id.lint,
122 span,
123 target_node_id,
124 BuiltinLintDiag::AttributeLint(kind),
125 )
126 },
127 )
128 }
129
130 pub fn parse_single<T>(
133 sess: &'sess Session,
134 attr: &ast::Attribute,
135 target_span: Span,
136 target_node_id: NodeId,
137 target: Target,
138 features: Option<&'sess Features>,
139 emit_errors: ShouldEmit,
140 parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser) -> Option<T>,
141 template: &AttributeTemplate,
142 ) -> Option<T> {
143 let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
144 {
::core::panicking::panic_fmt(format_args!("parse_single called on a doc attr"));
}panic!("parse_single called on a doc attr")
145 };
146 let parts =
147 normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
148
149 let path = AttrPath::from_ast(&normal_attr.item.path, identity);
150 let args = ArgParser::from_attr_args(
151 &normal_attr.item.args.unparsed_ref().unwrap(),
152 &parts,
153 &sess.psess,
154 emit_errors,
155 )?;
156 Self::parse_single_args(
157 sess,
158 attr.span,
159 normal_attr.item.span(),
160 attr.style,
161 path,
162 Some(normal_attr.item.unsafety),
163 ParsedDescription::Attribute,
164 target_span,
165 target_node_id,
166 target,
167 features,
168 emit_errors,
169 &args,
170 parse_fn,
171 template,
172 )
173 }
174
175 pub fn parse_single_args<T, I>(
178 sess: &'sess Session,
179 attr_span: Span,
180 inner_span: Span,
181 attr_style: AttrStyle,
182 attr_path: AttrPath,
183 attr_safety: Option<Safety>,
184 parsed_description: ParsedDescription,
185 target_span: Span,
186 target_node_id: NodeId,
187 target: Target,
188 features: Option<&'sess Features>,
189 emit_errors: ShouldEmit,
190 args: &I,
191 parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &I) -> T,
192 template: &AttributeTemplate,
193 ) -> T {
194 let mut parser = Self {
195 features,
196 tools: Vec::new(),
197 parse_only: None,
198 sess,
199 stage: Early { emit_errors },
200 };
201 let mut emit_lint = |lint_id: LintId, span: Span, kind: AttributeLintKind| {
202 sess.psess.buffer_lint(
203 lint_id.lint,
204 span,
205 target_node_id,
206 BuiltinLintDiag::AttributeLint(kind),
207 )
208 };
209 if let Some(safety) = attr_safety {
210 parser.check_attribute_safety(&attr_path, inner_span, safety, &mut emit_lint)
211 }
212 let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
213 shared: SharedContext {
214 cx: &mut parser,
215 target_span,
216 target,
217 emit_lint: &mut emit_lint,
218 },
219 attr_span,
220 inner_span,
221 attr_style,
222 parsed_description,
223 template,
224 attr_path,
225 };
226 parse_fn(&mut cx, args)
227 }
228}
229
230impl<'sess, S: Stage> AttributeParser<'sess, S> {
231 pub fn new(
232 sess: &'sess Session,
233 features: &'sess Features,
234 tools: Vec<Symbol>,
235 stage: S,
236 ) -> Self {
237 Self { features: Some(features), tools, parse_only: None, sess, stage }
238 }
239
240 pub(crate) fn sess(&self) -> &'sess Session {
241 &self.sess
242 }
243
244 pub(crate) fn features(&self) -> &'sess Features {
245 self.features.expect("features not available at this point in the compiler")
246 }
247
248 pub(crate) fn features_option(&self) -> Option<&'sess Features> {
249 self.features
250 }
251
252 pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
253 self.sess().dcx()
254 }
255
256 pub fn parse_attribute_list(
261 &mut self,
262 attrs: &[ast::Attribute],
263 target_span: Span,
264 target: Target,
265 omit_doc: OmitDoc,
266 lower_span: impl Copy + Fn(Span) -> Span,
267 mut emit_lint: impl FnMut(LintId, Span, AttributeLintKind),
268 ) -> Vec<Attribute> {
269 let mut attributes = Vec::new();
270 let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
271 let mut early_parsed_state = EarlyParsedState::default();
272
273 for attr in attrs {
274 if let Some(expected) = self.parse_only {
276 if !attr.has_name(expected) {
277 continue;
278 }
279 }
280
281 let is_doc_attribute = attr.has_name(sym::doc);
287 if omit_doc == OmitDoc::Skip && is_doc_attribute {
288 continue;
289 }
290
291 let attr_span = lower_span(attr.span);
292 match &attr.kind {
293 ast::AttrKind::DocComment(comment_kind, symbol) => {
294 if omit_doc == OmitDoc::Skip {
295 continue;
296 }
297
298 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
299 style: attr.style,
300 kind: DocFragmentKind::Sugared(*comment_kind),
301 span: attr_span,
302 comment: *symbol,
303 }))
304 }
305 ast::AttrKind::Normal(n) => {
306 attr_paths.push(PathParser(&n.item.path));
307 let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
308
309 let args = match &n.item.args {
310 AttrItemKind::Unparsed(args) => args,
311 AttrItemKind::Parsed(parsed) => {
312 early_parsed_state
313 .accept_early_parsed_attribute(attr_span, lower_span, parsed);
314 continue;
315 }
316 };
317
318 self.check_attribute_safety(
319 &attr_path,
320 lower_span(n.item.span()),
321 n.item.unsafety,
322 &mut emit_lint,
323 );
324
325 let parts =
326 n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
327
328 if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
329 let Some(args) = ArgParser::from_attr_args(
330 args,
331 &parts,
332 &self.sess.psess,
333 self.stage.should_emit(),
334 ) else {
335 continue;
336 };
337
338 if is_doc_attribute
354 && let ArgParser::NameValue(nv) = &args
355 && let Some(comment) = nv.value_as_str()
359 {
360 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
361 style: attr.style,
362 kind: DocFragmentKind::Raw(nv.value_span),
363 span: attr_span,
364 comment,
365 }));
366 continue;
367 }
368
369 for accept in accepts {
370 let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
371 shared: SharedContext {
372 cx: self,
373 target_span,
374 target,
375 emit_lint: &mut emit_lint,
376 },
377 attr_span,
378 inner_span: lower_span(n.item.span()),
379 attr_style: attr.style,
380 parsed_description: ParsedDescription::Attribute,
381 template: &accept.template,
382 attr_path: attr_path.clone(),
383 };
384
385 (accept.accept_fn)(&mut cx, &args);
386 if !#[allow(non_exhaustive_omitted_patterns)] match cx.stage.should_emit() {
ShouldEmit::Nothing => true,
_ => false,
}matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
387 Self::check_target(&accept.allowed_targets, target, &mut cx);
388 }
389 }
390 } else {
391 attributes.push(Attribute::Unparsed(Box::new(AttrItem {
407 path: attr_path.clone(),
408 args: self
409 .lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span),
410 id: HashIgnoredAttrId { attr_id: attr.id },
411 style: attr.style,
412 span: attr_span,
413 })));
414 }
415 }
416 }
417 }
418
419 early_parsed_state.finalize_early_parsed_attributes(&mut attributes);
420 for f in &S::parsers().finalizers {
421 if let Some(attr) = f(&mut FinalizeContext {
422 shared: SharedContext { cx: self, target_span, target, emit_lint: &mut emit_lint },
423 all_attrs: &attr_paths,
424 }) {
425 attributes.push(Attribute::Parsed(attr));
426 }
427 }
428
429 attributes
430 }
431
432 pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
434 const SPECIAL_ATTRIBUTES: &[&[Symbol]] = &[
437 &[sym::cfg],
439 &[sym::cfg_attr],
440 ];
441
442 Late::parsers().accepters.contains_key(path)
443 || EARLY_PARSED_ATTRIBUTES.contains(&path)
444 || SPECIAL_ATTRIBUTES.contains(&path)
445 }
446
447 fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
448 match args {
449 ast::AttrArgs::Empty => AttrArgs::Empty,
450 ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
451 ast::AttrArgs::Eq { eq_span, expr } => {
455 let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
458 && let Ok(lit) =
459 ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
460 {
461 lit
462 } else {
463 let guar = self.dcx().span_delayed_bug(
464 args.span().unwrap_or(DUMMY_SP),
465 "expr in place where literal is expected (builtin attr parsing)",
466 );
467 ast::MetaItemLit {
468 symbol: sym::dummy,
469 suffix: None,
470 kind: ast::LitKind::Err(guar),
471 span: DUMMY_SP,
472 }
473 };
474 AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
475 }
476 }
477 }
478}