ouroboros_macro/
covariance_detection.rsuse quote::ToTokens;
use syn::{GenericArgument, PathArguments, Type};
use crate::utils::uses_this_lifetime;
const STD_CONTAINER_TYPES: &[&str] = &["Box", "Arc", "Rc"];
pub fn apparent_std_container_type(raw_type: &Type) -> Option<(&'static str, &Type)> {
let tpath = if let Type::Path(x) = raw_type {
x
} else {
return None;
};
let segment = tpath.path.segments.last()?;
let args = if let PathArguments::AngleBracketed(args) = &segment.arguments {
args
} else {
return None;
};
if args.args.len() != 1 {
return None;
}
let arg = args.args.first().unwrap();
let eltype = if let GenericArgument::Type(x) = arg {
x
} else {
return None;
};
for type_name in STD_CONTAINER_TYPES {
if segment.ident == type_name {
return Some((type_name, eltype));
}
}
None
}
pub fn type_is_covariant_over_this_lifetime(ty: &syn::Type) -> Option<bool> {
use syn::Type::*;
if !uses_this_lifetime(ty.to_token_stream()) {
return Some(true);
}
match ty {
Array(arr) => type_is_covariant_over_this_lifetime(&arr.elem),
BareFn(f) => {
debug_assert!(uses_this_lifetime(f.to_token_stream()));
None
}
Group(ty) => type_is_covariant_over_this_lifetime(&ty.elem),
ImplTrait(..) => None, Infer(..) => None, Macro(..) => None, Never(..) => None,
Paren(ty) => type_is_covariant_over_this_lifetime(&ty.elem),
Path(path) => {
if let Some(qself) = &path.qself {
if !type_is_covariant_over_this_lifetime(&qself.ty)? {
return Some(false);
}
}
let mut all_parameters_are_covariant = false;
if apparent_std_container_type(ty).is_some() {
all_parameters_are_covariant = true;
}
for segment in path.path.segments.iter() {
let args = &segment.arguments;
if let syn::PathArguments::AngleBracketed(args) = &args {
for arg in args.args.iter() {
if let syn::GenericArgument::Type(ty) = arg {
if all_parameters_are_covariant {
if !type_is_covariant_over_this_lifetime(ty)? {
return Some(false);
}
} else if uses_this_lifetime(ty.to_token_stream()) {
return None;
}
} else if let syn::GenericArgument::Lifetime(lt) = arg {
if lt.ident == "this" && !all_parameters_are_covariant {
return None;
}
}
}
} else if let syn::PathArguments::Parenthesized(args) = &args {
for arg in args.inputs.iter() {
if uses_this_lifetime(arg.to_token_stream()) {
return None;
}
}
if let syn::ReturnType::Type(_, ty) = &args.output {
if uses_this_lifetime(ty.to_token_stream()) {
return None;
}
}
}
}
Some(true)
}
Ptr(ptr) => {
if ptr.mutability.is_some() {
Some(false)
} else {
type_is_covariant_over_this_lifetime(&ptr.elem)
}
}
Reference(rf) => {
if rf.mutability.is_some() {
Some(!uses_this_lifetime(rf.elem.to_token_stream()))
} else {
type_is_covariant_over_this_lifetime(&rf.elem)
}
}
Slice(sl) => type_is_covariant_over_this_lifetime(&sl.elem),
TraitObject(..) => None,
Tuple(tup) => {
let mut result = Some(true);
for ty in tup.elems.iter() {
match type_is_covariant_over_this_lifetime(ty) {
Some(true) => (),
Some(false) => return Some(false),
None => result = None,
}
}
result
}
Verbatim(..) => None,
_ => None,
}
}