]>
Commit | Line | Data |
---|---|---|
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, HashStable_Generic)] | |
70 | pub enum InlineAttr { | |
71 | None, | |
72 | Hint, | |
73 | Always, | |
74 | Never, | |
75 | } | |
76 | ||
77 | #[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)] | |
78 | pub enum InstructionSetAttr { | |
79 | ArmA32, | |
80 | ArmT32, | |
81 | } | |
82 | ||
83 | #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] | |
84 | pub enum OptimizeAttr { | |
85 | None, | |
86 | Speed, | |
87 | Size, | |
88 | } | |
89 | ||
90 | /// Represents the following attributes: | |
91 | /// | |
92 | /// - `#[stable]` | |
93 | /// - `#[unstable]` | |
94 | #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] | |
95 | #[derive(HashStable_Generic)] | |
96 | pub struct Stability { | |
97 | pub level: StabilityLevel, | |
98 | pub feature: Symbol, | |
99 | } | |
100 | ||
101 | /// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. | |
102 | #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] | |
103 | #[derive(HashStable_Generic)] | |
104 | pub struct ConstStability { | |
105 | pub level: StabilityLevel, | |
106 | pub feature: Symbol, | |
107 | /// whether the function has a `#[rustc_promotable]` attribute | |
108 | pub promotable: bool, | |
109 | } | |
110 | ||
111 | /// The available stability levels. | |
112 | #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] | |
113 | #[derive(HashStable_Generic)] | |
114 | pub enum StabilityLevel { | |
115 | // Reason for the current stability level and the relevant rust-lang issue | |
116 | Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool }, | |
117 | Stable { since: Symbol }, | |
118 | } | |
119 | ||
120 | impl StabilityLevel { | |
121 | pub fn is_unstable(&self) -> bool { | |
122 | matches!(self, StabilityLevel::Unstable { .. }) | |
123 | } | |
124 | pub fn is_stable(&self) -> bool { | |
125 | matches!(self, StabilityLevel::Stable { .. }) | |
126 | } | |
127 | } | |
128 | ||
129 | /// Collects stability info from all stability attributes in `attrs`. | |
130 | /// Returns `None` if no stability attributes are found. | |
131 | pub fn find_stability( | |
132 | sess: &Session, | |
133 | attrs: &[Attribute], | |
134 | item_sp: Span, | |
135 | ) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>) { | |
136 | find_stability_generic(sess, attrs.iter(), item_sp) | |
137 | } | |
138 | ||
139 | fn find_stability_generic<'a, I>( | |
140 | sess: &Session, | |
141 | attrs_iter: I, | |
142 | item_sp: Span, | |
143 | ) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>) | |
144 | where | |
145 | I: Iterator<Item = &'a Attribute>, | |
146 | { | |
147 | use StabilityLevel::*; | |
148 | ||
149 | let mut stab: Option<(Stability, Span)> = None; | |
150 | let mut const_stab: Option<(ConstStability, Span)> = None; | |
151 | let mut promotable = false; | |
152 | ||
153 | let diagnostic = &sess.parse_sess.span_diagnostic; | |
154 | ||
155 | 'outer: for attr in attrs_iter { | |
156 | if ![ | |
157 | sym::rustc_const_unstable, | |
158 | sym::rustc_const_stable, | |
159 | sym::unstable, | |
160 | sym::stable, | |
161 | sym::rustc_promotable, | |
162 | ] | |
163 | .iter() | |
164 | .any(|&s| attr.has_name(s)) | |
165 | { | |
166 | continue; // not a stability level | |
167 | } | |
168 | ||
169 | let meta = attr.meta(); | |
170 | ||
171 | if attr.has_name(sym::rustc_promotable) { | |
172 | promotable = true; | |
173 | } | |
174 | // attributes with data | |
175 | else if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta { | |
176 | let meta = meta.as_ref().unwrap(); | |
177 | let get = |meta: &MetaItem, item: &mut Option<Symbol>| { | |
178 | if item.is_some() { | |
179 | handle_errors( | |
180 | &sess.parse_sess, | |
181 | meta.span, | |
182 | AttrError::MultipleItem(pprust::path_to_string(&meta.path)), | |
183 | ); | |
184 | return false; | |
185 | } | |
186 | if let Some(v) = meta.value_str() { | |
187 | *item = Some(v); | |
188 | true | |
189 | } else { | |
190 | struct_span_err!(diagnostic, meta.span, E0539, "incorrect meta item").emit(); | |
191 | false | |
192 | } | |
193 | }; | |
194 | ||
195 | let meta_name = meta.name_or_empty(); | |
196 | match meta_name { | |
197 | sym::rustc_const_unstable | sym::unstable => { | |
198 | if meta_name == sym::unstable && stab.is_some() { | |
199 | handle_errors( | |
200 | &sess.parse_sess, | |
201 | attr.span, | |
202 | AttrError::MultipleStabilityLevels, | |
203 | ); | |
204 | break; | |
205 | } else if meta_name == sym::rustc_const_unstable && const_stab.is_some() { | |
206 | handle_errors( | |
207 | &sess.parse_sess, | |
208 | attr.span, | |
209 | AttrError::MultipleStabilityLevels, | |
210 | ); | |
211 | break; | |
212 | } | |
213 | ||
214 | let mut feature = None; | |
215 | let mut reason = None; | |
216 | let mut issue = None; | |
217 | let mut issue_num = None; | |
218 | let mut is_soft = false; | |
219 | for meta in metas { | |
220 | if let Some(mi) = meta.meta_item() { | |
221 | match mi.name_or_empty() { | |
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 | } | |
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( | |
251 | mi.name_value_literal_span().unwrap(), | |
252 | msg, | |
253 | ) | |
254 | .emit(); | |
255 | }; | |
256 | match issue.parse() { | |
257 | Ok(0) => { | |
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 | }; | |
272 | } | |
273 | sym::soft => { | |
274 | if !mi.is_word() { | |
275 | let msg = "`soft` should not have any arguments"; | |
276 | sess.parse_sess.span_diagnostic.span_err(mi.span, msg); | |
277 | } | |
278 | is_soft = true; | |
279 | } | |
280 | _ => { | |
281 | handle_errors( | |
282 | &sess.parse_sess, | |
283 | meta.span(), | |
284 | AttrError::UnknownMetaItem( | |
285 | pprust::path_to_string(&mi.path), | |
286 | &["feature", "reason", "issue", "soft"], | |
287 | ), | |
288 | ); | |
289 | continue 'outer; | |
290 | } | |
291 | } | |
292 | } else { | |
293 | handle_errors( | |
294 | &sess.parse_sess, | |
295 | meta.span(), | |
296 | AttrError::UnsupportedLiteral("unsupported literal", false), | |
297 | ); | |
298 | continue 'outer; | |
299 | } | |
300 | } | |
301 | ||
302 | match (feature, reason, issue) { | |
303 | (Some(feature), reason, Some(_)) => { | |
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 | } | |
312 | let level = Unstable { reason, issue: issue_num, is_soft }; | |
313 | if sym::unstable == meta_name { | |
314 | stab = Some((Stability { level, feature }, attr.span)); | |
315 | } else { | |
316 | const_stab = Some(( | |
317 | ConstStability { level, feature, promotable: false }, | |
318 | attr.span, | |
319 | )); | |
320 | } | |
321 | } | |
322 | (None, _, _) => { | |
323 | handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature); | |
324 | continue; | |
325 | } | |
326 | _ => { | |
327 | struct_span_err!(diagnostic, attr.span, E0547, "missing 'issue'") | |
328 | .emit(); | |
329 | continue; | |
330 | } | |
331 | } | |
332 | } | |
333 | sym::rustc_const_stable | sym::stable => { | |
334 | if meta_name == sym::stable && stab.is_some() { | |
335 | handle_errors( | |
336 | &sess.parse_sess, | |
337 | attr.span, | |
338 | AttrError::MultipleStabilityLevels, | |
339 | ); | |
340 | break; | |
341 | } else if meta_name == sym::rustc_const_stable && const_stab.is_some() { | |
342 | handle_errors( | |
343 | &sess.parse_sess, | |
344 | attr.span, | |
345 | AttrError::MultipleStabilityLevels, | |
346 | ); | |
347 | break; | |
348 | } | |
349 | ||
350 | let mut feature = None; | |
351 | let mut since = None; | |
352 | for meta in metas { | |
353 | match meta { | |
354 | NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() { | |
355 | sym::feature => { | |
356 | if !get(mi, &mut feature) { | |
357 | continue 'outer; | |
358 | } | |
359 | } | |
360 | sym::since => { | |
361 | if !get(mi, &mut since) { | |
362 | continue 'outer; | |
363 | } | |
364 | } | |
365 | _ => { | |
366 | handle_errors( | |
367 | &sess.parse_sess, | |
368 | meta.span(), | |
369 | AttrError::UnknownMetaItem( | |
370 | pprust::path_to_string(&mi.path), | |
371 | &["since", "note"], | |
372 | ), | |
373 | ); | |
374 | continue 'outer; | |
375 | } | |
376 | }, | |
377 | NestedMetaItem::Literal(lit) => { | |
378 | handle_errors( | |
379 | &sess.parse_sess, | |
380 | lit.span, | |
381 | AttrError::UnsupportedLiteral("unsupported literal", false), | |
382 | ); | |
383 | continue 'outer; | |
384 | } | |
385 | } | |
386 | } | |
387 | ||
388 | match (feature, since) { | |
389 | (Some(feature), Some(since)) => { | |
390 | let level = Stable { since }; | |
391 | if sym::stable == meta_name { | |
392 | stab = Some((Stability { level, feature }, attr.span)); | |
393 | } else { | |
394 | const_stab = Some(( | |
395 | ConstStability { level, feature, promotable: false }, | |
396 | attr.span, | |
397 | )); | |
398 | } | |
399 | } | |
400 | (None, _) => { | |
401 | handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature); | |
402 | continue; | |
403 | } | |
404 | _ => { | |
405 | handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince); | |
406 | continue; | |
407 | } | |
408 | } | |
409 | } | |
410 | _ => unreachable!(), | |
411 | } | |
412 | } | |
413 | } | |
414 | ||
415 | // Merge the const-unstable info into the stability info | |
416 | if promotable { | |
417 | if let Some((ref mut stab, _)) = const_stab { | |
418 | stab.promotable = promotable; | |
419 | } else { | |
420 | struct_span_err!( | |
421 | diagnostic, | |
422 | item_sp, | |
423 | E0717, | |
424 | "`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` \ | |
425 | or a `rustc_const_stable` attribute" | |
426 | ) | |
427 | .emit(); | |
428 | } | |
429 | } | |
430 | ||
431 | (stab, const_stab) | |
432 | } | |
433 | ||
434 | pub fn find_crate_name(sess: &Session, attrs: &[Attribute]) -> Option<Symbol> { | |
435 | sess.first_attr_value_str_by_name(attrs, sym::crate_name) | |
436 | } | |
437 | ||
438 | /// Tests if a cfg-pattern matches the cfg set | |
439 | pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool { | |
440 | eval_condition(cfg, sess, features, &mut |cfg| { | |
441 | try_gate_cfg(cfg, sess, features); | |
442 | let error = |span, msg| { | |
443 | sess.span_diagnostic.span_err(span, msg); | |
444 | true | |
445 | }; | |
446 | if cfg.path.segments.len() != 1 { | |
447 | return error(cfg.path.span, "`cfg` predicate key must be an identifier"); | |
448 | } | |
449 | match &cfg.kind { | |
450 | MetaItemKind::List(..) => { | |
451 | error(cfg.span, "unexpected parentheses after `cfg` predicate key") | |
452 | } | |
453 | MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { | |
454 | handle_errors( | |
455 | sess, | |
456 | lit.span, | |
457 | AttrError::UnsupportedLiteral( | |
458 | "literal in `cfg` predicate value must be a string", | |
459 | lit.kind.is_bytestr(), | |
460 | ), | |
461 | ); | |
462 | true | |
463 | } | |
464 | MetaItemKind::NameValue(..) | MetaItemKind::Word => { | |
465 | let ident = cfg.ident().expect("multi-segment cfg predicate"); | |
466 | sess.config.contains(&(ident.name, cfg.value_str())) | |
467 | } | |
468 | } | |
469 | }) | |
470 | } | |
471 | ||
472 | fn try_gate_cfg(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) { | |
473 | let gate = find_gated_cfg(|sym| cfg.has_name(sym)); | |
474 | if let (Some(feats), Some(gated_cfg)) = (features, gate) { | |
475 | gate_cfg(&gated_cfg, cfg.span, sess, feats); | |
476 | } | |
477 | } | |
478 | ||
479 | fn 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); | |
483 | feature_err(sess, *feature, cfg_span, &explain).emit(); | |
484 | } | |
485 | } | |
486 | ||
487 | #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] | |
488 | struct Version { | |
489 | major: u16, | |
490 | minor: u16, | |
491 | patch: u16, | |
492 | } | |
493 | ||
494 | fn 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 | ||
507 | /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to | |
508 | /// evaluate individual items. | |
509 | pub fn eval_condition( | |
510 | cfg: &ast::MetaItem, | |
511 | sess: &ParseSess, | |
512 | features: Option<&Features>, | |
513 | eval: &mut impl FnMut(&ast::MetaItem) -> bool, | |
514 | ) -> bool { | |
515 | match cfg.kind { | |
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 | }; | |
536 | let min_version = match parse_version(&min_version.as_str(), false) { | |
537 | Some(ver) => ver, | |
538 | None => { | |
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(); | |
545 | return false; | |
546 | } | |
547 | }; | |
548 | let rustc_version = parse_version(env!("CFG_RELEASE"), true).unwrap(); | |
549 | ||
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 | } | |
556 | } | |
557 | ast::MetaItemKind::List(ref mis) => { | |
558 | for mi in mis.iter() { | |
559 | if !mi.is_meta_item() { | |
560 | handle_errors( | |
561 | sess, | |
562 | mi.span(), | |
563 | AttrError::UnsupportedLiteral("unsupported literal", false), | |
564 | ); | |
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. | |
571 | match cfg.name_or_empty() { | |
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)), | |
578 | sym::not => { | |
579 | if mis.len() != 1 { | |
580 | struct_span_err!( | |
581 | sess.span_diagnostic, | |
582 | cfg.span, | |
583 | E0536, | |
584 | "expected 1 cfg-pattern" | |
585 | ) | |
586 | .emit(); | |
587 | return false; | |
588 | } | |
589 | ||
590 | !eval_condition(mis[0].meta_item().unwrap(), sess, features, eval) | |
591 | } | |
592 | _ => { | |
593 | struct_span_err!( | |
594 | sess.span_diagnostic, | |
595 | cfg.span, | |
596 | E0537, | |
597 | "invalid predicate `{}`", | |
598 | pprust::path_to_string(&cfg.path) | |
599 | ) | |
600 | .emit(); | |
601 | false | |
602 | } | |
603 | } | |
604 | } | |
605 | ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => eval(cfg), | |
606 | } | |
607 | } | |
608 | ||
609 | #[derive(Debug, Encodable, Decodable, Clone, HashStable_Generic)] | |
610 | pub struct Deprecation { | |
611 | pub since: Option<Symbol>, | |
612 | /// The note to issue a reason. | |
613 | pub note: Option<Symbol>, | |
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, | |
622 | } | |
623 | ||
624 | /// Finds the deprecation attribute. `None` if none exists. | |
625 | pub fn find_deprecation(sess: &Session, attrs: &[Attribute]) -> Option<(Deprecation, Span)> { | |
626 | find_deprecation_generic(sess, attrs.iter()) | |
627 | } | |
628 | ||
629 | fn find_deprecation_generic<'a, I>(sess: &Session, attrs_iter: I) -> Option<(Deprecation, Span)> | |
630 | where | |
631 | I: Iterator<Item = &'a Attribute>, | |
632 | { | |
633 | let mut depr: Option<(Deprecation, Span)> = None; | |
634 | let diagnostic = &sess.parse_sess.span_diagnostic; | |
635 | ||
636 | 'outer: for attr in attrs_iter { | |
637 | if !(attr.has_name(sym::deprecated) || attr.has_name(sym::rustc_deprecated)) { | |
638 | continue; | |
639 | } | |
640 | ||
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(); | |
646 | break; | |
647 | } | |
648 | ||
649 | let meta = match attr.meta() { | |
650 | Some(meta) => meta, | |
651 | None => continue, | |
652 | }; | |
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(), | |
659 | MetaItemKind::List(list) => { | |
660 | let get = |meta: &MetaItem, item: &mut Option<Symbol>| { | |
661 | if item.is_some() { | |
662 | handle_errors( | |
663 | &sess.parse_sess, | |
664 | meta.span, | |
665 | AttrError::MultipleItem(pprust::path_to_string(&meta.path)), | |
666 | ); | |
667 | return false; | |
668 | } | |
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( | |
675 | &sess.parse_sess, | |
676 | lit.span, | |
677 | AttrError::UnsupportedLiteral( | |
678 | "literal in `deprecated` \ | |
679 | value must be a string", | |
680 | lit.kind.is_bytestr(), | |
681 | ), | |
682 | ); | |
683 | } else { | |
684 | struct_span_err!(diagnostic, meta.span, E0551, "incorrect meta item") | |
685 | .emit(); | |
686 | } | |
687 | ||
688 | false | |
689 | } | |
690 | }; | |
691 | ||
692 | for meta in list { | |
693 | match meta { | |
694 | NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() { | |
695 | sym::since => { | |
696 | if !get(mi, &mut since) { | |
697 | continue 'outer; | |
698 | } | |
699 | } | |
700 | sym::note if attr.has_name(sym::deprecated) => { | |
701 | if !get(mi, &mut note) { | |
702 | continue 'outer; | |
703 | } | |
704 | } | |
705 | sym::reason if attr.has_name(sym::rustc_deprecated) => { | |
706 | if !get(mi, &mut note) { | |
707 | continue 'outer; | |
708 | } | |
709 | } | |
710 | sym::suggestion if attr.has_name(sym::rustc_deprecated) => { | |
711 | if !get(mi, &mut suggestion) { | |
712 | continue 'outer; | |
713 | } | |
714 | } | |
715 | _ => { | |
716 | handle_errors( | |
717 | &sess.parse_sess, | |
718 | meta.span(), | |
719 | AttrError::UnknownMetaItem( | |
720 | pprust::path_to_string(&mi.path), | |
721 | if attr.has_name(sym::deprecated) { | |
722 | &["since", "note"] | |
723 | } else { | |
724 | &["since", "reason", "suggestion"] | |
725 | }, | |
726 | ), | |
727 | ); | |
728 | continue 'outer; | |
729 | } | |
730 | }, | |
731 | NestedMetaItem::Literal(lit) => { | |
732 | handle_errors( | |
733 | &sess.parse_sess, | |
734 | lit.span, | |
735 | AttrError::UnsupportedLiteral( | |
736 | "item in `deprecated` must be a key/value pair", | |
737 | false, | |
738 | ), | |
739 | ); | |
740 | continue 'outer; | |
741 | } | |
742 | } | |
743 | } | |
744 | } | |
745 | } | |
746 | ||
747 | if suggestion.is_some() && attr.has_name(sym::deprecated) { | |
748 | unreachable!("only allowed on rustc_deprecated") | |
749 | } | |
750 | ||
751 | if attr.has_name(sym::rustc_deprecated) { | |
752 | if since.is_none() { | |
753 | handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince); | |
754 | continue; | |
755 | } | |
756 | ||
757 | if note.is_none() { | |
758 | struct_span_err!(diagnostic, attr.span, E0543, "missing 'reason'").emit(); | |
759 | continue; | |
760 | } | |
761 | } | |
762 | ||
763 | let is_since_rustc_version = attr.has_name(sym::rustc_deprecated); | |
764 | depr = Some((Deprecation { since, note, suggestion, is_since_rustc_version }, attr.span)); | |
765 | } | |
766 | ||
767 | depr | |
768 | } | |
769 | ||
770 | #[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)] | |
771 | pub enum ReprAttr { | |
772 | ReprInt(IntType), | |
773 | ReprC, | |
774 | ReprPacked(u32), | |
775 | ReprSimd, | |
776 | ReprTransparent, | |
777 | ReprAlign(u32), | |
778 | ReprNoNiche, | |
779 | } | |
780 | ||
781 | #[derive(Eq, PartialEq, Debug, Copy, Clone)] | |
782 | #[derive(Encodable, Decodable, HashStable_Generic)] | |
783 | pub enum IntType { | |
784 | SignedInt(ast::IntTy), | |
785 | UnsignedInt(ast::UintTy), | |
786 | } | |
787 | ||
788 | impl IntType { | |
789 | #[inline] | |
790 | pub fn is_signed(self) -> bool { | |
791 | use IntType::*; | |
792 | ||
793 | match self { | |
794 | SignedInt(..) => true, | |
795 | UnsignedInt(..) => false, | |
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 | |
805 | /// structure layout, `packed` to remove padding, and `transparent` to delegate representation | |
806 | /// concerns to the only non-ZST field. | |
807 | pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { | |
808 | use ReprAttr::*; | |
809 | ||
810 | let mut acc = Vec::new(); | |
811 | let diagnostic = &sess.parse_sess.span_diagnostic; | |
812 | if attr.has_name(sym::repr) { | |
813 | if let Some(items) = attr.meta_item_list() { | |
814 | for item in items { | |
815 | let mut recognised = false; | |
816 | if item.is_word() { | |
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), | |
822 | sym::no_niche => Some(ReprNoNiche), | |
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 | } | |
840 | name => int_type_of_word(name).map(ReprInt), | |
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() { | |
848 | let mut literal_error = None; | |
849 | if name == sym::align { | |
850 | recognised = true; | |
851 | match parse_alignment(&value.kind) { | |
852 | Ok(literal) => acc.push(ReprAlign(literal)), | |
853 | Err(message) => literal_error = Some(message), | |
854 | }; | |
855 | } else if name == sym::packed { | |
856 | recognised = true; | |
857 | match parse_alignment(&value.kind) { | |
858 | Ok(literal) => acc.push(ReprPacked(literal)), | |
859 | Err(message) => literal_error = Some(message), | |
860 | }; | |
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(); | |
872 | } | |
873 | if let Some(literal_error) = literal_error { | |
874 | struct_span_err!( | |
875 | diagnostic, | |
876 | item.span(), | |
877 | E0589, | |
878 | "invalid `repr({})` attribute: {}", | |
879 | name.to_ident_string(), | |
880 | literal_error | |
881 | ) | |
882 | .emit(); | |
883 | } | |
884 | } else if let Some(meta_item) = item.meta_item() { | |
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(); | |
888 | recognised = true; | |
889 | let mut err = struct_span_err!( | |
890 | diagnostic, | |
891 | item.span(), | |
892 | E0693, | |
893 | "incorrect `repr({})` attribute format", | |
894 | name, | |
895 | ); | |
896 | match value.kind { | |
897 | ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => { | |
898 | err.span_suggestion( | |
899 | item.span(), | |
900 | "use parentheses instead", | |
901 | format!("{}({})", name, int), | |
902 | Applicability::MachineApplicable, | |
903 | ); | |
904 | } | |
905 | ast::LitKind::Str(s, _) => { | |
906 | err.span_suggestion( | |
907 | item.span(), | |
908 | "use parentheses instead", | |
909 | format!("{}({})", name, s), | |
910 | Applicability::MachineApplicable, | |
911 | ); | |
912 | } | |
913 | _ => {} | |
914 | } | |
915 | err.emit(); | |
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(); | |
968 | } | |
969 | } | |
970 | } | |
971 | if !recognised { | |
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 | } | |
979 | } | |
980 | } | |
981 | } | |
982 | } | |
983 | acc | |
984 | } | |
985 | ||
986 | fn int_type_of_word(s: Symbol) -> Option<IntType> { | |
987 | use IntType::*; | |
988 | ||
989 | match s { | |
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)), | |
1002 | _ => None, | |
1003 | } | |
1004 | } | |
1005 | ||
1006 | pub enum TransparencyError { | |
1007 | UnknownTransparency(Symbol, Span), | |
1008 | MultipleTransparencyAttrs(Span, Span), | |
1009 | } | |
1010 | ||
1011 | pub fn find_transparency( | |
1012 | attrs: &[Attribute], | |
1013 | macro_rules: bool, | |
1014 | ) -> (Transparency, Option<TransparencyError>) { | |
1015 | let mut transparency = None; | |
1016 | let mut error = None; | |
1017 | for attr in attrs { | |
1018 | if attr.has_name(sym::rustc_macro_transparency) { | |
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() { | |
1023 | transparency = Some(( | |
1024 | match value { | |
1025 | sym::transparent => Transparency::Transparent, | |
1026 | sym::semitransparent => Transparency::SemiTransparent, | |
1027 | sym::opaque => Transparency::Opaque, | |
1028 | _ => { | |
1029 | error = Some(TransparencyError::UnknownTransparency(value, attr.span)); | |
1030 | continue; | |
1031 | } | |
1032 | }, | |
1033 | attr.span, | |
1034 | )); | |
1035 | } | |
1036 | } | |
1037 | } | |
1038 | let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque }; | |
1039 | (transparency.map_or(fallback, |t| t.0), error) | |
1040 | } | |
1041 | ||
1042 | pub fn allow_internal_unstable<'a>( | |
1043 | sess: &'a Session, | |
1044 | attrs: &'a [Attribute], | |
1045 | ) -> impl Iterator<Item = Symbol> + 'a { | |
1046 | allow_unstable(sess, attrs, sym::allow_internal_unstable) | |
1047 | } | |
1048 | ||
1049 | pub fn rustc_allow_const_fn_unstable<'a>( | |
1050 | sess: &'a Session, | |
1051 | attrs: &'a [Attribute], | |
1052 | ) -> impl Iterator<Item = Symbol> + 'a { | |
1053 | allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable) | |
1054 | } | |
1055 | ||
1056 | fn allow_unstable<'a>( | |
1057 | sess: &'a Session, | |
1058 | attrs: &'a [Attribute], | |
1059 | symbol: Symbol, | |
1060 | ) -> impl Iterator<Item = Symbol> + 'a { | |
1061 | let attrs = sess.filter_by_name(attrs, symbol); | |
1062 | let list = attrs | |
1063 | .filter_map(move |attr| { | |
1064 | attr.meta_item_list().or_else(|| { | |
1065 | sess.diagnostic().span_err( | |
1066 | attr.span, | |
1067 | &format!("`{}` expects a list of feature names", symbol.to_ident_string()), | |
1068 | ); | |
1069 | None | |
1070 | }) | |
1071 | }) | |
1072 | .flatten(); | |
1073 | ||
1074 | list.into_iter().filter_map(move |it| { | |
1075 | let name = it.ident().map(|ident| ident.name); | |
1076 | if name.is_none() { | |
1077 | sess.diagnostic().span_err( | |
1078 | it.span(), | |
1079 | &format!("`{}` expects feature names", symbol.to_ident_string()), | |
1080 | ); | |
1081 | } | |
1082 | name | |
1083 | }) | |
1084 | } | |
1085 | ||
1086 | pub 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 | } |