Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix leaking bounds #24

Merged
merged 22 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bbafa6f
Debug impl: pass whole ast to create_format_arg
ijackson Feb 20, 2024
e224ad8
Debug impl: Fix generated code for explicit methods
ijackson Feb 20, 2024
dea0d33
Default bounds: Use precisely the right bounds, from the fields
ijackson Feb 20, 2024
b2a99a7
Debug: Demonstrate generic type bounds in a test
ijackson Feb 20, 2024
610b6ad
Generic bounds: Handle supertraits
ijackson Feb 20, 2024
eb28c48
Tests: Demonstrate supertraits when educing PartialOrd
ijackson Feb 20, 2024
69f4c1c
Tests: Demonstrate supertraits when educing Ord
ijackson Feb 20, 2024
7690e47
Eq: Fix bounds
ijackson Feb 20, 2024
2e47d8b
Eq: Fix bounds (rustfmt)
ijackson Feb 20, 2024
725dae8
Eq: Demonstrate precise bounds in a test
ijackson Feb 20, 2024
b18f14f
Copy: Fix bounds
ijackson Feb 20, 2024
9a29080
Copy: Fix bounds (rustfmt)
ijackson Feb 20, 2024
1daf893
Copy, Clone: Demonstrate precise bounds in tests
ijackson Feb 20, 2024
1a09812
Clone of unions: Fix bounds
ijackson Feb 20, 2024
f3a3cb5
Abolish find_idents_*
ijackson Feb 20, 2024
ecaf0f2
Abolish into_where_predicates_by_generic_parameters
ijackson Feb 20, 2024
7b9a95c
Ord: Fix build when PartialOrd is not enabled
ijackson Feb 20, 2024
4b07ecb
Ord: Fix build when PartialOrd is not enabled (rustfmt)
ijackson Feb 20, 2024
a3965f1
Fix leaking trait bounds
ijackson Feb 26, 2024
6fdeb47
Don't pass &mut DeriveInput to anyone
ijackson Feb 26, 2024
988e5a6
Add test case for leaking trait bounds
ijackson Feb 26, 2024
166370f
fixup! Add test case for leaking trait bounds
ijackson Feb 26, 2024
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
91 changes: 50 additions & 41 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,50 +1,59 @@
[package]
name = "educe"
version = "0.5.11"
authors = ["Magic Len <[email protected]>"]
edition = "2021"
rust-version = "1.60"
repository = "https://github.com/magiclen/educe"
homepage = "https://magiclen.org/educe"
keywords = ["derive", "macro", "trait", "field", "procedural"]
categories = ["no-std", "rust-patterns"]
description = "This crate offers procedural macros designed to facilitate the swift implementation of Rust's built-in traits."
license = "MIT"
include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"]

[lib]
proc-macro = true

[dependencies]
syn = "2"
quote = "1"
proc-macro2 = "1"
proc-macro2="1"
quote="1"
syn="2"

enum-ordinalize = { version = "4.2", default-features = false, features = ["derive"] }
[dependencies.enum-ordinalize]
default-features=false
version="4.2"
features=["derive"]

[dev-dependencies]
syn = { version = "2", features = ["full"] }
assert-eq-float = "0.1"
rustversion = "1"
assert-eq-float="0.1"
rustversion="1"

[dev-dependencies.syn]
version="2"
features=["full"]

[features]
default = ["Debug", "Clone", "Copy", "PartialEq", "Eq", "PartialOrd", "Ord", "Hash", "Default", "Deref", "DerefMut", "Into"]

full = ["syn/full"]

Debug = []
Clone = []
Copy = []
PartialEq = []
Eq = []
PartialOrd = []
Ord = []
Hash = []
Default = []
Deref = []
DerefMut = []
Into = []
Clone=[]
Copy=[]
Debug=[]
Default=[]
Deref=[]
DerefMut=[]
Eq=[]
Hash=[]
Into=[]
Ord=[]
PartialEq=[]
PartialOrd=[]
default=["Debug", "Clone", "Copy", "PartialEq", "Eq", "PartialOrd", "Ord", "Hash", "Default", "Deref", "DerefMut", "Into"]
full=["syn/full"]

[lib]
proc-macro=true

[package]
description="This crate offers procedural macros designed to facilitate the swift implementation of Rust's built-in traits."
edition="2021"
homepage="https://magiclen.org/educe"
license="MIT"
name="educe"
repository="https://github.com/magiclen/educe"
rust-version="1.60"
version="0.5.11"
authors=["Magic Len <[email protected]>"]
categories=["no-std", "rust-patterns"]
include=["src/**/*", "Cargo.toml", "README.md", "LICENSE"]
keywords=["derive", "macro", "trait", "field", "procedural"]

[package.metadata]

[package.metadata.docs]

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
all-features=true
rustdoc-args=["--cfg", "docsrs"]
21 changes: 3 additions & 18 deletions src/common/bound.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use syn::{punctuated::Punctuated, token::Comma, GenericParam, Meta, Path, Type, WherePredicate};

use crate::common::where_predicates_bool::{
create_where_predicates_from_generic_parameters,
create_where_predicates_from_generic_parameters_check_types, meta_2_where_predicates,
WherePredicates, WherePredicatesOrBool,
};
Expand Down Expand Up @@ -33,34 +32,20 @@ impl Bound {
}

impl Bound {
#[inline]
pub(crate) fn into_where_predicates_by_generic_parameters(
self,
params: &Punctuated<GenericParam, Comma>,
bound_trait: &Path,
) -> Punctuated<WherePredicate, Comma> {
match self {
Self::Disabled => Punctuated::new(),
Self::Auto => create_where_predicates_from_generic_parameters(params, bound_trait),
Self::Custom(where_predicates) => where_predicates,
}
}

#[inline]
pub(crate) fn into_where_predicates_by_generic_parameters_check_types(
self,
params: &Punctuated<GenericParam, Comma>,
_params: &Punctuated<GenericParam, Comma>,
bound_trait: &Path,
types: &[&Type],
recursive: Option<(bool, bool, bool)>,
supertraits: &[proc_macro2::TokenStream],
) -> Punctuated<WherePredicate, Comma> {
match self {
Self::Disabled => Punctuated::new(),
Self::Auto => create_where_predicates_from_generic_parameters_check_types(
params,
bound_trait,
types,
recursive,
supertraits,
),
Self::Custom(where_predicates) => where_predicates,
}
Expand Down
113 changes: 1 addition & 112 deletions src/common/type.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use std::collections::HashSet;

use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
GenericArgument, Ident, Meta, Path, PathArguments, Token, Type, TypeParamBound,
Meta, Token, Type,
};

pub(crate) struct TypeWithPunctuatedMeta {
Expand Down Expand Up @@ -34,115 +32,6 @@ impl Parse for TypeWithPunctuatedMeta {
}
}

/// recursive (dereference, de_ptr, de_param)
#[inline]
pub(crate) fn find_idents_in_path<'a>(
set: &mut HashSet<&'a Ident>,
path: &'a Path,
recursive: Option<(bool, bool, bool)>,
) {
if let Some((_, _, de_param)) = recursive {
if de_param {
if let Some(segment) = path.segments.iter().last() {
if let PathArguments::AngleBracketed(a) = &segment.arguments {
// the ident is definitely not a generic parameter, so we don't insert it

for arg in a.args.iter() {
match arg {
GenericArgument::Type(ty) => {
find_idents_in_type(set, ty, recursive);
},
GenericArgument::AssocType(ty) => {
find_idents_in_type(set, &ty.ty, recursive);
},
_ => (),
}
}

return;
}
}
}
}

if let Some(ty) = path.get_ident() {
set.insert(ty);
}
}

/// recursive (dereference, de_ptr, de_param)
#[inline]
pub(crate) fn find_idents_in_type<'a>(
set: &mut HashSet<&'a Ident>,
ty: &'a Type,
recursive: Option<(bool, bool, bool)>,
) {
match ty {
Type::Array(ty) => {
if recursive.is_some() {
find_idents_in_type(set, ty.elem.as_ref(), recursive);
}
},
Type::Group(ty) => {
if recursive.is_some() {
find_idents_in_type(set, ty.elem.as_ref(), recursive);
}
},
Type::ImplTrait(ty) => {
// always recursive
for b in &ty.bounds {
if let TypeParamBound::Trait(ty) = b {
find_idents_in_path(set, &ty.path, recursive);
}
}
},
Type::Macro(ty) => {
if recursive.is_some() {
find_idents_in_path(set, &ty.mac.path, recursive);
}
},
Type::Paren(ty) => {
if recursive.is_some() {
find_idents_in_type(set, ty.elem.as_ref(), recursive);
}
},
Type::Path(ty) => {
find_idents_in_path(set, &ty.path, recursive);
},
Type::Ptr(ty) => {
if let Some((_, true, _)) = recursive {
find_idents_in_type(set, ty.elem.as_ref(), recursive);
}
},
Type::Reference(ty) => {
if let Some((true, ..)) = recursive {
find_idents_in_type(set, ty.elem.as_ref(), recursive);
}
},
Type::Slice(ty) => {
if recursive.is_some() {
find_idents_in_type(set, ty.elem.as_ref(), recursive);
}
},
Type::TraitObject(ty) => {
// always recursive
for b in &ty.bounds {
if let TypeParamBound::Trait(ty) = b {
find_idents_in_path(set, &ty.path, recursive);
}
}
},
Type::Tuple(ty) => {
if recursive.is_some() {
for ty in &ty.elems {
find_idents_in_type(set, ty, recursive)
}
}
},
_ => (),
}
}

#[inline]
pub(crate) fn dereference(ty: &Type) -> &Type {
if let Type::Reference(ty) = ty {
Expand Down
24 changes: 7 additions & 17 deletions src/common/where_predicates_bool.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::collections::HashSet;

use quote::{quote, ToTokens};
use syn::{
parse::{Parse, ParseStream},
Expand All @@ -9,7 +7,7 @@ use syn::{
Expr, GenericParam, Lit, Meta, MetaNameValue, Path, Token, Type, WherePredicate,
};

use super::{path::path_to_string, r#type::find_idents_in_type};
use super::path::path_to_string;

pub(crate) type WherePredicates = Punctuated<WherePredicate, Token![,]>;

Expand Down Expand Up @@ -89,7 +87,8 @@ pub(crate) fn meta_2_where_predicates(meta: &Meta) -> syn::Result<WherePredicate
}

#[inline]
pub(crate) fn create_where_predicates_from_generic_parameters(
#[allow(dead_code)]
pub(crate) fn create_where_predicates_from_all_generic_parameters(
params: &Punctuated<GenericParam, Comma>,
bound_trait: &Path,
) -> WherePredicates {
Expand All @@ -108,27 +107,18 @@ pub(crate) fn create_where_predicates_from_generic_parameters(

#[inline]
pub(crate) fn create_where_predicates_from_generic_parameters_check_types(
params: &Punctuated<GenericParam, Comma>,
bound_trait: &Path,
types: &[&Type],
recursive: Option<(bool, bool, bool)>,
supertraits: &[proc_macro2::TokenStream],
) -> WherePredicates {
let mut where_predicates = Punctuated::new();

let mut set = HashSet::new();

for t in types {
find_idents_in_type(&mut set, t, recursive);
where_predicates.push(syn::parse2(quote! { #t: #bound_trait }).unwrap());
}

for param in params {
if let GenericParam::Type(ty) = param {
let ident = &ty.ident;

if set.contains(ident) {
where_predicates.push(syn::parse2(quote! { #ident: #bound_trait }).unwrap());
}
}
for supertrait in supertraits {
where_predicates.push(syn::parse2(quote! { Self: #supertrait }).unwrap());
}

where_predicates
Expand Down
Loading
Loading