1use std::convert::identity;
3#[cfg(debug_assertions)]
4use std::sync::atomic::{AtomicBool, Ordering};
5
6use rustc_ast as ast;
7use rustc_ast::token::DocFragmentKind;
8use rustc_ast::{AttrStyle, CRATE_NODE_ID, NodeId, Safety};
9use rustc_attr_ir::target::{AstTarget, Target};
10use rustc_attr_ir::{AttrArgs, AttrItem, AttrPath, Attribute, AttributeKind, HashIgnoredAttrId};
11use rustc_data_structures::sync::{DynSend, DynSync};
12use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan};
13use rustc_feature::{BUILTIN_ATTRIBUTE_SET, Features};
14use rustc_lint_defs::{LintId, RegisteredTools};
15use rustc_session::Session;
16use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
17
18use crate::attributes::AttributeSafety;
19use crate::context::{
20 ATTRIBUTE_PARSERS, AcceptContext, FinalizeCheckContext, FinalizeCheckFn, FinalizeContext,
21 FinalizeFn, FinalizeOutput, SharedContext,
22};
23use crate::diagnostics::ParsedDescription;
24use crate::generated::GeneratedAttrState;
25use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser};
26use crate::synthetic::SyntheticAttrState;
27use crate::{AttributeTemplate, ShouldEmit, diagnostics};
28
29pub struct EmitAttribute(
30 pub Box<
31 dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a>
32 + DynSend
33 + DynSync
34 + 'static,
35 >,
36);
37
38pub struct AttributeParser<'sess> {
41 pub(crate) attr_tools: Option<&'sess RegisteredTools>,
42 pub(crate) features: Option<&'sess Features>,
43 pub(crate) sess: &'sess Session,
44 pub(crate) should_emit: ShouldEmit,
45
46 parse_filter: Option<&'sess dyn Fn(&ast::Attribute) -> bool>,
50}
51
52impl<'sess> AttributeParser<'sess> {
53 pub fn parse_limited(
70 sess: &'sess Session,
71 attrs: &[ast::Attribute],
72 parse_filter: &dyn Fn(&ast::Attribute) -> bool,
73 ) -> Option<Attribute> {
74 Self::parse_limited_should_emit(
75 sess,
76 attrs,
77 parse_filter,
78 DUMMY_SP,
80 None,
81 ShouldEmit::Nothing,
82 )
83 }
84
85 pub fn parse_limited_sym(
88 sess: &'sess Session,
89 attrs: &[ast::Attribute],
90 sym: &'static [Symbol],
91 ) -> Option<Attribute> {
92 Self::parse_limited(sess, attrs, &|attr| attr.path_matches(sym))
93 }
94
95 pub fn parse_limited_should_emit(
100 sess: &'sess Session,
101 attrs: &[ast::Attribute],
102 parse_filter: &dyn Fn(&ast::Attribute) -> bool,
103 target_span: Span,
104 features: Option<&'sess Features>,
105 should_emit: ShouldEmit,
106 ) -> Option<Attribute> {
107 let mut parsed = Self::parse_limited_all(
108 sess,
109 attrs,
110 Some(parse_filter),
111 AstTarget::None(Target::Crate),
112 target_span,
113 CRATE_NODE_ID,
114 features,
115 should_emit,
116 None,
117 );
118 if !(parsed.len() <= 1) {
::core::panicking::panic("assertion failed: parsed.len() <= 1")
};assert!(parsed.len() <= 1);
119 parsed.pop()
120 }
121
122 pub fn parse_limited_sym_should_emit(
125 sess: &'sess Session,
126 attrs: &[ast::Attribute],
127 sym: &'static [Symbol],
128 target_span: Span,
129 features: Option<&'sess Features>,
130 should_emit: ShouldEmit,
131 ) -> Option<Attribute> {
132 Self::parse_limited_should_emit(
133 sess,
134 attrs,
135 &|attr| attr.path_matches(sym),
136 target_span,
137 features,
138 should_emit,
139 )
140 }
141
142 pub fn parse_limited_all(
150 sess: &'sess Session,
151 attrs: &[ast::Attribute],
152 parse_filter: Option<&dyn Fn(&ast::Attribute) -> bool>,
153 ast_target: AstTarget<'_>,
154 target_span: Span,
155 target_node_id: NodeId,
156 features: Option<&'sess Features>,
157 should_emit: ShouldEmit,
158 attr_tools: Option<&'sess RegisteredTools>,
159 ) -> Vec<Attribute> {
160 let mut p = AttributeParser { features, attr_tools, parse_filter, sess, should_emit };
161 p.parse_attribute_list(
162 attrs,
163 target_span,
164 ast_target,
165 std::convert::identity,
166 |lint_id, span, kind| {
167 sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0)
168 },
169 )
170 }
171
172 pub fn parse_single<T>(
175 sess: &'sess Session,
176 attr: &ast::Attribute,
177 target_span: Span,
178 target_node_id: NodeId,
179 ast_target: AstTarget<'_>,
180 features: Option<&'sess Features>,
181 emit_errors: ShouldEmit,
182 parse_fn: fn(cx: &mut AcceptContext<'_, '_>, item: &ArgParser) -> Option<T>,
183 template: &AttributeTemplate,
184 allow_expr_metavar: AllowExprMetavar,
185 expected_safety: AttributeSafety,
186 ) -> Option<T> {
187 let attr_item = attr.get_normal_item();
188 let parts = attr_item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
189
190 let path = AttrPath::from_ast(&attr_item.path, identity);
191 let args = ArgParser::from_attr_args(
192 &attr_item.args,
193 &parts,
194 &sess.psess,
195 emit_errors,
196 allow_expr_metavar,
197 )?;
198 Self::parse_single_args(
199 sess,
200 attr.span,
201 attr_item.span,
202 attr.style,
203 path,
204 Some(attr_item.unsafety),
205 expected_safety,
206 ParsedDescription::Attribute,
207 target_span,
208 target_node_id,
209 ast_target,
210 features,
211 emit_errors,
212 &args,
213 parse_fn,
214 template,
215 )
216 }
217
218 pub fn parse_single_args<T, I>(
221 sess: &'sess Session,
222 attr_span: Span,
223 inner_span: Span,
224 attr_style: AttrStyle,
225 attr_path: AttrPath,
226 attr_safety: Option<Safety>,
227 expected_safety: AttributeSafety,
228 parsed_description: ParsedDescription,
229 target_span: Span,
230 target_node_id: NodeId,
231 ast_target: AstTarget<'_>,
232 features: Option<&'sess Features>,
233 should_emit: ShouldEmit,
234 args: &I,
235 parse_fn: fn(cx: &mut AcceptContext<'_, '_>, item: &I) -> T,
236 template: &AttributeTemplate,
237 ) -> T {
238 let mut parser = Self { features, attr_tools: None, parse_filter: None, sess, should_emit };
239 let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: EmitAttribute| {
240 sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0)
241 };
242 if let Some(safety) = attr_safety {
243 parser.check_attribute_safety(
244 &attr_path,
245 inner_span,
246 safety,
247 expected_safety,
248 &mut emit_lint,
249 );
250 }
251 let mut cx: AcceptContext<'_, 'sess> = AcceptContext {
252 shared: SharedContext {
253 cx: &mut parser,
254 target_span,
255 target: Target::from_ast_target(&ast_target),
256 emit_lint: &mut emit_lint,
257 #[cfg(debug_assertions)]
258 has_lint_been_emitted: AtomicBool::new(false),
259 ast_target,
260 },
261 attr_span,
262 inner_span,
263 attr_style,
264 parsed_description,
265 template,
266 attr_safety: attr_safety.unwrap_or(Safety::Default),
267 attr_path,
268 #[cfg(debug_assertions)]
269 has_target_been_checked: false,
270 attr_id: None,
271 };
272 parse_fn(&mut cx, args)
273 }
274}
275
276impl<'sess> AttributeParser<'sess> {
277 pub fn new(
278 sess: &'sess Session,
279 features: &'sess Features,
280 attr_tools: &'sess RegisteredTools,
281 should_emit: ShouldEmit,
282 ) -> Self {
283 Self {
284 features: Some(features),
285 attr_tools: Some(attr_tools),
286 parse_filter: None,
287 sess,
288 should_emit,
289 }
290 }
291
292 pub(crate) fn sess(&self) -> &'sess Session {
293 self.sess
294 }
295
296 #[track_caller]
297 pub(crate) fn features(&self) -> &'sess Features {
298 self.features.expect("features not available at this point in the compiler")
299 }
300
301 pub(crate) fn features_option(&self) -> Option<&'sess Features> {
302 self.features
303 }
304
305 pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
306 self.sess().dcx()
307 }
308
309 #[track_caller]
310 pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
311 self.should_emit.emit_err(self.sess.dcx().create_err(diag))
312 }
313
314 pub fn parse_attribute_list(
318 &mut self,
319 attrs: &[ast::Attribute],
320 target_span: Span,
321 ast_target: AstTarget<'_>,
322 lower_span: impl Copy + Fn(Span) -> Span,
323 mut emit_lint: impl FnMut(LintId, MultiSpan, EmitAttribute),
324 ) -> Vec<Attribute> {
325 let mut attributes = Vec::new();
326 let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
327 let mut synthetic_attr_state = SyntheticAttrState::default();
328 let mut generated_attr_state = GeneratedAttrState::default();
329
330 let mut finalizers: Vec<FinalizeFn> = Vec::with_capacity(attrs.len());
331
332 for attr in attrs {
333 if let Some(filter) = self.parse_filter {
335 if !filter(attr) {
336 continue;
337 }
338 }
339
340 let attr_span = lower_span(attr.span);
341 match &attr.kind {
342 ast::AttrKind::DocComment(comment_kind, symbol) => {
343 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
344 style: attr.style,
345 kind: DocFragmentKind::Sugared(*comment_kind),
346 span: attr_span,
347 comment: *symbol,
348 }));
349 }
350 ast::AttrKind::Synthetic(synthetic) => {
351 synthetic_attr_state.accept_synthetic_attr(attr_span, lower_span, synthetic);
352 }
353 ast::AttrKind::Generated(generated) => {
354 generated_attr_state.accept_generated_attr(attr_span, generated)
355 }
356 ast::AttrKind::Normal(n) => {
357 attr_paths.push(PathParser(&n.item.path));
358 let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
359 let parts =
360 n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
361 let inner_span = lower_span(n.item.span);
362
363 if let Some(accept) = ATTRIBUTE_PARSERS.accepters.get(parts.as_slice()) {
364 self.check_attribute_safety(
365 &attr_path,
366 inner_span,
367 n.item.unsafety,
368 accept.safety,
369 &mut emit_lint,
370 );
371 self.check_attribute_stability(&attr_path, attr_span, accept.stability);
372 if let [part] = parts.as_slice() {
373 if true {
if !BUILTIN_ATTRIBUTE_SET.contains(part) {
::core::panicking::panic("assertion failed: BUILTIN_ATTRIBUTE_SET.contains(part)")
};
};debug_assert!(BUILTIN_ATTRIBUTE_SET.contains(part));
374 }
375
376 let Some(args) = ArgParser::from_attr_args(
377 &n.item.args,
378 &parts,
379 &self.sess.psess,
380 self.should_emit,
381 AllowExprMetavar::No,
382 ) else {
383 continue;
384 };
385
386 if attr.has_name(sym::doc)
402 && let ArgParser::NameValue(nv) = &args
403 && let Some(comment) = nv.value_as_str()
407 {
408 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
409 style: attr.style,
410 kind: DocFragmentKind::Raw(nv.value_span),
411 span: attr_span,
412 comment,
413 }));
414 continue;
415 }
416
417 let mut cx: AcceptContext<'_, 'sess> = AcceptContext {
418 shared: SharedContext {
419 cx: self,
420 target_span,
421 target: Target::from_ast_target(&ast_target),
422 emit_lint: &mut emit_lint,
423 #[cfg(debug_assertions)]
424 has_lint_been_emitted: AtomicBool::new(false),
425 ast_target,
426 },
427 attr_span,
428 inner_span,
429 attr_style: attr.style,
430 parsed_description: ParsedDescription::Attribute,
431 template: &accept.template,
432 attr_safety: n.item.unsafety,
433 attr_path: attr_path.clone(),
434 #[cfg(debug_assertions)]
435 has_target_been_checked: false,
436 attr_id: Some(HashIgnoredAttrId { attr_id: attr.id }),
437 };
438
439 (accept.accept_fn)(&mut cx, &args);
440 finalizers.push(accept.finalizer);
441
442 Self::check_target(&accept.allowed_targets, "", &mut cx);
443 #[cfg(debug_assertions)]
444 if !cx.shared.has_lint_been_emitted.load(Ordering::Relaxed) {
445 cx.shared.cx.check_args_used(attr, &args)
446 }
447 } else if let [sym::diagnostic, _unknown, ..] = &*parts {
448 self.unknown_diagnostic_attr(&n.item.path.segments[1], &mut emit_lint);
449 } else if let [sym::lint, unknown, ..] = &*parts {
450 if let Some(tools) = self.attr_tools
454 && tools.iter().any(|i| i.name == sym::lint)
455 {
456 self.emit_err(diagnostics::UnknownLintHelper {
457 span: n.item.span,
458 unknown: *unknown,
459 });
460 } else {
461 self.dcx().span_delayed_bug(
462 n.item.span,
463 "encountered `lint` tool attribute but error not reported",
464 );
465 }
466 } else {
467 let attr = AttrItem {
468 path: attr_path.clone(),
469 args: self.lower_attr_args(&n.item.args, lower_span),
470 style: attr.style,
471 span: attr_span,
472 };
473
474 self.check_attribute_safety(
475 &attr_path,
476 inner_span,
477 n.item.unsafety,
478 AttributeSafety::Normal,
479 &mut emit_lint,
480 );
481
482 if !#[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.should_emit, ShouldEmit::Nothing)
483 && Target::from_ast_target(&ast_target) == Target::Crate
484 {
485 self.check_invalid_crate_level_attr_item(&attr, inner_span);
486 }
487
488 let attr = match attr_path.segments.len() {
489 0 => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("no path for {0:?}", attr)));
}unreachable!("no path for {attr:?}"),
490 1 => Attribute::DeriveHelper(Box::new(attr)),
491 _ => Attribute::Tool(Box::new(attr)),
492 };
493
494 attributes.push(attr);
495 };
496 }
497 }
498 }
499
500 synthetic_attr_state.finalize_synthetic_attrs(&mut attributes);
501 generated_attr_state.finalize_generated_attrs(&mut attributes);
502
503 let mut deferred_checks: Vec<(FinalizeCheckFn, Span)> = Vec::new();
508 for f in &finalizers {
509 let FinalizeOutput { attr, deferred_check } = f(&mut FinalizeContext {
510 shared: SharedContext {
511 cx: self,
512 target_span,
513 target: Target::from_ast_target(&ast_target),
514 ast_target,
515 emit_lint: &mut emit_lint,
516 #[cfg(debug_assertions)]
517 has_lint_been_emitted: AtomicBool::new(false),
518 },
519 all_attrs: &attr_paths,
520 });
521 if let Some(attr) = attr {
522 attributes.push(Attribute::Parsed(attr));
523 }
524 if let Some(deferred_check) = deferred_check {
525 deferred_checks.push(deferred_check);
526 }
527 }
528
529 if !#[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.should_emit, ShouldEmit::Nothing) {
532 for (check, attr_span) in deferred_checks {
533 check(
534 &mut FinalizeCheckContext {
535 shared: SharedContext {
536 cx: self,
537 target_span,
538 target: Target::from_ast_target(&ast_target),
539 ast_target,
540 emit_lint: &mut emit_lint,
541 #[cfg(debug_assertions)]
542 has_lint_been_emitted: AtomicBool::new(false),
543 },
544 all_attrs: &attr_paths,
545 parsed_attrs: &attributes,
546 },
547 attr_span,
548 );
549 }
550 }
551
552 if !#[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.should_emit, ShouldEmit::Nothing)
553 && Target::from_ast_target(&ast_target) == Target::WherePredicate
554 {
555 self.check_invalid_where_predicate_attrs(attributes.iter());
556 }
557
558 attributes
559 }
560
561 #[cfg(debug_assertions)]
562 fn check_args_used(&self, attr: &ast::Attribute, args: &ArgParser) {
565 if let ArgParser::List(items) = args {
566 for item in items.mixed() {
567 if let crate::parser::MetaItemOrLitParser::MetaItemParser(item) = item {
568 if !item.are_args_checked() {
569 self.dcx().span_delayed_bug(
570 item.span(),
571 "attribute args were not properly checked",
572 );
573 return;
574 }
575 self.check_args_used(attr, item.args());
576 }
577 }
578 }
579 }
580
581 pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
583 const SPECIAL_ATTRIBUTES: &[&[Symbol]] = &[
586 &[sym::cfg],
589 &[sym::cfg_attr],
590 ];
591
592 ATTRIBUTE_PARSERS.accepters.contains_key(path) || SPECIAL_ATTRIBUTES.contains(&path)
593 }
594
595 fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
596 match args {
597 ast::AttrArgs::Empty => AttrArgs::Empty,
598 ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
599 ast::AttrArgs::Eq { eq_span, expr } => {
603 let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
606 && let Ok(lit) =
607 ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
608 {
609 lit
610 } else {
611 let guar = self.dcx().span_delayed_bug(
612 args.span().unwrap_or(DUMMY_SP),
613 "expr in place where literal is expected (builtin attr parsing)",
614 );
615 ast::MetaItemLit {
616 symbol: sym::dummy,
617 suffix: None,
618 kind: ast::LitKind::Err(guar),
619 span: DUMMY_SP,
620 }
621 };
622 AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
623 }
624 }
625 }
626}