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_MAP, Features};
14use rustc_lint_defs::RegisteredTools;
15use rustc_session::Session;
16use rustc_session::lint::LintId;
17use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
18
19use crate::attributes::AttributeSafety;
20use crate::context::{
21 ATTRIBUTE_PARSERS, AcceptContext, FinalizeCheckContext, FinalizeCheckFn, FinalizeContext,
22 FinalizeFn, FinalizeOutput, SharedContext,
23};
24use crate::diagnostics::ParsedDescription;
25use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser};
26use crate::synthetic::SyntheticAttrState;
27use crate::{AttributeTemplate, OmitDoc, ShouldEmit};
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 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 target: Target,
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 target,
165 OmitDoc::Skip,
166 std::convert::identity,
167 |lint_id, span, kind| {
168 sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0)
169 },
170 )
171 }
172
173 pub fn parse_single<T>(
176 sess: &'sess Session,
177 attr: &ast::Attribute,
178 target_span: Span,
179 target_node_id: NodeId,
180 target: Target,
181 features: Option<&'sess Features>,
182 emit_errors: ShouldEmit,
183 parse_fn: fn(cx: &mut AcceptContext<'_, '_>, item: &ArgParser) -> Option<T>,
184 template: &AttributeTemplate,
185 allow_expr_metavar: AllowExprMetavar,
186 expected_safety: AttributeSafety,
187 ) -> Option<T> {
188 let attr_item = attr.get_normal_item();
189 let parts = attr_item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
190
191 let path = AttrPath::from_ast(&attr_item.path, identity);
192 let args = ArgParser::from_attr_args(
193 &attr_item.args,
194 &parts,
195 &sess.psess,
196 emit_errors,
197 allow_expr_metavar,
198 )?;
199 Self::parse_single_args(
200 sess,
201 attr.span,
202 attr_item.span,
203 attr.style,
204 path,
205 Some(attr_item.unsafety),
206 expected_safety,
207 ParsedDescription::Attribute,
208 target_span,
209 target_node_id,
210 target,
211 features,
212 emit_errors,
213 &args,
214 parse_fn,
215 template,
216 )
217 }
218
219 pub fn parse_single_args<T, I>(
222 sess: &'sess Session,
223 attr_span: Span,
224 inner_span: Span,
225 attr_style: AttrStyle,
226 attr_path: AttrPath,
227 attr_safety: Option<Safety>,
228 expected_safety: AttributeSafety,
229 parsed_description: ParsedDescription,
230 target_span: Span,
231 target_node_id: NodeId,
232 target: Target,
233 features: Option<&'sess Features>,
234 should_emit: ShouldEmit,
235 args: &I,
236 parse_fn: fn(cx: &mut AcceptContext<'_, '_>, item: &I) -> T,
237 template: &AttributeTemplate,
238 ) -> T {
239 let mut parser = Self { features, attr_tools: None, parse_filter: None, sess, should_emit };
240 let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: EmitAttribute| {
241 sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0)
242 };
243 if let Some(safety) = attr_safety {
244 parser.check_attribute_safety(
245 &attr_path,
246 inner_span,
247 safety,
248 expected_safety,
249 &mut emit_lint,
250 );
251 }
252 let mut cx: AcceptContext<'_, 'sess> = AcceptContext {
253 shared: SharedContext {
254 cx: &mut parser,
255 target_span,
256 target,
257 emit_lint: &mut emit_lint,
258 #[cfg(debug_assertions)]
259 has_lint_been_emitted: AtomicBool::new(false),
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 };
271 parse_fn(&mut cx, args)
272 }
273}
274
275impl<'sess> AttributeParser<'sess> {
276 pub fn new(
277 sess: &'sess Session,
278 features: &'sess Features,
279 attr_tools: &'sess RegisteredTools,
280 should_emit: ShouldEmit,
281 ) -> Self {
282 Self {
283 features: Some(features),
284 attr_tools: Some(attr_tools),
285 parse_filter: None,
286 sess,
287 should_emit,
288 }
289 }
290
291 pub(crate) fn sess(&self) -> &'sess Session {
292 self.sess
293 }
294
295 #[track_caller]
296 pub(crate) fn features(&self) -> &'sess Features {
297 self.features.expect("features not available at this point in the compiler")
298 }
299
300 pub(crate) fn features_option(&self) -> Option<&'sess Features> {
301 self.features
302 }
303
304 pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
305 self.sess().dcx()
306 }
307
308 pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
309 self.should_emit.emit_err(self.sess.dcx().create_err(diag))
310 }
311
312 pub fn parse_attribute_list(
317 &mut self,
318 attrs: &[ast::Attribute],
319 target_span: Span,
320 target: Target,
321 omit_doc: OmitDoc,
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
329 let mut finalizers: Vec<FinalizeFn> = Vec::with_capacity(attrs.len());
330
331 for attr in attrs {
332 if let Some(filter) = self.parse_filter {
334 if !filter(attr) {
335 continue;
336 }
337 }
338
339 let is_doc_attribute = attr.has_name(sym::doc);
345 if omit_doc == OmitDoc::Skip && is_doc_attribute {
346 continue;
347 }
348
349 let attr_span = lower_span(attr.span);
350 match &attr.kind {
351 ast::AttrKind::DocComment(comment_kind, symbol) => {
352 if omit_doc == OmitDoc::Skip {
353 continue;
354 }
355
356 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
357 style: attr.style,
358 kind: DocFragmentKind::Sugared(*comment_kind),
359 span: attr_span,
360 comment: *symbol,
361 }));
362 }
363 ast::AttrKind::Synthetic(synthetic) => {
364 synthetic_attr_state.accept_synthetic_attr(attr_span, lower_span, synthetic);
365 }
366 ast::AttrKind::Normal(n) => {
367 attr_paths.push(PathParser(&n.item.path));
368 let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
369 let parts =
370 n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
371 let inner_span = lower_span(n.item.span);
372
373 if let Some(accept) = ATTRIBUTE_PARSERS.accepters.get(parts.as_slice()) {
374 self.check_attribute_safety(
375 &attr_path,
376 inner_span,
377 n.item.unsafety,
378 accept.safety,
379 &mut emit_lint,
380 );
381 self.check_attribute_stability(&attr_path, attr_span, accept.stability);
382 if let [part] = parts.as_slice() {
383 if true {
if !BUILTIN_ATTRIBUTE_MAP.contains(part) {
::core::panicking::panic("assertion failed: BUILTIN_ATTRIBUTE_MAP.contains(part)")
};
};debug_assert!(BUILTIN_ATTRIBUTE_MAP.contains(part));
384 }
385
386 let Some(args) = ArgParser::from_attr_args(
387 &n.item.args,
388 &parts,
389 &self.sess.psess,
390 self.should_emit,
391 AllowExprMetavar::No,
392 ) else {
393 continue;
394 };
395
396 if is_doc_attribute
412 && let ArgParser::NameValue(nv) = &args
413 && let Some(comment) = nv.value_as_str()
417 {
418 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
419 style: attr.style,
420 kind: DocFragmentKind::Raw(nv.value_span),
421 span: attr_span,
422 comment,
423 }));
424 continue;
425 }
426
427 let mut cx: AcceptContext<'_, 'sess> = AcceptContext {
428 shared: SharedContext {
429 cx: self,
430 target_span,
431 target,
432 emit_lint: &mut emit_lint,
433 #[cfg(debug_assertions)]
434 has_lint_been_emitted: AtomicBool::new(false),
435 },
436 attr_span,
437 inner_span,
438 attr_style: attr.style,
439 parsed_description: ParsedDescription::Attribute,
440 template: &accept.template,
441 attr_safety: n.item.unsafety,
442 attr_path: attr_path.clone(),
443 #[cfg(debug_assertions)]
444 has_target_been_checked: false,
445 };
446
447 (accept.accept_fn)(&mut cx, &args);
448 finalizers.push(accept.finalizer);
449
450 Self::check_target(&accept.allowed_targets, "", &mut cx);
451 #[cfg(debug_assertions)]
452 if !cx.shared.has_lint_been_emitted.load(Ordering::Relaxed) {
453 cx.shared.cx.check_args_used(attr, &args)
454 }
455 } else if let [sym::diagnostic, _unknown, ..] = &*parts {
456 self.unknown_diagnostic_attr(&n.item.path.segments[1], &mut emit_lint);
457 } else {
458 let attr = AttrItem {
459 path: attr_path.clone(),
460 args: self.lower_attr_args(&n.item.args, lower_span),
461 id: HashIgnoredAttrId { attr_id: attr.id },
462 style: attr.style,
463 span: attr_span,
464 };
465
466 self.check_attribute_safety(
467 &attr_path,
468 inner_span,
469 n.item.unsafety,
470 AttributeSafety::Normal,
471 &mut emit_lint,
472 );
473
474 if !#[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.should_emit, ShouldEmit::Nothing)
475 && target == Target::Crate
476 {
477 self.check_invalid_crate_level_attr_item(&attr, inner_span);
478 }
479
480 attributes.push(Attribute::Unparsed(Box::new(attr)));
481 };
482 }
483 }
484 }
485
486 synthetic_attr_state.finalize_synthetic_attrs(&mut attributes);
487
488 let mut deferred_checks: Vec<(FinalizeCheckFn, Span)> = Vec::new();
493 for f in &finalizers {
494 let FinalizeOutput { attr, deferred_check } = f(&mut FinalizeContext {
495 shared: SharedContext {
496 cx: self,
497 target_span,
498 target,
499 emit_lint: &mut emit_lint,
500 #[cfg(debug_assertions)]
501 has_lint_been_emitted: AtomicBool::new(false),
502 },
503 all_attrs: &attr_paths,
504 });
505 if let Some(attr) = attr {
506 attributes.push(Attribute::Parsed(attr));
507 }
508 if let Some(deferred_check) = deferred_check {
509 deferred_checks.push(deferred_check);
510 }
511 }
512
513 for (check, attr_span) in deferred_checks {
516 check(
517 &FinalizeCheckContext {
518 shared: SharedContext {
519 cx: self,
520 target_span,
521 target,
522 emit_lint: &mut emit_lint,
523 #[cfg(debug_assertions)]
524 has_lint_been_emitted: AtomicBool::new(false),
525 },
526 all_attrs: &attr_paths,
527 parsed_attrs: &attributes,
528 },
529 attr_span,
530 );
531 }
532
533 if !#[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.should_emit, ShouldEmit::Nothing) && target == Target::WherePredicate {
534 self.check_invalid_where_predicate_attrs(attributes.iter());
535 }
536
537 attributes
538 }
539
540 #[cfg(debug_assertions)]
541 fn check_args_used(&self, attr: &ast::Attribute, args: &ArgParser) {
544 if let ArgParser::List(items) = args {
545 for item in items.mixed() {
546 if let crate::parser::MetaItemOrLitParser::MetaItemParser(item) = item {
547 if !item.are_args_checked() {
548 self.dcx().span_delayed_bug(
549 item.span(),
550 "attribute args were not properly checked",
551 );
552 return;
553 }
554 self.check_args_used(attr, item.args());
555 }
556 }
557 }
558 }
559
560 pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
562 const SPECIAL_ATTRIBUTES: &[&[Symbol]] = &[
565 &[sym::cfg],
568 &[sym::cfg_attr],
569 ];
570
571 ATTRIBUTE_PARSERS.accepters.contains_key(path) || SPECIAL_ATTRIBUTES.contains(&path)
572 }
573
574 fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
575 match args {
576 ast::AttrArgs::Empty => AttrArgs::Empty,
577 ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
578 ast::AttrArgs::Eq { eq_span, expr } => {
582 let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
585 && let Ok(lit) =
586 ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
587 {
588 lit
589 } else {
590 let guar = self.dcx().span_delayed_bug(
591 args.span().unwrap_or(DUMMY_SP),
592 "expr in place where literal is expected (builtin attr parsing)",
593 );
594 ast::MetaItemLit {
595 symbol: sym::dummy,
596 suffix: None,
597 kind: ast::LitKind::Err(guar),
598 span: DUMMY_SP,
599 }
600 };
601 AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
602 }
603 }
604 }
605}