migrate rustc_macros to syn 2.0

This commit is contained in:
Deadbeef 2023-03-27 13:44:06 +00:00
parent 33289132ec
commit af74ef8993
11 changed files with 275 additions and 344 deletions

View file

@ -632,7 +632,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn 1.0.102",
"synstructure",
"synstructure 0.12.6",
]
[[package]]
@ -4994,8 +4994,8 @@ dependencies = [
"fluent-syntax",
"proc-macro2",
"quote",
"syn 1.0.102",
"synstructure",
"syn 2.0.8",
"synstructure 0.13.0",
"unic-langid",
]
@ -6131,6 +6131,18 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "synstructure"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.8",
"unicode-xid",
]
[[package]]
name = "tar"
version = "0.4.38"
@ -7154,7 +7166,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn 1.0.102",
"synstructure",
"synstructure 0.12.6",
]
[[package]]
@ -7175,7 +7187,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn 1.0.102",
"synstructure",
"synstructure 0.12.6",
]
[[package]]
@ -7204,5 +7216,5 @@ dependencies = [
"proc-macro2",
"quote",
"syn 1.0.102",
"synstructure",
"synstructure 0.12.6",
]

View file

@ -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"] }

View file

@ -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 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 @@ pub fn each_variant<'s, F>(&mut self, structure: &mut Structure<'s>, f: F) -> To
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 @@ fn parse_subdiag_attribute(
};
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 @@ fn generate_structure_code_for_attr(
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()?;
let mut first = true;
if name == "diag" {
let Meta::List(MetaList { ref nested, .. }) = meta else {
throw_invalid_attr!(
attr,
&meta
);
};
let mut nested_iter = nested.into_iter().peekable();
match nested_iter.peek() {
Some(NestedMeta::Meta(Meta::Path(slug))) => {
self.slug.set_once(slug.clone(), slug.span().unwrap());
nested_iter.next();
}
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;
}
attr.parse_nested_meta(|nested| {
let path = &nested.path;
if first && (nested.input.is_empty() || nested.input.peek(Token![,])) {
self.slug.set_once(path.clone(), path.span().unwrap());
first = false;
return Ok(())
}
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);
if path.is_ident("code") {
self.code.set_once((), path.span().unwrap());
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(),
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 @@ fn generate_structure_code_for_attr(
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 @@ fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> Token
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 @@ fn generate_inner_field_code(
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 @@ fn generate_inner_field_code(
});
}
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 @@ fn generate_inner_field_code(
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 @@ fn generate_inner_field_code(
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(...)]`")

View file

@ -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;

View file

@ -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 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 @@ pub(crate) fn into_tokens(self, mut structure: Structure<'_>) -> TokenStream {
span_err(
span,
"`#[derive(Subdiagnostic)]` can only be used on structs and enums",
);
)
.emit();
}
}
@ -192,7 +192,7 @@ fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, Diagnostic
};
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 @@ fn generate_field_code_inner(
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 @@ fn generate_field_code_inner_path(
"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 @@ fn generate_field_code_inner_path(
// 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 @@ fn generate_field_code_inner_path(
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 @@ fn generate_field_code_inner_path(
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 @@ fn generate_field_code_inner_list(
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 @@ fn generate_field_code_inner_list(
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 @@ fn generate_field_code_inner_list(
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 @@ fn generate_field_code_inner_list(
};
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");

View file

@ -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::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 @@ pub(super) fn from_attr(
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 @@ pub(super) fn from_attr(
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 @@ pub(super) fn from_attr(
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 @@ pub(super) fn from_attr(
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 @@ pub(super) fn from_attr(
}
}
_ => {
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 @@ pub(super) fn from_attr(
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 @@ pub(super) fn from_attr(
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 @@ pub(super) fn from_attr(
// 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"
}

View file

@ -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

View file

@ -25,7 +25,7 @@ fn parse(input: ParseStream<'_>) -> Result<Self> {
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 @@ fn parse(input: ParseStream<'_>) -> Result<Self> {
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:?}");
}

View file

@ -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 @@ fn parse(input: ParseStream<'_>) -> Result<Self> {
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 @@ macro_rules! try_insert {
} 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 @@ macro_rules! try_insert {
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

View file

@ -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()

View file

@ -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") {