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 list = cx.expect_list(args, cx.attr_span)?;
315
316 for param in list.mixed() {
317 let param_span = param.span();
318 let Some(param) = param.meta_item() else {
319 cx.adcx().expected_not_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.adcx().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: &mut 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 implied_by = None;
380 let mut old_name = None;
381
382 let list = cx.expect_list(args, cx.attr_span)?;
383
384 for param in list.mixed() {
385 let Some(param) = param.meta_item() else {
386 cx.adcx().expected_not_literal(param.span());
387 return None;
388 };
389
390 let word = param.path().word();
391 match word.map(|i| i.name) {
392 Some(sym::feature) => {
393 insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())?
394 }
395 Some(sym::reason) => {
396 insert_value_into_option_or_error(cx, ¶m, &mut reason, word.unwrap())?
397 }
398 Some(sym::issue) => {
399 insert_value_into_option_or_error(cx, ¶m, &mut issue, word.unwrap())?;
400
401 issue_num = match issue.unwrap().as_str() {
404 "none" => None,
405 issue_str => match issue_str.parse::<NonZero<u32>>() {
406 Ok(num) => Some(num),
407 Err(err) => {
408 cx.emit_err(
409 session_diagnostics::InvalidIssueString {
410 span: param.span(),
411 cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
412 param.args().name_value().unwrap().value_span,
413 err.kind(),
414 ),
415 },
416 );
417 return None;
418 }
419 },
420 };
421 }
422 Some(sym::implied_by) => {
423 insert_value_into_option_or_error(cx, ¶m, &mut implied_by, word.unwrap())?
424 }
425 Some(sym::old_name) => {
426 insert_value_into_option_or_error(cx, ¶m, &mut old_name, word.unwrap())?
427 }
428 _ => {
429 cx.adcx().expected_specific_argument(
430 param.span(),
431 &[sym::feature, sym::reason, sym::issue, sym::implied_by, sym::old_name],
432 );
433 return None;
434 }
435 }
436 }
437
438 let feature = match feature {
439 Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
440 Some(_bad_feature) => {
441 Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
442 }
443 None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
444 };
445
446 let issue =
447 issue.ok_or_else(|| cx.emit_err(session_diagnostics::MissingIssue { span: cx.attr_span }));
448
449 match (feature, issue) {
450 (Ok(feature), Ok(_)) => {
451 if ACCEPTED_LANG_FEATURES.iter().any(|f| f.name == feature) {
454 cx.emit_err(session_diagnostics::UnstableAttrForAlreadyStableFeature {
455 attr_span: cx.attr_span,
456 item_span: cx.target_span,
457 });
458 return None;
459 }
460
461 let level = StabilityLevel::Unstable {
462 reason: UnstableReason::from_opt_reason(reason),
463 issue: issue_num,
464 implied_by,
465 old_name,
466 };
467 Some((feature, level))
468 }
469 (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None,
470 }
471}
472
473pub(crate) struct UnstableRemovedParser;
474
475impl<S: Stage> CombineAttributeParser<S> for UnstableRemovedParser {
476 type Item = UnstableRemovedFeature;
477 const PATH: &[Symbol] = &[sym::unstable_removed];
478 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
479 const TEMPLATE: AttributeTemplate =
480 ::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""#]);
481
482 const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableRemoved(items);
483
484 fn extend(
485 cx: &mut AcceptContext<'_, '_, S>,
486 args: &ArgParser,
487 ) -> impl IntoIterator<Item = Self::Item> {
488 let mut feature = None;
489 let mut reason = None;
490 let mut link = None;
491 let mut since = None;
492
493 if !cx.features().staged_api() {
494 cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span });
495 return None;
496 }
497
498 let list = cx.expect_list(args, cx.attr_span)?;
499
500 for param in list.mixed() {
501 let Some(param) = param.meta_item() else {
502 cx.adcx().expected_not_literal(param.span());
503 return None;
504 };
505
506 let Some(word) = param.path().word() else {
507 cx.adcx().expected_specific_argument(
508 param.span(),
509 &[sym::feature, sym::reason, sym::link, sym::since],
510 );
511 return None;
512 };
513 match word.name {
514 sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature, word)?,
515 sym::since => insert_value_into_option_or_error(cx, ¶m, &mut since, word)?,
516 sym::reason => insert_value_into_option_or_error(cx, ¶m, &mut reason, word)?,
517 sym::link => insert_value_into_option_or_error(cx, ¶m, &mut link, word)?,
518 _ => {
519 cx.adcx().expected_specific_argument(
520 param.span(),
521 &[sym::feature, sym::reason, sym::link, sym::since],
522 );
523 return None;
524 }
525 }
526 }
527
528 let Some(feature) = feature else {
530 cx.adcx().missing_name_value(list.span, sym::feature);
531 return None;
532 };
533 let Some(reason) = reason else {
534 cx.adcx().missing_name_value(list.span, sym::reason);
535 return None;
536 };
537 let Some(link) = link else {
538 cx.adcx().missing_name_value(list.span, sym::link);
539 return None;
540 };
541 let Some(since) = since else {
542 cx.adcx().missing_name_value(list.span, sym::since);
543 return None;
544 };
545
546 let Some(version) = parse_version(since) else {
547 cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
548 return None;
549 };
550
551 Some(UnstableRemovedFeature { feature, reason, link, since: version })
552 }
553}