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