1use std::num::NonZero;
2
3use rustc_errors::ErrorGuaranteed;
4use rustc_hir::target::GenericParamKind;
5use rustc_hir::{
6 DefaultBodyStability, MethodKind, PartialConstStability, Stability, StabilityLevel,
7 StableSince, Target, UnstableReason, VERSION_PLACEHOLDER,
8};
9
10use super::prelude::*;
11use super::util::parse_version;
12use crate::session_diagnostics::{self};
13
14macro_rules! reject_outside_std {
15 ($cx: ident) => {
16 if !$cx.features().staged_api() {
18 $cx.emit_err(session_diagnostics::StabilityOutsideStd { span: $cx.attr_span });
19 return;
20 }
21 };
22}
23
24const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
25 Allow(Target::Fn),
26 Allow(Target::Struct),
27 Allow(Target::Enum),
28 Allow(Target::Union),
29 Allow(Target::Method(MethodKind::Inherent)),
30 Allow(Target::Method(MethodKind::Trait { body: false })),
31 Allow(Target::Method(MethodKind::Trait { body: true })),
32 Allow(Target::Method(MethodKind::TraitImpl)),
33 Allow(Target::Impl { of_trait: false }),
34 Allow(Target::Impl { of_trait: true }),
35 Allow(Target::MacroDef),
36 Allow(Target::Crate),
37 Allow(Target::Mod),
38 Allow(Target::Use), Allow(Target::Const),
40 Allow(Target::AssocConst),
41 Allow(Target::AssocTy),
42 Allow(Target::Trait),
43 Allow(Target::TraitAlias),
44 Allow(Target::TyAlias),
45 Allow(Target::Variant),
46 Allow(Target::Field),
47 Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: true }),
48 Allow(Target::Static),
49 Allow(Target::ForeignFn),
50 Allow(Target::ForeignStatic),
51 Allow(Target::ExternCrate),
52]);
53
54#[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)]
55pub(crate) struct StabilityParser {
56 allowed_through_unstable_modules: Option<Symbol>,
57 stability: Option<(Stability, Span)>,
58}
59
60impl StabilityParser {
61 fn check_duplicate<S: Stage>(&self, cx: &AcceptContext<'_, '_, S>) -> bool {
63 if let Some((_, _)) = self.stability {
64 cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
65 true
66 } else {
67 false
68 }
69 }
70}
71
72impl<S: Stage> AttributeParser<S> for StabilityParser {
73 const ATTRIBUTES: AcceptMapping<Self, S> = &[
74 (
75 &[sym::stable],
76 ::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""#]),
77 |this, cx, args| {
78 if !cx.features().staged_api() {
cx.emit_err(session_diagnostics::StabilityOutsideStd {
span: cx.attr_span,
});
return;
};reject_outside_std!(cx);
79 if !this.check_duplicate(cx)
80 && let Some((feature, level)) = parse_stability(cx, args)
81 {
82 this.stability = Some((Stability { level, feature }, cx.attr_span));
83 }
84 },
85 ),
86 (
87 &[sym::unstable],
88 ::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""#]),
89 |this, cx, args| {
90 if !cx.features().staged_api() {
cx.emit_err(session_diagnostics::StabilityOutsideStd {
span: cx.attr_span,
});
return;
};reject_outside_std!(cx);
91 if !this.check_duplicate(cx)
92 && let Some((feature, level)) = parse_unstability(cx, args)
93 {
94 this.stability = Some((Stability { level, feature }, cx.attr_span));
95 }
96 },
97 ),
98 (
99 &[sym::rustc_allowed_through_unstable_modules],
100 ::rustc_feature::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["deprecation message"]),
docs: None,
}template!(NameValueStr: "deprecation message"),
101 |this, cx, args| {
102 if !cx.features().staged_api() {
cx.emit_err(session_diagnostics::StabilityOutsideStd {
span: cx.attr_span,
});
return;
};reject_outside_std!(cx);
103 let Some(nv) = args.name_value() else {
104 cx.expected_name_value(cx.attr_span, None);
105 return;
106 };
107 let Some(value_str) = nv.value_as_str() else {
108 cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
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<'_, '_, S>) -> 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<S: Stage> AttributeParser<S> for BodyStabilityParser {
158 const ATTRIBUTES: AcceptMapping<Self, S> = &[(
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<'_, '_, S>) -> Option<AttributeKind> {
174 let (stability, span) = self.stability?;
175
176 Some(AttributeKind::RustcBodyStability { stability, span })
177 }
178}
179
180pub(crate) struct ConstStabilityIndirectParser;
181impl<S: Stage> NoArgsAttributeParser<S> for ConstStabilityIndirectParser {
182 const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect];
183 const ON_DUPLICATE: OnDuplicate<S> = 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::RustcConstStabilityIndirect;
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<S: Stage>(&self, cx: &AcceptContext<'_, '_, S>) -> 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<S: Stage> AttributeParser<S> for ConstStabilityParser {
210 const ATTRIBUTES: AcceptMapping<Self, S> = &[
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<'_, '_, S>) -> 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<S: Stage>(
283 cx: &AcceptContext<'_, '_, S>,
284 param: &MetaItemParser,
285 item: &mut Option<Symbol>,
286 name: Ident,
287) -> Option<()> {
288 if item.is_some() {
289 cx.duplicate_key(name.span, name.name);
290 None
291 } else if let Some(v) = param.args().name_value()
292 && let Some(s) = v.value_as_str()
293 {
294 *item = Some(s);
295 Some(())
296 } else {
297 cx.expected_name_value(param.span(), Some(name.name));
298 None
299 }
300}
301
302pub(crate) fn parse_stability<S: Stage>(
305 cx: &AcceptContext<'_, '_, S>,
306 args: &ArgParser,
307) -> Option<(Symbol, StabilityLevel)> {
308 let mut feature = None;
309 let mut since = None;
310
311 let ArgParser::List(list) = args else {
312 cx.expected_list(cx.attr_span, args);
313 return None;
314 };
315
316 for param in list.mixed() {
317 let param_span = param.span();
318 let Some(param) = param.meta_item() else {
319 cx.unexpected_literal(param.span());
320 return None;
321 };
322
323 let word = param.path().word();
324 match word.map(|i| i.name) {
325 Some(sym::feature) => {
326 insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())?
327 }
328 Some(sym::since) => {
329 insert_value_into_option_or_error(cx, ¶m, &mut since, word.unwrap())?
330 }
331 _ => {
332 cx.expected_specific_argument(param_span, &[sym::feature, sym::since]);
333 return None;
334 }
335 }
336 }
337
338 let feature = match feature {
339 Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
340 Some(_bad_feature) => {
341 Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
342 }
343 None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
344 };
345
346 let since = if let Some(since) = since {
347 if since.as_str() == VERSION_PLACEHOLDER {
348 StableSince::Current
349 } else if let Some(version) = parse_version(since) {
350 StableSince::Version(version)
351 } else {
352 let err = cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
353 StableSince::Err(err)
354 }
355 } else {
356 let err = cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
357 StableSince::Err(err)
358 };
359
360 match feature {
361 Ok(feature) => {
362 let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None };
363 Some((feature, level))
364 }
365 Err(ErrorGuaranteed { .. }) => None,
366 }
367}
368
369pub(crate) fn parse_unstability<S: Stage>(
372 cx: &AcceptContext<'_, '_, S>,
373 args: &ArgParser,
374) -> Option<(Symbol, StabilityLevel)> {
375 let mut feature = None;
376 let mut reason = None;
377 let mut issue = None;
378 let mut issue_num = None;
379 let mut is_soft = false;
380 let mut implied_by = None;
381 let mut old_name = None;
382
383 let ArgParser::List(list) = args else {
384 cx.expected_list(cx.attr_span, args);
385 return None;
386 };
387
388 for param in list.mixed() {
389 let Some(param) = param.meta_item() else {
390 cx.unexpected_literal(param.span());
391 return None;
392 };
393
394 let word = param.path().word();
395 match word.map(|i| i.name) {
396 Some(sym::feature) => {
397 insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())?
398 }
399 Some(sym::reason) => {
400 insert_value_into_option_or_error(cx, ¶m, &mut reason, word.unwrap())?
401 }
402 Some(sym::issue) => {
403 insert_value_into_option_or_error(cx, ¶m, &mut issue, word.unwrap())?;
404
405 issue_num = match issue.unwrap().as_str() {
408 "none" => None,
409 issue_str => match issue_str.parse::<NonZero<u32>>() {
410 Ok(num) => Some(num),
411 Err(err) => {
412 cx.emit_err(
413 session_diagnostics::InvalidIssueString {
414 span: param.span(),
415 cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
416 param.args().name_value().unwrap().value_span,
417 err.kind(),
418 ),
419 },
420 );
421 return None;
422 }
423 },
424 };
425 }
426 Some(sym::soft) => {
427 if let Err(span) = args.no_args() {
428 cx.emit_err(session_diagnostics::SoftNoArgs { span });
429 }
430 is_soft = true;
431 }
432 Some(sym::implied_by) => {
433 insert_value_into_option_or_error(cx, ¶m, &mut implied_by, word.unwrap())?
434 }
435 Some(sym::old_name) => {
436 insert_value_into_option_or_error(cx, ¶m, &mut old_name, word.unwrap())?
437 }
438 _ => {
439 cx.expected_specific_argument(
440 param.span(),
441 &[
442 sym::feature,
443 sym::reason,
444 sym::issue,
445 sym::soft,
446 sym::implied_by,
447 sym::old_name,
448 ],
449 );
450 return None;
451 }
452 }
453 }
454
455 let feature = match feature {
456 Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
457 Some(_bad_feature) => {
458 Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
459 }
460 None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
461 };
462
463 let issue =
464 issue.ok_or_else(|| cx.emit_err(session_diagnostics::MissingIssue { span: cx.attr_span }));
465
466 match (feature, issue) {
467 (Ok(feature), Ok(_)) => {
468 let level = StabilityLevel::Unstable {
469 reason: UnstableReason::from_opt_reason(reason),
470 issue: issue_num,
471 is_soft,
472 implied_by,
473 old_name,
474 };
475 Some((feature, level))
476 }
477 (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None,
478 }
479}