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, MultiSpan};
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::LintId;
13use rustc_span::{DUMMY_SP, Span, Symbol, sym};
14
15use crate::context::{AcceptContext, FinalizeContext, FinalizeFn, SharedContext, Stage};
16use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
17use crate::parser::{AllowExprMetavar, 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<&'static [Symbol]>,
33}
34
35impl<'sess> AttributeParser<'sess, Early> {
36 pub fn parse_limited(
51 sess: &'sess Session,
52 attrs: &[ast::Attribute],
53 sym: &'static [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 Target::Crate, features,
66 ShouldEmit::Nothing,
67 )
68 }
69
70 pub fn parse_limited_should_emit(
73 sess: &'sess Session,
74 attrs: &[ast::Attribute],
75 sym: &'static [Symbol],
76 target_span: Span,
77 target_node_id: NodeId,
78 target: Target,
79 features: Option<&'sess Features>,
80 should_emit: ShouldEmit,
81 ) -> Option<Attribute> {
82 let mut parsed = Self::parse_limited_all(
83 sess,
84 attrs,
85 Some(sym),
86 target,
87 target_span,
88 target_node_id,
89 features,
90 should_emit,
91 );
92 if !(parsed.len() <= 1) {
::core::panicking::panic("assertion failed: parsed.len() <= 1")
};assert!(parsed.len() <= 1);
93 parsed.pop()
94 }
95
96 pub fn parse_limited_all(
104 sess: &'sess Session,
105 attrs: &[ast::Attribute],
106 parse_only: Option<&'static [Symbol]>,
107 target: Target,
108 target_span: Span,
109 target_node_id: NodeId,
110 features: Option<&'sess Features>,
111 emit_errors: ShouldEmit,
112 ) -> Vec<Attribute> {
113 let mut p =
114 Self { features, tools: Vec::new(), parse_only, sess, stage: Early { emit_errors } };
115 p.parse_attribute_list(
116 attrs,
117 target_span,
118 target,
119 OmitDoc::Skip,
120 std::convert::identity,
121 |lint_id, span, kind| sess.psess.buffer_lint(lint_id.lint, span, target_node_id, kind),
122 )
123 }
124
125 pub fn parse_single<T>(
128 sess: &'sess Session,
129 attr: &ast::Attribute,
130 target_span: Span,
131 target_node_id: NodeId,
132 target: Target,
133 features: Option<&'sess Features>,
134 emit_errors: ShouldEmit,
135 parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser) -> Option<T>,
136 template: &AttributeTemplate,
137 allow_expr_metavar: AllowExprMetavar,
138 ) -> Option<T> {
139 let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
140 {
::core::panicking::panic_fmt(format_args!("parse_single called on a doc attr"));
}panic!("parse_single called on a doc attr")
141 };
142 let parts =
143 normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
144
145 let path = AttrPath::from_ast(&normal_attr.item.path, identity);
146 let args = ArgParser::from_attr_args(
147 &normal_attr.item.args.unparsed_ref().unwrap(),
148 &parts,
149 &sess.psess,
150 emit_errors,
151 allow_expr_metavar,
152 )?;
153 Self::parse_single_args(
154 sess,
155 attr.span,
156 normal_attr.item.span(),
157 attr.style,
158 path,
159 Some(normal_attr.item.unsafety),
160 ParsedDescription::Attribute,
161 target_span,
162 target_node_id,
163 target,
164 features,
165 emit_errors,
166 &args,
167 parse_fn,
168 template,
169 )
170 }
171
172 pub fn parse_single_args<T, I>(
175 sess: &'sess Session,
176 attr_span: Span,
177 inner_span: Span,
178 attr_style: AttrStyle,
179 attr_path: AttrPath,
180 attr_safety: Option<Safety>,
181 parsed_description: ParsedDescription,
182 target_span: Span,
183 target_node_id: NodeId,
184 target: Target,
185 features: Option<&'sess Features>,
186 emit_errors: ShouldEmit,
187 args: &I,
188 parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &I) -> T,
189 template: &AttributeTemplate,
190 ) -> T {
191 let mut parser = Self {
192 features,
193 tools: Vec::new(),
194 parse_only: None,
195 sess,
196 stage: Early { emit_errors },
197 };
198 let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: AttributeLintKind| {
199 sess.psess.buffer_lint(lint_id.lint, span, target_node_id, kind)
200 };
201 if let Some(safety) = attr_safety {
202 parser.check_attribute_safety(&attr_path, inner_span, safety, &mut emit_lint)
203 }
204 let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
205 shared: SharedContext {
206 cx: &mut parser,
207 target_span,
208 target,
209 emit_lint: &mut emit_lint,
210 },
211 attr_span,
212 inner_span,
213 attr_style,
214 parsed_description,
215 template,
216 attr_path,
217 };
218 parse_fn(&mut cx, args)
219 }
220}
221
222impl<'sess, S: Stage> AttributeParser<'sess, S> {
223 pub fn new(
224 sess: &'sess Session,
225 features: &'sess Features,
226 tools: Vec<Symbol>,
227 stage: S,
228 ) -> Self {
229 Self { features: Some(features), tools, parse_only: None, sess, stage }
230 }
231
232 pub(crate) fn sess(&self) -> &'sess Session {
233 &self.sess
234 }
235
236 pub(crate) fn features(&self) -> &'sess Features {
237 self.features.expect("features not available at this point in the compiler")
238 }
239
240 pub(crate) fn features_option(&self) -> Option<&'sess Features> {
241 self.features
242 }
243
244 pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
245 self.sess().dcx()
246 }
247
248 pub fn parse_attribute_list(
253 &mut self,
254 attrs: &[ast::Attribute],
255 target_span: Span,
256 target: Target,
257 omit_doc: OmitDoc,
258 lower_span: impl Copy + Fn(Span) -> Span,
259 mut emit_lint: impl FnMut(LintId, MultiSpan, AttributeLintKind),
260 ) -> Vec<Attribute> {
261 let mut attributes = Vec::new();
262 let mut dropped_attributes = Vec::new();
267 let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
268 let mut early_parsed_state = EarlyParsedState::default();
269
270 let mut finalizers: Vec<&FinalizeFn<S>> = Vec::with_capacity(attrs.len());
271
272 for attr in attrs {
273 if let Some(expected) = self.parse_only {
275 if !attr.path_matches(expected) {
276 continue;
277 }
278 }
279
280 let is_doc_attribute = attr.has_name(sym::doc);
286 if omit_doc == OmitDoc::Skip && is_doc_attribute {
287 continue;
288 }
289
290 let attr_span = lower_span(attr.span);
291 match &attr.kind {
292 ast::AttrKind::DocComment(comment_kind, symbol) => {
293 if omit_doc == OmitDoc::Skip {
294 continue;
295 }
296
297 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
298 style: attr.style,
299 kind: DocFragmentKind::Sugared(*comment_kind),
300 span: attr_span,
301 comment: *symbol,
302 }));
303 }
304 ast::AttrKind::Normal(n) => {
305 attr_paths.push(PathParser(&n.item.path));
306 let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
307
308 let args = match &n.item.args {
309 AttrItemKind::Unparsed(args) => args,
310 AttrItemKind::Parsed(parsed) => {
311 early_parsed_state
312 .accept_early_parsed_attribute(attr_span, lower_span, parsed);
313 continue;
314 }
315 };
316
317 self.check_attribute_safety(
318 &attr_path,
319 lower_span(n.item.span()),
320 n.item.unsafety,
321 &mut emit_lint,
322 );
323
324 let parts =
325 n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
326
327 if let Some(accept) = S::parsers().accepters.get(parts.as_slice()) {
328 let Some(args) = ArgParser::from_attr_args(
329 args,
330 &parts,
331 &self.sess.psess,
332 self.stage.should_emit(),
333 AllowExprMetavar::No,
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 let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
370 shared: SharedContext {
371 cx: self,
372 target_span,
373 target,
374 emit_lint: &mut emit_lint,
375 },
376 attr_span,
377 inner_span: lower_span(n.item.span()),
378 attr_style: attr.style,
379 parsed_description: ParsedDescription::Attribute,
380 template: &accept.template,
381 attr_path: attr_path.clone(),
382 };
383
384 (accept.accept_fn)(&mut cx, &args);
385 finalizers.push(&accept.finalizer);
386
387 if !#[allow(non_exhaustive_omitted_patterns)] match cx.stage.should_emit() {
ShouldEmit::Nothing => true,
_ => false,
}matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
388 Self::check_target(&accept.allowed_targets, target, &mut cx);
389 }
390 } else {
391 let attr = AttrItem {
392 path: attr_path.clone(),
393 args: self
394 .lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span),
395 id: HashIgnoredAttrId { attr_id: attr.id },
396 style: attr.style,
397 span: attr_span,
398 };
399
400 if !#[allow(non_exhaustive_omitted_patterns)] match self.stage.should_emit() {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.stage.should_emit(), ShouldEmit::Nothing)
401 && target == Target::Crate
402 {
403 self.check_invalid_crate_level_attr_item(&attr, n.item.span());
404 }
405
406 let attr = Attribute::Unparsed(Box::new(attr));
407
408 if self.tools.contains(&parts[0])
409 || [sym::allow, sym::deny, sym::expect, sym::forbid, sym::warn]
412 .contains(&parts[0])
413 {
414 attributes.push(attr);
415 } else {
416 dropped_attributes.push(attr);
417 }
418 }
419 }
420 }
421 }
422
423 early_parsed_state.finalize_early_parsed_attributes(&mut attributes);
424 for f in &finalizers {
425 if let Some(attr) = f(&mut FinalizeContext {
426 shared: SharedContext { cx: self, target_span, target, emit_lint: &mut emit_lint },
427 all_attrs: &attr_paths,
428 }) {
429 attributes.push(Attribute::Parsed(attr));
430 }
431 }
432
433 if !#[allow(non_exhaustive_omitted_patterns)] match self.stage.should_emit() {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.stage.should_emit(), ShouldEmit::Nothing)
434 && target == Target::WherePredicate
435 {
436 self.check_invalid_where_predicate_attrs(attributes.iter().chain(&dropped_attributes));
437 }
438
439 attributes
440 }
441
442 pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
444 const SPECIAL_ATTRIBUTES: &[&[Symbol]] = &[
447 &[sym::cfg],
449 &[sym::cfg_attr],
450 ];
451
452 Late::parsers().accepters.contains_key(path)
453 || EARLY_PARSED_ATTRIBUTES.contains(&path)
454 || SPECIAL_ATTRIBUTES.contains(&path)
455 }
456
457 fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
458 match args {
459 ast::AttrArgs::Empty => AttrArgs::Empty,
460 ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
461 ast::AttrArgs::Eq { eq_span, expr } => {
465 let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
468 && let Ok(lit) =
469 ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
470 {
471 lit
472 } else {
473 let guar = self.dcx().span_delayed_bug(
474 args.span().unwrap_or(DUMMY_SP),
475 "expr in place where literal is expected (builtin attr parsing)",
476 );
477 ast::MetaItemLit {
478 symbol: sym::dummy,
479 suffix: None,
480 kind: ast::LitKind::Err(guar),
481 span: DUMMY_SP,
482 }
483 };
484 AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
485 }
486 }
487 }
488}