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