Path: blob/master/rust/pin-init/internal/src/helpers.rs
29281 views
// SPDX-License-Identifier: Apache-2.0 OR MIT12#[cfg(not(kernel))]3use proc_macro2 as proc_macro;45use proc_macro::{TokenStream, TokenTree};67/// Parsed generics.8///9/// See the field documentation for an explanation what each of the fields represents.10///11/// # Examples12///13/// ```rust,ignore14/// # let input = todo!();15/// let (Generics { decl_generics, impl_generics, ty_generics }, rest) = parse_generics(input);16/// quote! {17/// struct Foo<$($decl_generics)*> {18/// // ...19/// }20///21/// impl<$impl_generics> Foo<$ty_generics> {22/// fn foo() {23/// // ...24/// }25/// }26/// }27/// ```28pub(crate) struct Generics {29/// The generics with bounds and default values (e.g. `T: Clone, const N: usize = 0`).30///31/// Use this on type definitions e.g. `struct Foo<$decl_generics> ...` (or `union`/`enum`).32pub(crate) decl_generics: Vec<TokenTree>,33/// The generics with bounds (e.g. `T: Clone, const N: usize`).34///35/// Use this on `impl` blocks e.g. `impl<$impl_generics> Trait for ...`.36pub(crate) impl_generics: Vec<TokenTree>,37/// The generics without bounds and without default values (e.g. `T, N`).38///39/// Use this when you use the type that is declared with these generics e.g.40/// `Foo<$ty_generics>`.41pub(crate) ty_generics: Vec<TokenTree>,42}4344/// Parses the given `TokenStream` into `Generics` and the rest.45///46/// The generics are not present in the rest, but a where clause might remain.47pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {48// The generics with bounds and default values.49let mut decl_generics = vec![];50// `impl_generics`, the declared generics with their bounds.51let mut impl_generics = vec![];52// Only the names of the generics, without any bounds.53let mut ty_generics = vec![];54// Tokens not related to the generics e.g. the `where` token and definition.55let mut rest = vec![];56// The current level of `<`.57let mut nesting = 0;58let mut toks = input.into_iter();59// If we are at the beginning of a generic parameter.60let mut at_start = true;61let mut skip_until_comma = false;62while let Some(tt) = toks.next() {63if nesting == 1 && matches!(&tt, TokenTree::Punct(p) if p.as_char() == '>') {64// Found the end of the generics.65break;66} else if nesting >= 1 {67decl_generics.push(tt.clone());68}69match tt.clone() {70TokenTree::Punct(p) if p.as_char() == '<' => {71if nesting >= 1 && !skip_until_comma {72// This is inside of the generics and part of some bound.73impl_generics.push(tt);74}75nesting += 1;76}77TokenTree::Punct(p) if p.as_char() == '>' => {78// This is a parsing error, so we just end it here.79if nesting == 0 {80break;81} else {82nesting -= 1;83if nesting >= 1 && !skip_until_comma {84// We are still inside of the generics and part of some bound.85impl_generics.push(tt);86}87}88}89TokenTree::Punct(p) if skip_until_comma && p.as_char() == ',' => {90if nesting == 1 {91impl_generics.push(tt.clone());92impl_generics.push(tt);93skip_until_comma = false;94}95}96_ if !skip_until_comma => {97match nesting {98// If we haven't entered the generics yet, we still want to keep these tokens.990 => rest.push(tt),1001 => {101// Here depending on the token, it might be a generic variable name.102match tt.clone() {103TokenTree::Ident(i) if at_start && i.to_string() == "const" => {104let Some(name) = toks.next() else {105// Parsing error.106break;107};108impl_generics.push(tt);109impl_generics.push(name.clone());110ty_generics.push(name.clone());111decl_generics.push(name);112at_start = false;113}114TokenTree::Ident(_) if at_start => {115impl_generics.push(tt.clone());116ty_generics.push(tt);117at_start = false;118}119TokenTree::Punct(p) if p.as_char() == ',' => {120impl_generics.push(tt.clone());121ty_generics.push(tt);122at_start = true;123}124// Lifetimes begin with `'`.125TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {126impl_generics.push(tt.clone());127ty_generics.push(tt);128}129// Generics can have default values, we skip these.130TokenTree::Punct(p) if p.as_char() == '=' => {131skip_until_comma = true;132}133_ => impl_generics.push(tt),134}135}136_ => impl_generics.push(tt),137}138}139_ => {}140}141}142rest.extend(toks);143(144Generics {145impl_generics,146decl_generics,147ty_generics,148},149rest,150)151}152153154