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