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_data_structures::sync::{DynSend, DynSync};
10use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan};
11use rustc_feature::{BUILTIN_ATTRIBUTE_MAP, Features};
12use rustc_hir::attrs::AttributeKind;
13use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
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::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser};
25use crate::session_diagnostics::ParsedDescription;
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) 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_only: Option<&'static [Symbol]>,
50}
51
52impl<'sess> AttributeParser<'sess> {
53 pub fn parse_limited(
70 sess: &'sess Session,
71 attrs: &[ast::Attribute],
72 sym: &'static [Symbol],
73 ) -> Option<Attribute> {
74 Self::parse_limited_should_emit(
75 sess,
76 attrs,
77 sym,
78 DUMMY_SP,
80 CRATE_NODE_ID,
81 Target::Crate,
82 None,
83 ShouldEmit::Nothing,
84 )
85 }
86
87 pub fn parse_limited_should_emit(
92 sess: &'sess Session,
93 attrs: &[ast::Attribute],
94 sym: &'static [Symbol],
95 target_span: Span,
96 target_node_id: NodeId,
97 target: Target,
98 features: Option<&'sess Features>,
99 should_emit: ShouldEmit,
100 ) -> Option<Attribute> {
101 let mut parsed = Self::parse_limited_all(
102 sess,
103 attrs,
104 Some(sym),
105 target,
106 target_span,
107 target_node_id,
108 features,
109 should_emit,
110 None,
111 );
112 if !(parsed.len() <= 1) {
::core::panicking::panic("assertion failed: parsed.len() <= 1")
};assert!(parsed.len() <= 1);
113 parsed.pop()
114 }
115
116 pub fn parse_limited_all(
124 sess: &'sess Session,
125 attrs: &[ast::Attribute],
126 parse_only: Option<&'static [Symbol]>,
127 target: Target,
128 target_span: Span,
129 target_node_id: NodeId,
130 features: Option<&'sess Features>,
131 should_emit: ShouldEmit,
132 tools: Option<&'sess RegisteredTools>,
133 ) -> Vec<Attribute> {
134 let mut p = Self { features, tools, parse_only, sess, should_emit };
135 p.parse_attribute_list(
136 attrs,
137 target_span,
138 target,
139 OmitDoc::Skip,
140 std::convert::identity,
141 |lint_id, span, kind| {
142 sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0)
143 },
144 )
145 }
146
147 pub fn parse_single<T>(
150 sess: &'sess Session,
151 attr: &ast::Attribute,
152 target_span: Span,
153 target_node_id: NodeId,
154 target: Target,
155 features: Option<&'sess Features>,
156 emit_errors: ShouldEmit,
157 parse_fn: fn(cx: &mut AcceptContext<'_, '_>, item: &ArgParser) -> Option<T>,
158 template: &AttributeTemplate,
159 allow_expr_metavar: AllowExprMetavar,
160 expected_safety: AttributeSafety,
161 ) -> Option<T> {
162 let attr_item = attr.get_normal_item();
163 let parts = attr_item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
164
165 let path = AttrPath::from_ast(&attr_item.path, identity);
166 let args = ArgParser::from_attr_args(
167 &attr_item.args,
168 &parts,
169 &sess.psess,
170 emit_errors,
171 allow_expr_metavar,
172 )?;
173 Self::parse_single_args(
174 sess,
175 attr.span,
176 attr_item.span,
177 attr.style,
178 path,
179 Some(attr_item.unsafety),
180 expected_safety,
181 ParsedDescription::Attribute,
182 target_span,
183 target_node_id,
184 target,
185 features,
186 emit_errors,
187 &args,
188 parse_fn,
189 template,
190 )
191 }
192
193 pub fn parse_single_args<T, I>(
196 sess: &'sess Session,
197 attr_span: Span,
198 inner_span: Span,
199 attr_style: AttrStyle,
200 attr_path: AttrPath,
201 attr_safety: Option<Safety>,
202 expected_safety: AttributeSafety,
203 parsed_description: ParsedDescription,
204 target_span: Span,
205 target_node_id: NodeId,
206 target: Target,
207 features: Option<&'sess Features>,
208 should_emit: ShouldEmit,
209 args: &I,
210 parse_fn: fn(cx: &mut AcceptContext<'_, '_>, item: &I) -> T,
211 template: &AttributeTemplate,
212 ) -> T {
213 let mut parser = Self { features, tools: None, parse_only: None, sess, should_emit };
214 let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: EmitAttribute| {
215 sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0)
216 };
217 if let Some(safety) = attr_safety {
218 parser.check_attribute_safety(
219 &attr_path,
220 inner_span,
221 safety,
222 expected_safety,
223 &mut emit_lint,
224 );
225 }
226 let mut cx: AcceptContext<'_, 'sess> = AcceptContext {
227 shared: SharedContext {
228 cx: &mut parser,
229 target_span,
230 target,
231 emit_lint: &mut emit_lint,
232 #[cfg(debug_assertions)]
233 has_lint_been_emitted: AtomicBool::new(false),
234 },
235 attr_span,
236 inner_span,
237 attr_style,
238 parsed_description,
239 template,
240 attr_safety: attr_safety.unwrap_or(Safety::Default),
241 attr_path,
242 #[cfg(debug_assertions)]
243 has_target_been_checked: false,
244 };
245 parse_fn(&mut cx, args)
246 }
247}
248
249impl<'sess> AttributeParser<'sess> {
250 pub fn new(
251 sess: &'sess Session,
252 features: &'sess Features,
253 tools: &'sess RegisteredTools,
254 should_emit: ShouldEmit,
255 ) -> Self {
256 Self { features: Some(features), tools: Some(tools), parse_only: None, sess, should_emit }
257 }
258
259 pub(crate) fn sess(&self) -> &'sess Session {
260 self.sess
261 }
262
263 pub(crate) fn features(&self) -> &'sess Features {
264 self.features.expect("features not available at this point in the compiler")
265 }
266
267 pub(crate) fn features_option(&self) -> Option<&'sess Features> {
268 self.features
269 }
270
271 pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
272 self.sess().dcx()
273 }
274
275 pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
276 self.should_emit.emit_err(self.sess.dcx().create_err(diag))
277 }
278
279 pub fn parse_attribute_list(
284 &mut self,
285 attrs: &[ast::Attribute],
286 target_span: Span,
287 target: Target,
288 omit_doc: OmitDoc,
289 lower_span: impl Copy + Fn(Span) -> Span,
290 mut emit_lint: impl FnMut(LintId, MultiSpan, EmitAttribute),
291 ) -> Vec<Attribute> {
292 let mut attributes = Vec::new();
293 let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
294 let mut synthetic_attr_state = SyntheticAttrState::default();
295
296 let mut finalizers: Vec<FinalizeFn> = Vec::with_capacity(attrs.len());
297
298 for attr in attrs {
299 if let Some(expected) = self.parse_only {
301 if !attr.path_matches(expected) {
302 continue;
303 }
304 }
305
306 let is_doc_attribute = attr.has_name(sym::doc);
312 if omit_doc == OmitDoc::Skip && is_doc_attribute {
313 continue;
314 }
315
316 let attr_span = lower_span(attr.span);
317 match &attr.kind {
318 ast::AttrKind::DocComment(comment_kind, symbol) => {
319 if omit_doc == OmitDoc::Skip {
320 continue;
321 }
322
323 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
324 style: attr.style,
325 kind: DocFragmentKind::Sugared(*comment_kind),
326 span: attr_span,
327 comment: *symbol,
328 }));
329 }
330 ast::AttrKind::Synthetic(synthetic) => {
331 synthetic_attr_state.accept_synthetic_attr(attr_span, lower_span, synthetic);
332 }
333 ast::AttrKind::Normal(n) => {
334 attr_paths.push(PathParser(&n.item.path));
335 let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
336 let parts =
337 n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
338 let inner_span = lower_span(n.item.span);
339
340 if let Some(accept) = ATTRIBUTE_PARSERS.accepters.get(parts.as_slice()) {
341 self.check_attribute_safety(
342 &attr_path,
343 inner_span,
344 n.item.unsafety,
345 accept.safety,
346 &mut emit_lint,
347 );
348 self.check_attribute_stability(&attr_path, attr_span, accept.stability);
349 if let [part] = parts.as_slice() {
350 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));
351 }
352
353 let Some(args) = ArgParser::from_attr_args(
354 &n.item.args,
355 &parts,
356 &self.sess.psess,
357 self.should_emit,
358 AllowExprMetavar::No,
359 ) else {
360 continue;
361 };
362
363 if is_doc_attribute
379 && let ArgParser::NameValue(nv) = &args
380 && let Some(comment) = nv.value_as_str()
384 {
385 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
386 style: attr.style,
387 kind: DocFragmentKind::Raw(nv.value_span),
388 span: attr_span,
389 comment,
390 }));
391 continue;
392 }
393
394 let mut cx: AcceptContext<'_, 'sess> = AcceptContext {
395 shared: SharedContext {
396 cx: self,
397 target_span,
398 target,
399 emit_lint: &mut emit_lint,
400 #[cfg(debug_assertions)]
401 has_lint_been_emitted: AtomicBool::new(false),
402 },
403 attr_span,
404 inner_span,
405 attr_style: attr.style,
406 parsed_description: ParsedDescription::Attribute,
407 template: &accept.template,
408 attr_safety: n.item.unsafety,
409 attr_path: attr_path.clone(),
410 #[cfg(debug_assertions)]
411 has_target_been_checked: false,
412 };
413
414 (accept.accept_fn)(&mut cx, &args);
415 finalizers.push(accept.finalizer);
416
417 Self::check_target(&accept.allowed_targets, "", &mut cx);
418 #[cfg(debug_assertions)]
419 if !cx.shared.has_lint_been_emitted.load(Ordering::Relaxed) {
420 cx.shared.cx.check_args_used(attr, &args)
421 }
422 } else {
423 let attr = AttrItem {
424 path: attr_path.clone(),
425 args: self.lower_attr_args(&n.item.args, lower_span),
426 id: HashIgnoredAttrId { attr_id: attr.id },
427 style: attr.style,
428 span: attr_span,
429 };
430
431 self.check_attribute_safety(
432 &attr_path,
433 inner_span,
434 n.item.unsafety,
435 AttributeSafety::Normal,
436 &mut emit_lint,
437 );
438
439 if !#[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.should_emit, ShouldEmit::Nothing)
440 && target == Target::Crate
441 {
442 self.check_invalid_crate_level_attr_item(&attr, inner_span);
443 }
444
445 attributes.push(Attribute::Unparsed(Box::new(attr)));
446 };
447 }
448 }
449 }
450
451 synthetic_attr_state.finalize_synthetic_attrs(&mut attributes);
452
453 let mut deferred_checks: Vec<(FinalizeCheckFn, Span)> = Vec::new();
458 for f in &finalizers {
459 let FinalizeOutput { attr, deferred_check } = f(&mut FinalizeContext {
460 shared: SharedContext {
461 cx: self,
462 target_span,
463 target,
464 emit_lint: &mut emit_lint,
465 #[cfg(debug_assertions)]
466 has_lint_been_emitted: AtomicBool::new(false),
467 },
468 all_attrs: &attr_paths,
469 });
470 if let Some(attr) = attr {
471 attributes.push(Attribute::Parsed(attr));
472 }
473 if let Some(deferred_check) = deferred_check {
474 deferred_checks.push(deferred_check);
475 }
476 }
477
478 for (check, attr_span) in deferred_checks {
481 check(
482 &FinalizeCheckContext {
483 shared: SharedContext {
484 cx: self,
485 target_span,
486 target,
487 emit_lint: &mut emit_lint,
488 #[cfg(debug_assertions)]
489 has_lint_been_emitted: AtomicBool::new(false),
490 },
491 all_attrs: &attr_paths,
492 parsed_attrs: &attributes,
493 },
494 attr_span,
495 );
496 }
497
498 if !#[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.should_emit, ShouldEmit::Nothing) && target == Target::WherePredicate {
499 self.check_invalid_where_predicate_attrs(attributes.iter());
500 }
501
502 attributes
503 }
504
505 #[cfg(debug_assertions)]
506 fn check_args_used(&self, attr: &ast::Attribute, args: &ArgParser) {
509 if let ArgParser::List(items) = args {
510 for item in items.mixed() {
511 if let crate::parser::MetaItemOrLitParser::MetaItemParser(item) = item {
512 if !item.are_args_checked() {
513 self.dcx().span_delayed_bug(
514 item.span(),
515 "attribute args were not properly checked",
516 );
517 return;
518 }
519 self.check_args_used(attr, item.args());
520 }
521 }
522 }
523 }
524
525 pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
527 const SPECIAL_ATTRIBUTES: &[&[Symbol]] = &[
530 &[sym::cfg],
533 &[sym::cfg_attr],
534 ];
535
536 ATTRIBUTE_PARSERS.accepters.contains_key(path) || SPECIAL_ATTRIBUTES.contains(&path)
537 }
538
539 fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
540 match args {
541 ast::AttrArgs::Empty => AttrArgs::Empty,
542 ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
543 ast::AttrArgs::Eq { eq_span, expr } => {
547 let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
550 && let Ok(lit) =
551 ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
552 {
553 lit
554 } else {
555 let guar = self.dcx().span_delayed_bug(
556 args.span().unwrap_or(DUMMY_SP),
557 "expr in place where literal is expected (builtin attr parsing)",
558 );
559 ast::MetaItemLit {
560 symbol: sym::dummy,
561 suffix: None,
562 kind: ast::LitKind::Err(guar),
563 span: DUMMY_SP,
564 }
565 };
566 AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
567 }
568 }
569 }
570}