]> git.proxmox.com Git - rustc.git/blame - vendor/rustc-ap-rustc_attr/src/builtin.rs
Update upstream source from tag 'upstream/1.52.1+dfsg1'
[rustc.git] / vendor / rustc-ap-rustc_attr / src / builtin.rs
CommitLineData
f20569fa
XL
1//! Parsing and validation of builtin attributes
2
3use rustc_ast::{self as ast, Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem};
4use rustc_ast_pretty::pprust;
5use rustc_errors::{struct_span_err, Applicability};
6use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
7use rustc_macros::HashStable_Generic;
8use rustc_session::parse::{feature_err, ParseSess};
9use rustc_session::Session;
10use rustc_span::hygiene::Transparency;
11use rustc_span::{symbol::sym, symbol::Symbol, Span};
12use std::num::NonZeroU32;
13
14pub fn is_builtin_attr(attr: &Attribute) -> bool {
15 attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some()
16}
17
18enum AttrError {
19 MultipleItem(String),
20 UnknownMetaItem(String, &'static [&'static str]),
21 MissingSince,
22 NonIdentFeature,
23 MissingFeature,
24 MultipleStabilityLevels,
25 UnsupportedLiteral(&'static str, /* is_bytestr */ bool),
26}
27
28fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
29 let diag = &sess.span_diagnostic;
30 match error {
31 AttrError::MultipleItem(item) => {
32 struct_span_err!(diag, span, E0538, "multiple '{}' items", item).emit();
33 }
34 AttrError::UnknownMetaItem(item, expected) => {
35 let expected = expected.iter().map(|name| format!("`{}`", name)).collect::<Vec<_>>();
36 struct_span_err!(diag, span, E0541, "unknown meta item '{}'", item)
37 .span_label(span, format!("expected one of {}", expected.join(", ")))
38 .emit();
39 }
40 AttrError::MissingSince => {
41 struct_span_err!(diag, span, E0542, "missing 'since'").emit();
42 }
43 AttrError::NonIdentFeature => {
44 struct_span_err!(diag, span, E0546, "'feature' is not an identifier").emit();
45 }
46 AttrError::MissingFeature => {
47 struct_span_err!(diag, span, E0546, "missing 'feature'").emit();
48 }
49 AttrError::MultipleStabilityLevels => {
50 struct_span_err!(diag, span, E0544, "multiple stability levels").emit();
51 }
52 AttrError::UnsupportedLiteral(msg, is_bytestr) => {
53 let mut err = struct_span_err!(diag, span, E0565, "{}", msg);
54 if is_bytestr {
55 if let Ok(lint_str) = sess.source_map().span_to_snippet(span) {
56 err.span_suggestion(
57 span,
58 "consider removing the prefix",
59 lint_str[1..].to_string(),
60 Applicability::MaybeIncorrect,
61 );
62 }
63 }
64 err.emit();
65 }
66 }
67}
68
69#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug)]
70pub enum InlineAttr {
71 None,
72 Hint,
73 Always,
74 Never,
75}
76
77#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
78pub enum InstructionSetAttr {
79 ArmA32,
80 ArmT32,
81}
82
83#[derive(Clone, Encodable, Decodable, Debug)]
84pub enum OptimizeAttr {
85 None,
86 Speed,
87 Size,
88}
89
90#[derive(Copy, Clone, PartialEq)]
91pub enum UnwindAttr {
92 Allowed,
93 Aborts,
94}
95
96/// Determine what `#[unwind]` attribute is present in `attrs`, if any.
97pub fn find_unwind_attr(sess: &Session, attrs: &[Attribute]) -> Option<UnwindAttr> {
98 attrs.iter().fold(None, |ia, attr| {
99 if sess.check_name(attr, sym::unwind) {
100 if let Some(meta) = attr.meta() {
101 if let MetaItemKind::List(items) = meta.kind {
102 if items.len() == 1 {
103 if items[0].has_name(sym::allowed) {
104 return Some(UnwindAttr::Allowed);
105 } else if items[0].has_name(sym::aborts) {
106 return Some(UnwindAttr::Aborts);
107 }
108 }
109
110 struct_span_err!(
111 sess.diagnostic(),
112 attr.span,
113 E0633,
114 "malformed `unwind` attribute input"
115 )
116 .span_label(attr.span, "invalid argument")
117 .span_suggestions(
118 attr.span,
119 "the allowed arguments are `allowed` and `aborts`",
120 (vec!["allowed", "aborts"])
121 .into_iter()
122 .map(|s| format!("#[unwind({})]", s)),
123 Applicability::MachineApplicable,
124 )
125 .emit();
126 }
127 }
128 }
129
130 ia
131 })
132}
133
134/// Represents the following attributes:
135///
136/// - `#[stable]`
137/// - `#[unstable]`
138#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
139#[derive(HashStable_Generic)]
140pub struct Stability {
141 pub level: StabilityLevel,
142 pub feature: Symbol,
143}
144
145/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
146#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
147#[derive(HashStable_Generic)]
148pub struct ConstStability {
149 pub level: StabilityLevel,
150 pub feature: Symbol,
151 /// whether the function has a `#[rustc_promotable]` attribute
152 pub promotable: bool,
153}
154
155/// The available stability levels.
156#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
157#[derive(HashStable_Generic)]
158pub enum StabilityLevel {
159 // Reason for the current stability level and the relevant rust-lang issue
160 Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
161 Stable { since: Symbol },
162}
163
164impl StabilityLevel {
165 pub fn is_unstable(&self) -> bool {
166 matches!(self, StabilityLevel::Unstable { .. })
167 }
168 pub fn is_stable(&self) -> bool {
169 matches!(self, StabilityLevel::Stable { .. })
170 }
171}
172
173/// Collects stability info from all stability attributes in `attrs`.
174/// Returns `None` if no stability attributes are found.
175pub fn find_stability(
176 sess: &Session,
177 attrs: &[Attribute],
178 item_sp: Span,
179) -> (Option<Stability>, Option<ConstStability>) {
180 find_stability_generic(sess, attrs.iter(), item_sp)
181}
182
183fn find_stability_generic<'a, I>(
184 sess: &Session,
185 attrs_iter: I,
186 item_sp: Span,
187) -> (Option<Stability>, Option<ConstStability>)
188where
189 I: Iterator<Item = &'a Attribute>,
190{
191 use StabilityLevel::*;
192
193 let mut stab: Option<Stability> = None;
194 let mut const_stab: Option<ConstStability> = None;
195 let mut promotable = false;
196 let diagnostic = &sess.parse_sess.span_diagnostic;
197
198 'outer: for attr in attrs_iter {
199 if ![
200 sym::rustc_const_unstable,
201 sym::rustc_const_stable,
202 sym::unstable,
203 sym::stable,
204 sym::rustc_promotable,
205 ]
206 .iter()
207 .any(|&s| attr.has_name(s))
208 {
209 continue; // not a stability level
210 }
211
212 sess.mark_attr_used(attr);
213
214 let meta = attr.meta();
215
216 if attr.has_name(sym::rustc_promotable) {
217 promotable = true;
218 }
219 // attributes with data
220 else if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta {
221 let meta = meta.as_ref().unwrap();
222 let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
223 if item.is_some() {
224 handle_errors(
225 &sess.parse_sess,
226 meta.span,
227 AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
228 );
229 return false;
230 }
231 if let Some(v) = meta.value_str() {
232 *item = Some(v);
233 true
234 } else {
235 struct_span_err!(diagnostic, meta.span, E0539, "incorrect meta item").emit();
236 false
237 }
238 };
239
240 let meta_name = meta.name_or_empty();
241 match meta_name {
242 sym::rustc_const_unstable | sym::unstable => {
243 if meta_name == sym::unstable && stab.is_some() {
244 handle_errors(
245 &sess.parse_sess,
246 attr.span,
247 AttrError::MultipleStabilityLevels,
248 );
249 break;
250 } else if meta_name == sym::rustc_const_unstable && const_stab.is_some() {
251 handle_errors(
252 &sess.parse_sess,
253 attr.span,
254 AttrError::MultipleStabilityLevels,
255 );
256 break;
257 }
258
259 let mut feature = None;
260 let mut reason = None;
261 let mut issue = None;
262 let mut issue_num = None;
263 let mut is_soft = false;
264 for meta in metas {
265 if let Some(mi) = meta.meta_item() {
266 match mi.name_or_empty() {
267 sym::feature => {
268 if !get(mi, &mut feature) {
269 continue 'outer;
270 }
271 }
272 sym::reason => {
273 if !get(mi, &mut reason) {
274 continue 'outer;
275 }
276 }
277 sym::issue => {
278 if !get(mi, &mut issue) {
279 continue 'outer;
280 }
281
282 // These unwraps are safe because `get` ensures the meta item
283 // is a name/value pair string literal.
284 issue_num = match &*issue.unwrap().as_str() {
285 "none" => None,
286 issue => {
287 let emit_diag = |msg: &str| {
288 struct_span_err!(
289 diagnostic,
290 mi.span,
291 E0545,
292 "`issue` must be a non-zero numeric string \
293 or \"none\"",
294 )
295 .span_label(
296 mi.name_value_literal_span().unwrap(),
297 msg,
298 )
299 .emit();
300 };
301 match issue.parse() {
302 Ok(0) => {
303 emit_diag(
304 "`issue` must not be \"0\", \
305 use \"none\" instead",
306 );
307 continue 'outer;
308 }
309 Ok(num) => NonZeroU32::new(num),
310 Err(err) => {
311 emit_diag(&err.to_string());
312 continue 'outer;
313 }
314 }
315 }
316 };
317 }
318 sym::soft => {
319 if !mi.is_word() {
320 let msg = "`soft` should not have any arguments";
321 sess.parse_sess.span_diagnostic.span_err(mi.span, msg);
322 }
323 is_soft = true;
324 }
325 _ => {
326 handle_errors(
327 &sess.parse_sess,
328 meta.span(),
329 AttrError::UnknownMetaItem(
330 pprust::path_to_string(&mi.path),
331 &["feature", "reason", "issue", "soft"],
332 ),
333 );
334 continue 'outer;
335 }
336 }
337 } else {
338 handle_errors(
339 &sess.parse_sess,
340 meta.span(),
341 AttrError::UnsupportedLiteral("unsupported literal", false),
342 );
343 continue 'outer;
344 }
345 }
346
347 match (feature, reason, issue) {
348 (Some(feature), reason, Some(_)) => {
349 if !rustc_lexer::is_ident(&feature.as_str()) {
350 handle_errors(
351 &sess.parse_sess,
352 attr.span,
353 AttrError::NonIdentFeature,
354 );
355 continue;
356 }
357 let level = Unstable { reason, issue: issue_num, is_soft };
358 if sym::unstable == meta_name {
359 stab = Some(Stability { level, feature });
360 } else {
361 const_stab =
362 Some(ConstStability { level, feature, promotable: false });
363 }
364 }
365 (None, _, _) => {
366 handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
367 continue;
368 }
369 _ => {
370 struct_span_err!(diagnostic, attr.span, E0547, "missing 'issue'")
371 .emit();
372 continue;
373 }
374 }
375 }
376 sym::rustc_const_stable | sym::stable => {
377 if meta_name == sym::stable && stab.is_some() {
378 handle_errors(
379 &sess.parse_sess,
380 attr.span,
381 AttrError::MultipleStabilityLevels,
382 );
383 break;
384 } else if meta_name == sym::rustc_const_stable && const_stab.is_some() {
385 handle_errors(
386 &sess.parse_sess,
387 attr.span,
388 AttrError::MultipleStabilityLevels,
389 );
390 break;
391 }
392
393 let mut feature = None;
394 let mut since = None;
395 for meta in metas {
396 match meta {
397 NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() {
398 sym::feature => {
399 if !get(mi, &mut feature) {
400 continue 'outer;
401 }
402 }
403 sym::since => {
404 if !get(mi, &mut since) {
405 continue 'outer;
406 }
407 }
408 _ => {
409 handle_errors(
410 &sess.parse_sess,
411 meta.span(),
412 AttrError::UnknownMetaItem(
413 pprust::path_to_string(&mi.path),
414 &["since", "note"],
415 ),
416 );
417 continue 'outer;
418 }
419 },
420 NestedMetaItem::Literal(lit) => {
421 handle_errors(
422 &sess.parse_sess,
423 lit.span,
424 AttrError::UnsupportedLiteral("unsupported literal", false),
425 );
426 continue 'outer;
427 }
428 }
429 }
430
431 match (feature, since) {
432 (Some(feature), Some(since)) => {
433 let level = Stable { since };
434 if sym::stable == meta_name {
435 stab = Some(Stability { level, feature });
436 } else {
437 const_stab =
438 Some(ConstStability { level, feature, promotable: false });
439 }
440 }
441 (None, _) => {
442 handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
443 continue;
444 }
445 _ => {
446 handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
447 continue;
448 }
449 }
450 }
451 _ => unreachable!(),
452 }
453 }
454 }
455
456 // Merge the const-unstable info into the stability info
457 if promotable {
458 if let Some(ref mut stab) = const_stab {
459 stab.promotable = promotable;
460 } else {
461 struct_span_err!(
462 diagnostic,
463 item_sp,
464 E0717,
465 "`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` \
466 or a `rustc_const_stable` attribute"
467 )
468 .emit();
469 }
470 }
471
472 (stab, const_stab)
473}
474
475pub fn find_crate_name(sess: &Session, attrs: &[Attribute]) -> Option<Symbol> {
476 sess.first_attr_value_str_by_name(attrs, sym::crate_name)
477}
478
479/// Tests if a cfg-pattern matches the cfg set
480pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
481 eval_condition(cfg, sess, features, &mut |cfg| {
482 try_gate_cfg(cfg, sess, features);
483 let error = |span, msg| {
484 sess.span_diagnostic.span_err(span, msg);
485 true
486 };
487 if cfg.path.segments.len() != 1 {
488 return error(cfg.path.span, "`cfg` predicate key must be an identifier");
489 }
490 match &cfg.kind {
491 MetaItemKind::List(..) => {
492 error(cfg.span, "unexpected parentheses after `cfg` predicate key")
493 }
494 MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
495 handle_errors(
496 sess,
497 lit.span,
498 AttrError::UnsupportedLiteral(
499 "literal in `cfg` predicate value must be a string",
500 lit.kind.is_bytestr(),
501 ),
502 );
503 true
504 }
505 MetaItemKind::NameValue(..) | MetaItemKind::Word => {
506 let ident = cfg.ident().expect("multi-segment cfg predicate");
507 sess.config.contains(&(ident.name, cfg.value_str()))
508 }
509 }
510 })
511}
512
513fn try_gate_cfg(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) {
514 let gate = find_gated_cfg(|sym| cfg.has_name(sym));
515 if let (Some(feats), Some(gated_cfg)) = (features, gate) {
516 gate_cfg(&gated_cfg, cfg.span, sess, feats);
517 }
518}
519
520fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &Features) {
521 let (cfg, feature, has_feature) = gated_cfg;
522 if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
523 let explain = format!("`cfg({})` is experimental and subject to change", cfg);
524 feature_err(sess, *feature, cfg_span, &explain).emit();
525 }
526}
527
528#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
529struct Version {
530 major: u16,
531 minor: u16,
532 patch: u16,
533}
534
535fn parse_version(s: &str, allow_appendix: bool) -> Option<Version> {
536 let mut components = s.split('-');
537 let d = components.next()?;
538 if !allow_appendix && components.next().is_some() {
539 return None;
540 }
541 let mut digits = d.splitn(3, '.');
542 let major = digits.next()?.parse().ok()?;
543 let minor = digits.next()?.parse().ok()?;
544 let patch = digits.next().unwrap_or("0").parse().ok()?;
545 Some(Version { major, minor, patch })
546}
547
548/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
549/// evaluate individual items.
550pub fn eval_condition(
551 cfg: &ast::MetaItem,
552 sess: &ParseSess,
553 features: Option<&Features>,
554 eval: &mut impl FnMut(&ast::MetaItem) -> bool,
555) -> bool {
556 match cfg.kind {
557 ast::MetaItemKind::List(ref mis) if cfg.name_or_empty() == sym::version => {
558 try_gate_cfg(cfg, sess, features);
559 let (min_version, span) = match &mis[..] {
560 [NestedMetaItem::Literal(Lit { kind: LitKind::Str(sym, ..), span, .. })] => {
561 (sym, span)
562 }
563 [NestedMetaItem::Literal(Lit { span, .. })
564 | NestedMetaItem::MetaItem(MetaItem { span, .. })] => {
565 sess.span_diagnostic
566 .struct_span_err(*span, "expected a version literal")
567 .emit();
568 return false;
569 }
570 [..] => {
571 sess.span_diagnostic
572 .struct_span_err(cfg.span, "expected single version literal")
573 .emit();
574 return false;
575 }
576 };
577 let min_version = match parse_version(&min_version.as_str(), false) {
578 Some(ver) => ver,
579 None => {
580 sess.span_diagnostic
581 .struct_span_warn(
582 *span,
583 "unknown version literal format, assuming it refers to a future version",
584 )
585 .emit();
586 return false;
587 }
588 };
589 let rustc_version = parse_version(env!("CFG_RELEASE"), true).unwrap();
590
591 // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
592 if sess.assume_incomplete_release {
593 rustc_version > min_version
594 } else {
595 rustc_version >= min_version
596 }
597 }
598 ast::MetaItemKind::List(ref mis) => {
599 for mi in mis.iter() {
600 if !mi.is_meta_item() {
601 handle_errors(
602 sess,
603 mi.span(),
604 AttrError::UnsupportedLiteral("unsupported literal", false),
605 );
606 return false;
607 }
608 }
609
610 // The unwraps below may look dangerous, but we've already asserted
611 // that they won't fail with the loop above.
612 match cfg.name_or_empty() {
613 sym::any => mis
614 .iter()
615 .any(|mi| eval_condition(mi.meta_item().unwrap(), sess, features, eval)),
616 sym::all => mis
617 .iter()
618 .all(|mi| eval_condition(mi.meta_item().unwrap(), sess, features, eval)),
619 sym::not => {
620 if mis.len() != 1 {
621 struct_span_err!(
622 sess.span_diagnostic,
623 cfg.span,
624 E0536,
625 "expected 1 cfg-pattern"
626 )
627 .emit();
628 return false;
629 }
630
631 !eval_condition(mis[0].meta_item().unwrap(), sess, features, eval)
632 }
633 _ => {
634 struct_span_err!(
635 sess.span_diagnostic,
636 cfg.span,
637 E0537,
638 "invalid predicate `{}`",
639 pprust::path_to_string(&cfg.path)
640 )
641 .emit();
642 false
643 }
644 }
645 }
646 ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => eval(cfg),
647 }
648}
649
650#[derive(Debug, Encodable, Decodable, Clone, HashStable_Generic)]
651pub struct Deprecation {
652 pub since: Option<Symbol>,
653 /// The note to issue a reason.
654 pub note: Option<Symbol>,
655 /// A text snippet used to completely replace any use of the deprecated item in an expression.
656 ///
657 /// This is currently unstable.
658 pub suggestion: Option<Symbol>,
659
660 /// Whether to treat the since attribute as being a Rust version identifier
661 /// (rather than an opaque string).
662 pub is_since_rustc_version: bool,
663}
664
665/// Finds the deprecation attribute. `None` if none exists.
666pub fn find_deprecation(sess: &Session, attrs: &[Attribute]) -> Option<(Deprecation, Span)> {
667 find_deprecation_generic(sess, attrs.iter())
668}
669
670fn find_deprecation_generic<'a, I>(sess: &Session, attrs_iter: I) -> Option<(Deprecation, Span)>
671where
672 I: Iterator<Item = &'a Attribute>,
673{
674 let mut depr: Option<(Deprecation, Span)> = None;
675 let diagnostic = &sess.parse_sess.span_diagnostic;
676
677 'outer: for attr in attrs_iter {
678 if !(sess.check_name(attr, sym::deprecated) || sess.check_name(attr, sym::rustc_deprecated))
679 {
680 continue;
681 }
682
683 if let Some((_, span)) = &depr {
684 struct_span_err!(diagnostic, attr.span, E0550, "multiple deprecated attributes")
685 .span_label(attr.span, "repeated deprecation attribute")
686 .span_label(*span, "first deprecation attribute")
687 .emit();
688 break;
689 }
690
691 let meta = match attr.meta() {
692 Some(meta) => meta,
693 None => continue,
694 };
695 let mut since = None;
696 let mut note = None;
697 let mut suggestion = None;
698 match &meta.kind {
699 MetaItemKind::Word => {}
700 MetaItemKind::NameValue(..) => note = meta.value_str(),
701 MetaItemKind::List(list) => {
702 let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
703 if item.is_some() {
704 handle_errors(
705 &sess.parse_sess,
706 meta.span,
707 AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
708 );
709 return false;
710 }
711 if let Some(v) = meta.value_str() {
712 *item = Some(v);
713 true
714 } else {
715 if let Some(lit) = meta.name_value_literal() {
716 handle_errors(
717 &sess.parse_sess,
718 lit.span,
719 AttrError::UnsupportedLiteral(
720 "literal in `deprecated` \
721 value must be a string",
722 lit.kind.is_bytestr(),
723 ),
724 );
725 } else {
726 struct_span_err!(diagnostic, meta.span, E0551, "incorrect meta item")
727 .emit();
728 }
729
730 false
731 }
732 };
733
734 for meta in list {
735 match meta {
736 NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() {
737 sym::since => {
738 if !get(mi, &mut since) {
739 continue 'outer;
740 }
741 }
742 sym::note if sess.check_name(attr, sym::deprecated) => {
743 if !get(mi, &mut note) {
744 continue 'outer;
745 }
746 }
747 sym::reason if sess.check_name(attr, sym::rustc_deprecated) => {
748 if !get(mi, &mut note) {
749 continue 'outer;
750 }
751 }
752 sym::suggestion if sess.check_name(attr, sym::rustc_deprecated) => {
753 if !get(mi, &mut suggestion) {
754 continue 'outer;
755 }
756 }
757 _ => {
758 handle_errors(
759 &sess.parse_sess,
760 meta.span(),
761 AttrError::UnknownMetaItem(
762 pprust::path_to_string(&mi.path),
763 if sess.check_name(attr, sym::deprecated) {
764 &["since", "note"]
765 } else {
766 &["since", "reason", "suggestion"]
767 },
768 ),
769 );
770 continue 'outer;
771 }
772 },
773 NestedMetaItem::Literal(lit) => {
774 handle_errors(
775 &sess.parse_sess,
776 lit.span,
777 AttrError::UnsupportedLiteral(
778 "item in `deprecated` must be a key/value pair",
779 false,
780 ),
781 );
782 continue 'outer;
783 }
784 }
785 }
786 }
787 }
788
789 if suggestion.is_some() && sess.check_name(attr, sym::deprecated) {
790 unreachable!("only allowed on rustc_deprecated")
791 }
792
793 if sess.check_name(attr, sym::rustc_deprecated) {
794 if since.is_none() {
795 handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
796 continue;
797 }
798
799 if note.is_none() {
800 struct_span_err!(diagnostic, attr.span, E0543, "missing 'reason'").emit();
801 continue;
802 }
803 }
804
805 sess.mark_attr_used(&attr);
806
807 let is_since_rustc_version = sess.check_name(attr, sym::rustc_deprecated);
808 depr = Some((Deprecation { since, note, suggestion, is_since_rustc_version }, attr.span));
809 }
810
811 depr
812}
813
814#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)]
815pub enum ReprAttr {
816 ReprInt(IntType),
817 ReprC,
818 ReprPacked(u32),
819 ReprSimd,
820 ReprTransparent,
821 ReprAlign(u32),
822 ReprNoNiche,
823}
824
825#[derive(Eq, PartialEq, Debug, Copy, Clone)]
826#[derive(Encodable, Decodable, HashStable_Generic)]
827pub enum IntType {
828 SignedInt(ast::IntTy),
829 UnsignedInt(ast::UintTy),
830}
831
832impl IntType {
833 #[inline]
834 pub fn is_signed(self) -> bool {
835 use IntType::*;
836
837 match self {
838 SignedInt(..) => true,
839 UnsignedInt(..) => false,
840 }
841 }
842}
843
844/// Parse #[repr(...)] forms.
845///
846/// Valid repr contents: any of the primitive integral type names (see
847/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
848/// the same discriminant size that the corresponding C enum would or C
849/// structure layout, `packed` to remove padding, and `transparent` to elegate representation
850/// concerns to the only non-ZST field.
851pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
852 use ReprAttr::*;
853
854 let mut acc = Vec::new();
855 let diagnostic = &sess.parse_sess.span_diagnostic;
856 if attr.has_name(sym::repr) {
857 if let Some(items) = attr.meta_item_list() {
858 sess.mark_attr_used(attr);
859 for item in items {
860 if !item.is_meta_item() {
861 handle_errors(
862 &sess.parse_sess,
863 item.span(),
864 AttrError::UnsupportedLiteral(
865 "meta item in `repr` must be an identifier",
866 false,
867 ),
868 );
869 continue;
870 }
871
872 let mut recognised = false;
873 if item.is_word() {
874 let hint = match item.name_or_empty() {
875 sym::C => Some(ReprC),
876 sym::packed => Some(ReprPacked(1)),
877 sym::simd => Some(ReprSimd),
878 sym::transparent => Some(ReprTransparent),
879 sym::no_niche => Some(ReprNoNiche),
880 name => int_type_of_word(name).map(ReprInt),
881 };
882
883 if let Some(h) = hint {
884 recognised = true;
885 acc.push(h);
886 }
887 } else if let Some((name, value)) = item.name_value_literal() {
888 let parse_alignment = |node: &ast::LitKind| -> Result<u32, &'static str> {
889 if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
890 if literal.is_power_of_two() {
891 // rustc_middle::ty::layout::Align restricts align to <= 2^29
892 if *literal <= 1 << 29 {
893 Ok(*literal as u32)
894 } else {
895 Err("larger than 2^29")
896 }
897 } else {
898 Err("not a power of two")
899 }
900 } else {
901 Err("not an unsuffixed integer")
902 }
903 };
904
905 let mut literal_error = None;
906 if name == sym::align {
907 recognised = true;
908 match parse_alignment(&value.kind) {
909 Ok(literal) => acc.push(ReprAlign(literal)),
910 Err(message) => literal_error = Some(message),
911 };
912 } else if name == sym::packed {
913 recognised = true;
914 match parse_alignment(&value.kind) {
915 Ok(literal) => acc.push(ReprPacked(literal)),
916 Err(message) => literal_error = Some(message),
917 };
918 }
919 if let Some(literal_error) = literal_error {
920 struct_span_err!(
921 diagnostic,
922 item.span(),
923 E0589,
924 "invalid `repr(align)` attribute: {}",
925 literal_error
926 )
927 .emit();
928 }
929 } else if let Some(meta_item) = item.meta_item() {
930 if meta_item.has_name(sym::align) {
931 if let MetaItemKind::NameValue(ref value) = meta_item.kind {
932 recognised = true;
933 let mut err = struct_span_err!(
934 diagnostic,
935 item.span(),
936 E0693,
937 "incorrect `repr(align)` attribute format"
938 );
939 match value.kind {
940 ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
941 err.span_suggestion(
942 item.span(),
943 "use parentheses instead",
944 format!("align({})", int),
945 Applicability::MachineApplicable,
946 );
947 }
948 ast::LitKind::Str(s, _) => {
949 err.span_suggestion(
950 item.span(),
951 "use parentheses instead",
952 format!("align({})", s),
953 Applicability::MachineApplicable,
954 );
955 }
956 _ => {}
957 }
958 err.emit();
959 }
960 }
961 }
962 if !recognised {
963 // Not a word we recognize
964 struct_span_err!(
965 diagnostic,
966 item.span(),
967 E0552,
968 "unrecognized representation hint"
969 )
970 .emit();
971 }
972 }
973 }
974 }
975 acc
976}
977
978fn int_type_of_word(s: Symbol) -> Option<IntType> {
979 use IntType::*;
980
981 match s {
982 sym::i8 => Some(SignedInt(ast::IntTy::I8)),
983 sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
984 sym::i16 => Some(SignedInt(ast::IntTy::I16)),
985 sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
986 sym::i32 => Some(SignedInt(ast::IntTy::I32)),
987 sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
988 sym::i64 => Some(SignedInt(ast::IntTy::I64)),
989 sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
990 sym::i128 => Some(SignedInt(ast::IntTy::I128)),
991 sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
992 sym::isize => Some(SignedInt(ast::IntTy::Isize)),
993 sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
994 _ => None,
995 }
996}
997
998pub enum TransparencyError {
999 UnknownTransparency(Symbol, Span),
1000 MultipleTransparencyAttrs(Span, Span),
1001}
1002
1003pub fn find_transparency(
1004 sess: &Session,
1005 attrs: &[Attribute],
1006 macro_rules: bool,
1007) -> (Transparency, Option<TransparencyError>) {
1008 let mut transparency = None;
1009 let mut error = None;
1010 for attr in attrs {
1011 if sess.check_name(attr, sym::rustc_macro_transparency) {
1012 if let Some((_, old_span)) = transparency {
1013 error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span));
1014 break;
1015 } else if let Some(value) = attr.value_str() {
1016 transparency = Some((
1017 match value {
1018 sym::transparent => Transparency::Transparent,
1019 sym::semitransparent => Transparency::SemiTransparent,
1020 sym::opaque => Transparency::Opaque,
1021 _ => {
1022 error = Some(TransparencyError::UnknownTransparency(value, attr.span));
1023 continue;
1024 }
1025 },
1026 attr.span,
1027 ));
1028 }
1029 }
1030 }
1031 let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque };
1032 (transparency.map_or(fallback, |t| t.0), error)
1033}
1034
1035pub fn allow_internal_unstable<'a>(
1036 sess: &'a Session,
1037 attrs: &'a [Attribute],
1038) -> Option<impl Iterator<Item = Symbol> + 'a> {
1039 allow_unstable(sess, attrs, sym::allow_internal_unstable)
1040}
1041
1042pub fn rustc_allow_const_fn_unstable<'a>(
1043 sess: &'a Session,
1044 attrs: &'a [Attribute],
1045) -> Option<impl Iterator<Item = Symbol> + 'a> {
1046 allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
1047}
1048
1049fn allow_unstable<'a>(
1050 sess: &'a Session,
1051 attrs: &'a [Attribute],
1052 symbol: Symbol,
1053) -> Option<impl Iterator<Item = Symbol> + 'a> {
1054 let attrs = sess.filter_by_name(attrs, symbol);
1055 let list = attrs
1056 .filter_map(move |attr| {
1057 attr.meta_item_list().or_else(|| {
1058 sess.diagnostic().span_err(
1059 attr.span,
1060 &format!("`{}` expects a list of feature names", symbol.to_ident_string()),
1061 );
1062 None
1063 })
1064 })
1065 .flatten();
1066
1067 Some(list.into_iter().filter_map(move |it| {
1068 let name = it.ident().map(|ident| ident.name);
1069 if name.is_none() {
1070 sess.diagnostic().span_err(
1071 it.span(),
1072 &format!("`{}` expects feature names", symbol.to_ident_string()),
1073 );
1074 }
1075 name
1076 }))
1077}