Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501// SPDX-License-Identifier: Apache-2.0 OR MIT
use std::collections::{BTreeMap, BTreeSet};
use proc_macro2::{Ident, TokenStream};use quote::{format_ident, ToTokens};use syn::{ parse_quote, visit::Visit, visit_mut::VisitMut, Attribute, BoundLifetimes, GenericParam, Generics, Index, Lifetime, Member, Token, Type, TypePath,};
use crate::DiagCtxt;
pub(crate) trait AttrListExt { fn extract_cfg_attrs(&mut self) -> Vec<TokenStream>;
/// Extract attribute with identifier `path`. /// /// Report error if the attribute appears multiple times. fn extract_single_attr(&mut self, dcx: &DiagCtxt, path: &str) -> Option<Attribute>;}
impl AttrListExt for Vec<Attribute> { fn extract_cfg_attrs(&mut self) -> Vec<TokenStream> { let cfg: Vec<_> = self .iter() .filter(|a| a.path().is_ident("cfg")) .map(|a| { a.parse_args::<TokenStream>() .expect("parse as token stream cannot fail") }) .collect();
if !cfg.is_empty() { self.retain(|a| !a.path().is_ident("cfg")); }
cfg }
fn extract_single_attr(&mut self, dcx: &DiagCtxt, path: &str) -> Option<Attribute> { // FIXME: Replace with `extract_if` when MSRV >= 1.85. let attr_pos = self.iter().position(|attr| attr.path().is_ident(path))?; let attr = self.remove(attr_pos); self.retain(|attr| { if !attr.path().is_ident(path) { return true; }
dcx.error( attr, format!("`#[{path}]` attribute specified more than once"), ); false }); Some(attr) }}
pub(crate) trait MemberExt { /// Returns an identifier for the member. /// /// Tuple fields have no name of their own, so they are named `_0`, `_1`, ... instead. fn as_ident(&self) -> Ident;
/// Obtain a display name for the member in diagnostics. fn display_name(&self) -> String;}
impl MemberExt for Member { fn as_ident(&self) -> Ident { match self { Member::Named(ident) => ident.clone(), Member::Unnamed(Index { index, span }) => format_ident!("_{index}", span = *span), } }
fn display_name(&self) -> String { match self { Member::Named(ident) => format!("`{ident}`"), Member::Unnamed(Index { index, .. }) => format!("index `{index}`"), } }}
pub(crate) struct CombinedGenerics<'a>(pub(crate) Vec<&'a Generics>);pub(crate) struct CombinedImplGenerics<'a>(&'a CombinedGenerics<'a>);pub(crate) struct CombinedTypeGenerics<'a>(&'a CombinedGenerics<'a>);pub(crate) struct CombinedWhereClauses<'a>(&'a CombinedGenerics<'a>);
impl CombinedGenerics<'_> { pub(crate) fn split_for_impl( &self, ) -> ( CombinedImplGenerics<'_>, CombinedTypeGenerics<'_>, CombinedWhereClauses<'_>, ) { ( CombinedImplGenerics(self), CombinedTypeGenerics(self), CombinedWhereClauses(self), ) }}
impl ToTokens for CombinedGenerics<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { // Most of the time we are only adding lifetimes, so we prefer to place // the user provided generics last. self.0 .last() .and_then(|x| x.lt_token) .unwrap_or_default() .to_tokens(tokens);
let comma: Token![,] = Default::default();
// Output lifetimes first. for generics in self.0.iter() { for param in generics.params.pairs() { if let GenericParam::Lifetime(lt) = param.value() { lt.to_tokens(tokens); param.punct().unwrap_or(&&comma).to_tokens(tokens); } } }
for generics in self.0.iter() { for param in generics.params.pairs() { if let GenericParam::Lifetime(_) = param.value() { continue; }; param.value().to_tokens(tokens); param.punct().unwrap_or(&&comma).to_tokens(tokens); } }
self.0 .last() .and_then(|x| x.gt_token) .unwrap_or_default() .to_tokens(tokens); }}
impl ToTokens for CombinedImplGenerics<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { self.0 .0 .last() .and_then(|x| x.lt_token) .unwrap_or_default() .to_tokens(tokens);
let comma: Token![,] = Default::default();
// Output lifetimes first. for generics in self.0 .0.iter() { for param in generics.params.pairs() { if let GenericParam::Lifetime(lt) = param.value() { lt.to_tokens(tokens); param.punct().unwrap_or(&&comma).to_tokens(tokens); } } }
for generics in self.0 .0.iter() { for param in generics.params.pairs() { // Leave out defaults. match param.value() { GenericParam::Lifetime(_) => continue, GenericParam::Type(param) => { param.ident.to_tokens(tokens); if !param.bounds.is_empty() { param .colon_token .unwrap_or_else(Default::default) .to_tokens(tokens); param.bounds.to_tokens(tokens); } } GenericParam::Const(param) => { param.const_token.to_tokens(tokens); param.ident.to_tokens(tokens); param.colon_token.to_tokens(tokens); param.ty.to_tokens(tokens); } } param.punct().unwrap_or(&&comma).to_tokens(tokens); } }
self.0 .0 .last() .and_then(|x| x.gt_token) .unwrap_or_default() .to_tokens(tokens); }}
impl ToTokens for CombinedTypeGenerics<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { self.0 .0 .last() .and_then(|x| x.lt_token) .unwrap_or_default() .to_tokens(tokens);
let comma: Token![,] = Default::default();
// Output lifetimes first. for generics in self.0 .0.iter() { for param in generics.params.pairs() { if let GenericParam::Lifetime(lt) = param.value() { // Leave out bounds lt.lifetime.to_tokens(tokens); param.punct().unwrap_or(&&comma).to_tokens(tokens); } } }
for generics in self.0 .0.iter() { for param in generics.params.pairs() { // Leave out bounds and defaults. match param.value() { GenericParam::Lifetime(_) => continue, GenericParam::Type(param) => { param.ident.to_tokens(tokens); } GenericParam::Const(param) => { param.ident.to_tokens(tokens); } } param.punct().unwrap_or(&&comma).to_tokens(tokens); } }
self.0 .0 .last() .and_then(|x| x.gt_token) .unwrap_or_default() .to_tokens(tokens); }}
impl ToTokens for CombinedWhereClauses<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { self.0 .0 .iter() .filter_map(|x| Some(x.where_clause.as_ref()?.where_token)) .next_back() .unwrap_or_default() .to_tokens(tokens);
let comma: Token![,] = Default::default();
for generics in self.0 .0.iter() { let Some(where_clause) = &generics.where_clause else { continue; };
where_clause.predicates.to_tokens(tokens); if !where_clause.predicates.empty_or_trailing() { comma.to_tokens(tokens); } } }}
pub(crate) trait LifetimeExt { /// Get a visitor that call the provided function for all unbound lifetimes. fn visitor<'a>(f: impl FnMut(&'a Lifetime)) -> impl Visit<'a>;
/// Obtain a lifetime from a identifier. /// /// The created lifetime has the same span. fn from_ident(ident: &Ident) -> Self;}
impl LifetimeExt for Lifetime { fn visitor<'a>(f: impl FnMut(&'a Lifetime)) -> impl Visit<'a> { LifetimeVisitor { bound: BTreeSet::new(), visit: f, } }
fn from_ident(ident: &Ident) -> Self { Lifetime { apostrophe: ident.span(), ident: ident.clone(), } }}
struct LifetimeVisitor<'a, F> { bound: BTreeSet<&'a Lifetime>, visit: F,}
impl<'a, F> LifetimeVisitor<'a, F> { fn with_bound_lifetimes( &mut self, bound: Option<&'a BoundLifetimes>, f: impl FnOnce(&mut Self), ) { // In case the type includes a lifetime binder, e.g. `dyn for<'a> Foo`, // the lifetimes in the binder are bound and should not be visited.
let mut to_remove = Vec::new(); if let Some(bound) = bound { for lt in &bound.lifetimes { let GenericParam::Lifetime(lt) = lt else { continue; }; if !self.bound.contains(&<.lifetime) { self.bound.insert(<.lifetime); to_remove.push(<.lifetime); } } }
f(self);
for lt in to_remove { self.bound.remove(lt); } }}
impl<'a, F: FnMut(&'a Lifetime)> Visit<'a> for LifetimeVisitor<'a, F> { fn visit_lifetime(&mut self, lt: &'a Lifetime) { if lt.ident == "static" { return; }
if !self.bound.contains(lt) { (self.visit)(lt); } }
fn visit_trait_bound(&mut self, bound: &'a syn::TraitBound) { self.with_bound_lifetimes(bound.lifetimes.as_ref(), |this| { this.visit_path(&bound.path) }); }
fn visit_type_bare_fn(&mut self, bare_fn: &'a syn::TypeBareFn) { self.with_bound_lifetimes(bare_fn.lifetimes.as_ref(), |this| { for input in bare_fn.inputs.iter() { this.visit_bare_fn_arg(input); } this.visit_return_type(&bare_fn.output); }); }}
pub(crate) trait GenericParamExt { fn maybe_type_params_visitor<'a>(f: impl FnMut(&'a Ident)) -> impl Visit<'a>;}
impl GenericParamExt for GenericParam { fn maybe_type_params_visitor<'a>(f: impl FnMut(&'a Ident)) -> impl Visit<'a> { struct TypeParamVisitor<F>(F);
impl<'a, F> Visit<'a> for TypeParamVisitor<F> where F: FnMut(&'a Ident), { fn visit_type_path(&mut self, ty: &'a TypePath) { if ty.qself.is_none() && ty.path.leading_colon.is_none() && ty.path.segments[0].arguments.is_none() { (self.0)(&ty.path.segments[0].ident); } syn::visit::visit_type_path(self, ty); } }
TypeParamVisitor(f) }}
pub(crate) trait TypeExt { /// Check if the type includes macro invocations. /// /// Proc-macros cannot expand macros and peek into them, so if macro is involved sometimes /// special handling is required. fn has_macro(&self) -> bool;
fn replace_lifetimes(&self, needle: &[&Lifetime], replacement: &[&Lifetime]) -> Type;}
impl TypeExt for Type { fn has_macro(&self) -> bool { struct HasMacro(bool);
impl<'ast> Visit<'ast> for HasMacro { fn visit_macro(&mut self, _: &'ast syn::Macro) { self.0 = true; } }
let mut visitor = HasMacro(false); visitor.visit_type(self); visitor.0 }
fn replace_lifetimes(&self, needle: &[&Lifetime], replacement: &[&Lifetime]) -> Type { if needle.is_empty() { return self.clone(); }
// If the type has macro, we cannot peek into it. Use some different approach to replace // the type using GAT. if self.has_macro() { return parse_quote!( < for<#(#needle,)*> fn(#(&#needle (),)*) -> #self as ::pin_init::__internal::FnOutput<(#(&#replacement (),)*)> >::Output ); }
struct LifetimeReplacer<'a> { to_replace: BTreeMap<&'a Lifetime, &'a Lifetime>, }
impl<'a> LifetimeReplacer<'a> { fn with_bound_lifetimes( &mut self, bound: Option<&BoundLifetimes>, f: impl FnOnce(&mut Self), ) { // In case the type includes a lifetime binder, e.g. `dyn for<'a> Foo`, // temporarily remove them from to_replace if they're.
let mut removed = Vec::new(); if let Some(bound) = bound { for lt in &bound.lifetimes { let GenericParam::Lifetime(lt) = lt else { continue; }; if let Some(entry) = self.to_replace.remove_entry(<.lifetime) { removed.push(entry); } } }
f(self);
for (key, val) in removed { self.to_replace.insert(key, val); } } }
impl VisitMut for LifetimeReplacer<'_> { fn visit_lifetime_mut(&mut self, lt: &mut syn::Lifetime) { if let Some(&replacement) = self.to_replace.get(lt) { *lt = replacement.clone(); } }
fn visit_trait_bound_mut(&mut self, bound: &mut syn::TraitBound) { self.with_bound_lifetimes(bound.lifetimes.as_ref(), |this| { this.visit_path_mut(&mut bound.path) }); }
fn visit_type_bare_fn_mut(&mut self, bare_fn: &mut syn::TypeBareFn) { self.with_bound_lifetimes(bare_fn.lifetimes.as_ref(), |this| { for input in bare_fn.inputs.iter_mut() { this.visit_bare_fn_arg_mut(input); }
this.visit_return_type_mut(&mut bare_fn.output); }); } }
let mut ret = self.clone(); LifetimeReplacer { to_replace: needle .iter() .copied() .zip(replacement.iter().copied()) .collect(), } .visit_type_mut(&mut ret); ret }}