1 // SPDX-License-Identifier: Apache-2.0 OR MIT 2 3 use std::collections::BTreeSet; 4 5 use proc_macro2::{Ident, TokenStream}; 6 use quote::{format_ident, ToTokens}; 7 use syn::{ 8 visit::Visit, Attribute, BoundLifetimes, GenericParam, Generics, Index, Lifetime, Member, 9 Token, TypePath, 10 }; 11 12 use crate::DiagCtxt; 13 14 pub(crate) trait AttrListExt { 15 fn extract_cfg_attrs(&mut self) -> Vec<TokenStream>; 16 17 /// Extract attribute with identifier `path`. 18 /// 19 /// Report error if the attribute appears multiple times. 20 fn extract_single_attr(&mut self, dcx: &DiagCtxt, path: &str) -> Option<Attribute>; 21 } 22 23 impl AttrListExt for Vec<Attribute> { 24 fn extract_cfg_attrs(&mut self) -> Vec<TokenStream> { 25 let cfg: Vec<_> = self 26 .iter() 27 .filter(|a| a.path().is_ident("cfg")) 28 .map(|a| { 29 a.parse_args::<TokenStream>() 30 .expect("parse as token stream cannot fail") 31 }) 32 .collect(); 33 34 if !cfg.is_empty() { 35 self.retain(|a| !a.path().is_ident("cfg")); 36 } 37 38 cfg 39 } 40 41 fn extract_single_attr(&mut self, dcx: &DiagCtxt, path: &str) -> Option<Attribute> { 42 // FIXME: Replace with `extract_if` when MSRV >= 1.85. 43 let attr_pos = self.iter().position(|attr| attr.path().is_ident(path))?; 44 let attr = self.remove(attr_pos); 45 self.retain(|attr| { 46 if !attr.path().is_ident(path) { 47 return true; 48 } 49 50 dcx.error( 51 attr, 52 format!("`#[{path}]` attribute specified more than once"), 53 ); 54 false 55 }); 56 Some(attr) 57 } 58 } 59 60 pub(crate) trait MemberExt { 61 /// Returns an identifier for the member. 62 /// 63 /// Tuple fields have no name of their own, so they are named `_0`, `_1`, ... instead. 64 fn as_ident(&self) -> Ident; 65 66 /// Obtain a display name for the member in diagnostics. 67 fn display_name(&self) -> String; 68 } 69 70 impl MemberExt for Member { 71 fn as_ident(&self) -> Ident { 72 match self { 73 Member::Named(ident) => ident.clone(), 74 Member::Unnamed(Index { index, span }) => format_ident!("_{index}", span = *span), 75 } 76 } 77 78 fn display_name(&self) -> String { 79 match self { 80 Member::Named(ident) => format!("`{ident}`"), 81 Member::Unnamed(Index { index, .. }) => format!("index `{index}`"), 82 } 83 } 84 } 85 86 pub(crate) struct CombinedGenerics<'a>(pub(crate) Vec<&'a Generics>); 87 pub(crate) struct CombinedImplGenerics<'a>(&'a CombinedGenerics<'a>); 88 pub(crate) struct CombinedTypeGenerics<'a>(&'a CombinedGenerics<'a>); 89 pub(crate) struct CombinedWhereClauses<'a>(&'a CombinedGenerics<'a>); 90 91 impl CombinedGenerics<'_> { 92 pub(crate) fn split_for_impl( 93 &self, 94 ) -> ( 95 CombinedImplGenerics<'_>, 96 CombinedTypeGenerics<'_>, 97 CombinedWhereClauses<'_>, 98 ) { 99 ( 100 CombinedImplGenerics(self), 101 CombinedTypeGenerics(self), 102 CombinedWhereClauses(self), 103 ) 104 } 105 } 106 107 impl ToTokens for CombinedGenerics<'_> { 108 fn to_tokens(&self, tokens: &mut TokenStream) { 109 // Most of the time we are only adding lifetimes, so we prefer to place 110 // the user provided generics last. 111 self.0 112 .last() 113 .and_then(|x| x.lt_token) 114 .unwrap_or_default() 115 .to_tokens(tokens); 116 117 let comma: Token![,] = Default::default(); 118 119 // Output lifetimes first. 120 for generics in self.0.iter() { 121 for param in generics.params.pairs() { 122 if let GenericParam::Lifetime(lt) = param.value() { 123 lt.to_tokens(tokens); 124 param.punct().unwrap_or(&&comma).to_tokens(tokens); 125 } 126 } 127 } 128 129 for generics in self.0.iter() { 130 for param in generics.params.pairs() { 131 if let GenericParam::Lifetime(_) = param.value() { 132 continue; 133 }; 134 param.value().to_tokens(tokens); 135 param.punct().unwrap_or(&&comma).to_tokens(tokens); 136 } 137 } 138 139 self.0 140 .last() 141 .and_then(|x| x.gt_token) 142 .unwrap_or_default() 143 .to_tokens(tokens); 144 } 145 } 146 147 impl ToTokens for CombinedImplGenerics<'_> { 148 fn to_tokens(&self, tokens: &mut TokenStream) { 149 self.0 150 .0 151 .last() 152 .and_then(|x| x.lt_token) 153 .unwrap_or_default() 154 .to_tokens(tokens); 155 156 let comma: Token![,] = Default::default(); 157 158 // Output lifetimes first. 159 for generics in self.0 .0.iter() { 160 for param in generics.params.pairs() { 161 if let GenericParam::Lifetime(lt) = param.value() { 162 lt.to_tokens(tokens); 163 param.punct().unwrap_or(&&comma).to_tokens(tokens); 164 } 165 } 166 } 167 168 for generics in self.0 .0.iter() { 169 for param in generics.params.pairs() { 170 // Leave out defaults. 171 match param.value() { 172 GenericParam::Lifetime(_) => continue, 173 GenericParam::Type(param) => { 174 param.ident.to_tokens(tokens); 175 if !param.bounds.is_empty() { 176 param 177 .colon_token 178 .unwrap_or_else(Default::default) 179 .to_tokens(tokens); 180 param.bounds.to_tokens(tokens); 181 } 182 } 183 GenericParam::Const(param) => { 184 param.const_token.to_tokens(tokens); 185 param.ident.to_tokens(tokens); 186 param.colon_token.to_tokens(tokens); 187 param.ty.to_tokens(tokens); 188 } 189 } 190 param.punct().unwrap_or(&&comma).to_tokens(tokens); 191 } 192 } 193 194 self.0 195 .0 196 .last() 197 .and_then(|x| x.gt_token) 198 .unwrap_or_default() 199 .to_tokens(tokens); 200 } 201 } 202 203 impl ToTokens for CombinedTypeGenerics<'_> { 204 fn to_tokens(&self, tokens: &mut TokenStream) { 205 self.0 206 .0 207 .last() 208 .and_then(|x| x.lt_token) 209 .unwrap_or_default() 210 .to_tokens(tokens); 211 212 let comma: Token![,] = Default::default(); 213 214 // Output lifetimes first. 215 for generics in self.0 .0.iter() { 216 for param in generics.params.pairs() { 217 if let GenericParam::Lifetime(lt) = param.value() { 218 // Leave out bounds 219 lt.lifetime.to_tokens(tokens); 220 param.punct().unwrap_or(&&comma).to_tokens(tokens); 221 } 222 } 223 } 224 225 for generics in self.0 .0.iter() { 226 for param in generics.params.pairs() { 227 // Leave out bounds and defaults. 228 match param.value() { 229 GenericParam::Lifetime(_) => continue, 230 GenericParam::Type(param) => { 231 param.ident.to_tokens(tokens); 232 } 233 GenericParam::Const(param) => { 234 param.ident.to_tokens(tokens); 235 } 236 } 237 param.punct().unwrap_or(&&comma).to_tokens(tokens); 238 } 239 } 240 241 self.0 242 .0 243 .last() 244 .and_then(|x| x.gt_token) 245 .unwrap_or_default() 246 .to_tokens(tokens); 247 } 248 } 249 250 impl ToTokens for CombinedWhereClauses<'_> { 251 fn to_tokens(&self, tokens: &mut TokenStream) { 252 self.0 253 .0 254 .iter() 255 .filter_map(|x| Some(x.where_clause.as_ref()?.where_token)) 256 .next_back() 257 .unwrap_or_default() 258 .to_tokens(tokens); 259 260 let comma: Token![,] = Default::default(); 261 262 for generics in self.0 .0.iter() { 263 let Some(where_clause) = &generics.where_clause else { 264 continue; 265 }; 266 267 where_clause.predicates.to_tokens(tokens); 268 if !where_clause.predicates.empty_or_trailing() { 269 comma.to_tokens(tokens); 270 } 271 } 272 } 273 } 274 275 pub(crate) trait LifetimeExt { 276 /// Get a visitor that call the provided function for all unbound lifetimes. 277 fn visitor<'a>(f: impl FnMut(&'a Lifetime)) -> impl Visit<'a>; 278 279 /// Obtain a lifetime from a identifier. 280 /// 281 /// The created lifetime has the same span. 282 fn from_ident(ident: &Ident) -> Self; 283 } 284 285 impl LifetimeExt for Lifetime { 286 fn visitor<'a>(f: impl FnMut(&'a Lifetime)) -> impl Visit<'a> { 287 LifetimeVisitor { 288 bound: BTreeSet::new(), 289 visit: f, 290 } 291 } 292 293 fn from_ident(ident: &Ident) -> Self { 294 Lifetime { 295 apostrophe: ident.span(), 296 ident: ident.clone(), 297 } 298 } 299 } 300 301 struct LifetimeVisitor<'a, F> { 302 bound: BTreeSet<&'a Lifetime>, 303 visit: F, 304 } 305 306 impl<'a, F> LifetimeVisitor<'a, F> { 307 fn with_bound_lifetimes( 308 &mut self, 309 bound: Option<&'a BoundLifetimes>, 310 f: impl FnOnce(&mut Self), 311 ) { 312 // In case the type includes a lifetime binder, e.g. `dyn for<'a> Foo`, 313 // the lifetimes in the binder are bound and should not be visited. 314 315 let mut to_remove = Vec::new(); 316 if let Some(bound) = bound { 317 for lt in &bound.lifetimes { 318 let GenericParam::Lifetime(lt) = lt else { 319 continue; 320 }; 321 if !self.bound.contains(&<.lifetime) { 322 self.bound.insert(<.lifetime); 323 to_remove.push(<.lifetime); 324 } 325 } 326 } 327 328 f(self); 329 330 for lt in to_remove { 331 self.bound.remove(lt); 332 } 333 } 334 } 335 336 impl<'a, F: FnMut(&'a Lifetime)> Visit<'a> for LifetimeVisitor<'a, F> { 337 fn visit_lifetime(&mut self, lt: &'a Lifetime) { 338 if lt.ident == "static" { 339 return; 340 } 341 342 if !self.bound.contains(lt) { 343 (self.visit)(lt); 344 } 345 } 346 347 fn visit_trait_bound(&mut self, bound: &'a syn::TraitBound) { 348 self.with_bound_lifetimes(bound.lifetimes.as_ref(), |this| { 349 this.visit_path(&bound.path) 350 }); 351 } 352 353 fn visit_type_bare_fn(&mut self, bare_fn: &'a syn::TypeBareFn) { 354 self.with_bound_lifetimes(bare_fn.lifetimes.as_ref(), |this| { 355 for input in bare_fn.inputs.iter() { 356 this.visit_bare_fn_arg(input); 357 } 358 this.visit_return_type(&bare_fn.output); 359 }); 360 } 361 } 362 363 pub(crate) trait GenericParamExt { 364 fn maybe_type_params_visitor<'a>(f: impl FnMut(&'a Ident)) -> impl Visit<'a>; 365 } 366 367 impl GenericParamExt for GenericParam { 368 fn maybe_type_params_visitor<'a>(f: impl FnMut(&'a Ident)) -> impl Visit<'a> { 369 struct TypeParamVisitor<F>(F); 370 371 impl<'a, F> Visit<'a> for TypeParamVisitor<F> 372 where 373 F: FnMut(&'a Ident), 374 { 375 fn visit_type_path(&mut self, ty: &'a TypePath) { 376 if ty.qself.is_none() 377 && ty.path.leading_colon.is_none() 378 && ty.path.segments[0].arguments.is_none() 379 { 380 (self.0)(&ty.path.segments[0].ident); 381 } 382 syn::visit::visit_type_path(self, ty); 383 } 384 } 385 386 TypeParamVisitor(f) 387 } 388 } 389