1use std::borrow::Cow;
2
3use rustc_ast as ast;
4use rustc_ast::NodeId;
5use rustc_errors::DiagCtxtHandle;
6use rustc_feature::{AttributeTemplate, Features};
7use rustc_hir::attrs::AttributeKind;
8use rustc_hir::lints::AttributeLint;
9use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
10use rustc_session::Session;
11use rustc_span::{DUMMY_SP, Span, Symbol, sym};
12
13use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
14use crate::parser::{ArgParser, MetaItemParser, PathParser};
15use crate::{Early, Late, OmitDoc, ShouldEmit};
16
17pub struct AttributeParser<'sess, S: Stage = Late> {
20 pub(crate) tools: Vec<Symbol>,
21 pub(crate) features: Option<&'sess Features>,
22 pub(crate) sess: &'sess Session,
23 pub(crate) stage: S,
24
25 parse_only: Option<Symbol>,
29}
30
31impl<'sess> AttributeParser<'sess, Early> {
32 pub fn parse_limited(
47 sess: &'sess Session,
48 attrs: &[ast::Attribute],
49 sym: Symbol,
50 target_span: Span,
51 target_node_id: NodeId,
52 features: Option<&'sess Features>,
53 ) -> Option<Attribute> {
54 Self::parse_limited_should_emit(
55 sess,
56 attrs,
57 sym,
58 target_span,
59 target_node_id,
60 features,
61 ShouldEmit::Nothing,
62 )
63 }
64
65 pub fn parse_limited_should_emit(
67 sess: &'sess Session,
68 attrs: &[ast::Attribute],
69 sym: Symbol,
70 target_span: Span,
71 target_node_id: NodeId,
72 features: Option<&'sess Features>,
73 should_emit: ShouldEmit,
74 ) -> Option<Attribute> {
75 let mut parsed = Self::parse_limited_all(
76 sess,
77 attrs,
78 Some(sym),
79 Target::Crate, target_span,
81 target_node_id,
82 features,
83 should_emit,
84 );
85 assert!(parsed.len() <= 1);
86 parsed.pop()
87 }
88
89 pub fn parse_limited_all(
90 sess: &'sess Session,
91 attrs: &[ast::Attribute],
92 parse_only: Option<Symbol>,
93 target: Target,
94 target_span: Span,
95 target_node_id: NodeId,
96 features: Option<&'sess Features>,
97 emit_errors: ShouldEmit,
98 ) -> Vec<Attribute> {
99 let mut p =
100 Self { features, tools: Vec::new(), parse_only, sess, stage: Early { emit_errors } };
101 p.parse_attribute_list(
102 attrs,
103 target_span,
104 target_node_id,
105 target,
106 OmitDoc::Skip,
107 std::convert::identity,
108 |lint| {
109 crate::lints::emit_attribute_lint(&lint, sess);
110 },
111 )
112 }
113
114 pub fn parse_single<T>(
115 sess: &'sess Session,
116 attr: &ast::Attribute,
117 target_span: Span,
118 target_node_id: NodeId,
119 features: Option<&'sess Features>,
120 emit_errors: ShouldEmit,
121 parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option<T>,
122 template: &AttributeTemplate,
123 ) -> Option<T> {
124 let mut parser = Self {
125 features,
126 tools: Vec::new(),
127 parse_only: None,
128 sess,
129 stage: Early { emit_errors },
130 };
131 let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
132 panic!("parse_single called on a doc attr")
133 };
134 let parts =
135 normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
136 let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?;
137 let path = meta_parser.path();
138 let args = meta_parser.args();
139 let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
140 shared: SharedContext {
141 cx: &mut parser,
142 target_span,
143 target_id: target_node_id,
144 emit_lint: &mut |lint| {
145 crate::lints::emit_attribute_lint(&lint, sess);
146 },
147 },
148 attr_span: attr.span,
149 attr_style: attr.style,
150 template,
151 attr_path: path.get_attribute_path(),
152 };
153 parse_fn(&mut cx, args)
154 }
155}
156
157impl<'sess, S: Stage> AttributeParser<'sess, S> {
158 pub fn new(
159 sess: &'sess Session,
160 features: &'sess Features,
161 tools: Vec<Symbol>,
162 stage: S,
163 ) -> Self {
164 Self { features: Some(features), tools, parse_only: None, sess, stage }
165 }
166
167 pub(crate) fn sess(&self) -> &'sess Session {
168 &self.sess
169 }
170
171 pub(crate) fn features(&self) -> &'sess Features {
172 self.features.expect("features not available at this point in the compiler")
173 }
174
175 pub(crate) fn features_option(&self) -> Option<&'sess Features> {
176 self.features
177 }
178
179 pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
180 self.sess().dcx()
181 }
182
183 pub fn parse_attribute_list(
188 &mut self,
189 attrs: &[ast::Attribute],
190 target_span: Span,
191 target_id: S::Id,
192 target: Target,
193 omit_doc: OmitDoc,
194
195 lower_span: impl Copy + Fn(Span) -> Span,
196 mut emit_lint: impl FnMut(AttributeLint<S::Id>),
197 ) -> Vec<Attribute> {
198 let mut attributes = Vec::new();
199 let mut attr_paths = Vec::new();
200
201 for attr in attrs {
202 if let Some(expected) = self.parse_only {
204 if !attr.has_name(expected) {
205 continue;
206 }
207 }
208
209 if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
215 continue;
216 }
217
218 match &attr.kind {
219 ast::AttrKind::DocComment(comment_kind, symbol) => {
220 if omit_doc == OmitDoc::Skip {
221 continue;
222 }
223
224 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
225 style: attr.style,
226 kind: *comment_kind,
227 span: lower_span(attr.span),
228 comment: *symbol,
229 }))
230 }
231 ast::AttrKind::Normal(n) => {
243 attr_paths.push(PathParser(Cow::Borrowed(&n.item.path)));
244
245 let parts =
246 n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
247
248 if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
249 let Some(parser) = MetaItemParser::from_attr(
250 n,
251 &parts,
252 &self.sess.psess,
253 self.stage.should_emit(),
254 ) else {
255 continue;
256 };
257 let path = parser.path();
258 let args = parser.args();
259 for accept in accepts {
260 let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
261 shared: SharedContext {
262 cx: self,
263 target_span,
264 target_id,
265 emit_lint: &mut emit_lint,
266 },
267 attr_span: lower_span(attr.span),
268 attr_style: attr.style,
269 template: &accept.template,
270 attr_path: path.get_attribute_path(),
271 };
272
273 (accept.accept_fn)(&mut cx, args);
274
275 if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
276 self.check_target(
277 path.get_attribute_path(),
278 attr.span,
279 &accept.allowed_targets,
280 target,
281 target_id,
282 &mut emit_lint,
283 );
284 }
285 }
286 } else {
287 attributes.push(Attribute::Unparsed(Box::new(AttrItem {
303 path: AttrPath::from_ast(&n.item.path),
304 args: self.lower_attr_args(&n.item.args, lower_span),
305 id: HashIgnoredAttrId { attr_id: attr.id },
306 style: attr.style,
307 span: lower_span(attr.span),
308 })));
309 }
310 }
311 }
312 }
313
314 let mut parsed_attributes = Vec::new();
315 for f in &S::parsers().finalizers {
316 if let Some(attr) = f(&mut FinalizeContext {
317 shared: SharedContext {
318 cx: self,
319 target_span,
320 target_id,
321 emit_lint: &mut emit_lint,
322 },
323 all_attrs: &attr_paths,
324 }) {
325 parsed_attributes.push(Attribute::Parsed(attr));
326 }
327 }
328
329 attributes.extend(parsed_attributes);
330
331 attributes
332 }
333
334 pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
336 Late::parsers().accepters.contains_key(path)
337 }
338
339 fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
340 match args {
341 ast::AttrArgs::Empty => AttrArgs::Empty,
342 ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
343 ast::AttrArgs::Eq { eq_span, expr } => {
347 let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
350 && let Ok(lit) =
351 ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
352 {
353 lit
354 } else {
355 let guar = self.dcx().span_delayed_bug(
356 args.span().unwrap_or(DUMMY_SP),
357 "expr in place where literal is expected (builtin attr parsing)",
358 );
359 ast::MetaItemLit {
360 symbol: sym::dummy,
361 suffix: None,
362 kind: ast::LitKind::Err(guar),
363 span: DUMMY_SP,
364 }
365 };
366 AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
367 }
368 }
369 }
370}