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::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(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()
121 .emit_err(diagnostics::RustcAllowedUnstablePairing { span: cx.target_span });
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(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().emit_err(diagnostics::MultipleStabilityLevels { span: cx.attr_span });
155 } else if let Some((feature, level)) = parse_unstability(cx, args) {
156 this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span));
157 }
158 },
159 )];
160 const ALLOWED_TARGETS: AllowedTargets<'_> = ALLOWED_TARGETS;
161
162 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
163 let (stability, span) = self.stability?;
164
165 Some(AttributeKind::RustcBodyStability { stability, span })
166 }
167}
168
169pub(crate) struct RustcConstStableIndirectParser;
170impl NoArgsAttributeParser for RustcConstStableIndirectParser {
171 const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect];
172 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore;
173 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
174 Allow(Target::Fn),
175 Allow(Target::Method(MethodKind::Inherent)),
176 ]);
177 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
gate_check: rustc_feature::Features::rustc_attrs,
notes: &[],
}unstable!(rustc_attrs);
178 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConstStableIndirect;
179}
180
181#[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)]
182pub(crate) struct ConstStabilityParser {
183 promotable: bool,
184 stability: Option<(PartialConstStability, Span)>,
185}
186
187impl ConstStabilityParser {
188 fn check_duplicate(&self, cx: &AcceptContext<'_, '_>) -> bool {
190 if let Some((_, _)) = self.stability {
191 cx.emit_err(diagnostics::MultipleStabilityLevels { span: cx.attr_span });
192 true
193 } else {
194 false
195 }
196 }
197}
198
199impl AttributeParser for ConstStabilityParser {
200 const ATTRIBUTES: AcceptMapping<Self> = &[
201 (
202 &[sym::rustc_const_stable],
203 crate::AttributeTemplate {
word: false,
list: Some(&[r#"feature = "name""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"feature = "name""#]),
204 AttributeStability::Unstable {
gate_name: rustc_span::sym::staged_api,
gate_check: rustc_feature::Features::staged_api,
notes: &[],
}unstable!(staged_api),
205 |this, cx, args| {
206 if !this.check_duplicate(cx)
207 && let Some((feature, level)) = parse_stability(cx, args)
208 {
209 this.stability = Some((
210 PartialConstStability { level, feature, promotable: false },
211 cx.attr_path.span,
212 ));
213 }
214 },
215 ),
216 (
217 &[sym::rustc_const_unstable],
218 crate::AttributeTemplate {
word: false,
list: Some(&[r#"feature = "name""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"feature = "name""#]),
219 AttributeStability::Unstable {
gate_name: rustc_span::sym::staged_api,
gate_check: rustc_feature::Features::staged_api,
notes: &[],
}unstable!(staged_api),
220 |this, cx, args| {
221 if !this.check_duplicate(cx)
222 && let Some((feature, level)) = parse_unstability(cx, args)
223 {
224 this.stability = Some((
225 PartialConstStability { level, feature, promotable: false },
226 cx.attr_path.span,
227 ));
228 }
229 },
230 ),
231 (&[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, _| {
232 this.promotable = true;
233 }),
234 ];
235 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
236 Allow(Target::Fn),
237 Allow(Target::Method(MethodKind::Inherent)),
238 Allow(Target::Method(MethodKind::TraitImpl)),
239 Allow(Target::Method(MethodKind::Trait { body: true })),
240 Allow(Target::Impl { of_trait: false }),
241 Allow(Target::Impl { of_trait: true }),
242 Allow(Target::Use), Allow(Target::Const),
244 Allow(Target::AssocConst),
245 Allow(Target::Trait),
246 Allow(Target::Static),
247 Allow(Target::Crate),
248 ]);
249
250 fn finalize(mut self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
251 if self.promotable {
252 if let Some((ref mut stab, _)) = self.stability {
253 stab.promotable = true;
254 } else {
255 cx.dcx().emit_err(diagnostics::RustcPromotablePairing { span: cx.target_span });
256 }
257 }
258
259 let (stability, span) = self.stability?;
260
261 Some(AttributeKind::RustcConstStability { stability, span })
262 }
263}
264
265fn insert_value_into_option_or_error(
270 cx: &mut AcceptContext<'_, '_>,
271 param: &MetaItemParser,
272 item: &mut Option<Symbol>,
273 name: Ident,
274) -> Option<()> {
275 if item.is_some() {
276 cx.adcx().duplicate_key(name.span, name.name);
277 return None;
278 }
279
280 let (_ident, arg) = cx.expect_name_value(param, param.span(), Some(name.name))?;
281 let s = cx.expect_string_literal(arg)?;
282
283 *item = Some(s);
284
285 Some(())
286}
287
288pub(crate) fn parse_stability(
291 cx: &mut AcceptContext<'_, '_>,
292 args: &ArgParser,
293) -> Option<(Symbol, StabilityLevel)> {
294 let mut feature = None;
295 let mut since = None;
296
297 let list = cx.expect_list(args, cx.attr_span)?;
298
299 for param in list.mixed() {
300 let param_span = param.span();
301 let Some(param) = param.meta_item() else {
302 cx.adcx().expected_not_literal(param.span());
303 return None;
304 };
305
306 let word = param.path().word();
307 match word.map(|i| i.name) {
308 Some(sym::feature) => {
309 insert_value_into_option_or_error(cx, param, &mut feature, word.unwrap())?
310 }
311 Some(sym::since) => {
312 insert_value_into_option_or_error(cx, param, &mut since, word.unwrap())?
313 }
314 _ => {
315 cx.adcx().expected_specific_argument(param_span, &[sym::feature, sym::since]);
316 return None;
317 }
318 }
319 }
320
321 let feature = match feature {
322 Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
323 Some(_bad_feature) => Err(cx.emit_err(diagnostics::NonIdentFeature { span: cx.attr_span })),
324 None => Err(cx.emit_err(diagnostics::MissingFeature { span: cx.attr_span })),
325 };
326
327 let since = if let Some(since) = since {
328 if since.as_str() == VERSION_PLACEHOLDER {
329 StableSince::Current
330 } else if let Some(version) = parse_version(since) {
331 StableSince::Version(version)
332 } else {
333 let err = cx.emit_err(diagnostics::InvalidSince { span: cx.attr_span });
334 StableSince::Err(err)
335 }
336 } else {
337 let err = cx.emit_err(diagnostics::MissingSince { span: cx.attr_span });
338 StableSince::Err(err)
339 };
340
341 match feature {
342 Ok(feature) => {
343 let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None };
344 Some((feature, level))
345 }
346 Err(ErrorGuaranteed { .. }) => None,
347 }
348}
349
350pub(crate) fn parse_unstability(
353 cx: &mut AcceptContext<'_, '_>,
354 args: &ArgParser,
355) -> Option<(Symbol, StabilityLevel)> {
356 let mut feature = None;
357 let mut reason = None;
358 let mut issue = None;
359 let mut issue_num = None;
360 let mut implied_by = None;
361 let mut old_name = None;
362
363 let list = cx.expect_list(args, cx.attr_span)?;
364
365 for param in list.mixed() {
366 let Some(param) = param.meta_item() else {
367 cx.adcx().expected_not_literal(param.span());
368 return None;
369 };
370
371 let word = param.path().word();
372 match word.map(|i| i.name) {
373 Some(sym::feature) => {
374 insert_value_into_option_or_error(cx, param, &mut feature, word.unwrap())?
375 }
376 Some(sym::reason) => {
377 insert_value_into_option_or_error(cx, param, &mut reason, word.unwrap())?
378 }
379 Some(sym::issue) => {
380 insert_value_into_option_or_error(cx, param, &mut issue, word.unwrap())?;
381
382 issue_num = match issue.unwrap().as_str() {
385 "none" => None,
386 issue_str => match issue_str.parse::<NonZero<u32>>() {
387 Ok(num) => Some(num),
388 Err(err) => {
389 cx.emit_err(diagnostics::InvalidIssueString {
390 span: param.span(),
391 cause: diagnostics::InvalidIssueStringCause::from_int_error_kind(
392 param.args().as_name_value().unwrap().value_span,
393 err.kind(),
394 ),
395 });
396 return None;
397 }
398 },
399 };
400 }
401 Some(sym::implied_by) => {
402 insert_value_into_option_or_error(cx, param, &mut implied_by, word.unwrap())?
403 }
404 Some(sym::old_name) => {
405 insert_value_into_option_or_error(cx, param, &mut old_name, word.unwrap())?
406 }
407 _ => {
408 cx.adcx().expected_specific_argument(
409 param.span(),
410 &[sym::feature, sym::reason, sym::issue, sym::implied_by, sym::old_name],
411 );
412 return None;
413 }
414 }
415 }
416
417 let feature = match feature {
418 Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
419 Some(_bad_feature) => Err(cx.emit_err(diagnostics::NonIdentFeature { span: cx.attr_span })),
420 None => Err(cx.emit_err(diagnostics::MissingFeature { span: cx.attr_span })),
421 };
422
423 let issue = issue.ok_or_else(|| cx.emit_err(diagnostics::MissingIssue { span: cx.attr_span }));
424
425 match (feature, issue) {
426 (Ok(feature), Ok(_)) => {
427 if ACCEPTED_LANG_FEATURES.iter().any(|f| f.name == feature) {
430 cx.emit_err(diagnostics::UnstableAttrForAlreadyStableFeature {
431 attr_span: cx.attr_span,
432 item_span: cx.target_span,
433 });
434 return None;
435 }
436
437 let level = StabilityLevel::Unstable {
438 reason: UnstableReason::from_opt_reason(reason),
439 issue: issue_num,
440 implied_by,
441 old_name,
442 };
443 Some((feature, level))
444 }
445 (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None,
446 }
447}
448
449pub(crate) struct UnstableRemovedParser;
450
451impl CombineAttributeParser for UnstableRemovedParser {
452 type Item = UnstableRemovedFeature;
453 const PATH: &[Symbol] = &[sym::unstable_removed];
454 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
455 const TEMPLATE: AttributeTemplate =
456 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""#]);
457 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::staged_api,
gate_check: rustc_feature::Features::staged_api,
notes: &[],
}unstable!(staged_api);
458
459 const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableRemoved(items);
460
461 fn extend(
462 cx: &mut AcceptContext<'_, '_>,
463 args: &ArgParser,
464 ) -> impl IntoIterator<Item = Self::Item> {
465 let mut feature = None;
466 let mut reason = None;
467 let mut link = None;
468 let mut since = None;
469
470 let list = cx.expect_list(args, cx.attr_span)?;
471
472 for param in list.mixed() {
473 let Some(param) = param.meta_item() else {
474 cx.adcx().expected_not_literal(param.span());
475 return None;
476 };
477
478 let Some(word) = param.path().word() else {
479 cx.adcx().expected_specific_argument(
480 param.span(),
481 &[sym::feature, sym::reason, sym::link, sym::since],
482 );
483 return None;
484 };
485 match word.name {
486 sym::feature => insert_value_into_option_or_error(cx, param, &mut feature, word)?,
487 sym::since => insert_value_into_option_or_error(cx, param, &mut since, word)?,
488 sym::reason => insert_value_into_option_or_error(cx, param, &mut reason, word)?,
489 sym::link => insert_value_into_option_or_error(cx, param, &mut link, word)?,
490 _ => {
491 cx.adcx().expected_specific_argument(
492 param.span(),
493 &[sym::feature, sym::reason, sym::link, sym::since],
494 );
495 return None;
496 }
497 }
498 }
499
500 let Some(feature) = feature else {
502 cx.adcx().missing_name_value(list.span, sym::feature);
503 return None;
504 };
505 let Some(reason) = reason else {
506 cx.adcx().missing_name_value(list.span, sym::reason);
507 return None;
508 };
509 let Some(link) = link else {
510 cx.adcx().missing_name_value(list.span, sym::link);
511 return None;
512 };
513 let Some(since) = since else {
514 cx.adcx().missing_name_value(list.span, sym::since);
515 return None;
516 };
517
518 let Some(version) = parse_version(since) else {
519 cx.emit_err(diagnostics::InvalidSince { span: cx.attr_span });
520 return None;
521 };
522
523 Some(UnstableRemovedFeature { feature, reason, link, since: version })
524 }
525}