MCPcopy Create free account
hub / github.com/MaterializeInc/materialize / classify_generic_usage

Function classify_generic_usage

src/expr-derive-impl/src/sqlfunc.rs:386–456  ·  view source on GitHub ↗

Classifies how a generic type parameter appears in a type. Strips `Option<...>`, `Result<..., E>`, and `ExcludeNull<...>` wrappers before inspecting the inner type. Any generic type wrapping `T` that isn't `Option`, `Result`, or `ExcludeNull` is treated as a container. If the container doesn't implement `SqlContainerType`, the generated code won't compile (a clear error).

(ty: &syn::Type, generic_name: &Ident)

Source from the content-addressed store, hash-verified

384/// `Result`, or `ExcludeNull` is treated as a container. If the container doesn't
385/// implement `SqlContainerType`, the generated code won't compile (a clear error).
386fn classify_generic_usage(ty: &syn::Type, generic_name: &Ident) -> GenericUsage {
387 match ty {
388 syn::Type::Path(type_path) => {
389 if type_path.path.is_ident(generic_name) {
390 return GenericUsage::Bare;
391 }
392 if let Some(last) = type_path.path.segments.last() {
393 let ident_str = last.ident.to_string();
394 // Unwrap Option, Result, ExcludeNull wrappers
395 if ident_str == "Option" || ident_str == "Result" || ident_str == "ExcludeNull" {
396 if let syn::PathArguments::AngleBracketed(args) = &last.arguments {
397 if let Some(syn::GenericArgument::Type(inner)) = args.args.first() {
398 return classify_generic_usage(inner, generic_name);
399 }
400 }
401 }
402 // Check if any angle-bracketed arg contains the generic param.
403 // If so, treat this type as a container.
404 if let syn::PathArguments::AngleBracketed(args) = &last.arguments {
405 let has_generic_arg = args.args.iter().any(|arg| {
406 if let syn::GenericArgument::Type(inner) = arg {
407 type_contains_ident(inner, generic_name)
408 } else {
409 false
410 }
411 });
412 if has_generic_arg {
413 // Build the erased container type path (T → Datum<'a>).
414 let erased = erase_generic_param(ty, generic_name);
415 if let syn::Type::Path(erased_path) = erased {
416 return GenericUsage::InContainer(erased_path);
417 }
418 }
419 // Recurse into args for nested containers
420 // (e.g., Option<DatumList<'a, T>> was already handled by
421 // the Option unwrapping above, but handle other nestings)
422 for arg in &args.args {
423 if let syn::GenericArgument::Type(inner) = arg {
424 let inner_usage = classify_generic_usage(inner, generic_name);
425 if inner_usage != GenericUsage::Absent {
426 return inner_usage;
427 }
428 }
429 }
430 }
431 }
432 GenericUsage::Absent
433 }
434 syn::Type::Reference(r) => classify_generic_usage(&r.elem, generic_name),
435 syn::Type::Tuple(t) => {
436 // Prefer container usages over bare. For example, `(T, DatumList<'_, T>)`
437 // should classify as `InDatumList`, not `Bare`.
438 let mut best = GenericUsage::Absent;
439 for elem in &t.elems {
440 let usage = classify_generic_usage(elem, generic_name);
441 match (&best, &usage) {
442 (GenericUsage::Absent, _) => best = usage,
443 (GenericUsage::Bare, u) if *u != GenericUsage::Absent => best = usage.clone(),

Callers 2

Calls 8

type_contains_identFunction · 0.85
erase_generic_paramFunction · 0.85
lastMethod · 0.80
anyMethod · 0.80
to_stringMethod · 0.45
firstMethod · 0.45
iterMethod · 0.45
cloneMethod · 0.45

Tested by

no test coverage detected