1 // SPDX-License-Identifier: Apache-2.0 OR MIT 2 3 use std::collections::{BTreeMap, BTreeSet}; 4 5 use proc_macro2::{Ident, TokenStream}; 6 use quote::{format_ident, ToTokens}; 7 use syn::{ 8 parse_quote, visit::Visit, visit_mut::VisitMut, Attribute, BoundLifetimes, GenericParam, 9 Generics, Index, Lifetime, Member, Token, Type, 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 390 pub(crate) trait TypeExt { 391 /// Check if the type includes macro invocations. 392 /// 393 /// Proc-macros cannot expand macros and peek into them, so if macro is involved sometimes 394 /// special handling is required. 395 fn has_macro(&self) -> bool; 396 397 fn replace_lifetimes(&self, needle: &[&Lifetime], replacement: &[&Lifetime]) -> Type; 398 } 399 400 impl TypeExt for Type { 401 fn has_macro(&self) -> bool { 402 struct HasMacro(bool); 403 404 impl<'ast> Visit<'ast> for HasMacro { 405 fn visit_macro(&mut self, _: &'ast syn::Macro) { 406 self.0 = true; 407 } 408 } 409 410 let mut visitor = HasMacro(false); 411 visitor.visit_type(self); 412 visitor.0 413 } 414 415 fn replace_lifetimes(&self, needle: &[&Lifetime], replacement: &[&Lifetime]) -> Type { 416 if needle.is_empty() { 417 return self.clone(); 418 } 419 420 // If the type has macro, we cannot peek into it. Use some different approach to replace 421 // the type using GAT. 422 if self.has_macro() { 423 return parse_quote!( 424 < 425 for<#(#needle,)*> fn(#(&#needle (),)*) -> #self 426 as 427 ::pin_init::__internal::FnOutput<(#(&#replacement (),)*)> 428 >::Output 429 ); 430 } 431 432 struct LifetimeReplacer<'a> { 433 to_replace: BTreeMap<&'a Lifetime, &'a Lifetime>, 434 } 435 436 impl<'a> LifetimeReplacer<'a> { 437 fn with_bound_lifetimes( 438 &mut self, 439 bound: Option<&BoundLifetimes>, 440 f: impl FnOnce(&mut Self), 441 ) { 442 // In case the type includes a lifetime binder, e.g. `dyn for<'a> Foo`, 443 // temporarily remove them from to_replace if they're. 444 445 let mut removed = Vec::new(); 446 if let Some(bound) = bound { 447 for lt in &bound.lifetimes { 448 let GenericParam::Lifetime(lt) = lt else { 449 continue; 450 }; 451 if let Some(entry) = self.to_replace.remove_entry(<.lifetime) { 452 removed.push(entry); 453 } 454 } 455 } 456 457 f(self); 458 459 for (key, val) in removed { 460 self.to_replace.insert(key, val); 461 } 462 } 463 } 464 465 impl VisitMut for LifetimeReplacer<'_> { 466 fn visit_lifetime_mut(&mut self, lt: &mut syn::Lifetime) { 467 if let Some(&replacement) = self.to_replace.get(lt) { 468 *lt = replacement.clone(); 469 } 470 } 471 472 fn visit_trait_bound_mut(&mut self, bound: &mut syn::TraitBound) { 473 self.with_bound_lifetimes(bound.lifetimes.as_ref(), |this| { 474 this.visit_path_mut(&mut bound.path) 475 }); 476 } 477 478 fn visit_type_bare_fn_mut(&mut self, bare_fn: &mut syn::TypeBareFn) { 479 self.with_bound_lifetimes(bare_fn.lifetimes.as_ref(), |this| { 480 for input in bare_fn.inputs.iter_mut() { 481 this.visit_bare_fn_arg_mut(input); 482 } 483 484 this.visit_return_type_mut(&mut bare_fn.output); 485 }); 486 } 487 } 488 489 let mut ret = self.clone(); 490 LifetimeReplacer { 491 to_replace: needle 492 .iter() 493 .copied() 494 .zip(replacement.iter().copied()) 495 .collect(), 496 } 497 .visit_type_mut(&mut ret); 498 ret 499 } 500 } 501