chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,24 @@
use syn::Generics;
use crate::usage::{IdentSet, LifetimeSet};
/// Extension trait for pulling specific generics data from a generics AST representation.
pub trait GenericsExt {
/// Get the set of all lifetimes declared by the syntax element.
/// This does not look for usage of the lifetime; see `UsesLifetimes` for that.
fn declared_lifetimes(&self) -> LifetimeSet;
/// Get the set of all type parameters declared by the syntax element.
/// This does not look for usage of the type parameter; see `UsesTypeParams` for that.
fn declared_type_params(&self) -> IdentSet;
}
impl GenericsExt for Generics {
fn declared_lifetimes(&self) -> LifetimeSet {
self.lifetimes().map(|lt| lt.lifetime.clone()).collect()
}
fn declared_type_params(&self) -> IdentSet {
self.type_params().map(|tp| tp.ident.clone()).collect()
}
}

View File

@@ -0,0 +1,8 @@
use fnv::FnvHashSet;
use syn::Ident;
/// A set of idents.
pub type IdentSet = FnvHashSet<Ident>;
/// A set of references to idents.
pub type IdentRefSet<'a> = FnvHashSet<&'a Ident>;

View File

@@ -0,0 +1,351 @@
use fnv::FnvHashSet;
use syn::punctuated::Punctuated;
use syn::{Lifetime, Type};
use crate::usage::Options;
/// A set of lifetimes.
pub type LifetimeSet = FnvHashSet<Lifetime>;
/// A set of references to lifetimes.
pub type LifetimeRefSet<'a> = FnvHashSet<&'a Lifetime>;
/// Searcher for finding lifetimes in a syntax tree.
/// This can be used to determine which lifetimes must be emitted in generated code.
pub trait UsesLifetimes {
/// Returns the subset of the queried lifetimes that are used by the implementing syntax element.
///
/// This method only accounts for direct usage by the element; indirect usage via bounds or `where`
/// predicates are not detected.
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a>;
/// Find all used lifetimes, then clone them and return that set.
fn uses_lifetimes_cloned(&self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet {
self.uses_lifetimes(options, lifetimes)
.into_iter()
.cloned()
.collect()
}
}
/// Searcher for finding lifetimes in an iterator.
///
/// This trait extends iterators, providing a way to turn a filtered list of fields or variants into a set
/// of lifetimes.
pub trait CollectLifetimes {
/// Consume an iterator, accumulating all lifetimes in the elements which occur in `lifetimes`.
fn collect_lifetimes<'a>(
self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a>;
/// Consume an iterator using `collect_lifetimes`, then clone all found lifetimes and return that set.
fn collect_lifetimes_cloned(self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet;
}
impl<'i, I, T> CollectLifetimes for T
where
T: IntoIterator<Item = &'i I>,
I: 'i + UsesLifetimes,
{
fn collect_lifetimes<'a>(
self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
self.into_iter()
.fold(Default::default(), |mut state, value| {
state.extend(value.uses_lifetimes(options, lifetimes));
state
})
}
fn collect_lifetimes_cloned(self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet {
self.collect_lifetimes(options, lifetimes)
.into_iter()
.cloned()
.collect()
}
}
impl<T: UsesLifetimes> UsesLifetimes for Vec<T> {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
self.collect_lifetimes(options, lifetimes)
}
}
impl<T: UsesLifetimes, U> UsesLifetimes for Punctuated<T, U> {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
self.collect_lifetimes(options, lifetimes)
}
}
impl<T: UsesLifetimes> UsesLifetimes for Option<T> {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
self.as_ref()
.map(|v| v.uses_lifetimes(options, lifetimes))
.unwrap_or_default()
}
}
impl UsesLifetimes for Lifetime {
fn uses_lifetimes<'a>(&self, _: &Options, lifetimes: &'a LifetimeSet) -> LifetimeRefSet<'a> {
lifetimes.iter().filter(|lt| *lt == self).collect()
}
}
uses_lifetimes!(syn::AngleBracketedGenericArguments, args);
uses_lifetimes!(syn::AssocType, ty);
uses_lifetimes!(syn::BareFnArg, ty);
uses_lifetimes!(syn::BoundLifetimes, lifetimes);
uses_lifetimes!(syn::ConstParam, ty);
uses_lifetimes!(syn::Constraint, bounds);
uses_lifetimes!(syn::DataEnum, variants);
uses_lifetimes!(syn::DataStruct, fields);
uses_lifetimes!(syn::DataUnion, fields);
uses_lifetimes!(syn::Field, ty);
uses_lifetimes!(syn::FieldsNamed, named);
uses_lifetimes!(syn::LifetimeParam, lifetime, bounds);
uses_lifetimes!(syn::ParenthesizedGenericArguments, inputs, output);
uses_lifetimes!(syn::Path, segments);
uses_lifetimes!(syn::PathSegment, arguments);
uses_lifetimes!(syn::PredicateLifetime, lifetime, bounds);
uses_lifetimes!(syn::PredicateType, lifetimes, bounded_ty, bounds);
uses_lifetimes!(syn::QSelf, ty);
uses_lifetimes!(syn::TraitBound, path, lifetimes);
uses_lifetimes!(syn::TypeArray, elem);
uses_lifetimes!(syn::TypeBareFn, inputs, output);
uses_lifetimes!(syn::TypeGroup, elem);
uses_lifetimes!(syn::TypeImplTrait, bounds);
uses_lifetimes!(syn::TypeParam, bounds);
uses_lifetimes!(syn::TypeParen, elem);
uses_lifetimes!(syn::TypePtr, elem);
uses_lifetimes!(syn::TypeReference, lifetime, elem);
uses_lifetimes!(syn::TypeSlice, elem);
uses_lifetimes!(syn::TypeTuple, elems);
uses_lifetimes!(syn::TypeTraitObject, bounds);
uses_lifetimes!(syn::Variant, fields);
impl UsesLifetimes for syn::Data {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
match *self {
syn::Data::Struct(ref v) => v.uses_lifetimes(options, lifetimes),
syn::Data::Enum(ref v) => v.uses_lifetimes(options, lifetimes),
syn::Data::Union(ref v) => v.uses_lifetimes(options, lifetimes),
}
}
}
impl UsesLifetimes for Type {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
match *self {
Type::Slice(ref v) => v.uses_lifetimes(options, lifetimes),
Type::Array(ref v) => v.uses_lifetimes(options, lifetimes),
Type::Ptr(ref v) => v.uses_lifetimes(options, lifetimes),
Type::Reference(ref v) => v.uses_lifetimes(options, lifetimes),
Type::BareFn(ref v) => v.uses_lifetimes(options, lifetimes),
Type::Tuple(ref v) => v.uses_lifetimes(options, lifetimes),
Type::Path(ref v) => v.uses_lifetimes(options, lifetimes),
Type::Paren(ref v) => v.uses_lifetimes(options, lifetimes),
Type::Group(ref v) => v.uses_lifetimes(options, lifetimes),
Type::TraitObject(ref v) => v.uses_lifetimes(options, lifetimes),
Type::ImplTrait(ref v) => v.uses_lifetimes(options, lifetimes),
Type::Macro(_) | Type::Verbatim(_) | Type::Infer(_) | Type::Never(_) => {
Default::default()
}
_ => panic!("Unknown syn::Type: {:?}", self),
}
}
}
impl UsesLifetimes for syn::Fields {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
self.collect_lifetimes(options, lifetimes)
}
}
impl UsesLifetimes for syn::TypePath {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
let mut hits = self.path.uses_lifetimes(options, lifetimes);
if options.include_type_path_qself() {
hits.extend(self.qself.uses_lifetimes(options, lifetimes));
}
hits
}
}
impl UsesLifetimes for syn::ReturnType {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
if let syn::ReturnType::Type(_, ref ty) = *self {
ty.uses_lifetimes(options, lifetimes)
} else {
Default::default()
}
}
}
impl UsesLifetimes for syn::PathArguments {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
match *self {
syn::PathArguments::None => Default::default(),
syn::PathArguments::AngleBracketed(ref v) => v.uses_lifetimes(options, lifetimes),
syn::PathArguments::Parenthesized(ref v) => v.uses_lifetimes(options, lifetimes),
}
}
}
impl UsesLifetimes for syn::WherePredicate {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
match *self {
syn::WherePredicate::Type(ref v) => v.uses_lifetimes(options, lifetimes),
syn::WherePredicate::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
// non-exhaustive enum
// TODO: replace panic with failible function
_ => panic!("Unknown syn::WherePredicate: {:?}", self),
}
}
}
impl UsesLifetimes for syn::GenericArgument {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
match *self {
syn::GenericArgument::Type(ref v) => v.uses_lifetimes(options, lifetimes),
syn::GenericArgument::AssocType(ref v) => v.uses_lifetimes(options, lifetimes),
syn::GenericArgument::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
syn::GenericArgument::Constraint(ref v) => v.uses_lifetimes(options, lifetimes),
syn::GenericArgument::AssocConst(_) | syn::GenericArgument::Const(_) => {
Default::default()
}
// non-exhaustive enum
// TODO: replace panic with failible function
_ => panic!("Unknown syn::GenericArgument: {:?}", self),
}
}
}
impl UsesLifetimes for syn::GenericParam {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
match *self {
syn::GenericParam::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
syn::GenericParam::Type(ref v) => v.uses_lifetimes(options, lifetimes),
syn::GenericParam::Const(ref v) => v.uses_lifetimes(options, lifetimes),
}
}
}
impl UsesLifetimes for syn::TypeParamBound {
fn uses_lifetimes<'a>(
&self,
options: &Options,
lifetimes: &'a LifetimeSet,
) -> LifetimeRefSet<'a> {
match *self {
syn::TypeParamBound::Trait(ref v) => v.uses_lifetimes(options, lifetimes),
syn::TypeParamBound::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
// non-exhaustive enum
// TODO: replace panic with failible function
_ => panic!("Unknown syn::TypeParamBound: {:?}", self),
}
}
}
#[cfg(test)]
mod tests {
use proc_macro2::Span;
use syn::{parse_quote, DeriveInput};
use super::UsesLifetimes;
use crate::usage::GenericsExt;
use crate::usage::Purpose::*;
#[test]
fn struct_named() {
let input: DeriveInput = parse_quote! {
struct Foo<'a, 'b: 'a> {
parent: &'b Bar,
child: &'a Baz,
}
};
let omitted = syn::Lifetime::new("'c", Span::call_site());
let lifetimes = {
let mut lt = input.generics.declared_lifetimes();
lt.insert(omitted);
lt
};
let matches = input.data.uses_lifetimes(&BoundImpl.into(), &lifetimes);
assert_eq!(matches.len(), 2);
}
#[test]
fn qself() {
let input: DeriveInput = parse_quote! {
struct Foo<'a, 'b: 'a> {
parent: &'b Bar,
child: <Bar<'a> as MyIterator>::Item,
}
};
let lifetimes = input.generics.declared_lifetimes();
let matches = input.data.uses_lifetimes(&BoundImpl.into(), &lifetimes);
assert_eq!(matches.len(), 1);
let decl_matches = input.data.uses_lifetimes(&Declare.into(), &lifetimes);
assert_eq!(decl_matches.len(), 2);
}
}

111
vendor/darling_core/src/usage/mod.rs vendored Normal file
View File

@@ -0,0 +1,111 @@
//! Traits and types used for tracking the usage of generic parameters through a proc-macro input.
//!
//! When generating trait impls, libraries often want to automatically figure out which type parameters
//! are used in which fields, and then emit bounds that will produce the most permissive compilable
//! code.
//!
//! # Usage
//!
//! ## Example 1: Filtering
//! This example accepts a proc-macro input, then finds all lifetimes and type parameters used
//! by private fields.
//!
//! ```rust
//! # extern crate darling_core;
//! # extern crate syn;
//! #
//! # // in real-world usage, import from `darling`
//! # use darling_core::usage::{self, CollectLifetimes, CollectTypeParams, GenericsExt, Purpose};
//! # use syn::{Data, DeriveInput, GenericParam, Generics, Visibility};
//! #
//! # #[allow(dead_code)]
//! fn process(input: &DeriveInput) -> Generics {
//! let type_params = input.generics.declared_type_params();
//! let lifetimes = input.generics.declared_lifetimes();
//!
//! let mut ret_generics = input.generics.clone();
//!
//! if let Data::Struct(ref body) = input.data {
//! let internal_fields = body
//! .fields
//! .iter()
//! .filter(|field| field.vis == Visibility::Inherited)
//! .collect::<Vec<_>>();
//!
//! let int_type_params = internal_fields
//! .collect_type_params(&Purpose::BoundImpl.into(), &type_params);
//!
//! // We could reuse the vec from above, but here we'll instead
//! // directly consume the chained iterator.
//! let int_lifetimes = body
//! .fields
//! .iter()
//! .filter(|field| field.vis == Visibility::Inherited)
//! .collect_lifetimes(&Purpose::BoundImpl.into(), &lifetimes);
//!
//!
//! ret_generics.params = ret_generics
//! .params
//! .into_iter()
//! .filter(|gp| {
//! match *gp {
//! GenericParam::Type(ref ty) => int_type_params.contains(&ty.ident),
//! GenericParam::Lifetime(ref lt) => int_lifetimes.contains(&lt.lifetime),
//! _ => true,
//! }
//! })
//! .collect();
//! }
//!
//! ret_generics
//! }
//!
//! # fn main() {}
//! ```
//!
//! ## Example 2: Integrating with `FromDeriveInput`
//! It is possible to use `darling`'s magic fields feature in tandem with the `usage` feature set.
//! While there is no custom derive for `UsesTypeParams` or `UsesLifetimes`, there are macros to
//! generate impls.
//!
//! ```rust,ignore
//! #![allow(dead_code)]
//!
//! #[derive(FromField)]
//! #[darling(attributes(speak))]
//! struct SpeakerField {
//! ident: Option<syn::Ident>,
//! ty: syn::Type,
//! #[darling(default)]
//! volume: Option<u32>,
//! }
//!
//! uses_type_params!(SpeakerField, ty);
//! uses_lifetimes!(SpeakerField, ty);
//!
//! #[derive(FromDeriveInput)]
//! struct SpeakerOptions {
//! generics: syn::Generics,
//! data: darling::ast::Data<darling::util::Ignored, SpeakerField>,
//! }
//! ```
//!
//! At this point, you are able to call `uses_type_params` on `SpeakerOptions.data`, or any filtered
//! view of it. `darling` internally uses this in conjunction with the `skip` meta-item to determine
//! which type parameters don't require the `FromMeta` bound in generated impls.
//!
//! **Note:** If you are performing operations referencing generic params in meta-items parsed by `darling`,
//! you should determine if those impact the emitted code and wire up `UsesTypeParams` accordingly for
//! your field/variant.
mod generics_ext;
mod ident_set;
mod lifetimes;
mod options;
mod type_params;
pub use self::generics_ext::GenericsExt;
pub use self::ident_set::{IdentRefSet, IdentSet};
pub use self::lifetimes::{CollectLifetimes, LifetimeRefSet, LifetimeSet, UsesLifetimes};
pub use self::options::{Options, Purpose};
pub use self::type_params::{CollectTypeParams, UsesTypeParams};

View File

@@ -0,0 +1,58 @@
/// The goal of tracing generic parameter usage.
///
/// Not all uses of type parameters imply a need to add bounds to a generated trait impl.
/// For example, a field of type `<Vec<T> as a::b::Trait>::Associated` does not need a
/// `where T: Serialize` bound in `serde`.
/// However, a proc macro that is attempting to generate a helper struct _would_ need to
/// know about this usage, or else the generated code would reference an unknown type `T`
/// and fail to compile.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Purpose {
/// The tracing is being used to generate an `impl` block.
///
/// Uses such as `syn::TypePath.qself` will _not_ be returned.
BoundImpl,
/// The tracing is being used to generate a new struct or enum.
///
/// All uses will be returned.
Declare,
}
/// Control struct for searching type parameters.
///
/// This acts as the search context, preserving information that might have been
/// kept on a visitor in a different implementation.
/// Trait implementers are required to pass this through on any invocations they make.
///
/// # Usage
/// For extensibility, `Options` hides all of its fields from consumers.
/// To create an instance, use the `From<Purpose>` trait implementation:
///
/// ```rust
/// # use darling_core::usage::{Options, Purpose};
/// let opts: Options = Purpose::BoundImpl.into();
/// assert!(!opts.include_type_path_qself());
/// ```
#[derive(Debug, Clone)]
pub struct Options {
purpose: Purpose,
#[doc(hidden)]
__nonexhaustive: (),
}
impl From<Purpose> for Options {
fn from(purpose: Purpose) -> Self {
Self {
purpose,
__nonexhaustive: (),
}
}
}
impl Options {
/// Returns `true` if the implementer of `UseTypeParams` should search
/// `<___ as ...>::...` when looking for type parameter uses.
pub fn include_type_path_qself(&self) -> bool {
self.purpose == Purpose::Declare
}
}

View File

@@ -0,0 +1,364 @@
use syn::punctuated::Punctuated;
use syn::{Ident, Type};
use crate::usage::{IdentRefSet, IdentSet, Options};
/// Searcher for finding type params in a syntax tree.
/// This can be used to determine if a given type parameter needs to be bounded in a generated impl.
pub trait UsesTypeParams {
/// Returns the subset of the queried type parameters that are used by the implementing syntax element.
///
/// This method only accounts for direct usage by the element; indirect usage via bounds or `where`
/// predicates are not detected.
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a>;
/// Find all type params using `uses_type_params`, then clone the found values and return the set.
fn uses_type_params_cloned(&self, options: &Options, type_set: &IdentSet) -> IdentSet {
self.uses_type_params(options, type_set)
.into_iter()
.cloned()
.collect()
}
}
/// Searcher for finding type params in an iterator.
///
/// This trait extends iterators, providing a way to turn a filtered list of fields or variants into a set
/// of type parameter idents.
pub trait CollectTypeParams {
/// Consume an iterator, accumulating all type parameters in the elements which occur in `type_set`.
fn collect_type_params<'a>(self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a>;
/// Consume an iterator using `collect_type_params`, then clone all found type params and return that set.
fn collect_type_params_cloned(self, options: &Options, type_set: &IdentSet) -> IdentSet;
}
impl<'i, T, I> CollectTypeParams for T
where
T: IntoIterator<Item = &'i I>,
I: 'i + UsesTypeParams,
{
fn collect_type_params<'a>(self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
self.into_iter().fold(
IdentRefSet::with_capacity_and_hasher(type_set.len(), Default::default()),
|state, value| union_in_place(state, value.uses_type_params(options, type_set)),
)
}
fn collect_type_params_cloned(self, options: &Options, type_set: &IdentSet) -> IdentSet {
self.collect_type_params(options, type_set)
.into_iter()
.cloned()
.collect()
}
}
/// Insert the contents of `right` into `left`.
fn union_in_place<'a>(mut left: IdentRefSet<'a>, right: IdentRefSet<'a>) -> IdentRefSet<'a> {
left.extend(right);
left
}
impl UsesTypeParams for () {
fn uses_type_params<'a>(&self, _options: &Options, _type_set: &'a IdentSet) -> IdentRefSet<'a> {
Default::default()
}
}
impl<T: UsesTypeParams> UsesTypeParams for Option<T> {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
self.as_ref()
.map(|v| v.uses_type_params(options, type_set))
.unwrap_or_default()
}
}
impl<T: UsesTypeParams> UsesTypeParams for Vec<T> {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
self.collect_type_params(options, type_set)
}
}
impl<T: UsesTypeParams, U> UsesTypeParams for Punctuated<T, U> {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
self.collect_type_params(options, type_set)
}
}
uses_type_params!(syn::AngleBracketedGenericArguments, args);
uses_type_params!(syn::AssocType, ty);
uses_type_params!(syn::BareFnArg, ty);
uses_type_params!(syn::Constraint, bounds);
uses_type_params!(syn::DataEnum, variants);
uses_type_params!(syn::DataStruct, fields);
uses_type_params!(syn::DataUnion, fields);
uses_type_params!(syn::Field, ty);
uses_type_params!(syn::FieldsNamed, named);
uses_type_params!(syn::ParenthesizedGenericArguments, inputs, output);
uses_type_params!(syn::PredicateType, bounded_ty, bounds);
uses_type_params!(syn::QSelf, ty);
uses_type_params!(syn::TraitBound, path);
uses_type_params!(syn::TypeArray, elem);
uses_type_params!(syn::TypeBareFn, inputs, output);
uses_type_params!(syn::TypeGroup, elem);
uses_type_params!(syn::TypeImplTrait, bounds);
uses_type_params!(syn::TypeParen, elem);
uses_type_params!(syn::TypePtr, elem);
uses_type_params!(syn::TypeReference, elem);
uses_type_params!(syn::TypeSlice, elem);
uses_type_params!(syn::TypeTuple, elems);
uses_type_params!(syn::TypeTraitObject, bounds);
uses_type_params!(syn::Variant, fields);
impl UsesTypeParams for syn::Data {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
match *self {
syn::Data::Struct(ref v) => v.uses_type_params(options, type_set),
syn::Data::Enum(ref v) => v.uses_type_params(options, type_set),
syn::Data::Union(ref v) => v.uses_type_params(options, type_set),
}
}
}
impl UsesTypeParams for syn::Fields {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
self.collect_type_params(options, type_set)
}
}
/// Check if an Ident exactly matches one of the sought-after type parameters.
impl UsesTypeParams for Ident {
fn uses_type_params<'a>(&self, _options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
type_set.iter().filter(|v| *v == self).collect()
}
}
impl UsesTypeParams for syn::ReturnType {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
if let syn::ReturnType::Type(_, ref ty) = *self {
ty.uses_type_params(options, type_set)
} else {
Default::default()
}
}
}
impl UsesTypeParams for Type {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
match *self {
Type::Slice(ref v) => v.uses_type_params(options, type_set),
Type::Array(ref v) => v.uses_type_params(options, type_set),
Type::Ptr(ref v) => v.uses_type_params(options, type_set),
Type::Reference(ref v) => v.uses_type_params(options, type_set),
Type::BareFn(ref v) => v.uses_type_params(options, type_set),
Type::Tuple(ref v) => v.uses_type_params(options, type_set),
Type::Path(ref v) => v.uses_type_params(options, type_set),
Type::Paren(ref v) => v.uses_type_params(options, type_set),
Type::Group(ref v) => v.uses_type_params(options, type_set),
Type::TraitObject(ref v) => v.uses_type_params(options, type_set),
Type::ImplTrait(ref v) => v.uses_type_params(options, type_set),
Type::Macro(_) | Type::Verbatim(_) | Type::Infer(_) | Type::Never(_) => {
Default::default()
}
_ => panic!("Unknown syn::Type: {:?}", self),
}
}
}
impl UsesTypeParams for syn::TypePath {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
let hits = self.path.uses_type_params(options, type_set);
if options.include_type_path_qself() {
union_in_place(hits, self.qself.uses_type_params(options, type_set))
} else {
hits
}
}
}
impl UsesTypeParams for syn::Path {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
// Not sure if this is even possible, but a path with no segments definitely
// can't use type parameters.
if self.segments.is_empty() {
return Default::default();
}
// A path segment ident can only match if it is not global and it is the first segment
// in the path.
let ident_hits = if self.leading_colon.is_none() {
self.segments[0].ident.uses_type_params(options, type_set)
} else {
Default::default()
};
// Merge ident hit, if any, with all hits from path arguments
self.segments.iter().fold(ident_hits, |state, segment| {
union_in_place(state, segment.arguments.uses_type_params(options, type_set))
})
}
}
impl UsesTypeParams for syn::PathArguments {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
match *self {
syn::PathArguments::None => Default::default(),
syn::PathArguments::AngleBracketed(ref v) => v.uses_type_params(options, type_set),
syn::PathArguments::Parenthesized(ref v) => v.uses_type_params(options, type_set),
}
}
}
impl UsesTypeParams for syn::WherePredicate {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
match *self {
syn::WherePredicate::Lifetime(_) => Default::default(),
syn::WherePredicate::Type(ref v) => v.uses_type_params(options, type_set),
// non-exhaustive enum
// TODO: replace panic with failible function
_ => panic!("Unknown syn::WherePredicate: {:?}", self),
}
}
}
impl UsesTypeParams for syn::GenericArgument {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
match *self {
syn::GenericArgument::Type(ref v) => v.uses_type_params(options, type_set),
syn::GenericArgument::AssocType(ref v) => v.uses_type_params(options, type_set),
syn::GenericArgument::Constraint(ref v) => v.uses_type_params(options, type_set),
syn::GenericArgument::AssocConst(_)
| syn::GenericArgument::Const(_)
| syn::GenericArgument::Lifetime(_) => Default::default(),
// non-exhaustive enum
// TODO: replace panic with failible function
_ => panic!("Unknown syn::GenericArgument: {:?}", self),
}
}
}
impl UsesTypeParams for syn::TypeParamBound {
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
match *self {
syn::TypeParamBound::Trait(ref v) => v.uses_type_params(options, type_set),
syn::TypeParamBound::Lifetime(_) => Default::default(),
// non-exhaustive enum
// TODO: replace panic with failible function
_ => panic!("Unknown syn::TypeParamBound: {:?}", self),
}
}
}
#[cfg(test)]
mod tests {
use proc_macro2::Span;
use syn::{parse_quote, DeriveInput, Ident};
use super::UsesTypeParams;
use crate::usage::IdentSet;
use crate::usage::Purpose::*;
fn ident_set(idents: Vec<&str>) -> IdentSet {
idents
.into_iter()
.map(|s| Ident::new(s, Span::call_site()))
.collect()
}
#[test]
fn finds_simple() {
let input: DeriveInput = parse_quote! { struct Foo<T, U>(T, i32, A, U); };
let generics = ident_set(vec!["T", "U", "X"]);
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
assert_eq!(matches.len(), 2);
assert!(matches.contains::<Ident>(&parse_quote!(T)));
assert!(matches.contains::<Ident>(&parse_quote!(U)));
assert!(!matches.contains::<Ident>(&parse_quote!(X)));
assert!(!matches.contains::<Ident>(&parse_quote!(A)));
}
#[test]
fn finds_named() {
let input: DeriveInput = parse_quote! {
struct Foo<T, U = usize> {
bar: T,
world: U,
}
};
let generics = ident_set(vec!["T", "U", "X"]);
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
assert_eq!(matches.len(), 2);
assert!(matches.contains::<Ident>(&parse_quote!(T)));
assert!(matches.contains::<Ident>(&parse_quote!(U)));
assert!(!matches.contains::<Ident>(&parse_quote!(X)));
assert!(!matches.contains::<Ident>(&parse_quote!(A)));
}
#[test]
fn finds_as_type_arg() {
let input: DeriveInput = parse_quote! {
struct Foo<T, U> {
bar: T,
world: Vec<U>,
}
};
let generics = ident_set(vec!["T", "U", "X"]);
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
assert_eq!(matches.len(), 2);
assert!(matches.contains::<Ident>(&parse_quote!(T)));
assert!(matches.contains::<Ident>(&parse_quote!(U)));
assert!(!matches.contains::<Ident>(&parse_quote!(X)));
assert!(!matches.contains::<Ident>(&parse_quote!(A)));
}
#[test]
fn associated_type() {
let input: DeriveInput =
parse_quote! { struct Foo<'a, T> where T: Iterator { peek: T::Item } };
let generics = ident_set(vec!["T", "INTO"]);
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
assert_eq!(matches.len(), 1);
}
#[test]
fn box_fn_output() {
let input: DeriveInput = parse_quote! { struct Foo<T>(Box<dyn Fn() -> T>); };
let generics = ident_set(vec!["T"]);
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
assert_eq!(matches.len(), 1);
assert!(matches.contains::<Ident>(&parse_quote!(T)));
}
#[test]
fn box_fn_input() {
let input: DeriveInput = parse_quote! { struct Foo<T>(Box<dyn Fn(&T) -> ()>); };
let generics = ident_set(vec!["T"]);
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
assert_eq!(matches.len(), 1);
assert!(matches.contains::<Ident>(&parse_quote!(T)));
}
/// Test that `syn::TypePath` is correctly honoring the different modes a
/// search can execute in.
#[test]
fn qself_vec() {
let input: DeriveInput =
parse_quote! { struct Foo<T>(<Vec<T> as a::b::Trait>::AssociatedItem); };
let generics = ident_set(vec!["T", "U"]);
let bound_matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
assert_eq!(bound_matches.len(), 0);
let declare_matches = input.data.uses_type_params(&Declare.into(), &generics);
assert_eq!(declare_matches.len(), 1);
assert!(declare_matches.contains::<Ident>(&parse_quote!(T)));
}
}