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