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(&self, cx: &AcceptContext<'_, '_>) -> 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 AttributeParser for StabilityParser {
75 const ATTRIBUTES: AcceptMapping<Self> = &[
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) = cx.expect_name_value(args, cx.attr_span, None) else {
106 return;
107 };
108 let Some(value_str) = nv.value_as_str() else {
109 cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
110 return;
111 };
112 this.allowed_through_unstable_modules = Some(value_str);
113 },
114 ),
115 ];
116 const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS;
117
118 fn finalize(mut self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
119 if let Some(atum) = self.allowed_through_unstable_modules {
120 if let Some((
121 Stability {
122 level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. },
123 ..
124 },
125 _,
126 )) = self.stability
127 {
128 *allowed_through_unstable_modules = Some(atum);
129 } else {
130 cx.dcx().emit_err(session_diagnostics::RustcAllowedUnstablePairing {
131 span: cx.target_span,
132 });
133 }
134 }
135
136 if let Some((Stability { level: StabilityLevel::Stable { .. }, .. }, _)) = self.stability {
137 for other_attr in cx.all_attrs {
138 if other_attr.word_is(sym::unstable_feature_bound) {
139 cx.emit_err(session_diagnostics::UnstableFeatureBoundIncompatibleStability {
140 span: cx.target_span,
141 });
142 }
143 }
144 }
145
146 let (stability, span) = self.stability?;
147
148 Some(AttributeKind::Stability { stability, span })
149 }
150}
151
152#[derive(#[automatically_derived]
impl ::core::default::Default for BodyStabilityParser {
#[inline]
fn default() -> BodyStabilityParser {
BodyStabilityParser { stability: ::core::default::Default::default() }
}
}Default)]
154pub(crate) struct BodyStabilityParser {
155 stability: Option<(DefaultBodyStability, Span)>,
156}
157
158impl AttributeParser for BodyStabilityParser {
159 const ATTRIBUTES: AcceptMapping<Self> = &[(
160 &[sym::rustc_default_body_unstable],
161 ::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""#]),
162 |this, cx, args| {
163 if !cx.features().staged_api() {
cx.emit_err(session_diagnostics::StabilityOutsideStd {
span: cx.attr_span,
});
return;
};reject_outside_std!(cx);
164 if this.stability.is_some() {
165 cx.dcx()
166 .emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
167 } else if let Some((feature, level)) = parse_unstability(cx, args) {
168 this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span));
169 }
170 },
171 )];
172 const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS;
173
174 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
175 let (stability, span) = self.stability?;
176
177 Some(AttributeKind::RustcBodyStability { stability, span })
178 }
179}
180
181pub(crate) struct RustcConstStableIndirectParser;
182impl NoArgsAttributeParser for RustcConstStableIndirectParser {
183 const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect];
184 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore;
185 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
186 Allow(Target::Fn),
187 Allow(Target::Method(MethodKind::Inherent)),
188 ]);
189 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConstStableIndirect;
190}
191
192#[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)]
193pub(crate) struct ConstStabilityParser {
194 promotable: bool,
195 stability: Option<(PartialConstStability, Span)>,
196}
197
198impl ConstStabilityParser {
199 fn check_duplicate(&self, cx: &AcceptContext<'_, '_>) -> bool {
201 if let Some((_, _)) = self.stability {
202 cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
203 true
204 } else {
205 false
206 }
207 }
208}
209
210impl AttributeParser for ConstStabilityParser {
211 const ATTRIBUTES: AcceptMapping<Self> = &[
212 (
213 &[sym::rustc_const_stable],
214 ::rustc_feature::AttributeTemplate {
word: false,
list: Some(&[r#"feature = "name""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"feature = "name""#]),
215 |this, cx, args| {
216 if !cx.features().staged_api() {
cx.emit_err(session_diagnostics::StabilityOutsideStd {
span: cx.attr_span,
});
return;
};reject_outside_std!(cx);
217
218 if !this.check_duplicate(cx)
219 && let Some((feature, level)) = parse_stability(cx, args)
220 {
221 this.stability = Some((
222 PartialConstStability { level, feature, promotable: false },
223 cx.attr_span,
224 ));
225 }
226 },
227 ),
228 (
229 &[sym::rustc_const_unstable],
230 ::rustc_feature::AttributeTemplate {
word: false,
list: Some(&[r#"feature = "name""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"feature = "name""#]),
231 |this, cx, args| {
232 if !cx.features().staged_api() {
cx.emit_err(session_diagnostics::StabilityOutsideStd {
span: cx.attr_span,
});
return;
};reject_outside_std!(cx);
233 if !this.check_duplicate(cx)
234 && let Some((feature, level)) = parse_unstability(cx, args)
235 {
236 this.stability = Some((
237 PartialConstStability { level, feature, promotable: false },
238 cx.attr_span,
239 ));
240 }
241 },
242 ),
243 (&[sym::rustc_promotable], ::rustc_feature::AttributeTemplate {
word: true,
list: None,
one_of: &[],
name_value_str: None,
docs: None,
}template!(Word), |this, cx, _| {
244 if !cx.features().staged_api() {
cx.emit_err(session_diagnostics::StabilityOutsideStd {
span: cx.attr_span,
});
return;
};reject_outside_std!(cx);
245 this.promotable = true;
246 }),
247 ];
248 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
249 Allow(Target::Fn),
250 Allow(Target::Method(MethodKind::Inherent)),
251 Allow(Target::Method(MethodKind::TraitImpl)),
252 Allow(Target::Method(MethodKind::Trait { body: true })),
253 Allow(Target::Impl { of_trait: false }),
254 Allow(Target::Impl { of_trait: true }),
255 Allow(Target::Use), Allow(Target::Const),
257 Allow(Target::AssocConst),
258 Allow(Target::Trait),
259 Allow(Target::Static),
260 Allow(Target::Crate),
261 ]);
262
263 fn finalize(mut self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
264 if self.promotable {
265 if let Some((ref mut stab, _)) = self.stability {
266 stab.promotable = true;
267 } else {
268 cx.dcx()
269 .emit_err(session_diagnostics::RustcPromotablePairing { span: cx.target_span });
270 }
271 }
272
273 let (stability, span) = self.stability?;
274
275 Some(AttributeKind::RustcConstStability { stability, span })
276 }
277}
278
279fn insert_value_into_option_or_error(
284 cx: &mut AcceptContext<'_, '_>,
285 param: &MetaItemParser,
286 item: &mut Option<Symbol>,
287 name: Ident,
288) -> Option<()> {
289 if item.is_some() {
290 cx.adcx().duplicate_key(name.span, name.name);
291 return None;
292 }
293
294 let (_ident, arg) = cx.expect_name_value(param, param.span(), Some(name.name))?;
295
296 let Some(s) = arg.value_as_str() else {
297 cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit()));
298 return None;
299 };
300
301 *item = Some(s);
302
303 Some(())
304}
305
306pub(crate) fn parse_stability(
309 cx: &mut AcceptContext<'_, '_>,
310 args: &ArgParser,
311) -> Option<(Symbol, StabilityLevel)> {
312 let mut feature = None;
313 let mut since = None;
314
315 let list = cx.expect_list(args, cx.attr_span)?;
316
317 for param in list.mixed() {
318 let param_span = param.span();
319 let Some(param) = param.meta_item() else {
320 cx.adcx().expected_not_literal(param.span());
321 return None;
322 };
323
324 let word = param.path().word();
325 match word.map(|i| i.name) {
326 Some(sym::feature) => {
327 insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())?
328 }
329 Some(sym::since) => {
330 insert_value_into_option_or_error(cx, ¶m, &mut since, word.unwrap())?
331 }
332 _ => {
333 cx.adcx().expected_specific_argument(param_span, &[sym::feature, sym::since]);
334 return None;
335 }
336 }
337 }
338
339 let feature = match feature {
340 Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
341 Some(_bad_feature) => {
342 Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
343 }
344 None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
345 };
346
347 let since = if let Some(since) = since {
348 if since.as_str() == VERSION_PLACEHOLDER {
349 StableSince::Current
350 } else if let Some(version) = parse_version(since) {
351 StableSince::Version(version)
352 } else {
353 let err = cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
354 StableSince::Err(err)
355 }
356 } else {
357 let err = cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
358 StableSince::Err(err)
359 };
360
361 match feature {
362 Ok(feature) => {
363 let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None };
364 Some((feature, level))
365 }
366 Err(ErrorGuaranteed { .. }) => None,
367 }
368}
369
370pub(crate) fn parse_unstability(
373 cx: &mut AcceptContext<'_, '_>,
374 args: &ArgParser,
375) -> Option<(Symbol, StabilityLevel)> {
376 let mut feature = None;
377 let mut reason = None;
378 let mut issue = None;
379 let mut issue_num = None;
380 let mut implied_by = None;
381 let mut old_name = None;
382
383 let list = cx.expect_list(args, cx.attr_span)?;
384
385 for param in list.mixed() {
386 let Some(param) = param.meta_item() else {
387 cx.adcx().expected_not_literal(param.span());
388 return None;
389 };
390
391 let word = param.path().word();
392 match word.map(|i| i.name) {
393 Some(sym::feature) => {
394 insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())?
395 }
396 Some(sym::reason) => {
397 insert_value_into_option_or_error(cx, ¶m, &mut reason, word.unwrap())?
398 }
399 Some(sym::issue) => {
400 insert_value_into_option_or_error(cx, ¶m, &mut issue, word.unwrap())?;
401
402 issue_num = match issue.unwrap().as_str() {
405 "none" => None,
406 issue_str => match issue_str.parse::<NonZero<u32>>() {
407 Ok(num) => Some(num),
408 Err(err) => {
409 cx.emit_err(
410 session_diagnostics::InvalidIssueString {
411 span: param.span(),
412 cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
413 param.args().as_name_value().unwrap().value_span,
414 err.kind(),
415 ),
416 },
417 );
418 return None;
419 }
420 },
421 };
422 }
423 Some(sym::implied_by) => {
424 insert_value_into_option_or_error(cx, ¶m, &mut implied_by, word.unwrap())?
425 }
426 Some(sym::old_name) => {
427 insert_value_into_option_or_error(cx, ¶m, &mut old_name, word.unwrap())?
428 }
429 _ => {
430 cx.adcx().expected_specific_argument(
431 param.span(),
432 &[sym::feature, sym::reason, sym::issue, sym::implied_by, sym::old_name],
433 );
434 return None;
435 }
436 }
437 }
438
439 let feature = match feature {
440 Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
441 Some(_bad_feature) => {
442 Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
443 }
444 None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
445 };
446
447 let issue =
448 issue.ok_or_else(|| cx.emit_err(session_diagnostics::MissingIssue { span: cx.attr_span }));
449
450 match (feature, issue) {
451 (Ok(feature), Ok(_)) => {
452 if ACCEPTED_LANG_FEATURES.iter().any(|f| f.name == feature) {
455 cx.emit_err(session_diagnostics::UnstableAttrForAlreadyStableFeature {
456 attr_span: cx.attr_span,
457 item_span: cx.target_span,
458 });
459 return None;
460 }
461
462 let level = StabilityLevel::Unstable {
463 reason: UnstableReason::from_opt_reason(reason),
464 issue: issue_num,
465 implied_by,
466 old_name,
467 };
468 Some((feature, level))
469 }
470 (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None,
471 }
472}
473
474pub(crate) struct UnstableRemovedParser;
475
476impl CombineAttributeParser for UnstableRemovedParser {
477 type Item = UnstableRemovedFeature;
478 const PATH: &[Symbol] = &[sym::unstable_removed];
479 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
480 const TEMPLATE: AttributeTemplate =
481 ::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""#]);
482
483 const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableRemoved(items);
484
485 fn extend(
486 cx: &mut AcceptContext<'_, '_>,
487 args: &ArgParser,
488 ) -> impl IntoIterator<Item = Self::Item> {
489 let mut feature = None;
490 let mut reason = None;
491 let mut link = None;
492 let mut since = None;
493
494 if !cx.features().staged_api() {
495 cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span });
496 return None;
497 }
498
499 let list = cx.expect_list(args, cx.attr_span)?;
500
501 for param in list.mixed() {
502 let Some(param) = param.meta_item() else {
503 cx.adcx().expected_not_literal(param.span());
504 return None;
505 };
506
507 let Some(word) = param.path().word() else {
508 cx.adcx().expected_specific_argument(
509 param.span(),
510 &[sym::feature, sym::reason, sym::link, sym::since],
511 );
512 return None;
513 };
514 match word.name {
515 sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature, word)?,
516 sym::since => insert_value_into_option_or_error(cx, ¶m, &mut since, word)?,
517 sym::reason => insert_value_into_option_or_error(cx, ¶m, &mut reason, word)?,
518 sym::link => insert_value_into_option_or_error(cx, ¶m, &mut link, word)?,
519 _ => {
520 cx.adcx().expected_specific_argument(
521 param.span(),
522 &[sym::feature, sym::reason, sym::link, sym::since],
523 );
524 return None;
525 }
526 }
527 }
528
529 let Some(feature) = feature else {
531 cx.adcx().missing_name_value(list.span, sym::feature);
532 return None;
533 };
534 let Some(reason) = reason else {
535 cx.adcx().missing_name_value(list.span, sym::reason);
536 return None;
537 };
538 let Some(link) = link else {
539 cx.adcx().missing_name_value(list.span, sym::link);
540 return None;
541 };
542 let Some(since) = since else {
543 cx.adcx().missing_name_value(list.span, sym::since);
544 return None;
545 };
546
547 let Some(version) = parse_version(since) else {
548 cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
549 return None;
550 };
551
552 Some(UnstableRemovedFeature { feature, reason, link, since: version })
553 }
554}