1use std::convert::identity;
2
3use rustc_ast as ast;
4use rustc_ast::token::DocFragmentKind;
5use rustc_ast::{AttrItemKind, AttrStyle, CRATE_NODE_ID, NodeId, Safety};
6use rustc_data_structures::sync::{DynSend, DynSync};
7use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan};
8use rustc_feature::{AttributeTemplate, Features};
9use rustc_hir::attrs::AttributeKind;
10use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
11use rustc_lint_defs::RegisteredTools;
12use rustc_session::Session;
13use rustc_session::lint::LintId;
14use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
15
16use crate::attributes::AttributeSafety;
17use crate::context::{
18 ATTRIBUTE_PARSERS, AcceptContext, FinalizeContext, FinalizeFn, SharedContext,
19};
20use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
21use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser};
22use crate::session_diagnostics::ParsedDescription;
23use crate::{OmitDoc, ShouldEmit};
24
25pub struct EmitAttribute(
26 pub Box<
27 dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()>
28 + DynSend
29 + DynSync
30 + 'static,
31 >,
32);
33
34pub struct AttributeParser<'sess> {
37 pub(crate) tools: Option<&'sess RegisteredTools>,
38 pub(crate) features: Option<&'sess Features>,
39 pub(crate) sess: &'sess Session,
40 pub(crate) should_emit: ShouldEmit,
41
42 parse_only: Option<&'static [Symbol]>,
46}
47
48impl<'sess> AttributeParser<'sess> {
49 pub fn parse_limited(
66 sess: &'sess Session,
67 attrs: &[ast::Attribute],
68 sym: &'static [Symbol],
69 ) -> Option<Attribute> {
70 Self::parse_limited_should_emit(
71 sess,
72 attrs,
73 sym,
74 DUMMY_SP,
76 CRATE_NODE_ID,
77 Target::Crate,
78 None,
79 ShouldEmit::Nothing,
80 )
81 }
82
83 pub fn parse_limited_should_emit(
88 sess: &'sess Session,
89 attrs: &[ast::Attribute],
90 sym: &'static [Symbol],
91 target_span: Span,
92 target_node_id: NodeId,
93 target: Target,
94 features: Option<&'sess Features>,
95 should_emit: ShouldEmit,
96 ) -> Option<Attribute> {
97 let mut parsed = Self::parse_limited_all(
98 sess,
99 attrs,
100 Some(sym),
101 target,
102 target_span,
103 target_node_id,
104 features,
105 should_emit,
106 None,
107 );
108 if !(parsed.len() <= 1) {
::core::panicking::panic("assertion failed: parsed.len() <= 1")
};assert!(parsed.len() <= 1);
109 parsed.pop()
110 }
111
112 pub fn parse_limited_all(
120 sess: &'sess Session,
121 attrs: &[ast::Attribute],
122 parse_only: Option<&'static [Symbol]>,
123 target: Target,
124 target_span: Span,
125 target_node_id: NodeId,
126 features: Option<&'sess Features>,
127 should_emit: ShouldEmit,
128 tools: Option<&'sess RegisteredTools>,
129 ) -> Vec<Attribute> {
130 let mut p = Self { features, tools, parse_only, sess, should_emit };
131 p.parse_attribute_list(
132 attrs,
133 target_span,
134 target,
135 OmitDoc::Skip,
136 std::convert::identity,
137 |lint_id, span, kind| {
138 sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0)
139 },
140 )
141 }
142
143 pub fn parse_single<T>(
146 sess: &'sess Session,
147 attr: &ast::Attribute,
148 target_span: Span,
149 target_node_id: NodeId,
150 target: Target,
151 features: Option<&'sess Features>,
152 emit_errors: ShouldEmit,
153 parse_fn: fn(cx: &mut AcceptContext<'_, '_>, item: &ArgParser) -> Option<T>,
154 template: &AttributeTemplate,
155 allow_expr_metavar: AllowExprMetavar,
156 expected_safety: AttributeSafety,
157 ) -> Option<T> {
158 let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
159 {
::core::panicking::panic_fmt(format_args!("parse_single called on a doc attr"));
}panic!("parse_single called on a doc attr")
160 };
161 let parts =
162 normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
163
164 let path = AttrPath::from_ast(&normal_attr.item.path, identity);
165 let args = ArgParser::from_attr_args(
166 &normal_attr.item.args.unparsed_ref().unwrap(),
167 &parts,
168 &sess.psess,
169 emit_errors,
170 allow_expr_metavar,
171 )?;
172 Self::parse_single_args(
173 sess,
174 attr.span,
175 normal_attr.item.span(),
176 attr.style,
177 path,
178 Some(normal_attr.item.unsafety),
179 expected_safety,
180 ParsedDescription::Attribute,
181 target_span,
182 target_node_id,
183 target,
184 features,
185 emit_errors,
186 &args,
187 parse_fn,
188 template,
189 )
190 }
191
192 pub fn parse_single_args<T, I>(
195 sess: &'sess Session,
196 attr_span: Span,
197 inner_span: Span,
198 attr_style: AttrStyle,
199 attr_path: AttrPath,
200 attr_safety: Option<Safety>,
201 expected_safety: AttributeSafety,
202 parsed_description: ParsedDescription,
203 target_span: Span,
204 target_node_id: NodeId,
205 target: Target,
206 features: Option<&'sess Features>,
207 should_emit: ShouldEmit,
208 args: &I,
209 parse_fn: fn(cx: &mut AcceptContext<'_, '_>, item: &I) -> T,
210 template: &AttributeTemplate,
211 ) -> T {
212 let mut parser = Self { features, tools: None, parse_only: None, sess, should_emit };
213 let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: EmitAttribute| {
214 sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0)
215 };
216 if let Some(safety) = attr_safety {
217 parser.check_attribute_safety(
218 &attr_path,
219 inner_span,
220 safety,
221 expected_safety,
222 &mut emit_lint,
223 );
224 }
225 let mut cx: AcceptContext<'_, 'sess> = AcceptContext {
226 shared: SharedContext {
227 cx: &mut parser,
228 target_span,
229 target,
230 emit_lint: &mut emit_lint,
231 },
232 attr_span,
233 inner_span,
234 attr_style,
235 parsed_description,
236 template,
237 attr_path,
238 };
239 parse_fn(&mut cx, args)
240 }
241}
242
243impl<'sess> AttributeParser<'sess> {
244 pub fn new(
245 sess: &'sess Session,
246 features: &'sess Features,
247 tools: &'sess RegisteredTools,
248 should_emit: ShouldEmit,
249 ) -> Self {
250 Self { features: Some(features), tools: Some(tools), parse_only: None, sess, should_emit }
251 }
252
253 pub(crate) fn sess(&self) -> &'sess Session {
254 &self.sess
255 }
256
257 pub(crate) fn features(&self) -> &'sess Features {
258 self.features.expect("features not available at this point in the compiler")
259 }
260
261 pub(crate) fn features_option(&self) -> Option<&'sess Features> {
262 self.features
263 }
264
265 pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
266 self.sess().dcx()
267 }
268
269 pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
270 self.should_emit.emit_err(self.sess.dcx().create_err(diag))
271 }
272
273 pub fn parse_attribute_list(
278 &mut self,
279 attrs: &[ast::Attribute],
280 target_span: Span,
281 target: Target,
282 omit_doc: OmitDoc,
283 lower_span: impl Copy + Fn(Span) -> Span,
284 mut emit_lint: impl FnMut(LintId, MultiSpan, EmitAttribute),
285 ) -> Vec<Attribute> {
286 let mut attributes = Vec::new();
287 let mut dropped_attributes = Vec::new();
292 let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
293 let mut early_parsed_state = EarlyParsedState::default();
294
295 let mut finalizers: Vec<FinalizeFn> = Vec::with_capacity(attrs.len());
296
297 for attr in attrs {
298 if let Some(expected) = self.parse_only {
300 if !attr.path_matches(expected) {
301 continue;
302 }
303 }
304
305 let is_doc_attribute = attr.has_name(sym::doc);
311 if omit_doc == OmitDoc::Skip && is_doc_attribute {
312 continue;
313 }
314
315 let attr_span = lower_span(attr.span);
316 match &attr.kind {
317 ast::AttrKind::DocComment(comment_kind, symbol) => {
318 if omit_doc == OmitDoc::Skip {
319 continue;
320 }
321
322 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
323 style: attr.style,
324 kind: DocFragmentKind::Sugared(*comment_kind),
325 span: attr_span,
326 comment: *symbol,
327 }));
328 }
329 ast::AttrKind::Normal(n) => {
330 attr_paths.push(PathParser(&n.item.path));
331 let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
332
333 let args = match &n.item.args {
334 AttrItemKind::Unparsed(args) => args,
335 AttrItemKind::Parsed(parsed) => {
336 early_parsed_state
337 .accept_early_parsed_attribute(attr_span, lower_span, parsed);
338 continue;
339 }
340 };
341
342 let parts =
343 n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
344
345 if let Some(accept) = ATTRIBUTE_PARSERS.accepters.get(parts.as_slice()) {
346 self.check_attribute_safety(
347 &attr_path,
348 lower_span(n.item.span()),
349 n.item.unsafety,
350 accept.safety,
351 &mut emit_lint,
352 );
353
354 let Some(args) = ArgParser::from_attr_args(
355 args,
356 &parts,
357 &self.sess.psess,
358 self.should_emit,
359 AllowExprMetavar::No,
360 ) else {
361 continue;
362 };
363
364 if is_doc_attribute
380 && let ArgParser::NameValue(nv) = &args
381 && let Some(comment) = nv.value_as_str()
385 {
386 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
387 style: attr.style,
388 kind: DocFragmentKind::Raw(nv.value_span),
389 span: attr_span,
390 comment,
391 }));
392 continue;
393 }
394
395 let mut cx: AcceptContext<'_, 'sess> = AcceptContext {
396 shared: SharedContext {
397 cx: self,
398 target_span,
399 target,
400 emit_lint: &mut emit_lint,
401 },
402 attr_span,
403 inner_span: lower_span(n.item.span()),
404 attr_style: attr.style,
405 parsed_description: ParsedDescription::Attribute,
406 template: &accept.template,
407 attr_path: attr_path.clone(),
408 };
409
410 (accept.accept_fn)(&mut cx, &args);
411 finalizers.push(accept.finalizer);
412
413 if !#[allow(non_exhaustive_omitted_patterns)] match cx.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(cx.should_emit, ShouldEmit::Nothing) {
414 Self::check_target(&accept.allowed_targets, target, &mut cx);
415 }
416 } else {
417 let attr = AttrItem {
418 path: attr_path.clone(),
419 args: self
420 .lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span),
421 id: HashIgnoredAttrId { attr_id: attr.id },
422 style: attr.style,
423 span: attr_span,
424 };
425
426 self.check_attribute_safety(
427 &attr_path,
428 lower_span(n.item.span()),
429 n.item.unsafety,
430 AttributeSafety::Normal,
431 &mut emit_lint,
432 );
433
434 if !#[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.should_emit, ShouldEmit::Nothing)
435 && target == Target::Crate
436 {
437 self.check_invalid_crate_level_attr_item(&attr, n.item.span());
438 }
439
440 let attr = Attribute::Unparsed(Box::new(attr));
441
442 if self.tools.is_some_and(|tools| {
443 tools.iter().any(|tool| tool.name == parts[0])
444 || [sym::allow, sym::deny, sym::expect, sym::forbid, sym::warn]
447 .contains(&parts[0])
448 }) {
449 attributes.push(attr);
450 } else {
451 dropped_attributes.push(attr);
452 }
453 }
454 }
455 }
456 }
457
458 early_parsed_state.finalize_early_parsed_attributes(&mut attributes);
459 for f in &finalizers {
460 if let Some(attr) = f(&mut FinalizeContext {
461 shared: SharedContext { cx: self, target_span, target, emit_lint: &mut emit_lint },
462 all_attrs: &attr_paths,
463 }) {
464 attributes.push(Attribute::Parsed(attr));
465 }
466 }
467
468 if !#[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.should_emit, ShouldEmit::Nothing) && target == Target::WherePredicate {
469 self.check_invalid_where_predicate_attrs(attributes.iter().chain(&dropped_attributes));
470 }
471
472 attributes
473 }
474
475 pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
477 const SPECIAL_ATTRIBUTES: &[&[Symbol]] = &[
480 &[sym::cfg],
482 &[sym::cfg_attr],
483 ];
484
485 ATTRIBUTE_PARSERS.accepters.contains_key(path)
486 || EARLY_PARSED_ATTRIBUTES.contains(&path)
487 || SPECIAL_ATTRIBUTES.contains(&path)
488 }
489
490 fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
491 match args {
492 ast::AttrArgs::Empty => AttrArgs::Empty,
493 ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
494 ast::AttrArgs::Eq { eq_span, expr } => {
498 let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
501 && let Ok(lit) =
502 ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
503 {
504 lit
505 } else {
506 let guar = self.dcx().span_delayed_bug(
507 args.span().unwrap_or(DUMMY_SP),
508 "expr in place where literal is expected (builtin attr parsing)",
509 );
510 ast::MetaItemLit {
511 symbol: sym::dummy,
512 suffix: None,
513 kind: ast::LitKind::Err(guar),
514 span: DUMMY_SP,
515 }
516 };
517 AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
518 }
519 }
520 }
521}