use syn::{FnArg, GenericParam, Ident, PatType, Signature, Token, Type, TypeParamBound};
use crate::generated::{
token_to_arch, token_to_features, token_to_magetypes_namespace, trait_to_arch,
trait_to_features, trait_to_magetypes_namespace,
};
pub(crate) enum TokenTypeInfo {
Concrete(String),
ImplTrait(Vec<String>),
Generic(String),
}
pub(crate) fn extract_token_type_info(ty: &Type) -> Option<TokenTypeInfo> {
match ty {
Type::Path(type_path) => {
type_path.path.segments.last().map(|seg| {
let name = seg.ident.to_string();
if token_to_features(&name).is_some() {
TokenTypeInfo::Concrete(name)
} else {
TokenTypeInfo::Generic(name)
}
})
}
Type::Reference(type_ref) => {
extract_token_type_info(&type_ref.elem)
}
Type::ImplTrait(impl_trait) => {
let traits: Vec<String> = extract_trait_names_from_bounds(&impl_trait.bounds);
if traits.is_empty() {
None
} else {
Some(TokenTypeInfo::ImplTrait(traits))
}
}
_ => None,
}
}
pub(crate) fn extract_trait_names_from_bounds(
bounds: &syn::punctuated::Punctuated<TypeParamBound, Token![+]>,
) -> Vec<String> {
bounds
.iter()
.filter_map(|bound| {
if let TypeParamBound::Trait(trait_bound) = bound {
trait_bound
.path
.segments
.last()
.map(|seg| seg.ident.to_string())
} else {
None
}
})
.collect()
}
pub(crate) fn find_generic_bounds(sig: &Signature, type_name: &str) -> Option<Vec<String>> {
for param in &sig.generics.params {
if let GenericParam::Type(type_param) = param
&& type_param.ident == type_name
{
let traits = extract_trait_names_from_bounds(&type_param.bounds);
if !traits.is_empty() {
return Some(traits);
}
}
}
if let Some(where_clause) = &sig.generics.where_clause {
for predicate in &where_clause.predicates {
if let syn::WherePredicate::Type(pred_type) = predicate
&& let Type::Path(type_path) = &pred_type.bounded_ty
&& let Some(seg) = type_path.path.segments.last()
&& seg.ident == type_name
{
let traits = extract_trait_names_from_bounds(&pred_type.bounds);
if !traits.is_empty() {
return Some(traits);
}
}
}
}
None
}
pub(crate) fn traits_to_features(trait_names: &[String]) -> Option<Vec<&'static str>> {
let mut all_features = Vec::new();
for trait_name in trait_names {
if let Some(features) = trait_to_features(trait_name) {
for &feature in features {
if !all_features.contains(&feature) {
all_features.push(feature);
}
}
}
}
if all_features.is_empty() {
None
} else {
Some(all_features)
}
}
pub(crate) const FEATURELESS_TRAIT_NAMES: &[&str] = &["SimdToken", "IntoConcreteToken"];
pub(crate) fn find_featureless_trait(trait_names: &[String]) -> Option<&'static str> {
for name in trait_names {
for &featureless in FEATURELESS_TRAIT_NAMES {
if name == featureless {
return Some(featureless);
}
}
}
None
}
pub(crate) fn diagnose_featureless_token(sig: &Signature) -> Option<&'static str> {
for arg in &sig.inputs {
if let FnArg::Typed(PatType { ty, .. }) = arg
&& let Some(info) = extract_token_type_info(ty)
{
match &info {
TokenTypeInfo::ImplTrait(names) => {
if let Some(name) = find_featureless_trait(names) {
return Some(name);
}
}
TokenTypeInfo::Generic(type_name) => {
let as_vec = vec![type_name.clone()];
if let Some(name) = find_featureless_trait(&as_vec) {
return Some(name);
}
if let Some(bounds) = find_generic_bounds(sig, type_name)
&& let Some(name) = find_featureless_trait(&bounds)
{
return Some(name);
}
}
TokenTypeInfo::Concrete(_) => {}
}
}
}
None
}
pub(crate) struct TokenParamInfo {
pub ident: Ident,
pub features: Vec<&'static str>,
pub target_arch: Option<&'static str>,
pub token_type_name: Option<String>,
pub magetypes_namespace: Option<&'static str>,
}
pub(crate) fn traits_to_magetypes_namespace(trait_names: &[String]) -> Option<&'static str> {
for name in trait_names {
if let Some(ns) = trait_to_magetypes_namespace(name) {
return Some(ns);
}
}
None
}
pub(crate) fn traits_to_arch(trait_names: &[String]) -> Option<&'static str> {
for name in trait_names {
if let Some(arch) = trait_to_arch(name) {
return Some(arch);
}
}
None
}
pub(crate) fn find_token_param(sig: &Signature) -> Option<TokenParamInfo> {
for arg in &sig.inputs {
match arg {
FnArg::Receiver(_) => {
continue;
}
FnArg::Typed(PatType { pat, ty, .. }) => {
if let Some(info) = extract_token_type_info(ty) {
let (features, arch, token_name, mage_ns) = match info {
TokenTypeInfo::Concrete(ref name) => {
let features = token_to_features(name).map(|f| f.to_vec());
let arch = token_to_arch(name);
let ns = token_to_magetypes_namespace(name);
(features, arch, Some(name.clone()), ns)
}
TokenTypeInfo::ImplTrait(ref trait_names) => {
let ns = traits_to_magetypes_namespace(trait_names);
let arch = traits_to_arch(trait_names);
(traits_to_features(trait_names), arch, None, ns)
}
TokenTypeInfo::Generic(type_name) => {
let bounds = find_generic_bounds(sig, &type_name);
let features = bounds.as_ref().and_then(|t| traits_to_features(t));
let ns = bounds
.as_ref()
.and_then(|t| traits_to_magetypes_namespace(t));
let arch = bounds.as_ref().and_then(|t| traits_to_arch(t));
(features, arch, None, ns)
}
};
if let Some(features) = features {
let ident = match pat.as_ref() {
syn::Pat::Ident(pat_ident) => Some(pat_ident.ident.clone()),
syn::Pat::Wild(w) => {
Some(Ident::new("__archmage_token", w.underscore_token.span))
}
_ => None,
};
if let Some(ident) = ident {
return Some(TokenParamInfo {
ident,
features,
target_arch: arch,
token_type_name: token_name,
magetypes_namespace: mage_ns,
});
}
}
}
}
}
}
None
}