mirror of https://github.com/zkat/miette.git
Simplify the trait bound store and just register where clauses directly
Signed-off-by: Justus Flügel <justusfluegel@gmail.com>
This commit is contained in:
parent
a23114fffe
commit
ff32d81cb0
|
|
@ -11,7 +11,7 @@ use crate::label::Labels;
|
||||||
use crate::related::Related;
|
use crate::related::Related;
|
||||||
use crate::severity::Severity;
|
use crate::severity::Severity;
|
||||||
use crate::source_code::SourceCode;
|
use crate::source_code::SourceCode;
|
||||||
use crate::trait_bounds::TraitBoundStore;
|
use crate::trait_bounds::TypeParamBoundStore;
|
||||||
use crate::url::Url;
|
use crate::url::Url;
|
||||||
|
|
||||||
pub enum Diagnostic {
|
pub enum Diagnostic {
|
||||||
|
|
@ -20,13 +20,13 @@ pub enum Diagnostic {
|
||||||
ident: syn::Ident,
|
ident: syn::Ident,
|
||||||
fields: syn::Fields,
|
fields: syn::Fields,
|
||||||
args: DiagnosticDefArgs,
|
args: DiagnosticDefArgs,
|
||||||
bound_store: TraitBoundStore,
|
bound_store: TypeParamBoundStore,
|
||||||
},
|
},
|
||||||
Enum {
|
Enum {
|
||||||
ident: syn::Ident,
|
ident: syn::Ident,
|
||||||
generics: syn::Generics,
|
generics: syn::Generics,
|
||||||
variants: Vec<DiagnosticDef>,
|
variants: Vec<DiagnosticDef>,
|
||||||
bound_store: TraitBoundStore,
|
bound_store: TypeParamBoundStore,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,7 +76,7 @@ pub struct DiagnosticConcreteArgs {
|
||||||
impl DiagnosticConcreteArgs {
|
impl DiagnosticConcreteArgs {
|
||||||
fn for_fields(
|
fn for_fields(
|
||||||
fields: &syn::Fields,
|
fields: &syn::Fields,
|
||||||
bounds_store: &mut TraitBoundStore,
|
bounds_store: &mut TypeParamBoundStore,
|
||||||
) -> Result<Self, syn::Error> {
|
) -> Result<Self, syn::Error> {
|
||||||
let labels = Labels::from_fields(fields, bounds_store)?;
|
let labels = Labels::from_fields(fields, bounds_store)?;
|
||||||
let source_code = SourceCode::from_fields(fields, bounds_store)?;
|
let source_code = SourceCode::from_fields(fields, bounds_store)?;
|
||||||
|
|
@ -162,7 +162,7 @@ impl DiagnosticDefArgs {
|
||||||
_ident: &syn::Ident,
|
_ident: &syn::Ident,
|
||||||
fields: &syn::Fields,
|
fields: &syn::Fields,
|
||||||
attrs: &[&syn::Attribute],
|
attrs: &[&syn::Attribute],
|
||||||
bounds_store: &mut TraitBoundStore,
|
bounds_store: &mut TypeParamBoundStore,
|
||||||
allow_transparent: bool,
|
allow_transparent: bool,
|
||||||
) -> syn::Result<Self> {
|
) -> syn::Result<Self> {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|
@ -233,7 +233,7 @@ impl Diagnostic {
|
||||||
.collect::<Vec<&syn::Attribute>>();
|
.collect::<Vec<&syn::Attribute>>();
|
||||||
Ok(match input.data {
|
Ok(match input.data {
|
||||||
syn::Data::Struct(data_struct) => {
|
syn::Data::Struct(data_struct) => {
|
||||||
let mut bounds_store = TraitBoundStore::new(&input.generics);
|
let mut bounds_store = TypeParamBoundStore::new(&input.generics);
|
||||||
|
|
||||||
let args = DiagnosticDefArgs::parse(
|
let args = DiagnosticDefArgs::parse(
|
||||||
&input.ident,
|
&input.ident,
|
||||||
|
|
@ -253,7 +253,7 @@ impl Diagnostic {
|
||||||
}
|
}
|
||||||
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
||||||
let mut vars = Vec::new();
|
let mut vars = Vec::new();
|
||||||
let mut bound_store = TraitBoundStore::new(&input.generics);
|
let mut bound_store = TypeParamBoundStore::new(&input.generics);
|
||||||
for var in variants {
|
for var in variants {
|
||||||
let mut variant_attrs = input_attrs.clone();
|
let mut variant_attrs = input_attrs.clone();
|
||||||
variant_attrs
|
variant_attrs
|
||||||
|
|
@ -297,7 +297,7 @@ impl Diagnostic {
|
||||||
bound_store,
|
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 where_clause = bound_store.add_to_where_clause(where_clause);
|
||||||
|
|
||||||
match args {
|
match args {
|
||||||
DiagnosticDefArgs::Transparent(forward) => {
|
DiagnosticDefArgs::Transparent(forward) => {
|
||||||
|
|
@ -397,7 +397,7 @@ impl Diagnostic {
|
||||||
bound_store,
|
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 where_clause = bound_store.add_to_where_clause(where_clause);
|
||||||
|
|
||||||
let code_body = Code::gen_enum(variants);
|
let code_body = Code::gen_enum(variants);
|
||||||
let help_body = Help::gen_enum(variants);
|
let help_body = Help::gen_enum(variants);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use quote::quote;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
use crate::forward::WhichFn;
|
use crate::forward::WhichFn;
|
||||||
use crate::trait_bounds::TraitBoundStore;
|
use crate::trait_bounds::TypeParamBoundStore;
|
||||||
use crate::{
|
use crate::{
|
||||||
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
|
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
|
||||||
utils::{display_pat_members, gen_all_variants_with},
|
utils::{display_pat_members, gen_all_variants_with},
|
||||||
|
|
@ -14,7 +14,7 @@ pub struct DiagnosticSource(syn::Member);
|
||||||
impl DiagnosticSource {
|
impl DiagnosticSource {
|
||||||
pub(crate) fn from_fields(
|
pub(crate) fn from_fields(
|
||||||
fields: &syn::Fields,
|
fields: &syn::Fields,
|
||||||
bounds_store: &mut TraitBoundStore,
|
bounds_store: &mut TypeParamBoundStore,
|
||||||
) -> syn::Result<Option<Self>> {
|
) -> syn::Result<Option<Self>> {
|
||||||
match fields {
|
match fields {
|
||||||
syn::Fields::Named(named) => {
|
syn::Fields::Named(named) => {
|
||||||
|
|
@ -29,7 +29,7 @@ impl DiagnosticSource {
|
||||||
|
|
||||||
fn from_fields_vec(
|
fn from_fields_vec(
|
||||||
fields: Vec<&syn::Field>,
|
fields: Vec<&syn::Field>,
|
||||||
bounds_store: &mut TraitBoundStore,
|
bounds_store: &mut TypeParamBoundStore,
|
||||||
) -> syn::Result<Option<Self>> {
|
) -> syn::Result<Option<Self>> {
|
||||||
for (i, field) in fields.iter().enumerate() {
|
for (i, field) in fields.iter().enumerate() {
|
||||||
for attr in &field.attrs {
|
for attr in &field.attrs {
|
||||||
|
|
@ -43,7 +43,10 @@ impl DiagnosticSource {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
bounds_store.register_source_usage(&field.ty);
|
let ty = &field.ty;
|
||||||
|
bounds_store.add_where_predicate(
|
||||||
|
syn::parse_quote!(#ty: ::miette::Diagnostic + 'static),
|
||||||
|
);
|
||||||
|
|
||||||
return Ok(Some(DiagnosticSource(diagnostic_source)));
|
return Ok(Some(DiagnosticSource(diagnostic_source)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use syn::{
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::trait_bounds::TraitBoundStore;
|
use crate::trait_bounds::TypeParamBoundStore;
|
||||||
|
|
||||||
pub enum Forward {
|
pub enum Forward {
|
||||||
Unnamed(usize),
|
Unnamed(usize),
|
||||||
|
|
@ -98,7 +98,7 @@ impl WhichFn {
|
||||||
impl Forward {
|
impl Forward {
|
||||||
pub fn for_transparent_field(
|
pub fn for_transparent_field(
|
||||||
fields: &syn::Fields,
|
fields: &syn::Fields,
|
||||||
bounds_store: &mut TraitBoundStore,
|
bounds_store: &mut TypeParamBoundStore,
|
||||||
) -> syn::Result<Self> {
|
) -> syn::Result<Self> {
|
||||||
let make_err = || {
|
let make_err = || {
|
||||||
syn::Error::new(
|
syn::Error::new(
|
||||||
|
|
@ -118,7 +118,9 @@ impl Forward {
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| format_ident!("unnamed"));
|
.unwrap_or_else(|| format_ident!("unnamed"));
|
||||||
|
|
||||||
bounds_store.register_transparent_usage(&field.ty);
|
let ty = &field.ty;
|
||||||
|
bounds_store
|
||||||
|
.add_where_predicate(syn::parse_quote! {#ty: ::miette::Diagnostic + 'static});
|
||||||
Ok(Self::Named(field_name))
|
Ok(Self::Named(field_name))
|
||||||
}
|
}
|
||||||
syn::Fields::Unnamed(unnamed) => {
|
syn::Fields::Unnamed(unnamed) => {
|
||||||
|
|
@ -128,7 +130,9 @@ impl Forward {
|
||||||
return Err(make_err());
|
return Err(make_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
bounds_store.register_transparent_usage(&field.ty);
|
let ty = &field.ty;
|
||||||
|
bounds_store
|
||||||
|
.add_where_predicate(syn::parse_quote! {#ty: ::miette::Diagnostic + 'static});
|
||||||
Ok(Self::Unnamed(0))
|
Ok(Self::Unnamed(0))
|
||||||
}
|
}
|
||||||
_ => Err(syn::Error::new(
|
_ => Err(syn::Error::new(
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,15 @@ use syn::{
|
||||||
parenthesized,
|
parenthesized,
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
Token,
|
Lifetime, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
|
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
|
||||||
fmt::{self, Display},
|
fmt::{self, Display},
|
||||||
forward::WhichFn,
|
forward::WhichFn,
|
||||||
trait_bounds::TraitBoundStore,
|
trait_bounds::TypeParamBoundStore,
|
||||||
utils::{display_pat_members, gen_all_variants_with},
|
utils::{display_pat_members, extract_option, gen_all_variants_with},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Labels(Vec<Label>);
|
pub struct Labels(Vec<Label>);
|
||||||
|
|
@ -110,7 +110,7 @@ impl Parse for LabelAttr {
|
||||||
impl Labels {
|
impl Labels {
|
||||||
pub fn from_fields(
|
pub fn from_fields(
|
||||||
fields: &syn::Fields,
|
fields: &syn::Fields,
|
||||||
bounds_store: &mut TraitBoundStore,
|
bounds_store: &mut TypeParamBoundStore,
|
||||||
) -> syn::Result<Option<Self>> {
|
) -> syn::Result<Option<Self>> {
|
||||||
match fields {
|
match fields {
|
||||||
syn::Fields::Named(named) => {
|
syn::Fields::Named(named) => {
|
||||||
|
|
@ -125,7 +125,7 @@ impl Labels {
|
||||||
|
|
||||||
fn from_fields_vec(
|
fn from_fields_vec(
|
||||||
fields: Vec<&syn::Field>,
|
fields: Vec<&syn::Field>,
|
||||||
bounds_store: &mut TraitBoundStore,
|
bounds_store: &mut TypeParamBoundStore,
|
||||||
) -> syn::Result<Option<Self>> {
|
) -> syn::Result<Option<Self>> {
|
||||||
let mut labels = Vec::new();
|
let mut labels = Vec::new();
|
||||||
for (i, field) in fields.iter().enumerate() {
|
for (i, field) in fields.iter().enumerate() {
|
||||||
|
|
@ -156,11 +156,31 @@ impl Labels {
|
||||||
|
|
||||||
match lbl_ty {
|
match lbl_ty {
|
||||||
LabelType::Default | LabelType::Primary => {
|
LabelType::Default | LabelType::Primary => {
|
||||||
bounds_store.register_label_usage(&field.ty);
|
let option_ty = extract_option(&field.ty).unwrap_or(&field.ty);
|
||||||
|
bounds_store.extend_where_predicates(syn::parse_quote!{
|
||||||
|
#option_ty: ::std::borrow::ToOwned,
|
||||||
|
<#option_ty as ::std::borrow::ToOwned>::Owned: ::std::convert::Into<::miette::SourceSpan>
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelType::Collection => {
|
LabelType::Collection => {
|
||||||
bounds_store.register_label_collection_usage(&field.ty);
|
let ty = &field.ty;
|
||||||
|
let lt: Lifetime = syn::parse_quote!('__miette_internal_lt);
|
||||||
|
bounds_store.extend_where_predicates(syn::parse_quote!{
|
||||||
|
for<#lt> &#lt #ty: ::std::iter::IntoIterator,
|
||||||
|
for<#lt> <&#lt #ty as ::std::iter::IntoIterator>::Item: ::std::ops::Deref,
|
||||||
|
for<#lt> <
|
||||||
|
<&#lt #ty as ::std::iter::IntoIterator>::Item
|
||||||
|
as ::std::ops::Deref
|
||||||
|
>::Target : ::std::borrow::ToOwned,
|
||||||
|
for<#lt> <
|
||||||
|
<
|
||||||
|
<&#lt #ty as ::std::iter::IntoIterator>::Item
|
||||||
|
as ::std::ops::Deref
|
||||||
|
>::Target
|
||||||
|
as ::std::borrow::ToOwned
|
||||||
|
>::Owned: ::std::convert::Into<::miette::SourceSpan>
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use syn::spanned::Spanned;
|
||||||
use crate::{
|
use crate::{
|
||||||
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
|
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
|
||||||
forward::WhichFn,
|
forward::WhichFn,
|
||||||
trait_bounds::TraitBoundStore,
|
trait_bounds::TypeParamBoundStore,
|
||||||
utils::{display_pat_members, gen_all_variants_with},
|
utils::{display_pat_members, gen_all_variants_with},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ pub struct Related(syn::Member);
|
||||||
impl Related {
|
impl Related {
|
||||||
pub(crate) fn from_fields(
|
pub(crate) fn from_fields(
|
||||||
fields: &syn::Fields,
|
fields: &syn::Fields,
|
||||||
bounds_store: &mut TraitBoundStore,
|
bounds_store: &mut TypeParamBoundStore,
|
||||||
) -> syn::Result<Option<Self>> {
|
) -> syn::Result<Option<Self>> {
|
||||||
match fields {
|
match fields {
|
||||||
syn::Fields::Named(named) => {
|
syn::Fields::Named(named) => {
|
||||||
|
|
@ -29,7 +29,7 @@ impl Related {
|
||||||
|
|
||||||
fn from_fields_vec(
|
fn from_fields_vec(
|
||||||
fields: Vec<&syn::Field>,
|
fields: Vec<&syn::Field>,
|
||||||
bounds_store: &mut TraitBoundStore,
|
bounds_store: &mut TypeParamBoundStore,
|
||||||
) -> syn::Result<Option<Self>> {
|
) -> syn::Result<Option<Self>> {
|
||||||
for (i, field) in fields.iter().enumerate() {
|
for (i, field) in fields.iter().enumerate() {
|
||||||
for attr in &field.attrs {
|
for attr in &field.attrs {
|
||||||
|
|
@ -42,7 +42,17 @@ impl Related {
|
||||||
span: field.span(),
|
span: field.span(),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
bounds_store.register_related_usage(&field.ty);
|
// this is somewhat hacky and only supports concrete types for the #[related] type
|
||||||
|
// ittself but supports generics for the arguments, i.e. Vec<T> where T is generic.
|
||||||
|
//
|
||||||
|
// I think that this is a current limitation of the design of the Diagnostic trait,
|
||||||
|
// since we'd need bounds on the method and we can't do that (to refer to the lifetime)
|
||||||
|
//
|
||||||
|
// Someone smarter than me might be able to figure out a better solution (?)
|
||||||
|
let ty = &field.ty;
|
||||||
|
bounds_store.add_where_predicate(syn::parse_quote!(
|
||||||
|
<#ty as ::std::iter::IntoIterator>::Item: ::miette::Diagnostic + 'static
|
||||||
|
));
|
||||||
return Ok(Some(Related(related)));
|
return Ok(Some(Related(related)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::{spanned::Spanned, AngleBracketedGenericArguments, GenericArgument, PathArguments};
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
|
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
|
||||||
forward::WhichFn,
|
forward::WhichFn,
|
||||||
trait_bounds::TraitBoundStore,
|
trait_bounds::TypeParamBoundStore,
|
||||||
utils::{display_pat_members, gen_all_variants_with},
|
utils::{display_pat_members, extract_option, gen_all_variants_with},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SourceCode {
|
pub struct SourceCode {
|
||||||
|
|
@ -17,7 +17,7 @@ pub struct SourceCode {
|
||||||
impl SourceCode {
|
impl SourceCode {
|
||||||
pub fn from_fields(
|
pub fn from_fields(
|
||||||
fields: &syn::Fields,
|
fields: &syn::Fields,
|
||||||
bounds_store: &mut TraitBoundStore,
|
bounds_store: &mut TypeParamBoundStore,
|
||||||
) -> syn::Result<Option<Self>> {
|
) -> syn::Result<Option<Self>> {
|
||||||
match fields {
|
match fields {
|
||||||
syn::Fields::Named(named) => {
|
syn::Fields::Named(named) => {
|
||||||
|
|
@ -32,18 +32,16 @@ impl SourceCode {
|
||||||
|
|
||||||
fn from_fields_vec(
|
fn from_fields_vec(
|
||||||
fields: Vec<&syn::Field>,
|
fields: Vec<&syn::Field>,
|
||||||
bounds_store: &mut TraitBoundStore,
|
bounds_store: &mut TypeParamBoundStore,
|
||||||
) -> syn::Result<Option<Self>> {
|
) -> syn::Result<Option<Self>> {
|
||||||
for (i, field) in fields.iter().enumerate() {
|
for (i, field) in fields.iter().enumerate() {
|
||||||
for attr in &field.attrs {
|
for attr in &field.attrs {
|
||||||
if attr.path().is_ident("source_code") {
|
if attr.path().is_ident("source_code") {
|
||||||
let is_option = TraitBoundStore::extract_option(&field.ty);
|
let is_option = extract_option(&field.ty);
|
||||||
|
|
||||||
if let Some(option_ty) = is_option {
|
let code_ty = is_option.unwrap_or(&field.ty);
|
||||||
bounds_store.register_source_code_usage(option_ty);
|
bounds_store
|
||||||
} else {
|
.add_where_predicate(syn::parse_quote!(#code_ty: ::miette::SourceCode));
|
||||||
bounds_store.register_source_code_usage(&field.ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
let source_code = if let Some(ident) = field.ident.clone() {
|
let source_code = if let Some(ident) = field.ident.clone() {
|
||||||
syn::Member::Named(ident)
|
syn::Member::Named(ident)
|
||||||
|
|
|
||||||
|
|
@ -1,176 +1,51 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, HashSet, VecDeque},
|
||||||
iter::{once, FromIterator},
|
iter::once,
|
||||||
};
|
};
|
||||||
|
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use syn::{
|
use syn::{
|
||||||
punctuated::Punctuated, token::Plus, AngleBracketedGenericArguments, AssocType, BoundLifetimes,
|
punctuated::Punctuated, AngleBracketedGenericArguments, AssocType, BoundLifetimes,
|
||||||
GenericArgument, GenericParam, Generics, ParenthesizedGenericArguments, Path, PathArguments,
|
GenericArgument, GenericParam, Generics, ParenthesizedGenericArguments, PathArguments,
|
||||||
PathSegment, PredicateType, ReturnType, Token, TraitBound, Type, TypeArray, TypeGroup,
|
PredicateType, ReturnType, Token, Type, TypeArray, TypeGroup, TypeParamBound, TypeParen,
|
||||||
TypeParamBound, TypeParen, TypePath, TypePtr, TypeReference, TypeSlice, TypeTuple, WhereClause,
|
TypePath, TypePtr, TypeReference, TypeSlice, TypeTuple, WhereClause, WherePredicate,
|
||||||
WherePredicate,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
// Potential improvement, although idk if this actually ends up
|
||||||
pub struct RequiredTraitBound {
|
// mattering is to switch this to something like FxHashMap like the rustc compiler uses internally
|
||||||
r#static: bool,
|
pub struct TypeParamBoundStore(HashMap<(Option<BoundLifetimes>, Type), HashSet<TypeParamBound>>);
|
||||||
std_error: bool,
|
|
||||||
miette_diagnostic: bool,
|
|
||||||
source_code: bool,
|
|
||||||
into_source_span: bool,
|
|
||||||
std_into_iter: bool,
|
|
||||||
std_deref: bool,
|
|
||||||
std_to_owned: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RequiredTraitBound {
|
impl TypeParamBoundStore {
|
||||||
fn to_bounds(&self) -> Punctuated<TypeParamBound, Plus> {
|
/// Creates a new TraitBoundStore, filling it with some generics which are used to heuristically remove trivial bounds.
|
||||||
let mut bounds = Punctuated::new();
|
///
|
||||||
if self.std_error && !self.miette_diagnostic {
|
/// Note that it is essential that all relevant generics are actually passed here, since if they aren't bounds which are required might be heuristically removed.
|
||||||
bounds.push(TypeParamBound::Trait(syn::parse_quote!(
|
|
||||||
::std::error::Error
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.miette_diagnostic {
|
|
||||||
bounds.push(TypeParamBound::Trait(syn::parse_quote!(
|
|
||||||
::miette::Diagnostic
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.source_code {
|
|
||||||
bounds.push(TypeParamBound::Trait(syn::parse_quote!(
|
|
||||||
::miette::SourceCode
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.into_source_span {
|
|
||||||
bounds.push(TypeParamBound::Trait(syn::parse_quote!(
|
|
||||||
::std::convert::Into<::miette::SourceSpan>
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.std_into_iter {
|
|
||||||
bounds.push(TypeParamBound::Trait(syn::parse_quote!(
|
|
||||||
::std::iter::IntoIterator
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.std_deref {
|
|
||||||
bounds.push(TypeParamBound::Trait(syn::parse_quote!(::std::ops::Deref)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.std_to_owned {
|
|
||||||
bounds.push(TypeParamBound::Trait(syn::parse_quote!(
|
|
||||||
::std::borrow::ToOwned
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.r#static {
|
|
||||||
bounds.push(TypeParamBound::Lifetime(syn::parse_quote!('static)))
|
|
||||||
}
|
|
||||||
|
|
||||||
bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_transparent_usage(&mut self) {
|
|
||||||
self.r#static = true;
|
|
||||||
self.miette_diagnostic = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_source_code_usage(&mut self) {
|
|
||||||
self.source_code = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_label_usage(&mut self) {
|
|
||||||
self.into_source_span = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_collection_usage(&mut self) {
|
|
||||||
self.std_into_iter = true;
|
|
||||||
}
|
|
||||||
fn register_related_item_usage(&mut self) {
|
|
||||||
self.miette_diagnostic = true;
|
|
||||||
self.r#static = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_source_usage(&mut self) {
|
|
||||||
self.miette_diagnostic = true;
|
|
||||||
self.r#static = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_deref_usage(&mut self) {
|
|
||||||
self.std_deref = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_to_owned_usage(&mut self) {
|
|
||||||
self.std_to_owned = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TraitBoundStore(HashMap<(Option<BoundLifetimes>, Type), RequiredTraitBound>);
|
|
||||||
|
|
||||||
impl TraitBoundStore {
|
|
||||||
pub fn new(generics: &Generics) -> Self {
|
pub fn new(generics: &Generics) -> Self {
|
||||||
let hash_map = generics
|
let hash_map = generics
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|param| {
|
.filter_map(|param| match param {
|
||||||
if let GenericParam::Type(ty) = param {
|
GenericParam::Type(ty) => Some(ty),
|
||||||
Some(ty)
|
_ => None,
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.map(|param| {
|
.map(|param| {
|
||||||
Type::Path(TypePath {
|
let ident = ¶m.ident;
|
||||||
qself: None,
|
Type::Path(syn::parse_quote!(#ident))
|
||||||
path: Path {
|
|
||||||
leading_colon: None,
|
|
||||||
segments: Punctuated::from_iter(once(PathSegment {
|
|
||||||
ident: param.ident.clone(),
|
|
||||||
arguments: PathArguments::None,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.map(|v| ((None, v), RequiredTraitBound::default()))
|
.map(|ty| ((None, ty), Default::default()))
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
Self(hash_map)
|
Self(hash_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_option(r#type: &Type) -> Option<&Type> {
|
/// Checks heuristically if `type` is using any generic type inside it.
|
||||||
if let syn::Type::Path(syn::TypePath {
|
///
|
||||||
path: syn::Path { segments, .. },
|
/// This is guaranteed to never false-negative but might
|
||||||
..
|
/// false-positive if checking exhaustively would be expensive or
|
||||||
}) = r#type
|
/// an unexpected case is encountered which this can't handle.
|
||||||
{
|
///
|
||||||
segments
|
/// # Returns
|
||||||
.last()
|
/// Option with a simplified type if determined to be dependant, none otherwise
|
||||||
.filter(|seg| seg.ident == "Option")
|
fn generic_usage_heuristics(&self, mut r#type: Type) -> Option<Type> {
|
||||||
.and_then(|seg| match &seg.arguments {
|
|
||||||
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
|
||||||
args, ..
|
|
||||||
}) => {
|
|
||||||
let mut iter = args.iter();
|
|
||||||
|
|
||||||
let ty = iter.next();
|
|
||||||
iter.next().xor(ty)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.and_then(|arg| match arg {
|
|
||||||
GenericArgument::Type(ty) => Some(ty),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_generic_usage<'ty>(&self, mut r#type: &'ty Type) -> Option<&'ty Type> {
|
|
||||||
// in theory we could skip all this logic and just allow trivial bounds but that would add redundant trait bounds
|
// in theory we could skip all this logic and just allow trivial bounds but that would add redundant trait bounds
|
||||||
// to the derived impl - would be another choice to make. I choose to filter as much as possible so that we don't
|
// to the derived impl - would be another choice to make. I choose to filter as much as possible so that we don't
|
||||||
// introduce unneccessary bounds.
|
// introduce unneccessary bounds.
|
||||||
|
|
@ -178,8 +53,8 @@ impl TraitBoundStore {
|
||||||
// this reduces the type down as much as possible to remove unneeded groups.
|
// this reduces the type down as much as possible to remove unneeded groups.
|
||||||
let original_type = loop {
|
let original_type = loop {
|
||||||
match r#type {
|
match r#type {
|
||||||
Type::Paren(TypeParen { elem, .. }) => r#type = &**elem,
|
Type::Paren(TypeParen { elem, .. }) => r#type = *elem,
|
||||||
Type::Group(TypeGroup { elem, .. }) => r#type = &**elem,
|
Type::Group(TypeGroup { elem, .. }) => r#type = *elem,
|
||||||
x => break x,
|
x => break x,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -193,7 +68,7 @@ impl TraitBoundStore {
|
||||||
let max_depth = 8;
|
let max_depth = 8;
|
||||||
|
|
||||||
let mut to_check_queue: VecDeque<(&Type, usize)> = VecDeque::new();
|
let mut to_check_queue: VecDeque<(&Type, usize)> = VecDeque::new();
|
||||||
to_check_queue.push_back((original_type, 0));
|
to_check_queue.push_back((&original_type, 0));
|
||||||
|
|
||||||
while !depends_on_generic {
|
while !depends_on_generic {
|
||||||
// this needs to be like this cuz if-let-chains aren't supported yet
|
// this needs to be like this cuz if-let-chains aren't supported yet
|
||||||
|
|
@ -299,173 +174,70 @@ impl TraitBoundStore {
|
||||||
depends_on_generic.then_some(original_type)
|
depends_on_generic.then_some(original_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merge_with(&self, where_clause: Option<&WhereClause>) -> Option<WhereClause> {
|
pub fn add_predicate(
|
||||||
let mut where_clause = where_clause.cloned().unwrap_or_else(|| WhereClause {
|
&mut self,
|
||||||
where_token: Token),
|
PredicateType {
|
||||||
predicates: Punctuated::new(),
|
lifetimes,
|
||||||
});
|
bounded_ty,
|
||||||
|
colon_token: _,
|
||||||
|
bounds,
|
||||||
|
}: PredicateType,
|
||||||
|
) {
|
||||||
|
let Some(bounded_ty) = self.generic_usage_heuristics(bounded_ty) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
where_clause
|
self.0
|
||||||
.predicates
|
.entry((lifetimes, bounded_ty))
|
||||||
.extend(self.0.iter().filter_map(|(ty, bounds)| {
|
.or_default()
|
||||||
let bounds = bounds.to_bounds();
|
.extend(bounds);
|
||||||
(!bounds.is_empty()).then(|| {
|
}
|
||||||
WherePredicate::Type(PredicateType {
|
|
||||||
lifetimes: ty.0.clone(),
|
// Since syn for some reason doesn't implement `Parse` for `PredicateType`
|
||||||
bounded_ty: ty.1.clone(),
|
// this method is meant for ease of use with `syn::parse_quote!`.
|
||||||
colon_token: Token),
|
pub fn add_where_predicate(&mut self, predicate: WherePredicate) {
|
||||||
bounds,
|
let WherePredicate::Type(ty) = predicate else {
|
||||||
})
|
unimplemented!("Only type predicates are supported");
|
||||||
|
};
|
||||||
|
|
||||||
|
self.add_predicate(ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend_where_predicates(&mut self, predicates: Punctuated<WherePredicate, Token![,]>) {
|
||||||
|
for predicate in predicates {
|
||||||
|
self.add_where_predicate(predicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_to_where_clause(&self, where_clause: Option<&WhereClause>) -> Option<WhereClause> {
|
||||||
|
let predicates = self
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, bounds)| !bounds.is_empty())
|
||||||
|
.map(|(a, b)| (a.clone(), b.clone()))
|
||||||
|
.map(|((lifetimes, bounded_ty), bounds)| {
|
||||||
|
WherePredicate::Type(PredicateType {
|
||||||
|
lifetimes,
|
||||||
|
bounded_ty,
|
||||||
|
colon_token: Token),
|
||||||
|
bounds: bounds.into_iter().collect(),
|
||||||
})
|
})
|
||||||
}));
|
})
|
||||||
|
.peekable();
|
||||||
|
|
||||||
where_clause
|
// de-duplicate elements newly added and within existing where clause
|
||||||
.predicates
|
let predicates = predicates
|
||||||
.push(WherePredicate::Type(PredicateType {
|
.chain(
|
||||||
lifetimes: None,
|
where_clause
|
||||||
bounded_ty: Type::Path(TypePath {
|
.into_iter()
|
||||||
qself: None,
|
.flat_map(|where_clause| where_clause.predicates.clone()),
|
||||||
path: Path {
|
)
|
||||||
leading_colon: None,
|
.chain(once(syn::parse_quote!(Self: ::std::error::Error)))
|
||||||
segments: Punctuated::from_iter(once(PathSegment {
|
.collect::<HashSet<_>>();
|
||||||
ident: syn::Ident::new("Self", Span::mixed_site()),
|
|
||||||
arguments: PathArguments::None,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
colon_token: syn::Token),
|
|
||||||
bounds: Punctuated::from_iter(once(TypeParamBound::Trait(TraitBound {
|
|
||||||
paren_token: None,
|
|
||||||
modifier: syn::TraitBoundModifier::None,
|
|
||||||
lifetimes: None,
|
|
||||||
path: syn::parse_quote!(::std::error::Error),
|
|
||||||
}))),
|
|
||||||
}));
|
|
||||||
|
|
||||||
Some(where_clause)
|
Some(WhereClause {
|
||||||
}
|
where_token: Token),
|
||||||
|
predicates: predicates.into_iter().collect(),
|
||||||
pub fn register_transparent_usage(&mut self, r#type: &Type) {
|
})
|
||||||
let Some(r#type) = self.check_generic_usage(r#type) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let type_opts = self.0.entry((None, r#type.clone())).or_default();
|
|
||||||
type_opts.register_transparent_usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_source_code_usage(&mut self, r#type: &Type) {
|
|
||||||
let Some(r#type) = self.check_generic_usage(r#type) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let type_opts = self.0.entry((None, r#type.clone())).or_default();
|
|
||||||
type_opts.register_source_code_usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_label_usage(&mut self, r#type: &Type) {
|
|
||||||
let r#type = Self::extract_option(r#type).unwrap_or(r#type);
|
|
||||||
|
|
||||||
let Some(ty) = self.check_generic_usage(r#type) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let type_opts = self.0.entry((None, ty.clone())).or_default();
|
|
||||||
|
|
||||||
type_opts.register_to_owned_usage();
|
|
||||||
|
|
||||||
let type_opts_to_owned = self
|
|
||||||
.0
|
|
||||||
.entry((
|
|
||||||
None,
|
|
||||||
syn::parse_quote!(<#ty as ::std::borrow::ToOwned>::Owned),
|
|
||||||
))
|
|
||||||
.or_default();
|
|
||||||
type_opts_to_owned.register_label_usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_label_collection_usage(&mut self, r#type: &Type) {
|
|
||||||
let Some(ty) = self.check_generic_usage(r#type) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let ty: syn::Type = syn::parse_quote!(&'__miette_internal_lt #ty);
|
|
||||||
|
|
||||||
let type_opts = self
|
|
||||||
.0
|
|
||||||
.entry((
|
|
||||||
Some(syn::parse_quote!(for<'__miette_internal_lt>)),
|
|
||||||
ty.clone(),
|
|
||||||
))
|
|
||||||
.or_default();
|
|
||||||
type_opts.register_collection_usage();
|
|
||||||
|
|
||||||
let type_opts_item = self
|
|
||||||
.0
|
|
||||||
.entry((
|
|
||||||
Some(syn::parse_quote!(for<'__miette_internal_lt>)),
|
|
||||||
syn::parse_quote!(<#ty as ::std::iter::IntoIterator>::Item),
|
|
||||||
))
|
|
||||||
.or_default();
|
|
||||||
type_opts_item.register_deref_usage();
|
|
||||||
|
|
||||||
let type_opts_deref_item = self
|
|
||||||
.0
|
|
||||||
.entry((
|
|
||||||
Some(syn::parse_quote! {for<'__miette_internal_lt>}),
|
|
||||||
syn::parse_quote! {
|
|
||||||
<<#ty as ::std::iter::IntoIterator>::Item as ::std::ops::Deref>::Target
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.or_default();
|
|
||||||
type_opts_deref_item.register_to_owned_usage();
|
|
||||||
|
|
||||||
let type_opts_deref_to_owned_item = self
|
|
||||||
.0
|
|
||||||
.entry((
|
|
||||||
Some(syn::parse_quote! {for<'__miette_internal_lt>}),
|
|
||||||
syn::parse_quote! {
|
|
||||||
<
|
|
||||||
<
|
|
||||||
<#ty as ::std::iter::IntoIterator>::Item
|
|
||||||
as ::std::ops::Deref
|
|
||||||
>::Target
|
|
||||||
as ::std::borrow::ToOwned
|
|
||||||
>::Owned
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.or_default();
|
|
||||||
type_opts_deref_to_owned_item.register_label_usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_related_usage(&mut self, r#type: &Type) {
|
|
||||||
let Some(ty) = self.check_generic_usage(r#type) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// this is somewhat hacky and only supports concrete types for the #[related] type
|
|
||||||
// ittself but supports generics for the arguments, i.e. Vec<T> where T is generic.
|
|
||||||
//
|
|
||||||
// I think that this is a current limitation of the design of the Diagnostic trait,
|
|
||||||
// since we'd need bounds on the method and we can't do that (to refer to the lifetime)
|
|
||||||
//
|
|
||||||
// Someone smarter than me might be able to figure out a better solution (?)
|
|
||||||
let type_opts_item = self
|
|
||||||
.0
|
|
||||||
.entry((
|
|
||||||
None,
|
|
||||||
syn::parse_quote!(<#ty as ::std::iter::IntoIterator>::Item),
|
|
||||||
))
|
|
||||||
.or_default();
|
|
||||||
type_opts_item.register_related_item_usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_source_usage(&mut self, r#type: &Type) {
|
|
||||||
let Some(ty) = self.check_generic_usage(r#type) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let type_opts = self.0.entry((None, ty.clone())).or_default();
|
|
||||||
type_opts.register_source_usage();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::spanned::Spanned;
|
use syn::{spanned::Spanned, AngleBracketedGenericArguments, GenericArgument, PathArguments, Type};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
|
diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
|
||||||
|
|
@ -104,3 +104,36 @@ impl Display {
|
||||||
(fmt, args)
|
(fmt, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to extract the type of a (presumed) option type, returning the extracted type if it suceeded.
|
||||||
|
pub fn extract_option(r#type: &Type) -> Option<&Type> {
|
||||||
|
let syn::Type::Path(syn::TypePath {
|
||||||
|
path: syn::Path { segments, .. },
|
||||||
|
..
|
||||||
|
}) = r#type
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let last_segment = segments.last()?;
|
||||||
|
|
||||||
|
if last_segment.ident != "Option" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) =
|
||||||
|
&last_segment.arguments
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
if args.len() != 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(GenericArgument::Type(ty)) = args.first() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(ty)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue