#![allow(nonstandard_style, unused_imports, unused_braces)]
use ::core::{
mem,
ops::Not as _,
};
use ::proc_macro::{
TokenStream,
};
use ::proc_macro2::{
Span,
TokenStream as TokenStream2,
TokenTree as TT,
};
use ::quote::{
format_ident,
ToTokens,
};
use ::syn::{*,
parse::{Parse, Parser, ParseStream},
punctuated::Punctuated,
Result, spanned::Spanned,
};
use self::{
args::{
Args,
Crate,
},
utils::{
compile_warning,
quote, quote_spanned,
parse_quote, parse_quote_spanned,
PourIntoExt,
SpanLocationExt,
},
};
mod args;
mod utils;
#[proc_macro_attribute] pub
fn implied_bounds(
args: TokenStream,
input: TokenStream,
) -> TokenStream
{
implied_bounds_impl(args.into(), input.into())
.unwrap_or_else(|err| {
let mut errors =
err .into_iter()
.map(|err| Error::new(
err.span(),
format_args!("`#[::implied_bounds::implied_bounds]`: {}", err),
))
;
let mut err = errors.next().unwrap();
errors.for_each(|cur| err.combine(cur));
err.to_compile_error()
})
.into()
}
fn implied_bounds_impl(
args: TokenStream2,
input: TokenStream2,
) -> Result<TokenStream2>
{
let mut args: Args = parse2(args)?;
let mut trait_: ItemTrait = parse2(input)?;
let _guard = Crate::init(args.krate.take());
let mut debugged_predicates = vec![];
extract_non_implied_predicates(&mut trait_, &args, &mut debugged_predicates)
.into_iter()
.map(transform_into_equivalent_implied_predicate)
.map(WherePredicate::Type)
.chain(mem::take(&mut trait_.generics.make_where_clause().predicates))
.pour_into(&mut trait_.generics.make_where_clause().predicates);
let mut ret = trait_.into_token_stream();
debugged_predicates.into_iter().flatten().pour_into(&mut ret);
Ok(ret)
}
fn extract_non_implied_predicates(
trait_: &mut ItemTrait,
args: &Args,
debugged_predicates: &mut Vec<TokenStream2>,
) -> Vec<PredicateType>
{
let mut ret = vec![];
let mut found_clause = false;
let debug_report_clause: &mut dyn FnMut(&dyn ToTokens) = if args.debug.is_some() {
&mut |tts| {
found_clause = true;
debugged_predicates.push(
compile_warning(tts, "[debug] this predicate is not implied, adjusting it…")
);
}
} else {
&mut |_| {
found_clause = true;
}
};
trait_.generics.params.iter_mut().filter_map(|param_intro| {
let GenericParam::Type(param_intro) = param_intro else { return None };
let bounds = mem::take(&mut param_intro.bounds);
if bounds.is_empty() {
return None;
}
debug_report_clause(&bounds);
if may_be_higher_ranked(&bounds).not() {
param_intro.bounds.clone_from(&bounds);
}
Some(PredicateType {
lifetimes: None,
bounded_ty: {
let T @ _ = ¶m_intro.ident;
parse_quote!( #T )
},
colon_token: param_intro.colon_token?,
bounds,
})
}).pour_into(&mut ret);
if let Some(mut where_clause) = trait_.generics.where_clause.take() {
let mut retained_predicates = Vec::with_capacity(where_clause.predicates.len());
where_clause.predicates.into_iter().filter_map(|predicate| {
match predicate {
| WherePredicate::Type(predicate)
if predicate.bounds.is_empty().not()
&& matches!(
&predicate.bounded_ty,
Type::Path(TypePath {
qself: None,
path: Self_,
})
if Self_.is_ident("Self")
)
.not()
=> {
debug_report_clause(&predicate);
if (
predicate.lifetimes.as_ref().is_some_and(|it| it.lifetimes.is_empty().not())
||
may_be_higher_ranked(&predicate.bounds)
).not()
{
retained_predicates.push(WherePredicate::Type(predicate.clone()));
}
Some(predicate)
},
| _ => {
retained_predicates.push(predicate);
None
},
}
}).pour_into(&mut ret);
where_clause.predicates = retained_predicates.into_iter().collect();
trait_.generics.where_clause = Some(where_clause);
}
if args.allow_none.is_none() && found_clause.not() {
debugged_predicates.push(compile_warning(
&..,
"No non-implied clauses found for this trait, you may skip using this macro altogether.\
\n\n\
To silence this warning, use `#[…implied_bounds(allow_none, …)]`.",
));
}
ret
}
fn transform_into_equivalent_implied_predicate(
mut predicate: PredicateType
) -> PredicateType
{
let opening_span = predicate.span().location();
let closing_span =
predicate
.bounds
.pairs()
.last()
.and_then(|pair| pair.to_token_stream().into_iter().last())
.unwrap()
.span()
.location()
;
let closing_span_angle_bracket = quote_spanned!(closing_span=>
>
);
let krate = Crate::get().unwrap_or_else(|| quote_spanned!(opening_span=>
::implied_bounds
));
let bounded_ty = mem::replace(
&mut predicate.bounded_ty,
parse_quote_spanned!(opening_span=> Self ),
);
let bounds = predicate.bounds;
predicate.bounds = parse_quote_spanned!(opening_span=>
#krate::ImpliedPredicate<
#bounded_ty,
Impls : #bounds #closing_span_angle_bracket );
predicate
}
fn may_be_higher_ranked(
bounds: &Punctuated<TypeParamBound, Token![+]>,
) -> bool
{
bounds.iter().any(|bound| {
let TypeParamBound::Trait(bound) = bound else { return false };
bound.lifetimes.as_ref().is_some_and(|it| it.lifetimes.is_empty().not())
||
matches!(
bound.path.segments.last().unwrap().arguments,
PathArguments::Parenthesized { .. },
)
})
}