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