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