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