1use std::convert::identity;
2
3use rustc_ast as ast;
4use rustc_ast::token::DocFragmentKind;
5use rustc_ast::{AttrItemKind, AttrStyle, CRATE_NODE_ID, NodeId, Safety};
6use rustc_data_structures::sync::{DynSend, DynSync};
7use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan};
8use rustc_feature::{AttributeTemplate, Features};
9use rustc_hir::attrs::AttributeKind;
10use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
11use rustc_session::Session;
12use rustc_session::lint::LintId;
13use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
14
15use crate::attributes::AttributeSafety;
16use crate::context::{
17 ATTRIBUTE_PARSERS, AcceptContext, FinalizeContext, FinalizeFn, SharedContext,
18};
19use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
20use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser};
21use crate::session_diagnostics::ParsedDescription;
22use crate::{OmitDoc, ShouldEmit};
23
24pub struct EmitAttribute(
25 pub Box<
26 dyn for<'a> Fn(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()>
27 + DynSend
28 + DynSync
29 + 'static,
30 >,
31);
32
33pub struct AttributeParser<'sess> {
36 pub(crate) tools: Vec<Symbol>,
37 pub(crate) features: Option<&'sess Features>,
38 pub(crate) sess: &'sess Session,
39 pub(crate) should_emit: ShouldEmit,
40
41 parse_only: Option<&'static [Symbol]>,
45}
46
47impl<'sess> AttributeParser<'sess> {
48 pub fn parse_limited(
63 sess: &'sess Session,
64 attrs: &[ast::Attribute],
65 sym: &'static [Symbol],
66 ) -> Option<Attribute> {
67 Self::parse_limited_should_emit(
68 sess,
69 attrs,
70 sym,
71 DUMMY_SP,
73 CRATE_NODE_ID,
74 Target::Crate,
75 None,
76 ShouldEmit::Nothing,
77 )
78 }
79
80 pub fn parse_limited_should_emit(
83 sess: &'sess Session,
84 attrs: &[ast::Attribute],
85 sym: &'static [Symbol],
86 target_span: Span,
87 target_node_id: NodeId,
88 target: Target,
89 features: Option<&'sess Features>,
90 should_emit: ShouldEmit,
91 ) -> Option<Attribute> {
92 let mut parsed = Self::parse_limited_all(
93 sess,
94 attrs,
95 Some(sym),
96 target,
97 target_span,
98 target_node_id,
99 features,
100 should_emit,
101 );
102 if !(parsed.len() <= 1) {
::core::panicking::panic("assertion failed: parsed.len() <= 1")
};assert!(parsed.len() <= 1);
103 parsed.pop()
104 }
105
106 pub fn parse_limited_all(
114 sess: &'sess Session,
115 attrs: &[ast::Attribute],
116 parse_only: Option<&'static [Symbol]>,
117 target: Target,
118 target_span: Span,
119 target_node_id: NodeId,
120 features: Option<&'sess Features>,
121 should_emit: ShouldEmit,
122 ) -> Vec<Attribute> {
123 let mut p = Self { features, tools: Vec::new(), parse_only, sess, should_emit };
124 p.parse_attribute_list(
125 attrs,
126 target_span,
127 target,
128 OmitDoc::Skip,
129 std::convert::identity,
130 |lint_id, span, kind| {
131 sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0)
132 },
133 )
134 }
135
136 pub fn parse_single<T>(
139 sess: &'sess Session,
140 attr: &ast::Attribute,
141 target_span: Span,
142 target_node_id: NodeId,
143 target: Target,
144 features: Option<&'sess Features>,
145 emit_errors: ShouldEmit,
146 parse_fn: fn(cx: &mut AcceptContext<'_, '_>, item: &ArgParser) -> Option<T>,
147 template: &AttributeTemplate,
148 allow_expr_metavar: AllowExprMetavar,
149 expected_safety: AttributeSafety,
150 ) -> Option<T> {
151 let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
152 {
::core::panicking::panic_fmt(format_args!("parse_single called on a doc attr"));
}panic!("parse_single called on a doc attr")
153 };
154 let parts =
155 normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
156
157 let path = AttrPath::from_ast(&normal_attr.item.path, identity);
158 let args = ArgParser::from_attr_args(
159 &normal_attr.item.args.unparsed_ref().unwrap(),
160 &parts,
161 &sess.psess,
162 emit_errors,
163 allow_expr_metavar,
164 )?;
165 Self::parse_single_args(
166 sess,
167 attr.span,
168 normal_attr.item.span(),
169 attr.style,
170 path,
171 Some(normal_attr.item.unsafety),
172 expected_safety,
173 ParsedDescription::Attribute,
174 target_span,
175 target_node_id,
176 target,
177 features,
178 emit_errors,
179 &args,
180 parse_fn,
181 template,
182 )
183 }
184
185 pub fn parse_single_args<T, I>(
188 sess: &'sess Session,
189 attr_span: Span,
190 inner_span: Span,
191 attr_style: AttrStyle,
192 attr_path: AttrPath,
193 attr_safety: Option<Safety>,
194 expected_safety: AttributeSafety,
195 parsed_description: ParsedDescription,
196 target_span: Span,
197 target_node_id: NodeId,
198 target: Target,
199 features: Option<&'sess Features>,
200 should_emit: ShouldEmit,
201 args: &I,
202 parse_fn: fn(cx: &mut AcceptContext<'_, '_>, item: &I) -> T,
203 template: &AttributeTemplate,
204 ) -> T {
205 let mut parser = Self { features, tools: Vec::new(), parse_only: None, sess, should_emit };
206 let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: EmitAttribute| {
207 sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0)
208 };
209 if let Some(safety) = attr_safety {
210 parser.check_attribute_safety(
211 &attr_path,
212 inner_span,
213 safety,
214 expected_safety,
215 &mut emit_lint,
216 );
217 }
218 let mut cx: AcceptContext<'_, 'sess> = AcceptContext {
219 shared: SharedContext {
220 cx: &mut parser,
221 target_span,
222 target,
223 emit_lint: &mut emit_lint,
224 },
225 attr_span,
226 inner_span,
227 attr_style,
228 parsed_description,
229 template,
230 attr_path,
231 };
232 parse_fn(&mut cx, args)
233 }
234}
235
236impl<'sess> AttributeParser<'sess> {
237 pub fn new(
238 sess: &'sess Session,
239 features: &'sess Features,
240 tools: Vec<Symbol>,
241 should_emit: ShouldEmit,
242 ) -> Self {
243 Self { features: Some(features), tools, parse_only: None, sess, should_emit }
244 }
245
246 pub(crate) fn sess(&self) -> &'sess Session {
247 &self.sess
248 }
249
250 pub(crate) fn features(&self) -> &'sess Features {
251 self.features.expect("features not available at this point in the compiler")
252 }
253
254 pub(crate) fn features_option(&self) -> Option<&'sess Features> {
255 self.features
256 }
257
258 pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
259 self.sess().dcx()
260 }
261
262 pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
263 self.should_emit.emit_err(self.sess.dcx().create_err(diag))
264 }
265
266 pub fn parse_attribute_list(
271 &mut self,
272 attrs: &[ast::Attribute],
273 target_span: Span,
274 target: Target,
275 omit_doc: OmitDoc,
276 lower_span: impl Copy + Fn(Span) -> Span,
277 mut emit_lint: impl FnMut(LintId, MultiSpan, EmitAttribute),
278 ) -> Vec<Attribute> {
279 let mut attributes = Vec::new();
280 let mut dropped_attributes = Vec::new();
285 let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
286 let mut early_parsed_state = EarlyParsedState::default();
287
288 let mut finalizers: Vec<FinalizeFn> = Vec::with_capacity(attrs.len());
289
290 for attr in attrs {
291 if let Some(expected) = self.parse_only {
293 if !attr.path_matches(expected) {
294 continue;
295 }
296 }
297
298 let is_doc_attribute = attr.has_name(sym::doc);
304 if omit_doc == OmitDoc::Skip && is_doc_attribute {
305 continue;
306 }
307
308 let attr_span = lower_span(attr.span);
309 match &attr.kind {
310 ast::AttrKind::DocComment(comment_kind, symbol) => {
311 if omit_doc == OmitDoc::Skip {
312 continue;
313 }
314
315 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
316 style: attr.style,
317 kind: DocFragmentKind::Sugared(*comment_kind),
318 span: attr_span,
319 comment: *symbol,
320 }));
321 }
322 ast::AttrKind::Normal(n) => {
323 attr_paths.push(PathParser(&n.item.path));
324 let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
325
326 let args = match &n.item.args {
327 AttrItemKind::Unparsed(args) => args,
328 AttrItemKind::Parsed(parsed) => {
329 early_parsed_state
330 .accept_early_parsed_attribute(attr_span, lower_span, parsed);
331 continue;
332 }
333 };
334
335 let parts =
336 n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
337
338 if let Some(accept) = ATTRIBUTE_PARSERS.accepters.get(parts.as_slice()) {
339 self.check_attribute_safety(
340 &attr_path,
341 lower_span(n.item.span()),
342 n.item.unsafety,
343 accept.safety,
344 &mut emit_lint,
345 );
346
347 let Some(args) = ArgParser::from_attr_args(
348 args,
349 &parts,
350 &self.sess.psess,
351 self.should_emit,
352 AllowExprMetavar::No,
353 ) else {
354 continue;
355 };
356
357 if is_doc_attribute
373 && let ArgParser::NameValue(nv) = &args
374 && let Some(comment) = nv.value_as_str()
378 {
379 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
380 style: attr.style,
381 kind: DocFragmentKind::Raw(nv.value_span),
382 span: attr_span,
383 comment,
384 }));
385 continue;
386 }
387
388 let mut cx: AcceptContext<'_, 'sess> = AcceptContext {
389 shared: SharedContext {
390 cx: self,
391 target_span,
392 target,
393 emit_lint: &mut emit_lint,
394 },
395 attr_span,
396 inner_span: lower_span(n.item.span()),
397 attr_style: attr.style,
398 parsed_description: ParsedDescription::Attribute,
399 template: &accept.template,
400 attr_path: attr_path.clone(),
401 };
402
403 (accept.accept_fn)(&mut cx, &args);
404 finalizers.push(accept.finalizer);
405
406 if !#[allow(non_exhaustive_omitted_patterns)] match cx.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(cx.should_emit, ShouldEmit::Nothing) {
407 Self::check_target(&accept.allowed_targets, target, &mut cx);
408 }
409 } else {
410 let attr = AttrItem {
411 path: attr_path.clone(),
412 args: self
413 .lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span),
414 id: HashIgnoredAttrId { attr_id: attr.id },
415 style: attr.style,
416 span: attr_span,
417 };
418
419 self.check_attribute_safety(
420 &attr_path,
421 lower_span(n.item.span()),
422 n.item.unsafety,
423 AttributeSafety::Normal,
424 &mut emit_lint,
425 );
426
427 if !#[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.should_emit, ShouldEmit::Nothing)
428 && target == Target::Crate
429 {
430 self.check_invalid_crate_level_attr_item(&attr, n.item.span());
431 }
432
433 let attr = Attribute::Unparsed(Box::new(attr));
434
435 if self.tools.contains(&parts[0])
436 || [sym::allow, sym::deny, sym::expect, sym::forbid, sym::warn]
439 .contains(&parts[0])
440 {
441 attributes.push(attr);
442 } else {
443 dropped_attributes.push(attr);
444 }
445 }
446 }
447 }
448 }
449
450 early_parsed_state.finalize_early_parsed_attributes(&mut attributes);
451 for f in &finalizers {
452 if let Some(attr) = f(&mut FinalizeContext {
453 shared: SharedContext { cx: self, target_span, target, emit_lint: &mut emit_lint },
454 all_attrs: &attr_paths,
455 }) {
456 attributes.push(Attribute::Parsed(attr));
457 }
458 }
459
460 if !#[allow(non_exhaustive_omitted_patterns)] match self.should_emit {
ShouldEmit::Nothing => true,
_ => false,
}matches!(self.should_emit, ShouldEmit::Nothing) && target == Target::WherePredicate {
461 self.check_invalid_where_predicate_attrs(attributes.iter().chain(&dropped_attributes));
462 }
463
464 attributes
465 }
466
467 pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
469 const SPECIAL_ATTRIBUTES: &[&[Symbol]] = &[
472 &[sym::cfg],
474 &[sym::cfg_attr],
475 ];
476
477 ATTRIBUTE_PARSERS.accepters.contains_key(path)
478 || EARLY_PARSED_ATTRIBUTES.contains(&path)
479 || SPECIAL_ATTRIBUTES.contains(&path)
480 }
481
482 fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
483 match args {
484 ast::AttrArgs::Empty => AttrArgs::Empty,
485 ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
486 ast::AttrArgs::Eq { eq_span, expr } => {
490 let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
493 && let Ok(lit) =
494 ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
495 {
496 lit
497 } else {
498 let guar = self.dcx().span_delayed_bug(
499 args.span().unwrap_or(DUMMY_SP),
500 "expr in place where literal is expected (builtin attr parsing)",
501 );
502 ast::MetaItemLit {
503 symbol: sym::dummy,
504 suffix: None,
505 kind: ast::LitKind::Err(guar),
506 span: DUMMY_SP,
507 }
508 };
509 AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
510 }
511 }
512 }
513}