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