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