diff options
author | Deadbeef <ent3rm4n@gmail.com> | 2023-03-27 13:44:06 +0000 |
---|---|---|
committer | Deadbeef <ent3rm4n@gmail.com> | 2023-04-06 04:55:58 +0000 |
commit | af74ef89934ad30b24132b464b6b4cf7cb0e1381 (patch) | |
tree | 60eaf705873a5d7a894449f56b090bddc751c693 /compiler/rustc_macros | |
parent | 33289132ecc1805250f63801fded10fceb2300d8 (diff) | |
download | rust-af74ef89934ad30b24132b464b6b4cf7cb0e1381.tar.gz |
migrate rustc_macros to syn 2.0
Diffstat (limited to 'compiler/rustc_macros')
-rw-r--r-- | compiler/rustc_macros/Cargo.toml | 4 | ||||
-rw-r--r-- | compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs | 152 | ||||
-rw-r--r-- | compiler/rustc_macros/src/diagnostics/error.rs | 55 | ||||
-rw-r--r-- | compiler/rustc_macros/src/diagnostics/subdiagnostic.rs | 78 | ||||
-rw-r--r-- | compiler/rustc_macros/src/diagnostics/utils.rs | 186 | ||||
-rw-r--r-- | compiler/rustc_macros/src/hash_stable.rs | 45 | ||||
-rw-r--r-- | compiler/rustc_macros/src/newtype.rs | 10 | ||||
-rw-r--r-- | compiler/rustc_macros/src/query.rs | 8 | ||||
-rw-r--r-- | compiler/rustc_macros/src/type_foldable.rs | 27 | ||||
-rw-r--r-- | compiler/rustc_macros/src/type_visitable.rs | 30 |
10 files changed, 257 insertions, 338 deletions
diff --git a/compiler/rustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml index 547c8debb50..745983e7e86 100644 --- a/compiler/rustc_macros/Cargo.toml +++ b/compiler/rustc_macros/Cargo.toml @@ -10,8 +10,8 @@ proc-macro = true annotate-snippets = "0.9" fluent-bundle = "0.15.2" fluent-syntax = "0.11" -synstructure = "0.12.1" -syn = { version = "1", features = ["full"] } +synstructure = "0.13.0" +syn = { version = "2", features = ["full"] } proc-macro2 = "1" quote = "1" unic-langid = { version = "0.9.0", features = ["macros"] } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 7fbe3bc2888..fb75d87f8d2 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -1,8 +1,7 @@ #![deny(unused_must_use)] use crate::diagnostics::error::{ - invalid_nested_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, - DiagnosticDeriveError, + span_err, throw_invalid_attr, throw_span_err, DiagnosticDeriveError, }; use crate::diagnostics::utils::{ build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error, @@ -11,9 +10,8 @@ use crate::diagnostics::utils::{ }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; -use syn::{ - parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type, -}; +use syn::Token; +use syn::{parse_quote, spanned::Spanned, Attribute, Meta, Path, Type}; use synstructure::{BindingInfo, Structure, VariantInfo}; /// What kind of diagnostic is being derived - a fatal/error/warning or a lint? @@ -77,7 +75,7 @@ impl DiagnosticDeriveBuilder { match ast.data { syn::Data::Struct(..) | syn::Data::Enum(..) => (), syn::Data::Union(..) => { - span_err(span, "diagnostic derives can only be used on structs and enums"); + span_err(span, "diagnostic derives can only be used on structs and enums").emit(); } } @@ -160,8 +158,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { }; if let SubdiagnosticKind::MultipartSuggestion { .. } = subdiag { - let meta = attr.parse_meta()?; - throw_invalid_attr!(attr, &meta, |diag| diag + throw_invalid_attr!(attr, |diag| diag .help("consider creating a `Subdiagnostic` instead")); } @@ -191,71 +188,39 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { return Ok(quote! {}); } - let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = attr.path().segments.last().unwrap().ident.to_string(); let name = name.as_str(); - let meta = attr.parse_meta()?; - if name == "diag" { - let Meta::List(MetaList { ref nested, .. }) = meta else { - throw_invalid_attr!( - attr, - &meta - ); - }; + let mut first = true; - let mut nested_iter = nested.into_iter().peekable(); + if name == "diag" { + let mut tokens = TokenStream::new(); + attr.parse_nested_meta(|nested| { + let path = &nested.path; - match nested_iter.peek() { - Some(NestedMeta::Meta(Meta::Path(slug))) => { - self.slug.set_once(slug.clone(), slug.span().unwrap()); - nested_iter.next(); + if first && (nested.input.is_empty() || nested.input.peek(Token![,])) { + self.slug.set_once(path.clone(), path.span().unwrap()); + first = false; + return Ok(()) } - Some(NestedMeta::Meta(Meta::NameValue { .. })) => {} - Some(nested_attr) => throw_invalid_nested_attr!(attr, nested_attr, |diag| diag - .help("a diagnostic slug is required as the first argument")), - None => throw_invalid_attr!(attr, &meta, |diag| diag - .help("a diagnostic slug is required as the first argument")), - }; - // Remaining attributes are optional, only `code = ".."` at the moment. - let mut tokens = TokenStream::new(); - for nested_attr in nested_iter { - let (value, path) = match nested_attr { - NestedMeta::Meta(Meta::NameValue(MetaNameValue { - lit: syn::Lit::Str(value), - path, - .. - })) => (value, path), - NestedMeta::Meta(Meta::Path(_)) => { - invalid_nested_attr(attr, nested_attr) - .help("diagnostic slug must be the first argument") - .emit(); - continue; - } - _ => { - invalid_nested_attr(attr, nested_attr).emit(); - continue; - } + first = false; + + let Ok(nested) = nested.value() else { + span_err(nested.input.span().unwrap(), "diagnostic slug must be the first argument").emit(); + return Ok(()) }; - let nested_name = path.segments.last().unwrap().ident.to_string(); - // Struct attributes are only allowed to be applied once, and the diagnostic - // changes will be set in the initialisation code. - let span = value.span().unwrap(); - match nested_name.as_str() { - "code" => { - self.code.set_once((), span); - - let code = value.value(); - tokens.extend(quote! { - #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); - }); - } - _ => invalid_nested_attr(attr, nested_attr) - .help("only `code` is a valid nested attributes following the slug") - .emit(), + if path.is_ident("code") { + self.code.set_once((), path.span().unwrap()); + + let code = nested.parse::<TokenStream>()?; + tokens.extend(quote! { + #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); + }); } - } + Ok(()) + })?; return Ok(tokens); } @@ -270,7 +235,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { Ok(self.add_subdiagnostic(&fn_ident, slug)) } SubdiagnosticKind::Label | SubdiagnosticKind::Suggestion { .. } => { - throw_invalid_attr!(attr, &meta, |diag| diag + throw_invalid_attr!(attr, |diag| diag .help("`#[label]` and `#[suggestion]` can only be applied to fields")); } SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), @@ -309,7 +274,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { return quote! {}; } - let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = attr.path().segments.last().unwrap().ident.to_string(); let needs_clone = name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_)); let (binding, needs_destructure) = if needs_clone { @@ -343,11 +308,10 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { binding: TokenStream, ) -> Result<TokenStream, DiagnosticDeriveError> { let diag = &self.parent.diag; - let meta = attr.parse_meta()?; - let ident = &attr.path.segments.last().unwrap().ident; + let ident = &attr.path().segments.last().unwrap().ident; let name = ident.to_string(); - match (&meta, name.as_str()) { + match (&attr.meta, name.as_str()) { // Don't need to do anything - by virtue of the attribute existing, the // `set_arg` call will not be generated. (Meta::Path(_), "skip_arg") => return Ok(quote! {}), @@ -361,7 +325,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { }); } DiagnosticDeriveKind::LintDiagnostic => { - throw_invalid_attr!(attr, &meta, |diag| { + throw_invalid_attr!(attr, |diag| { diag.help("the `primary_span` field attribute is not valid for lint diagnostics") }) } @@ -378,26 +342,34 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { return Ok(quote! { #diag.subdiagnostic(#binding); }); } } - (Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => { - if nested.len() == 1 - && let Some(NestedMeta::Meta(Meta::Path(path))) = nested.first() - && path.is_ident("eager") { - let handler = match &self.parent.kind { - DiagnosticDeriveKind::Diagnostic { handler } => handler, - DiagnosticDeriveKind::LintDiagnostic => { - throw_invalid_attr!(attr, &meta, |diag| { - diag.help("eager subdiagnostics are not supported on lints") - }) - } - }; - return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); }); - } else { - throw_invalid_attr!(attr, &meta, |diag| { - diag.help( - "`eager` is the only supported nested attribute for `subdiagnostic`", - ) - }) + (Meta::List(meta_list), "subdiagnostic") => { + let err = || { + span_err( + meta_list.span().unwrap(), + "`eager` is the only supported nested attribute for `subdiagnostic`", + ) + .emit(); + }; + + let Ok(p): Result<Path, _> = meta_list.parse_args() else { + err(); + return Ok(quote! {}); + }; + + if !p.is_ident("eager") { + err(); + return Ok(quote! {}); } + + let handler = match &self.parent.kind { + DiagnosticDeriveKind::Diagnostic { handler } => handler, + DiagnosticDeriveKind::LintDiagnostic => { + throw_invalid_attr!(attr, |diag| { + diag.help("eager subdiagnostics are not supported on lints") + }) + } + }; + return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); }); } _ => (), } @@ -432,7 +404,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { code_init, } => { if let FieldInnerTy::Vec(_) = info.ty { - throw_invalid_attr!(attr, &meta, |diag| { + throw_invalid_attr!(attr, |diag| { diag .note("`#[suggestion(...)]` applied to `Vec` field is ambiguous") .help("to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]`") diff --git a/compiler/rustc_macros/src/diagnostics/error.rs b/compiler/rustc_macros/src/diagnostics/error.rs index 2d62d593163..b37dc826d28 100644 --- a/compiler/rustc_macros/src/diagnostics/error.rs +++ b/compiler/rustc_macros/src/diagnostics/error.rs @@ -1,7 +1,7 @@ use proc_macro::{Diagnostic, Level, MultiSpan}; use proc_macro2::TokenStream; use quote::quote; -use syn::{spanned::Spanned, Attribute, Error as SynError, Meta, NestedMeta}; +use syn::{spanned::Spanned, Attribute, Error as SynError, Meta}; #[derive(Debug)] pub(crate) enum DiagnosticDeriveError { @@ -53,6 +53,7 @@ fn path_to_string(path: &syn::Path) -> String { } /// Returns an error diagnostic on span `span` with msg `msg`. +#[must_use] pub(crate) fn span_err(span: impl MultiSpan, msg: &str) -> Diagnostic { Diagnostic::spanned(span, Level::Error, msg) } @@ -72,10 +73,10 @@ macro_rules! throw_span_err { pub(crate) use throw_span_err; /// Returns an error diagnostic for an invalid attribute. -pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic { +pub(crate) fn invalid_attr(attr: &Attribute) -> Diagnostic { let span = attr.span().unwrap(); - let path = path_to_string(&attr.path); - match meta { + let path = path_to_string(attr.path()); + match attr.meta { Meta::Path(_) => span_err(span, &format!("`#[{path}]` is not a valid attribute")), Meta::NameValue(_) => { span_err(span, &format!("`#[{path} = ...]` is not a valid attribute")) @@ -89,51 +90,11 @@ pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic { /// /// For methods that return a `Result<_, DiagnosticDeriveError>`: macro_rules! throw_invalid_attr { - ($attr:expr, $meta:expr) => {{ throw_invalid_attr!($attr, $meta, |diag| diag) }}; - ($attr:expr, $meta:expr, $f:expr) => {{ - let diag = crate::diagnostics::error::invalid_attr($attr, $meta); + ($attr:expr) => {{ throw_invalid_attr!($attr, |diag| diag) }}; + ($attr:expr, $f:expr) => {{ + let diag = crate::diagnostics::error::invalid_attr($attr); return Err(crate::diagnostics::error::_throw_err(diag, $f)); }}; } pub(crate) use throw_invalid_attr; - -/// Returns an error diagnostic for an invalid nested attribute. -pub(crate) fn invalid_nested_attr(attr: &Attribute, nested: &NestedMeta) -> Diagnostic { - let name = attr.path.segments.last().unwrap().ident.to_string(); - let name = name.as_str(); - - let span = nested.span().unwrap(); - let meta = match nested { - syn::NestedMeta::Meta(meta) => meta, - syn::NestedMeta::Lit(_) => { - return span_err(span, &format!("`#[{name}(\"...\")]` is not a valid attribute")); - } - }; - - let span = meta.span().unwrap(); - let path = path_to_string(meta.path()); - match meta { - Meta::NameValue(..) => { - span_err(span, &format!("`#[{name}({path} = ...)]` is not a valid attribute")) - } - Meta::Path(..) => span_err(span, &format!("`#[{name}({path})]` is not a valid attribute")), - Meta::List(..) => { - span_err(span, &format!("`#[{name}({path}(...))]` is not a valid attribute")) - } - } -} - -/// Emit an error diagnostic for an invalid nested attribute (optionally performing additional -/// decoration using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`. -/// -/// For methods that return a `Result<_, DiagnosticDeriveError>`: -macro_rules! throw_invalid_nested_attr { - ($attr:expr, $nested_attr:expr) => {{ throw_invalid_nested_attr!($attr, $nested_attr, |diag| diag) }}; - ($attr:expr, $nested_attr:expr, $f:expr) => {{ - let diag = crate::diagnostics::error::invalid_nested_attr($attr, $nested_attr); - return Err(crate::diagnostics::error::_throw_err(diag, $f)); - }}; -} - -pub(crate) use throw_invalid_nested_attr; diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 90660fc1f93..2f42a3b0bd4 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -1,8 +1,7 @@ #![deny(unused_must_use)] use crate::diagnostics::error::{ - invalid_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, - DiagnosticDeriveError, + invalid_attr, span_err, throw_invalid_attr, throw_span_err, DiagnosticDeriveError, }; use crate::diagnostics::utils::{ build_field_mapping, is_doc_comment, new_code_ident, @@ -11,7 +10,7 @@ use crate::diagnostics::utils::{ }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::{spanned::Spanned, Attribute, Meta, MetaList, NestedMeta, Path}; +use syn::{spanned::Spanned, Attribute, Meta, MetaList, Path}; use synstructure::{BindingInfo, Structure, VariantInfo}; use super::utils::{build_suggestion_code, AllowMultipleAlternatives}; @@ -39,7 +38,8 @@ impl SubdiagnosticDeriveBuilder { span_err( span, "`#[derive(Subdiagnostic)]` can only be used on structs and enums", - ); + ) + .emit(); } } @@ -192,7 +192,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { }; let Some(slug) = slug else { - let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = attr.path().segments.last().unwrap().ident.to_string(); let name = name.as_str(); throw_span_err!( @@ -265,17 +265,18 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { info: FieldInfo<'_>, clone_suggestion_code: bool, ) -> Result<TokenStream, DiagnosticDeriveError> { - let meta = attr.parse_meta()?; - match meta { - Meta::Path(path) => self.generate_field_code_inner_path(kind_stats, attr, info, path), - Meta::List(list @ MetaList { .. }) => self.generate_field_code_inner_list( + match &attr.meta { + Meta::Path(path) => { + self.generate_field_code_inner_path(kind_stats, attr, info, path.clone()) + } + Meta::List(list) => self.generate_field_code_inner_list( kind_stats, attr, info, list, clone_suggestion_code, ), - _ => throw_invalid_attr!(attr, &meta), + _ => throw_invalid_attr!(attr), } } @@ -296,7 +297,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { "skip_arg" => Ok(quote! {}), "primary_span" => { if kind_stats.has_multipart_suggestion { - invalid_attr(attr, &Meta::Path(path)) + invalid_attr(attr) .help( "multipart suggestions use one or more `#[suggestion_part]`s rather \ than one `#[primary_span]`", @@ -309,7 +310,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { // FIXME(#100717): support `Option<Span>` on `primary_span` like in the // diagnostic derive if !matches!(info.ty, FieldInnerTy::Plain(_)) { - throw_invalid_attr!(attr, &Meta::Path(path), |diag| { + throw_invalid_attr!(attr, |diag| { let diag = diag.note("there must be exactly one primary span"); if kind_stats.has_normal_suggestion { @@ -335,7 +336,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`") .emit(); } else { - invalid_attr(attr, &Meta::Path(path)) + invalid_attr(attr) .help( "`#[suggestion_part(...)]` is only valid in multipart suggestions, \ use `#[primary_span]` instead", @@ -375,7 +376,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { span_attrs.push("primary_span") } - invalid_attr(attr, &Meta::Path(path)) + invalid_attr(attr) .help(format!( "only `{}`, `applicability` and `skip_arg` are valid field attributes", span_attrs.join(", ") @@ -394,7 +395,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { kind_stats: KindsStatistics, attr: &Attribute, info: FieldInfo<'_>, - list: MetaList, + list: &MetaList, clone_suggestion_code: bool, ) -> Result<TokenStream, DiagnosticDeriveError> { let span = attr.span().unwrap(); @@ -405,7 +406,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { match name { "suggestion_part" => { if !kind_stats.has_multipart_suggestion { - throw_invalid_attr!(attr, &Meta::List(list), |diag| { + throw_invalid_attr!(attr, |diag| { diag.help( "`#[suggestion_part(...)]` is only valid in multipart suggestions", ) @@ -417,31 +418,26 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { report_error_if_not_applied_to_span(attr, &info)?; let mut code = None; - for nested_attr in list.nested.iter() { - let NestedMeta::Meta(ref meta) = nested_attr else { - throw_invalid_nested_attr!(attr, nested_attr); - }; - - let span = meta.span().unwrap(); - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); - - match nested_name { - "code" => { - let code_field = new_code_ident(); - let formatting_init = build_suggestion_code( - &code_field, - meta, - self, - AllowMultipleAlternatives::No, - ); - code.set_once((code_field, formatting_init), span); - } - _ => throw_invalid_nested_attr!(attr, nested_attr, |diag| { - diag.help("`code` is the only valid nested attribute") - }), + + list.parse_nested_meta(|nested| { + if nested.path.is_ident("code") { + let code_field = new_code_ident(); + let formatting_init = build_suggestion_code( + &code_field, + nested, + self, + AllowMultipleAlternatives::No, + ); + code.set_once((code_field, formatting_init), span); + } else { + span_err( + nested.path.span().unwrap(), + "`code` is the only valid nested attribute", + ) + .emit(); } - } + Ok(()) + })?; let Some((code_field, formatting_init)) = code.value() else { span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`") @@ -458,7 +454,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { }; Ok(quote! { suggestions.push((#binding, #code_field)); }) } - _ => throw_invalid_attr!(attr, &Meta::List(list), |diag| { + _ => throw_invalid_attr!(attr, |diag| { let mut span_attrs = vec![]; if kind_stats.has_multipart_suggestion { span_attrs.push("suggestion_part"); diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 65bb154d7f3..38292df6d85 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -1,5 +1,5 @@ use crate::diagnostics::error::{ - span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, DiagnosticDeriveError, + span_err, throw_invalid_attr, throw_span_err, DiagnosticDeriveError, }; use proc_macro::Span; use proc_macro2::{Ident, TokenStream}; @@ -8,11 +8,13 @@ use std::cell::RefCell; use std::collections::{BTreeSet, HashMap}; use std::fmt; use std::str::FromStr; +use syn::meta::ParseNestedMeta; +use syn::punctuated::Punctuated; +use syn::{parenthesized, LitStr, Path, Token}; use syn::{spanned::Spanned, Attribute, Field, Meta, Type, TypeTuple}; -use syn::{MetaList, MetaNameValue, NestedMeta, Path}; use synstructure::{BindingInfo, VariantInfo}; -use super::error::{invalid_attr, invalid_nested_attr}; +use super::error::invalid_attr; thread_local! { pub static CODE_IDENT_COUNT: RefCell<u32> = RefCell::new(0); @@ -60,8 +62,8 @@ pub(crate) fn report_type_error( attr: &Attribute, ty_name: &str, ) -> Result<!, DiagnosticDeriveError> { - let name = attr.path.segments.last().unwrap().ident.to_string(); - let meta = attr.parse_meta()?; + let name = attr.path().segments.last().unwrap().ident.to_string(); + let meta = &attr.meta; throw_span_err!( attr.span().unwrap(), @@ -422,55 +424,53 @@ pub(super) enum AllowMultipleAlternatives { /// `#[suggestion*(code("foo", "bar"))]` attribute field pub(super) fn build_suggestion_code( code_field: &Ident, - meta: &Meta, + nested: ParseNestedMeta<'_>, fields: &impl HasFieldMap, allow_multiple: AllowMultipleAlternatives, ) -> TokenStream { - let values = match meta { - // `code = "foo"` - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => vec![s], - // `code("foo", "bar")` - Meta::List(MetaList { nested, .. }) => { + let values = match (|| { + let values: Vec<LitStr> = if let Ok(val) = nested.value() { + vec![val.parse()?] + } else { + let content; + parenthesized!(content in nested.input); + if let AllowMultipleAlternatives::No = allow_multiple { span_err( - meta.span().unwrap(), + nested.input.span().unwrap(), "expected exactly one string literal for `code = ...`", ) .emit(); vec![] - } else if nested.is_empty() { - span_err( - meta.span().unwrap(), - "expected at least one string literal for `code(...)`", - ) - .emit(); - vec![] } else { - nested - .into_iter() - .filter_map(|item| { - if let NestedMeta::Lit(syn::Lit::Str(s)) = item { - Some(s) - } else { - span_err( - item.span().unwrap(), - "`code(...)` must contain only string literals", - ) - .emit(); - None - } - }) - .collect() + let literals = Punctuated::<LitStr, Token![,]>::parse_terminated(&content); + + match literals { + Ok(p) if p.is_empty() => { + span_err( + content.span().unwrap(), + "expected at least one string literal for `code(...)`", + ) + .emit(); + vec![] + } + Ok(p) => p.into_iter().collect(), + Err(_) => { + span_err( + content.span().unwrap(), + "`code(...)` must contain only string literals", + ) + .emit(); + vec![] + } + } } - } - _ => { - span_err( - meta.span().unwrap(), - r#"`code = "..."`/`code(...)` must contain only string literals"#, - ) - .emit(); - vec![] - } + }; + + Ok(values) + })() { + Ok(x) => x, + Err(e) => return e.into_compile_error(), }; if let AllowMultipleAlternatives::Yes = allow_multiple { @@ -601,11 +601,9 @@ impl SubdiagnosticKind { let span = attr.span().unwrap(); - let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = attr.path().segments.last().unwrap().ident.to_string(); let name = name.as_str(); - let meta = attr.parse_meta()?; - let mut kind = match name { "label" => SubdiagnosticKind::Label, "note" => SubdiagnosticKind::Note, @@ -618,7 +616,7 @@ impl SubdiagnosticKind { name.strip_prefix("suggestion").and_then(SuggestionKind::from_suffix) { if suggestion_kind != SuggestionKind::Normal { - invalid_attr(attr, &meta) + invalid_attr(attr) .help(format!( r#"Use `#[suggestion(..., style = "{suggestion_kind}")]` instead"# )) @@ -635,7 +633,7 @@ impl SubdiagnosticKind { name.strip_prefix("multipart_suggestion").and_then(SuggestionKind::from_suffix) { if suggestion_kind != SuggestionKind::Normal { - invalid_attr(attr, &meta) + invalid_attr(attr) .help(format!( r#"Use `#[multipart_suggestion(..., style = "{suggestion_kind}")]` instead"# )) @@ -647,16 +645,16 @@ impl SubdiagnosticKind { applicability: None, } } else { - throw_invalid_attr!(attr, &meta); + throw_invalid_attr!(attr); } } }; - let nested = match meta { - Meta::List(MetaList { ref nested, .. }) => { + let list = match &attr.meta { + Meta::List(list) => { // An attribute with properties, such as `#[suggestion(code = "...")]` or // `#[error(some::slug)]` - nested + list } Meta::Path(_) => { // An attribute without a slug or other properties, such as `#[note]` - return @@ -678,52 +676,51 @@ impl SubdiagnosticKind { } } _ => { - throw_invalid_attr!(attr, &meta) + throw_invalid_attr!(attr) } }; let mut code = None; let mut suggestion_kind = None; - let mut nested_iter = nested.into_iter().peekable(); + let mut first = true; + let mut slug = None; - // Peek at the first nested attribute: if it's a slug path, consume it. - let slug = if let Some(NestedMeta::Meta(Meta::Path(path))) = nested_iter.peek() { - let path = path.clone(); - // Advance the iterator. - nested_iter.next(); - Some(path) - } else { - None - }; - - for nested_attr in nested_iter { - let meta = match nested_attr { - NestedMeta::Meta(ref meta) => meta, - NestedMeta::Lit(_) => { - invalid_nested_attr(attr, nested_attr).emit(); - continue; + list.parse_nested_meta(|nested| { + if nested.input.is_empty() || nested.input.peek(Token![,]) { + if first { + slug = Some(nested.path); + } else { + span_err(nested.input.span().unwrap(), "a diagnostic slug must be the first argument to the attribute").emit(); } - }; - let span = meta.span().unwrap(); - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); + first = false; + return Ok(()); + } + + first = false; + + let nested_name = nested.path.segments.last().unwrap().ident.to_string(); let nested_name = nested_name.as_str(); - let string_value = match meta { - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => Some(value), + let path_span = nested.path.span().unwrap(); + let val_span = nested.input.span().unwrap(); - Meta::Path(_) => throw_invalid_nested_attr!(attr, nested_attr, |diag| { - diag.help("a diagnostic slug must be the first argument to the attribute") - }), - _ => None, - }; + macro get_string() { + { + let Ok(value) = nested.value().and_then(|x| x.parse::<LitStr>()) else { + span_err(val_span, "expected `= \"xxx\"`").emit(); + return Ok(()); + }; + value + } + } match (nested_name, &mut kind) { ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { let code_init = build_suggestion_code( code_field, - meta, + nested, fields, AllowMultipleAlternatives::Yes, ); @@ -734,13 +731,9 @@ impl SubdiagnosticKind { SubdiagnosticKind::Suggestion { ref mut applicability, .. } | SubdiagnosticKind::MultipartSuggestion { ref mut applicability, .. }, ) => { - let Some(value) = string_value else { - invalid_nested_attr(attr, nested_attr).emit(); - continue; - }; - + let value = get_string!(); let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| { - span_err(span, "invalid applicability").emit(); + span_err(value.span().unwrap(), "invalid applicability").emit(); Applicability::Unspecified }); applicability.set_once(value, span); @@ -750,10 +743,7 @@ impl SubdiagnosticKind { SubdiagnosticKind::Suggestion { .. } | SubdiagnosticKind::MultipartSuggestion { .. }, ) => { - let Some(value) = string_value else { - invalid_nested_attr(attr, nested_attr).emit(); - continue; - }; + let value = get_string!(); let value = value.value().parse().unwrap_or_else(|()| { span_err(value.span().unwrap(), "invalid suggestion style") @@ -767,22 +757,24 @@ impl SubdiagnosticKind { // Invalid nested attribute (_, SubdiagnosticKind::Suggestion { .. }) => { - invalid_nested_attr(attr, nested_attr) + span_err(path_span, "invalid nested attribute") .help( "only `style`, `code` and `applicability` are valid nested attributes", ) .emit(); } (_, SubdiagnosticKind::MultipartSuggestion { .. }) => { - invalid_nested_attr(attr, nested_attr) + span_err(path_span, "invalid nested attribute") .help("only `style` and `applicability` are valid nested attributes") - .emit() + .emit(); } _ => { - invalid_nested_attr(attr, nested_attr).emit(); + span_err(path_span, "invalid nested attribute").emit(); } } - } + + Ok(()) + })?; match kind { SubdiagnosticKind::Suggestion { @@ -845,5 +837,5 @@ pub(super) fn should_generate_set_arg(field: &Field) -> bool { } pub(super) fn is_doc_comment(attr: &Attribute) -> bool { - attr.path.segments.last().unwrap().ident == "doc" + attr.path().segments.last().unwrap().ident == "doc" } diff --git a/compiler/rustc_macros/src/hash_stable.rs b/compiler/rustc_macros/src/hash_stable.rs index 63bdcea87f8..75a2f7009c2 100644 --- a/compiler/rustc_macros/src/hash_stable.rs +++ b/compiler/rustc_macros/src/hash_stable.rs @@ -1,6 +1,6 @@ use proc_macro2::{self, Ident}; use quote::quote; -use syn::{self, parse_quote, Meta, NestedMeta}; +use syn::{self, parse_quote}; struct Attributes { ignore: bool, @@ -10,32 +10,29 @@ struct Attributes { fn parse_attributes(field: &syn::Field) -> Attributes { let mut attrs = Attributes { ignore: false, project: None }; for attr in &field.attrs { - if let Ok(meta) = attr.parse_meta() { - if !meta.path().is_ident("stable_hasher") { - continue; + let meta = &attr.meta; + if !meta.path().is_ident("stable_hasher") { + continue; + } + let mut any_attr = false; + let _ = attr.parse_nested_meta(|nested| { + if nested.path.is_ident("ignore") { + attrs.ignore = true; + any_attr = true; } - let mut any_attr = false; - if let Meta::List(list) = meta { - for nested in list.nested.iter() { - if let NestedMeta::Meta(meta) = nested { - if meta.path().is_ident("ignore") { - attrs.ignore = true; - any_attr = true; - } - if meta.path().is_ident("project") { - if let Meta::List(list) = meta { - if let Some(NestedMeta::Meta(meta)) = list.nested.iter().next() { - attrs.project = meta.path().get_ident().cloned(); - any_attr = true; - } - } - } + if nested.path.is_ident("project") { + let _ = nested.parse_nested_meta(|meta| { + if attrs.project.is_none() { + attrs.project = meta.path.get_ident().cloned(); } - } - } - if !any_attr { - panic!("error parsing stable_hasher"); + any_attr = true; + Ok(()) + }); } + Ok(()) + }); + if !any_attr { + panic!("error parsing stable_hasher"); } } attrs diff --git a/compiler/rustc_macros/src/newtype.rs b/compiler/rustc_macros/src/newtype.rs index 89ea89cf502..04e762e91de 100644 --- a/compiler/rustc_macros/src/newtype.rs +++ b/compiler/rustc_macros/src/newtype.rs @@ -25,7 +25,7 @@ impl Parse for Newtype { let mut encodable = true; let mut ord = true; - attrs.retain(|attr| match attr.path.get_ident() { + attrs.retain(|attr| match attr.path().get_ident() { Some(ident) => match &*ident.to_string() { "custom_encodable" => { encodable = false; @@ -36,22 +36,22 @@ impl Parse for Newtype { false } "max" => { - let Ok(Meta::NameValue(literal) )= attr.parse_meta() else { + let Meta::NameValue(MetaNameValue { value: Expr::Lit(lit), .. }) = &attr.meta else { panic!("#[max = NUMBER] attribute requires max value"); }; - if let Some(old) = max.replace(literal.lit) { + if let Some(old) = max.replace(lit.lit.clone()) { panic!("Specified multiple max: {old:?}"); } false } "debug_format" => { - let Ok(Meta::NameValue(literal) )= attr.parse_meta() else { + let Meta::NameValue(MetaNameValue { value: Expr::Lit(lit), .. } ) = &attr.meta else { panic!("#[debug_format = FMT] attribute requires a format"); }; - if let Some(old) = debug_format.replace(literal.lit) { + if let Some(old) = debug_format.replace(lit.lit.clone()) { panic!("Specified multiple debug format options: {old:?}"); } diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 08e42a8a08f..f85ba38003c 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -15,7 +15,7 @@ mod kw { /// Ensures only doc comment attributes are used fn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> { let inner = |attr: Attribute| { - if !attr.path.is_ident("doc") { + if !attr.path().is_ident("doc") { Err(Error::new(attr.span(), "attributes not supported on queries")) } else if attr.style != AttrStyle::Outer { Err(Error::new( @@ -48,7 +48,7 @@ impl Parse for Query { let name: Ident = input.parse()?; let arg_content; parenthesized!(arg_content in input); - let key = arg_content.parse()?; + let key = Pat::parse_single(&arg_content)?; arg_content.parse::<Token![:]>()?; let arg = arg_content.parse()?; let result = input.parse()?; @@ -158,7 +158,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> { } else { None }; - let list = attr_content.parse_terminated(Expr::parse)?; + let list = attr_content.parse_terminated(Expr::parse, Token![,])?; try_insert!(desc = (tcx, list)); } else if modifier == "cache_on_disk_if" { // Parse a cache modifier like: @@ -166,7 +166,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> { let args = if input.peek(token::Paren) { let args; parenthesized!(args in input); - let tcx = args.parse()?; + let tcx = Pat::parse_single(&args)?; Some(tcx) } else { None diff --git a/compiler/rustc_macros/src/type_foldable.rs b/compiler/rustc_macros/src/type_foldable.rs index 388e254cd64..5ee4d879313 100644 --- a/compiler/rustc_macros/src/type_foldable.rs +++ b/compiler/rustc_macros/src/type_foldable.rs @@ -1,5 +1,5 @@ use quote::{quote, ToTokens}; -use syn::{parse_quote, Attribute, Meta, NestedMeta}; +use syn::parse_quote; pub fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { if let syn::Data::Union(_) = s.ast().data { @@ -17,21 +17,20 @@ pub fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2:: vi.construct(|_, index| { let bind = &bindings[index]; + let mut fixed = false; + // retain value of fields with #[type_foldable(identity)] - let fixed = bind - .ast() - .attrs - .iter() - .map(Attribute::parse_meta) - .filter_map(Result::ok) - .flat_map(|attr| match attr { - Meta::List(list) if list.path.is_ident("type_foldable") => list.nested, - _ => Default::default(), - }) - .any(|nested| match nested { - NestedMeta::Meta(Meta::Path(path)) => path.is_ident("identity"), - _ => false, + bind.ast().attrs.iter().for_each(|x| { + if !x.path().is_ident("type_foldable") { + return; + } + let _ = x.parse_nested_meta(|nested| { + if nested.path.is_ident("identity") { + fixed = true; + } + Ok(()) }); + }); if fixed { bind.to_token_stream() diff --git a/compiler/rustc_macros/src/type_visitable.rs b/compiler/rustc_macros/src/type_visitable.rs index f6f4c4779c3..dcd505a105e 100644 --- a/compiler/rustc_macros/src/type_visitable.rs +++ b/compiler/rustc_macros/src/type_visitable.rs @@ -1,5 +1,5 @@ use quote::quote; -use syn::{parse_quote, Attribute, Meta, NestedMeta}; +use syn::parse_quote; pub fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { if let syn::Data::Union(_) = s.ast().data { @@ -8,19 +8,21 @@ pub fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: // ignore fields with #[type_visitable(ignore)] s.filter(|bi| { - !bi.ast() - .attrs - .iter() - .map(Attribute::parse_meta) - .filter_map(Result::ok) - .flat_map(|attr| match attr { - Meta::List(list) if list.path.is_ident("type_visitable") => list.nested, - _ => Default::default(), - }) - .any(|nested| match nested { - NestedMeta::Meta(Meta::Path(path)) => path.is_ident("ignore"), - _ => false, - }) + let mut ignored = false; + + bi.ast().attrs.iter().for_each(|attr| { + if !attr.path().is_ident("type_visitable") { + return; + } + let _ = attr.parse_nested_meta(|nested| { + if nested.path.is_ident("ignore") { + ignored = true; + } + Ok(()) + }); + }); + + !ignored }); if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { |