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