Skip to content

Commit

Permalink
Add required bounds to derived impl
Browse files Browse the repository at this point in the history
This is implemented using a new TypeBoundsStore struct which tracks
usage of types during parsing and stores required bounds for generics,
trying to use some heuristics to remove unneeded bounds.

Signed-off-by: Justus Fluegel <[email protected]>
  • Loading branch information
JustusFluegel committed Feb 2, 2025
1 parent b8c144f commit 43b375d
Show file tree
Hide file tree
Showing 9 changed files with 484 additions and 35 deletions.
2 changes: 1 addition & 1 deletion miette-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0.78"
quote = "1.0.35"
syn = "2.0.48"
syn = { version = "2.0.48", features = ["extra-traits"] }
46 changes: 35 additions & 11 deletions miette-derive/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::label::Labels;
use crate::related::Related;
use crate::severity::Severity;
use crate::source_code::SourceCode;
use crate::trait_bounds::TraitBoundStore;
use crate::url::Url;

pub enum Diagnostic {
Expand All @@ -19,11 +20,13 @@ pub enum Diagnostic {
ident: syn::Ident,
fields: syn::Fields,
args: DiagnosticDefArgs,
bound_store: TraitBoundStore,
},
Enum {
ident: syn::Ident,
generics: syn::Generics,
variants: Vec<DiagnosticDef>,
bound_store: TraitBoundStore,
},
}

Expand Down Expand Up @@ -71,12 +74,15 @@ pub struct DiagnosticConcreteArgs {
}

impl DiagnosticConcreteArgs {
fn for_fields(fields: &syn::Fields) -> Result<Self, syn::Error> {
let labels = Labels::from_fields(fields)?;
let source_code = SourceCode::from_fields(fields)?;
let related = Related::from_fields(fields)?;
fn for_fields(
fields: &syn::Fields,
bounds_store: &mut TraitBoundStore,
) -> Result<Self, syn::Error> {
let labels = Labels::from_fields(fields, bounds_store)?;
let source_code = SourceCode::from_fields(fields, bounds_store)?;
let related = Related::from_fields(fields, bounds_store)?;
let help = Help::from_fields(fields)?;
let diagnostic_source = DiagnosticSource::from_fields(fields)?;
let diagnostic_source = DiagnosticSource::from_fields(fields, bounds_store)?;
Ok(DiagnosticConcreteArgs {
code: None,
help,
Expand Down Expand Up @@ -156,6 +162,7 @@ impl DiagnosticDefArgs {
_ident: &syn::Ident,
fields: &syn::Fields,
attrs: &[&syn::Attribute],
bounds_store: &mut TraitBoundStore,
allow_transparent: bool,
) -> syn::Result<Self> {
let mut errors = Vec::new();
Expand All @@ -166,7 +173,7 @@ impl DiagnosticDefArgs {
attrs[0].parse_args_with(Punctuated::<DiagnosticArg, Token![,]>::parse_terminated)
{
if matches!(args.first(), Some(DiagnosticArg::Transparent)) {
let forward = Forward::for_transparent_field(fields)?;
let forward = Forward::for_transparent_field(fields, bounds_store)?;
return Ok(Self::Transparent(forward));
}
}
Expand All @@ -182,7 +189,7 @@ impl DiagnosticDefArgs {
matches!(d, DiagnosticArg::Transparent)
}

let mut concrete = DiagnosticConcreteArgs::for_fields(fields)?;
let mut concrete = DiagnosticConcreteArgs::for_fields(fields, bounds_store)?;
for attr in attrs {
let args =
attr.parse_args_with(Punctuated::<DiagnosticArg, Token![,]>::parse_terminated);
Expand Down Expand Up @@ -226,10 +233,13 @@ impl Diagnostic {
.collect::<Vec<&syn::Attribute>>();
Ok(match input.data {
syn::Data::Struct(data_struct) => {
let mut bounds_store = TraitBoundStore::new(&input.generics);

let args = DiagnosticDefArgs::parse(
&input.ident,
&data_struct.fields,
&input_attrs,
&mut bounds_store,
true,
)?;

Expand All @@ -238,16 +248,23 @@ impl Diagnostic {
ident: input.ident,
generics: input.generics,
args,
bound_store: bounds_store,
}
}
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
let mut vars = Vec::new();
let mut bound_store = TraitBoundStore::new(&input.generics);
for var in variants {
let mut variant_attrs = input_attrs.clone();
variant_attrs
.extend(var.attrs.iter().filter(|x| x.path().is_ident("diagnostic")));
let args =
DiagnosticDefArgs::parse(&var.ident, &var.fields, &variant_attrs, true)?;
let args = DiagnosticDefArgs::parse(
&var.ident,
&var.fields,
&variant_attrs,
&mut bound_store,
true,
)?;
vars.push(DiagnosticDef {
ident: var.ident,
fields: var.fields,
Expand All @@ -258,6 +275,7 @@ impl Diagnostic {
ident: input.ident,
generics: input.generics,
variants: vars,
bound_store,
}
}
syn::Data::Union(_) => {
Expand All @@ -276,8 +294,11 @@ impl Diagnostic {
fields,
generics,
args,
bound_store,
} => {
let (impl_generics, ty_generics, where_clause) = &generics.split_for_impl();
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let where_clause = bound_store.merge_with(where_clause);

match args {
DiagnosticDefArgs::Transparent(forward) => {
let code_method = forward.gen_struct_method(WhichFn::Code);
Expand Down Expand Up @@ -369,8 +390,11 @@ impl Diagnostic {
ident,
generics,
variants,
bound_store,
} => {
let (impl_generics, ty_generics, where_clause) = &generics.split_for_impl();
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let where_clause = bound_store.merge_with(where_clause);

let code_body = Code::gen_enum(variants);
let help_body = Help::gen_enum(variants);
let sev_body = Severity::gen_enum(variants);
Expand Down
20 changes: 16 additions & 4 deletions miette-derive/src/diagnostic_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use quote::quote;
use syn::spanned::Spanned;

use crate::forward::WhichFn;
use crate::trait_bounds::TraitBoundStore;
use crate::{
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
utils::{display_pat_members, gen_all_variants_with},
Expand All @@ -11,17 +12,25 @@ use crate::{
pub struct DiagnosticSource(syn::Member);

impl DiagnosticSource {
pub(crate) fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> {
pub(crate) fn from_fields(
fields: &syn::Fields,
bounds_store: &mut TraitBoundStore,
) -> syn::Result<Option<Self>> {
match fields {
syn::Fields::Named(named) => Self::from_fields_vec(named.named.iter().collect()),
syn::Fields::Named(named) => {
Self::from_fields_vec(named.named.iter().collect(), bounds_store)
}
syn::Fields::Unnamed(unnamed) => {
Self::from_fields_vec(unnamed.unnamed.iter().collect())
Self::from_fields_vec(unnamed.unnamed.iter().collect(), bounds_store)
}
syn::Fields::Unit => Ok(None),
}
}

fn from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>> {
fn from_fields_vec(
fields: Vec<&syn::Field>,
bounds_store: &mut TraitBoundStore,
) -> syn::Result<Option<Self>> {
for (i, field) in fields.iter().enumerate() {
for attr in &field.attrs {
if attr.path().is_ident("diagnostic_source") {
Expand All @@ -33,6 +42,9 @@ impl DiagnosticSource {
span: field.span(),
})
};

bounds_store.register_source_usage(&field.ty);

return Ok(Some(DiagnosticSource(diagnostic_source)));
}
}
Expand Down
15 changes: 13 additions & 2 deletions miette-derive/src/forward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use syn::{
spanned::Spanned,
};

use crate::trait_bounds::TraitBoundStore;

pub enum Forward {
Unnamed(usize),
Named(syn::Ident),
Expand Down Expand Up @@ -90,7 +92,10 @@ impl WhichFn {
}

impl Forward {
pub fn for_transparent_field(fields: &syn::Fields) -> syn::Result<Self> {
pub fn for_transparent_field(
fields: &syn::Fields,
bounds_store: &mut TraitBoundStore,
) -> syn::Result<Self> {
let make_err = || {
syn::Error::new(
fields.span(),
Expand All @@ -108,12 +113,18 @@ impl Forward {
.ident
.clone()
.unwrap_or_else(|| format_ident!("unnamed"));

bounds_store.register_transparent_usage(&field.ty);
Ok(Self::Named(field_name))
}
syn::Fields::Unnamed(unnamed) => {
if unnamed.unnamed.iter().len() != 1 {
let mut iter = unnamed.unnamed.iter();
let field = iter.next().ok_or_else(make_err)?;
if iter.next().is_some() {
return Err(make_err());
}

bounds_store.register_transparent_usage(&field.ty);
Ok(Self::Unnamed(0))
}
_ => Err(syn::Error::new(
Expand Down
28 changes: 24 additions & 4 deletions miette-derive/src/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
fmt::{self, Display},
forward::WhichFn,
trait_bounds::TraitBoundStore,
utils::{display_pat_members, gen_all_variants_with},
};

Expand Down Expand Up @@ -101,22 +102,31 @@ impl Parse for LabelAttr {
} else {
(LabelType::Default, None)
};

Ok(LabelAttr { label, lbl_ty })
}
}

impl Labels {
pub fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> {
pub fn from_fields(
fields: &syn::Fields,
bounds_store: &mut TraitBoundStore,
) -> syn::Result<Option<Self>> {
match fields {
syn::Fields::Named(named) => Self::from_fields_vec(named.named.iter().collect()),
syn::Fields::Named(named) => {
Self::from_fields_vec(named.named.iter().collect(), bounds_store)
}
syn::Fields::Unnamed(unnamed) => {
Self::from_fields_vec(unnamed.unnamed.iter().collect())
Self::from_fields_vec(unnamed.unnamed.iter().collect(), bounds_store)
}
syn::Fields::Unit => Ok(None),
}
}

fn from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>> {
fn from_fields_vec(
fields: Vec<&syn::Field>,
bounds_store: &mut TraitBoundStore,
) -> syn::Result<Option<Self>> {
let mut labels = Vec::new();
for (i, field) in fields.iter().enumerate() {
for attr in &field.attrs {
Expand Down Expand Up @@ -144,6 +154,16 @@ impl Labels {
));
}

match lbl_ty {
LabelType::Default | LabelType::Primary => {
bounds_store.register_source_span_usage(&field.ty);
}

LabelType::Collection => {
bounds_store.register_source_span_collection_usage(&field.ty);
}
}

labels.push(Label {
label,
span,
Expand Down
1 change: 1 addition & 0 deletions miette-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod label;
mod related;
mod severity;
mod source_code;
mod trait_bounds;
mod url;
mod utils;

Expand Down
18 changes: 14 additions & 4 deletions miette-derive/src/related.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,32 @@ use syn::spanned::Spanned;
use crate::{
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
forward::WhichFn,
trait_bounds::TraitBoundStore,
utils::{display_pat_members, gen_all_variants_with},
};

pub struct Related(syn::Member);

impl Related {
pub(crate) fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> {
pub(crate) fn from_fields(
fields: &syn::Fields,
bounds_store: &mut TraitBoundStore,
) -> syn::Result<Option<Self>> {
match fields {
syn::Fields::Named(named) => Self::from_fields_vec(named.named.iter().collect()),
syn::Fields::Named(named) => {
Self::from_fields_vec(named.named.iter().collect(), bounds_store)
}
syn::Fields::Unnamed(unnamed) => {
Self::from_fields_vec(unnamed.unnamed.iter().collect())
Self::from_fields_vec(unnamed.unnamed.iter().collect(), bounds_store)
}
syn::Fields::Unit => Ok(None),
}
}

fn from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>> {
fn from_fields_vec(
fields: Vec<&syn::Field>,
bounds_store: &mut TraitBoundStore,
) -> syn::Result<Option<Self>> {
for (i, field) in fields.iter().enumerate() {
for attr in &field.attrs {
if attr.path().is_ident("related") {
Expand All @@ -33,6 +42,7 @@ impl Related {
span: field.span(),
})
};
bounds_store.register_related_usage(&field.ty);
return Ok(Some(Related(related)));
}
}
Expand Down
Loading

0 comments on commit 43b375d

Please sign in to comment.