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