1use syn::spanned::Spanned as _;
2use syn::visit_mut::{self, VisitMut as _};
3use syn::{
4 AngleBracketedGenericArguments, Error, Expr, GenericArgument, Path, PathArguments, Result,
5 Token, Type,
6};
7
8#[derive(Clone, Debug)]
10pub enum SingleTypeArg {
11 None,
13 Infer,
15 Explicit(Box<Type>),
17}
18
19impl SingleTypeArg {
20 pub fn explicit_type(&self) -> Option<&Type> {
22 match self {
23 Self::Explicit(ty) => Some(ty),
24 Self::None | Self::Infer => None,
25 }
26 }
27
28 pub fn is_infer(&self) -> bool {
30 matches!(self, Self::Infer)
31 }
32}
33
34pub fn split_terminal_single_type_arg(
45 mut path: Path,
46 subject: &str,
47) -> Result<(Path, SingleTypeArg)> {
48 let path_span = path.span();
49 let last_segment = path
50 .segments
51 .last_mut()
52 .ok_or_else(|| Error::new(path_span, format!("expected {subject} path")))?;
53
54 let args = std::mem::replace(&mut last_segment.arguments, PathArguments::None);
55 let type_arg = match args {
56 PathArguments::None => SingleTypeArg::None,
57 PathArguments::AngleBracketed(mut angle_args) => {
58 if angle_args.args.len() != 1 {
59 return Err(Error::new(
60 angle_args.span(),
61 format!("{subject} type syntax expects exactly one type argument"),
62 ));
63 }
64
65 let arg = angle_args.args.pop().expect("len checked").into_value();
66 match arg {
67 GenericArgument::Type(Type::Infer(_)) => SingleTypeArg::Infer,
68 GenericArgument::Type(ty) => SingleTypeArg::Explicit(Box::new(ty)),
69 _ => Err(Error::new(
70 arg.span(),
71 format!("{subject} type syntax expects a type argument"),
72 ))?,
73 }
74 },
75 PathArguments::Parenthesized(args) => {
76 return Err(Error::new(
77 args.span(),
78 format!("{subject} path does not support parenthesized arguments"),
79 ));
80 },
81 };
82
83 Ok((path, type_arg))
84}
85
86pub fn substitute_infer_in_type(ty: &Type, replacement: &Type) -> Type {
88 match ty {
89 Type::Infer(_) => replacement.clone(),
90 Type::Path(type_path) => {
91 let mut type_path = type_path.clone();
92 type_path.path = substitute_infer_in_path(&type_path.path, replacement);
93 Type::Path(type_path)
94 },
95 Type::Array(array) => {
96 let mut array = array.clone();
97 array.elem = Box::new(substitute_infer_in_type(&array.elem, replacement));
98 Type::Array(array)
99 },
100 Type::Slice(slice) => {
101 let mut slice = slice.clone();
102 slice.elem = Box::new(substitute_infer_in_type(&slice.elem, replacement));
103 Type::Slice(slice)
104 },
105 Type::Ptr(ptr) => {
106 let mut ptr = ptr.clone();
107 ptr.elem = Box::new(substitute_infer_in_type(&ptr.elem, replacement));
108 Type::Ptr(ptr)
109 },
110 Type::BareFn(bare_fn) => {
111 let mut bare_fn = bare_fn.clone();
112 for input in &mut bare_fn.inputs {
113 input.ty = substitute_infer_in_type(&input.ty, replacement);
114 }
115 substitute_infer_in_return_type(&mut bare_fn.output, replacement);
116 Type::BareFn(bare_fn)
117 },
118 Type::TraitObject(trait_object) => {
119 let mut trait_object = trait_object.clone();
120 substitute_infer_in_bounds(&mut trait_object.bounds, replacement);
121 Type::TraitObject(trait_object)
122 },
123 Type::ImplTrait(impl_trait) => {
124 let mut impl_trait = impl_trait.clone();
125 substitute_infer_in_bounds(&mut impl_trait.bounds, replacement);
126 Type::ImplTrait(impl_trait)
127 },
128 Type::Tuple(tuple) => {
129 let mut tuple = tuple.clone();
130 tuple.elems = tuple
131 .elems
132 .iter()
133 .map(|ty| substitute_infer_in_type(ty, replacement))
134 .collect();
135 Type::Tuple(tuple)
136 },
137 Type::Paren(paren) => {
138 let mut paren = paren.clone();
139 paren.elem = Box::new(substitute_infer_in_type(&paren.elem, replacement));
140 Type::Paren(paren)
141 },
142 Type::Group(group) => {
143 let mut group = group.clone();
144 group.elem = Box::new(substitute_infer_in_type(&group.elem, replacement));
145 Type::Group(group)
146 },
147 Type::Reference(reference) => {
148 let mut reference = reference.clone();
149 *reference.elem = substitute_infer_in_type(&reference.elem, replacement);
150 Type::Reference(reference)
151 },
152 _ => ty.clone(),
153 }
154}
155
156pub fn substitute_infer_in_expr(expr: &Expr, replacement: &Type) -> Expr {
158 let mut expr = expr.clone();
159 InferSubstitutor { replacement }.visit_expr_mut(&mut expr);
160 expr
161}
162
163pub fn substitute_infer_in_path(path: &Path, replacement: &Type) -> Path {
165 let mut path = path.clone();
166
167 for segment in &mut path.segments {
168 substitute_infer_in_path_arguments(&mut segment.arguments, replacement);
169 }
170
171 path
172}
173
174struct InferSubstitutor<'a> {
175 replacement: &'a Type,
176}
177
178impl visit_mut::VisitMut for InferSubstitutor<'_> {
179 fn visit_type_mut(&mut self, node: &mut Type) {
180 *node = substitute_infer_in_type(node, self.replacement);
181 }
182
183 fn visit_path_mut(&mut self, node: &mut Path) {
184 *node = substitute_infer_in_path(node, self.replacement);
185 }
186}
187
188fn substitute_infer_in_return_type(return_type: &mut syn::ReturnType, replacement: &Type) {
189 if let syn::ReturnType::Type(_, ty) = return_type {
190 **ty = substitute_infer_in_type(ty, replacement);
191 }
192}
193
194fn substitute_infer_in_bounds(
195 bounds: &mut syn::punctuated::Punctuated<syn::TypeParamBound, Token![+]>,
196 replacement: &Type,
197) {
198 for bound in bounds {
199 if let syn::TypeParamBound::Trait(trait_bound) = bound {
200 trait_bound.path = substitute_infer_in_path(&trait_bound.path, replacement);
201 }
202 }
203}
204
205fn substitute_infer_in_path_arguments(arguments: &mut PathArguments, replacement: &Type) {
206 match arguments {
207 PathArguments::AngleBracketed(args) => {
208 substitute_infer_in_angle_bracketed_arguments(args, replacement);
209 },
210 PathArguments::Parenthesized(args) => {
211 args.inputs = args
212 .inputs
213 .iter()
214 .map(|ty| substitute_infer_in_type(ty, replacement))
215 .collect();
216 substitute_infer_in_return_type(&mut args.output, replacement);
217 },
218 PathArguments::None => {},
219 }
220}
221
222fn substitute_infer_in_angle_bracketed_arguments(
223 args: &mut AngleBracketedGenericArguments,
224 replacement: &Type,
225) {
226 for arg in &mut args.args {
227 match arg {
228 GenericArgument::Type(ty) => {
229 *ty = substitute_infer_in_type(ty, replacement);
230 },
231 GenericArgument::AssocType(assoc_type) => {
232 if let Some(generics) = &mut assoc_type.generics {
233 substitute_infer_in_angle_bracketed_arguments(generics, replacement);
234 }
235 assoc_type.ty = substitute_infer_in_type(&assoc_type.ty, replacement);
236 },
237 GenericArgument::Constraint(constraint) => {
238 if let Some(generics) = &mut constraint.generics {
239 substitute_infer_in_angle_bracketed_arguments(generics, replacement);
240 }
241 substitute_infer_in_bounds(&mut constraint.bounds, replacement);
242 },
243 _ => {},
244 }
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251 use syn::{Type, parse_quote};
252
253 fn compact(tokens: impl quote::ToTokens) -> String {
254 tokens
255 .to_token_stream()
256 .to_string()
257 .chars()
258 .filter(|ch| !ch.is_whitespace())
259 .collect()
260 }
261
262 fn parenthesized_path(output: Type) -> Path {
263 let mut inputs = syn::punctuated::Punctuated::new();
264 inputs.push(parse_quote!(_));
265
266 Path::from(syn::PathSegment {
267 ident: parse_quote!(FnOnce),
268 arguments: PathArguments::Parenthesized(syn::ParenthesizedGenericArguments {
269 paren_token: Default::default(),
270 inputs,
271 output: syn::ReturnType::Type(Default::default(), Box::new(output)),
272 }),
273 })
274 }
275
276 #[test]
277 fn splits_terminal_single_type_arg() {
278 let path: Path = parse_quote!(crate::RangeValidation::<_>);
279 let (path, arg) = split_terminal_single_type_arg(path, "validator").expect("valid path");
280 assert_eq!(compact(&path), "crate::RangeValidation");
281 assert!(arg.is_infer());
282
283 let path: Path = parse_quote!(crate::RangeValidation::<i32>);
284 let (_, arg) = split_terminal_single_type_arg(path, "validator").expect("valid path");
285 assert_eq!(compact(arg.explicit_type().expect("explicit type")), "i32");
286 }
287
288 #[test]
289 fn splits_absent_terminal_type_arg_and_rejects_invalid_args() {
290 let path: Path = parse_quote!(crate::RangeValidation);
291 let (path, arg) = split_terminal_single_type_arg(path, "validator").expect("valid path");
292 assert_eq!(compact(&path), "crate::RangeValidation");
293 assert!(!arg.is_infer());
294 assert!(arg.explicit_type().is_none());
295
296 let path: Path = parse_quote!(crate::RangeValidation::<i32, String>);
297 let err = split_terminal_single_type_arg(path, "validator").expect_err("too many args");
298 assert!(
299 err.to_string()
300 .contains("validator type syntax expects exactly one type argument"),
301 "{err}"
302 );
303
304 let path: Path = parse_quote!(crate::RangeValidation::<3>);
305 let err = split_terminal_single_type_arg(path, "validator").expect_err("const arg");
306 assert!(
307 err.to_string()
308 .contains("validator type syntax expects a type argument"),
309 "{err}"
310 );
311
312 let path = parenthesized_path(parse_quote!(i32));
313 let err = split_terminal_single_type_arg(path, "validator").expect_err("function args");
314 assert!(
315 err.to_string()
316 .contains("validator path does not support parenthesized arguments"),
317 "{err}"
318 );
319 }
320
321 #[test]
322 fn substitutes_infer_in_paths_types_and_exprs() {
323 let replacement: Type = parse_quote!(String);
324 let path: Path = parse_quote!(crate::Input<Option<_>>);
325 assert_eq!(
326 compact(substitute_infer_in_path(&path, &replacement)),
327 "crate::Input<Option<String>>"
328 );
329
330 let ty: Type = parse_quote!(fn([_; 2], &[_]) -> Option<_>);
331 assert_eq!(
332 compact(substitute_infer_in_type(&ty, &replacement)),
333 "fn([String;2],&[String])->Option<String>"
334 );
335
336 let expr: Expr = parse_quote!(crate::Select::<_>.searchable(true));
337 assert_eq!(
338 compact(substitute_infer_in_expr(&expr, &replacement)),
339 "crate::Select::<String>.searchable(true)"
340 );
341 }
342
343 #[test]
344 fn substitutes_infer_in_additional_type_forms() {
345 let replacement: Type = parse_quote!(String);
346
347 let ptr: Type = parse_quote!(*const _);
348 assert_eq!(
349 compact(substitute_infer_in_type(&ptr, &replacement)),
350 "*constString"
351 );
352
353 let trait_object: Type = parse_quote!(dyn Iterator<Item = _> + Send);
354 assert_eq!(
355 compact(substitute_infer_in_type(&trait_object, &replacement)),
356 "dynIterator<Item=String>+Send"
357 );
358
359 let impl_trait: Type = parse_quote!(impl Into<_> + Send);
360 assert_eq!(
361 compact(substitute_infer_in_type(&impl_trait, &replacement)),
362 "implInto<String>+Send"
363 );
364
365 let tuple: Type = parse_quote!((_, Option<_>));
366 assert_eq!(
367 compact(substitute_infer_in_type(&tuple, &replacement)),
368 "(String,Option<String>)"
369 );
370
371 let paren: Type = parse_quote!((Option<_>));
372 assert_eq!(
373 compact(substitute_infer_in_type(&paren, &replacement)),
374 "(Option<String>)"
375 );
376
377 let group = Type::Group(syn::TypeGroup {
378 group_token: Default::default(),
379 elem: Box::new(parse_quote!(Option<_>)),
380 });
381 assert_eq!(
382 compact(substitute_infer_in_type(&group, &replacement)),
383 "Option<String>"
384 );
385
386 let never: Type = parse_quote!(!);
387 assert_eq!(compact(substitute_infer_in_type(&never, &replacement)), "!");
388 }
389
390 #[test]
391 fn substitutes_infer_in_path_argument_variants() {
392 let replacement: Type = parse_quote!(String);
393
394 let parenthesized = parenthesized_path(parse_quote!(_));
395 assert_eq!(
396 compact(substitute_infer_in_path(&parenthesized, &replacement)),
397 "FnOnce(String)->String"
398 );
399
400 let assoc_type: Path = parse_quote!(Trait<Assoc<_> = Result<_, _>>);
401 assert_eq!(
402 compact(substitute_infer_in_path(&assoc_type, &replacement)),
403 "Trait<Assoc<String>=Result<String,String>>"
404 );
405
406 let constraint: Path = parse_quote!(Trait<Assoc<_>: Into<_> + From<_>>);
407 assert_eq!(
408 compact(substitute_infer_in_path(&constraint, &replacement)),
409 "Trait<Assoc<String>:Into<String>+From<String>>"
410 );
411
412 let lifetime_and_const: Path = parse_quote!(Trait<'static, 3, _>);
413 assert_eq!(
414 compact(substitute_infer_in_path(&lifetime_and_const, &replacement)),
415 "Trait<'static,3,String>"
416 );
417 }
418
419 #[test]
420 fn substitutes_infer_inside_expression_types() {
421 let replacement: Type = parse_quote!(String);
422 let expr: Expr = parse_quote!(value as *const _);
423
424 assert_eq!(
425 compact(substitute_infer_in_expr(&expr, &replacement)),
426 "valueas*constString"
427 );
428 }
429}