1use std::num::NonZero;
2
3use rustc_attr_ir::target::{GenericParamKind, MethodKind, Target};
4use rustc_attr_ir::{
5 DefaultBodyStability, PartialConstStability, Stability, StabilityLevel, StableSince,
6 UnstableReason, UnstableRemovedFeature, VERSION_PLACEHOLDER,
7};
8use rustc_errors::ErrorGuaranteed;
9use rustc_feature::{ACCEPTED_LANG_FEATURES, AttributeStability};
10
11use super::prelude::*;
12use super::util::parse_version;
13use crate::diagnostics;
14
15const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
16 Allow(Target::Fn),
17 Allow(Target::Struct),
18 Allow(Target::Enum),
19 Allow(Target::Union),
20 Allow(Target::Method(MethodKind::Inherent)),
21 Allow(Target::Method(MethodKind::Trait { body: false })),
22 Allow(Target::Method(MethodKind::Trait { body: true })),
23 Allow(Target::Method(MethodKind::TraitImpl)),
24 Allow(Target::Impl { of_trait: false }),
25 Allow(Target::Impl { of_trait: true }),
26 Allow(Target::MacroDef),
27 Allow(Target::Crate),
28 Allow(Target::Mod),
29 Allow(Target::Use), Allow(Target::Const),
31 Allow(Target::AssocConst),
32 Allow(Target::AssocTy),
33 Allow(Target::Trait),
34 Allow(Target::TraitAlias),
35 Allow(Target::TyAlias),
36 Allow(Target::Variant),
37 Allow(Target::Field),
38 Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: true }),
39 Allow(Target::Static),
40 Allow(Target::ForeignFn),
41 Allow(Target::ForeignStatic),
42 Allow(Target::ForeignTy),
43 Allow(Target::ExternCrate),
44]);
45
46#[derive(#[automatically_derived]
impl ::core::default::Default for StabilityParser {
#[inline]
fn default() -> StabilityParser {
StabilityParser {
allowed_through_unstable_modules: ::core::default::Default::default(),
stability: ::core::default::Default::default(),
}
}
}Default)]
47pub(crate) struct StabilityParser {
48 allowed_through_unstable_modules: Option<Symbol>,
49 stability: Option<(Stability, Span)>,
50}
51
52impl StabilityParser {
53 fn check_duplicate(&self, cx: &AcceptContext<'_, '_>) -> bool {
55 if let Some((_, _)) = self.stability {
56 cx.emit_err(diagnostics::MultipleStabilityLevels { span: cx.attr_span });
57 true
58 } else {
59 false
60 }
61 }
62}
63
64impl AttributeParser for StabilityParser {
65 const ATTRIBUTES: AcceptMapping<Self> = &[
66 (
67 &[sym::stable],
68 crate::AttributeTemplate {
word: false,
list: Some(&[r#"feature = "name", since = "version""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"feature = "name", since = "version""#]),
69 AttributeStability::Unstable {
gate_name: rustc_span::sym::staged_api,
gate_check: rustc_feature::Features::staged_api,
notes: &[],
}unstable!(staged_api),
70 |this, cx, args| {
71 if !this.check_duplicate(cx)
72 && let Some((feature, level)) = parse_stability(cx, args)
73 {
74 this.stability = Some((Stability { level, feature }, cx.attr_span));
75 }
76 },
77 ),
78 (
79 &[sym::unstable],
80 crate::AttributeTemplate {
word: false,
list: Some(&[r#"feature = "name", reason = "...", issue = "N""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"feature = "name", reason = "...", issue = "N""#]),
81 AttributeStability::Unstable {
gate_name: rustc_span::sym::staged_api,
gate_check: rustc_feature::Features::staged_api,
notes: &[],
}unstable!(staged_api),
82 |this, cx, args| {
83 if !this.check_duplicate(cx)
84 && let Some((feature, level)) = parse_unstability(cx, args)
85 {
86 this.stability = Some((Stability { level, feature }, cx.attr_span));
87 }
88 },
89 ),
90 (
91 &[sym::rustc_allowed_through_unstable_modules],
92 crate::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["deprecation message"]),
docs: None,
}template!(NameValueStr: "deprecation message"),
93 AttributeStability::Unstable {
gate_name: rustc_span::sym::staged_api,
gate_check: rustc_feature::Features::staged_api,
notes: &[],
}unstable!(staged_api),
94 |this, cx, args| {
95 let Some(nv) = cx.expect_name_value(args, cx.attr_span, None) else {
96 return;
97 };
98 let Some(value_str) = cx.expect_string_literal(nv) else {
99 return;
100 };
101 this.allowed_through_unstable_modules = Some(value_str);
102 },
103 ),
104 ];
105 const ALLOWED_TARGETS: AllowedTargets<'_> = ALLOWED_TARGETS;
106
107 fn finalize(mut self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
108 if let Some(atum) = self.allowed_through_unstable_modules {
109 if let Some((
110 Stability {
111 level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. },
112 ..
113 },
114 _,
115 )) = self.stability
116 {
117 *allowed_through_unstable_modules = Some(atum);
118 } else {
119 cx.dcx()
120 .emit_err(diagnostics::RustcAllowedUnstablePairing { span: cx.target_span });
121 }
122 }
123
124 if let Some((Stability { level: StabilityLevel::Stable { .. }, .. }, _)) = self.stability {
125 for other_attr in cx.all_attrs {
126 if other_attr.word_is(sym::unstable_feature_bound) {
127 cx.emit_err(diagnostics::UnstableFeatureBoundIncompatibleStability {
128 span: cx.target_span,
129 });
130 }
131 }
132 }
133
134 let (stability, span) = self.stability?;
135
136 Some(AttributeKind::Stability { stability, span })
137 }
138}
139
140#[derive(#[automatically_derived]
impl ::core::default::Default for BodyStabilityParser {
#[inline]
fn default() -> BodyStabilityParser {
BodyStabilityParser { stability: ::core::default::Default::default() }
}
}Default)]
142pub(crate) struct BodyStabilityParser {
143 stability: Option<(DefaultBodyStability, Span)>,
144}
145
146impl AttributeParser for BodyStabilityParser {
147 const ATTRIBUTES: AcceptMapping<Self> = &[(
148 &[sym::rustc_default_body_unstable],
149 crate::AttributeTemplate {
word: false,
list: Some(&[r#"feature = "name", reason = "...", issue = "N""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"feature = "name", reason = "...", issue = "N""#]),
150 AttributeStability::Unstable {
gate_name: rustc_span::sym::staged_api,
gate_check: rustc_feature::Features::staged_api,
notes: &[],
}unstable!(staged_api),
151 |this, cx, args| {
152 if this.stability.is_some() {
153 cx.dcx().emit_err(diagnostics::MultipleStabilityLevels { span: cx.attr_span });
154 } else if let Some((feature, level)) = parse_unstability(cx, args) {
155 this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span));
156 }
157 },
158 )];
159 const ALLOWED_TARGETS: AllowedTargets<'_> = ALLOWED_TARGETS;
160
161 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
162 let (stability, span) = self.stability?;
163
164 Some(AttributeKind::RustcBodyStability { stability, span })
165 }
166}
167
168pub(crate) struct RustcConstStableIndirectParser;
169impl NoArgsAttributeParser for RustcConstStableIndirectParser {
170 const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect];
171 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore;
172 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
173 Allow(Target::Fn),
174 Allow(Target::Method(MethodKind::Inherent)),
175 ]);
176 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
gate_check: rustc_feature::Features::rustc_attrs,
notes: &[],
}unstable!(rustc_attrs);
177 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConstStableIndirect;
178}
179
180#[derive(#[automatically_derived]
impl ::core::default::Default for ConstStabilityParser {
#[inline]
fn default() -> ConstStabilityParser {
ConstStabilityParser {
promotable: ::core::default::Default::default(),
stability: ::core::default::Default::default(),
}
}
}Default)]
181pub(crate) struct ConstStabilityParser {
182 promotable: bool,
183 stability: Option<(PartialConstStability, Span)>,
184}
185
186impl ConstStabilityParser {
187 fn check_duplicate(&self, cx: &AcceptContext<'_, '_>) -> bool {
189 if let Some((_, _)) = self.stability {
190 cx.emit_err(diagnostics::MultipleStabilityLevels { span: cx.attr_span });
191 true
192 } else {
193 false
194 }
195 }
196}
197
198impl AttributeParser for ConstStabilityParser {
199 const ATTRIBUTES: AcceptMapping<Self> = &[
200 (
201 &[sym::rustc_const_stable],
202 crate::AttributeTemplate {
word: false,
list: Some(&[r#"feature = "name""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"feature = "name""#]),
203 AttributeStability::Unstable {
gate_name: rustc_span::sym::staged_api,
gate_check: rustc_feature::Features::staged_api,
notes: &[],
}unstable!(staged_api),
204 |this, cx, args| {
205 if !this.check_duplicate(cx)
206 && let Some((feature, level)) = parse_stability(cx, args)
207 {
208 this.stability = Some((
209 PartialConstStability { level, feature, promotable: false },
210 cx.attr_path.span,
211 ));
212 }
213 },
214 ),
215 (
216 &[sym::rustc_const_unstable],
217 crate::AttributeTemplate {
word: false,
list: Some(&[r#"feature = "name""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"feature = "name""#]),
218 AttributeStability::Unstable {
gate_name: rustc_span::sym::staged_api,
gate_check: rustc_feature::Features::staged_api,
notes: &[],
}unstable!(staged_api),
219 |this, cx, args| {
220 if !this.check_duplicate(cx)
221 && let Some((feature, level)) = parse_unstability(cx, args)
222 {
223 this.stability = Some((
224 PartialConstStability { level, feature, promotable: false },
225 cx.attr_path.span,
226 ));
227 }
228 },
229 ),
230 (&[sym::rustc_promotable], crate::AttributeTemplate {
word: true,
list: None,
one_of: &[],
name_value_str: None,
docs: None,
}template!(Word), AttributeStability::Unstable {
gate_name: rustc_span::sym::staged_api,
gate_check: rustc_feature::Features::staged_api,
notes: &[],
}unstable!(staged_api), |this, _cx, _| {
231 this.promotable = true;
232 }),
233 ];
234 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
235 Allow(Target::Fn),
236 Allow(Target::Method(MethodKind::Inherent)),
237 Allow(Target::Method(MethodKind::TraitImpl)),
238 Allow(Target::Method(MethodKind::Trait { body: true })),
239 Allow(Target::Impl { of_trait: false }),
240 Allow(Target::Impl { of_trait: true }),
241 Allow(Target::Use), Allow(Target::Const),
243 Allow(Target::AssocConst),
244 Allow(Target::Trait),
245 Allow(Target::Static),
246 Allow(Target::Crate),
247 ]);
248
249 fn finalize(mut self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
250 if self.promotable {
251 if let Some((ref mut stab, _)) = self.stability {
252 stab.promotable = true;
253 } else {
254 cx.dcx().emit_err(diagnostics::RustcPromotablePairing { span: cx.target_span });
255 }
256 }
257
258 let (stability, span) = self.stability?;
259
260 Some(AttributeKind::RustcConstStability { stability, span })
261 }
262}
263
264fn insert_value_into_option_or_error(
269 cx: &mut AcceptContext<'_, '_>,
270 param: &MetaItemParser,
271 item: &mut Option<Symbol>,
272 name: Ident,
273) -> Option<()> {
274 if item.is_some() {
275 cx.adcx().duplicate_key(name.span, name.name);
276 return None;
277 }
278
279 let (_ident, arg) = cx.expect_name_value(param, param.span(), Some(name.name))?;
280 let s = cx.expect_string_literal(arg)?;
281
282 *item = Some(s);
283
284 Some(())
285}
286
287pub(crate) fn parse_stability(
290 cx: &mut AcceptContext<'_, '_>,
291 args: &ArgParser,
292) -> Option<(Symbol, StabilityLevel)> {
293 let mut feature = None;
294 let mut since = None;
295
296 let list = cx.expect_list(args, cx.attr_span)?;
297
298 for param in list.mixed() {
299 let param_span = param.span();
300 let Some(param) = param.meta_item() else {
301 cx.adcx().expected_not_literal(param.span());
302 return None;
303 };
304
305 let word = param.path().word();
306 match word.map(|i| i.name) {
307 Some(sym::feature) => {
308 insert_value_into_option_or_error(cx, param, &mut feature, word.unwrap())?
309 }
310 Some(sym::since) => {
311 insert_value_into_option_or_error(cx, param, &mut since, word.unwrap())?
312 }
313 _ => {
314 cx.adcx().expected_specific_argument(param_span, &[sym::feature, sym::since]);
315 return None;
316 }
317 }
318 }
319
320 let feature = match feature {
321 Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
322 Some(_bad_feature) => Err(cx.emit_err(diagnostics::NonIdentFeature { span: cx.attr_span })),
323 None => Err(cx.emit_err(diagnostics::MissingFeature { span: cx.attr_span })),
324 };
325
326 let since = if let Some(since) = since {
327 if since.as_str() == VERSION_PLACEHOLDER {
328 StableSince::Current
329 } else if let Some(version) = parse_version(since) {
330 StableSince::Version(version)
331 } else {
332 let err = cx.emit_err(diagnostics::InvalidSince { span: cx.attr_span });
333 StableSince::Err(err)
334 }
335 } else {
336 let err = cx.emit_err(diagnostics::MissingSince { span: cx.attr_span });
337 StableSince::Err(err)
338 };
339
340 match feature {
341 Ok(feature) => {
342 let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None };
343 Some((feature, level))
344 }
345 Err(ErrorGuaranteed { .. }) => None,
346 }
347}
348
349pub(crate) fn parse_unstability(
352 cx: &mut AcceptContext<'_, '_>,
353 args: &ArgParser,
354) -> Option<(Symbol, StabilityLevel)> {
355 let mut feature = None;
356 let mut reason = None;
357 let mut issue = None;
358 let mut issue_num = None;
359 let mut implied_by = None;
360 let mut old_name = None;
361
362 let list = cx.expect_list(args, cx.attr_span)?;
363
364 for param in list.mixed() {
365 let Some(param) = param.meta_item() else {
366 cx.adcx().expected_not_literal(param.span());
367 return None;
368 };
369
370 let word = param.path().word();
371 match word.map(|i| i.name) {
372 Some(sym::feature) => {
373 insert_value_into_option_or_error(cx, param, &mut feature, word.unwrap())?
374 }
375 Some(sym::reason) => {
376 insert_value_into_option_or_error(cx, param, &mut reason, word.unwrap())?
377 }
378 Some(sym::issue) => {
379 insert_value_into_option_or_error(cx, param, &mut issue, word.unwrap())?;
380
381 issue_num = match issue.unwrap().as_str() {
384 "none" => None,
385 issue_str => match issue_str.parse::<NonZero<u32>>() {
386 Ok(num) => Some(num),
387 Err(err) => {
388 cx.emit_err(diagnostics::InvalidIssueString {
389 span: param.span(),
390 cause: diagnostics::InvalidIssueStringCause::from_int_error_kind(
391 param.args().as_name_value().unwrap().value_span,
392 err.kind(),
393 ),
394 });
395 return None;
396 }
397 },
398 };
399 }
400 Some(sym::implied_by) => {
401 insert_value_into_option_or_error(cx, param, &mut implied_by, word.unwrap())?
402 }
403 Some(sym::old_name) => {
404 insert_value_into_option_or_error(cx, param, &mut old_name, word.unwrap())?
405 }
406 _ => {
407 cx.adcx().expected_specific_argument(
408 param.span(),
409 &[sym::feature, sym::reason, sym::issue, sym::implied_by, sym::old_name],
410 );
411 return None;
412 }
413 }
414 }
415
416 let feature = match feature {
417 Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
418 Some(_bad_feature) => Err(cx.emit_err(diagnostics::NonIdentFeature { span: cx.attr_span })),
419 None => Err(cx.emit_err(diagnostics::MissingFeature { span: cx.attr_span })),
420 };
421
422 let issue = issue.ok_or_else(|| cx.emit_err(diagnostics::MissingIssue { span: cx.attr_span }));
423
424 match (feature, issue) {
425 (Ok(feature), Ok(_)) => {
426 if ACCEPTED_LANG_FEATURES.iter().any(|f| f.name == feature) {
429 cx.emit_err(diagnostics::UnstableAttrForAlreadyStableFeature {
430 attr_span: cx.attr_span,
431 item_span: cx.target_span,
432 });
433 return None;
434 }
435
436 let level = StabilityLevel::Unstable {
437 reason: UnstableReason::from_opt_reason(reason),
438 issue: issue_num,
439 implied_by,
440 old_name,
441 };
442 Some((feature, level))
443 }
444 (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None,
445 }
446}
447
448pub(crate) struct UnstableRemovedParser;
449
450impl CombineAttributeParser for UnstableRemovedParser {
451 type Item = UnstableRemovedFeature;
452 const PATH: &[Symbol] = &[sym::unstable_removed];
453 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
454 const TEMPLATE: AttributeTemplate =
455 crate::AttributeTemplate {
word: false,
list: Some(&[r#"feature = "name", reason = "...", link = "...", since = "version""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"feature = "name", reason = "...", link = "...", since = "version""#]);
456 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::staged_api,
gate_check: rustc_feature::Features::staged_api,
notes: &[],
}unstable!(staged_api);
457
458 const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableRemoved(items);
459
460 fn extend(
461 cx: &mut AcceptContext<'_, '_>,
462 args: &ArgParser,
463 ) -> impl IntoIterator<Item = Self::Item> {
464 let mut feature = None;
465 let mut reason = None;
466 let mut link = None;
467 let mut since = None;
468
469 let list = cx.expect_list(args, cx.attr_span)?;
470
471 for param in list.mixed() {
472 let Some(param) = param.meta_item() else {
473 cx.adcx().expected_not_literal(param.span());
474 return None;
475 };
476
477 let Some(word) = param.path().word() else {
478 cx.adcx().expected_specific_argument(
479 param.span(),
480 &[sym::feature, sym::reason, sym::link, sym::since],
481 );
482 return None;
483 };
484 match word.name {
485 sym::feature => insert_value_into_option_or_error(cx, param, &mut feature, word)?,
486 sym::since => insert_value_into_option_or_error(cx, param, &mut since, word)?,
487 sym::reason => insert_value_into_option_or_error(cx, param, &mut reason, word)?,
488 sym::link => insert_value_into_option_or_error(cx, param, &mut link, word)?,
489 _ => {
490 cx.adcx().expected_specific_argument(
491 param.span(),
492 &[sym::feature, sym::reason, sym::link, sym::since],
493 );
494 return None;
495 }
496 }
497 }
498
499 let Some(feature) = feature else {
501 cx.adcx().missing_name_value(list.span, sym::feature);
502 return None;
503 };
504 let Some(reason) = reason else {
505 cx.adcx().missing_name_value(list.span, sym::reason);
506 return None;
507 };
508 let Some(link) = link else {
509 cx.adcx().missing_name_value(list.span, sym::link);
510 return None;
511 };
512 let Some(since) = since else {
513 cx.adcx().missing_name_value(list.span, sym::since);
514 return None;
515 };
516
517 let Some(version) = parse_version(since) else {
518 cx.emit_err(diagnostics::InvalidSince { span: cx.attr_span });
519 return None;
520 };
521
522 Some(UnstableRemovedFeature { feature, reason, link, since: version })
523 }
524}