Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion const-type-layout-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ proc-macro = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
syn = { version = "1.0", features = ["clone-impls", "derive", "parsing", "printing", "proc-macro"], default-features = false }
syn = { version = "2.0", features = ["clone-impls", "derive", "parsing", "printing", "proc-macro"], default-features = false }
quote = { version = "1.0", default-features = false }
proc-macro2 = { version = "1.0", default-features = false }
proc-macro-error2 = { version = "2.0", default-features = false }
Expand Down
192 changes: 86 additions & 106 deletions const-type-layout-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,102 +139,108 @@ fn parse_attributes(attrs: &[syn::Attribute], type_params: &mut Vec<&syn::Ident>
let mut crate_path = None;

for attr in attrs {
if attr.path.is_ident("repr") {
if let Ok(syn::Meta::List(syn::MetaList { nested, .. })) = attr.parse_meta() {
#[allow(clippy::collapsible_if)]
if attr.path().is_ident("repr") {
if let Ok(nested) = attr.parse_args_with(
syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated,
) {
for meta in nested {
reprs.push(match meta {
syn::NestedMeta::Lit(lit) => lit_to_string(&lit),
syn::NestedMeta::Meta(meta) => meta_to_string(&meta),
});
reprs.push(quote!(#meta).to_string());
}
} else {
emit_warning!(
attr.span(),
"[const-type-layout]: #[repr] attribute is not in meta list format."
);
}
} else if attr.path.is_ident("layout") {
if let Ok(syn::Meta::List(list)) = attr.parse_meta() {
for meta in &list.nested {
if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
path,
lit: syn::Lit::Str(s),
..
})) = &meta
{
if path.is_ident("free") {
match syn::parse_str::<syn::Ident>(&s.value()) {
Ok(param) => {
type_params.iter().position(|ty| **ty == param).map_or_else(
|| {
emit_error!(
s.span(),
"[const-type-layout]: Invalid #[layout(free)] \
attribute: \"{}\" is either not a type parameter \
or has already been freed (duplicate attribute).",
param,
);
},
|i| {
type_params.swap_remove(i);
},
);
},
Err(err) => emit_error!(
s.span(),
"[const-type-layout]: Invalid #[layout(free = \"<type>\")] \
attribute: {}.",
err
),
}
} else if path.is_ident("bound") {
match syn::parse_str(&s.value()) {
Ok(bound) => extra_bounds.push(bound),
Err(err) => emit_error!(
s.span(),
"[const-type-layout]: Invalid #[layout(bound = \
\"<where-predicate>\")] attribute: {}.",
err
),
}
} else if path.is_ident("crate") {
match syn::parse_str::<syn::Path>(&s.value()) {
Ok(new_crate_path) => {
if crate_path.is_none() {
crate_path = Some(
syn::parse_quote_spanned! { s.span() => #new_crate_path },
);
} else {
} else if attr.path().is_ident("layout") {
#[allow(clippy::blocks_in_conditions)]
if attr
.parse_nested_meta(|meta| {
let Ok(value) = meta.value() else {
emit_error!(
meta.path.span(),
"[const-type-layout]: Expected #[layout(attr = \"value\")] syntax."
);
return Ok(());
};
let Ok(s) = <syn::LitStr as syn::parse::Parse>::parse(value) else {
emit_error!(
value.span(),
"[const-type-layout]: Expected #[layout(attr = \"value\")] syntax."
);
return Ok(());
};

if meta.path.is_ident("free") {
match syn::parse_str::<syn::Ident>(&s.value()) {
Ok(param) => {
type_params.iter().position(|ty| **ty == param).map_or_else(
|| {
emit_error!(
s.span(),
"[const-type-layout]: Duplicate #[layout(crate)] \
attribute: the crate path for `const-type-layout` \
can only be set once per `derive`.",
"[const-type-layout]: Invalid #[layout(free)] \
attribute: \"{}\" is either not a type parameter or \
has already been freed (duplicate attribute).",
param,
);
}
},
Err(err) => emit_error!(
s.span(),
"[const-type-layout]: Invalid #[layout(crate = \
\"<crate-path>\")] attribute: {}.",
err
),
}
} else {
emit_error!(
path.span(),
"[const-type-layout]: Unknown attribute, use `bound`, `crate`, or \
`free`."
);
},
|i| {
type_params.swap_remove(i);
},
);
},
Err(err) => emit_error!(
s.span(),
"[const-type-layout]: Invalid #[layout(free = \"<type>\")] \
attribute: {}.",
err
),
}
} else if meta.path.is_ident("bound") {
match syn::parse_str::<syn::WherePredicate>(&s.value()) {
Ok(bound) => extra_bounds.push(bound),
Err(err) => emit_error!(
s.span(),
"[const-type-layout]: Invalid #[layout(bound = \
\"<where-predicate>\")] attribute: {}.",
err
),
}
} else if meta.path.is_ident("crate") {
match syn::parse_str::<syn::Path>(&s.value()) {
Ok(new_crate_path) => {
if crate_path.is_none() {
crate_path = Some(
syn::parse_quote_spanned! { s.span() => #new_crate_path },
);
} else {
emit_error!(
meta.path.span(),
"[const-type-layout]: Duplicate #[layout(crate)] \
attribute: the crate path for `const-type-layout` can \
only be set once per `derive`.",
);
}
},
Err(err) => emit_error!(
s.span(),
"[const-type-layout]: Invalid #[layout(crate = \"<crate-path>\")] \
attribute: {}.",
err
),
}
} else {
emit_error!(
meta.span(),
"[const-type-layout]: Expected #[layout(attr = \"value\")] syntax."
meta.path.span(),
"[const-type-layout]: Unknown attribute, use `bound`, `crate`, or \
`free`."
);
}
}
} else {
Ok(())
})
.is_err()
{
emit_error!(
attr.span(),
"[const-type-layout]: Expected #[layout(attr = \"value\")] syntax."
Expand All @@ -257,32 +263,6 @@ fn parse_attributes(attrs: &[syn::Attribute], type_params: &mut Vec<&syn::Ident>
}
}

fn meta_to_string(meta: &syn::Meta) -> String {
match meta {
syn::Meta::List(syn::MetaList { path, nested, .. }) => {
let mut list = nested
.iter()
.map(|meta| match meta {
syn::NestedMeta::Lit(lit) => lit_to_string(lit),
syn::NestedMeta::Meta(meta) => meta_to_string(meta),
})
.collect::<Vec<_>>();
list.sort();
list.dedup();

format!("{}({})", quote!(#path), intersperse_commas(list))
},
syn::Meta::NameValue(syn::MetaNameValue { path, lit, .. }) => {
format!("{}={}", quote!(#path), lit_to_string(lit))
},
syn::Meta::Path(path) => quote!(#path).to_string(),
}
}

fn lit_to_string(lit: &syn::Lit) -> String {
quote!(#lit).to_string().escape_default().to_string()
}

fn intersperse_commas(items: Vec<String>) -> String {
let mut acc = String::with_capacity(
items.iter().map(String::len).sum::<usize>() + items.len().saturating_sub(1),
Expand Down
6 changes: 1 addition & 5 deletions try-crate/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,8 @@ pub struct MyPhantomData<T> {
#[repr(transparent)]
pub struct Wrapper(f64);

// FIXME: move bound from where clause to impl bound
// https://github.com/dtolnay/syn/issues/1238
#[derive(TypeLayout)]
pub struct Bounded<T>(T)
where
T: std::fmt::Debug + TypeGraphLayout;
pub struct Bounded<T: std::fmt::Debug + TypeGraphLayout>(T);

fn main() {
println!("{:#?}", Foo1::TYPE_GRAPH);
Expand Down