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