ouroboros_macro/
covariance_detection.rs
1use quote::ToTokens;
2use syn::{GenericArgument, PathArguments, Type};
3
4use crate::utils::uses_this_lifetime;
5
6const STD_CONTAINER_TYPES: &[&str] = &["Box", "Arc", "Rc"];
7
8pub fn apparent_std_container_type(raw_type: &Type) -> Option<(&'static str, &Type)> {
11 let tpath = if let Type::Path(x) = raw_type {
12 x
13 } else {
14 return None;
15 };
16 let segment = tpath.path.segments.last()?;
17 let args = if let PathArguments::AngleBracketed(args) = &segment.arguments {
18 args
19 } else {
20 return None;
21 };
22 if args.args.len() != 1 {
23 return None;
24 }
25 let arg = args.args.first().unwrap();
26 let eltype = if let GenericArgument::Type(x) = arg {
27 x
28 } else {
29 return None;
30 };
31 for type_name in STD_CONTAINER_TYPES {
32 if segment.ident == type_name {
33 return Some((type_name, eltype));
34 }
35 }
36 None
37}
38
39pub fn type_is_covariant_over_this_lifetime(ty: &syn::Type) -> Option<bool> {
41 use syn::Type::*;
42 if !uses_this_lifetime(ty.to_token_stream()) {
45 return Some(true);
46 }
47 match ty {
48 Array(arr) => type_is_covariant_over_this_lifetime(&arr.elem),
49 BareFn(f) => {
50 debug_assert!(uses_this_lifetime(f.to_token_stream()));
51 None
52 }
53 Group(ty) => type_is_covariant_over_this_lifetime(&ty.elem),
54 ImplTrait(..) => None, Infer(..) => None, Macro(..) => None, Never(..) => None,
58 Paren(ty) => type_is_covariant_over_this_lifetime(&ty.elem),
59 Path(path) => {
60 if let Some(qself) = &path.qself {
61 if !type_is_covariant_over_this_lifetime(&qself.ty)? {
62 return Some(false);
63 }
64 }
65 let mut all_parameters_are_covariant = false;
66 if apparent_std_container_type(ty).is_some() {
68 all_parameters_are_covariant = true;
69 }
70 for segment in path.path.segments.iter() {
71 let args = &segment.arguments;
72 if let syn::PathArguments::AngleBracketed(args) = &args {
73 for arg in args.args.iter() {
74 if let syn::GenericArgument::Type(ty) = arg {
75 if all_parameters_are_covariant {
76 if !type_is_covariant_over_this_lifetime(ty)? {
77 return Some(false);
78 }
79 } else if uses_this_lifetime(ty.to_token_stream()) {
80 return None;
81 }
82 } else if let syn::GenericArgument::Lifetime(lt) = arg {
83 if lt.ident == "this" && !all_parameters_are_covariant {
84 return None;
85 }
86 }
87 }
88 } else if let syn::PathArguments::Parenthesized(args) = &args {
89 for arg in args.inputs.iter() {
90 if uses_this_lifetime(arg.to_token_stream()) {
91 return None;
92 }
93 }
94 if let syn::ReturnType::Type(_, ty) = &args.output {
95 if uses_this_lifetime(ty.to_token_stream()) {
96 return None;
97 }
98 }
99 }
100 }
101 Some(true)
102 }
103 Ptr(ptr) => {
104 if ptr.mutability.is_some() {
105 Some(false)
106 } else {
107 type_is_covariant_over_this_lifetime(&ptr.elem)
108 }
109 }
110 Reference(rf) => {
112 if rf.mutability.is_some() {
113 Some(!uses_this_lifetime(rf.elem.to_token_stream()))
114 } else {
115 type_is_covariant_over_this_lifetime(&rf.elem)
116 }
117 }
118 Slice(sl) => type_is_covariant_over_this_lifetime(&sl.elem),
119 TraitObject(..) => None,
120 Tuple(tup) => {
121 let mut result = Some(true);
122 for ty in tup.elems.iter() {
123 match type_is_covariant_over_this_lifetime(ty) {
124 Some(true) => (),
125 Some(false) => return Some(false),
126 None => result = None,
127 }
128 }
129 result
130 }
131 Verbatim(..) => None,
134 _ => None,
135 }
136}