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