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