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