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