1#![doc(test(
178 no_crate_inject,
179 attr(
180 deny(warnings, rust_2018_idioms, single_use_lifetimes),
181 allow(dead_code, unused_variables)
182 )
183))]
184#![forbid(unsafe_code)]
185
186#[allow(unused_extern_crates)]
188extern crate proc_macro;
189
190#[macro_use]
191mod error;
192
193mod ast;
194mod iter;
195mod to_tokens;
196
197use std::{collections::hash_map::DefaultHasher, hash::Hasher, iter::FromIterator, mem};
198
199use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
200
201use crate::{
202 ast::{
203 parsing, printing::punct, Attribute, AttributeKind, FnArg, GenericParam, Generics,
204 ImplItem, ItemImpl, ItemTrait, PredicateType, Signature, TraitItem, TraitItemConst,
205 TraitItemMethod, TraitItemType, TypeParam, Visibility, WherePredicate,
206 },
207 error::{Error, Result},
208 iter::TokenIter,
209 to_tokens::ToTokens,
210};
211
212#[proc_macro_attribute]
218pub fn ext(args: TokenStream, input: TokenStream) -> TokenStream {
219 expand(args, input).unwrap_or_else(Error::into_compile_error)
220}
221
222fn expand(args: TokenStream, input: TokenStream) -> Result<TokenStream> {
223 let trait_name = match parse_args(args)? {
224 None => Ident::new(&format!("__ExtTrait{}", hash(&input)), Span::call_site()),
225 Some(trait_name) => trait_name,
226 };
227
228 let mut item: ItemImpl = parsing::parse_impl(&mut TokenIter::new(input))?;
229
230 let mut tokens = trait_from_impl(&mut item, trait_name)?.to_token_stream();
231 tokens.extend(item.to_token_stream());
232 Ok(tokens)
233}
234
235fn parse_args(input: TokenStream) -> Result<Option<Ident>> {
236 let input = &mut TokenIter::new(input);
237 let vis = ast::parsing::parse_visibility(input)?;
238 if !vis.is_inherited() {
239 bail!(vis, "use `{} impl` instead", vis);
240 }
241 let trait_name = input.parse_ident_opt();
242 if !input.is_empty() {
243 let tt = input.next().unwrap();
244 bail!(tt, "unexpected token: `{}`", tt);
245 }
246 Ok(trait_name)
247}
248
249fn determine_trait_generics<'a>(
250 generics: &mut Generics,
251 self_ty: &'a [TokenTree],
252) -> Option<&'a Ident> {
253 if self_ty.len() != 1 {
254 return None;
255 }
256 if let TokenTree::Ident(self_ty) = &self_ty[0] {
257 let i = generics.params.iter().position(|(param, _)| {
258 if let GenericParam::Type(param) = param {
259 param.ident.to_string() == self_ty.to_string()
260 } else {
261 false
262 }
263 });
264 if let Some(i) = i {
265 let mut params = mem::replace(&mut generics.params, vec![]);
266 let (param, _) = params.remove(i);
267 generics.params = params;
268
269 if let GenericParam::Type(TypeParam {
270 colon_token: Some(colon_token), bounds, ..
271 }) = param
272 {
273 let bounds = bounds.into_iter().filter(|(b, _)| !b.is_maybe).collect::<Vec<_>>();
274 if !bounds.is_empty() {
275 let where_clause = generics.make_where_clause();
276 if let Some((_, p)) = where_clause.predicates.last_mut() {
277 p.get_or_insert_with(|| punct(',', Span::call_site()));
278 }
279 where_clause.predicates.push((
280 WherePredicate::Type(PredicateType {
281 lifetimes: None,
282 bounded_ty: vec![TokenTree::Ident(Ident::new("Self", self_ty.span()))]
283 .into_iter()
284 .collect(),
285 colon_token,
286 bounds,
287 }),
288 None,
289 ));
290 }
291 }
292
293 return Some(self_ty);
294 }
295 }
296 None
297}
298
299fn trait_from_impl(item: &mut ItemImpl, trait_name: Ident) -> Result<ItemTrait> {
300 struct ReplaceParam {
302 self_ty: String,
303 remove_maybe: bool,
307 }
308
309 impl ReplaceParam {
310 fn visit_token_stream(&self, tokens: &mut TokenStream) -> bool {
311 let mut out: Vec<TokenTree> = vec![];
312 let mut modified = false;
313 let iter = tokens.clone().into_iter();
314 for tt in iter {
315 match tt {
316 TokenTree::Ident(ident) => {
317 if ident.to_string() == self.self_ty {
318 modified = true;
319 let self_ = Ident::new("Self", ident.span());
320 out.push(self_.into());
321 } else {
322 out.push(TokenTree::Ident(ident));
323 }
324 }
325 TokenTree::Group(group) => {
326 let mut content = group.stream();
327 modified |= self.visit_token_stream(&mut content);
328 let mut new = Group::new(group.delimiter(), content);
329 new.set_span(group.span());
330 out.push(TokenTree::Group(new));
331 }
332 other => out.push(other),
333 }
334 }
335 if modified {
336 *tokens = TokenStream::from_iter(out);
337 }
338 modified
339 }
340
341 fn visit_trait_item_mut(&mut self, node: &mut TraitItem) {
344 match node {
345 TraitItem::Const(node) => {
346 self.visit_token_stream(&mut node.ty);
347 }
348 TraitItem::Method(node) => {
349 self.visit_signature_mut(&mut node.sig);
350 }
351 TraitItem::Type(node) => {
352 self.visit_generics_mut(&mut node.generics);
353 }
354 }
355 }
356
357 fn visit_signature_mut(&mut self, node: &mut Signature) {
358 self.visit_generics_mut(&mut node.generics);
359 for arg in &mut node.inputs {
360 self.visit_fn_arg_mut(arg);
361 }
362 if let Some(ty) = &mut node.output {
363 self.visit_token_stream(ty);
364 }
365 }
366
367 fn visit_fn_arg_mut(&mut self, node: &mut FnArg) {
368 match node {
369 FnArg::Receiver(pat, _) => {
370 self.visit_token_stream(pat);
371 }
372 FnArg::Typed(pat, _, ty, _) => {
373 self.visit_token_stream(pat);
374 self.visit_token_stream(ty);
375 }
376 }
377 }
378
379 fn visit_generics_mut(&mut self, generics: &mut Generics) {
380 for (param, _) in &mut generics.params {
381 match param {
382 GenericParam::Type(param) => {
383 for (bound, _) in &mut param.bounds {
384 self.visit_token_stream(&mut bound.tokens);
385 }
386 }
387 GenericParam::Const(_) | GenericParam::Lifetime(_) => {}
388 }
389 }
390 if let Some(where_clause) = &mut generics.where_clause {
391 let predicates = Vec::with_capacity(where_clause.predicates.len());
392 for (mut predicate, p) in mem::replace(&mut where_clause.predicates, predicates) {
393 match &mut predicate {
394 WherePredicate::Type(pred) => {
395 if self.remove_maybe {
396 let mut iter = pred.bounded_ty.clone().into_iter();
397 if let Some(TokenTree::Ident(i)) = iter.next() {
398 if iter.next().is_none() && self.self_ty == i.to_string() {
399 let bounds = mem::replace(&mut pred.bounds, vec![])
400 .into_iter()
401 .filter(|(b, _)| !b.is_maybe)
402 .collect::<Vec<_>>();
403 if !bounds.is_empty() {
404 self.visit_token_stream(&mut pred.bounded_ty);
405 pred.bounds = bounds;
406 for (bound, _) in &mut pred.bounds {
407 self.visit_token_stream(&mut bound.tokens);
408 }
409 where_clause.predicates.push((predicate, p));
410 }
411 continue;
412 }
413 }
414 }
415
416 self.visit_token_stream(&mut pred.bounded_ty);
417 for (bound, _) in &mut pred.bounds {
418 self.visit_token_stream(&mut bound.tokens);
419 }
420 }
421 WherePredicate::Lifetime(_) => {}
422 }
423 where_clause.predicates.push((predicate, p));
424 }
425 }
426 }
427 }
428
429 let mut generics = item.generics.clone();
430 let mut visitor = determine_trait_generics(&mut generics, &item.self_ty)
431 .map(|self_ty| ReplaceParam { self_ty: self_ty.to_string(), remove_maybe: false });
432
433 if let Some(visitor) = &mut visitor {
434 visitor.remove_maybe = true;
435 visitor.visit_generics_mut(&mut generics);
436 visitor.remove_maybe = false;
437 }
438 let ty_generics = generics.ty_generics();
439 item.trait_ = Some((
440 trait_name.clone(),
441 ty_generics.to_token_stream(),
442 Ident::new("for", Span::call_site()),
443 ));
444
445 let impl_vis = if item.vis.is_inherited() { None } else { Some(item.vis.clone()) };
447 let mut assoc_vis = None;
449 let mut items = Vec::with_capacity(item.items.len());
450 item.items.iter_mut().try_for_each(|item| {
451 trait_item_from_impl_item(item, &mut assoc_vis, &impl_vis).map(|mut item| {
452 if let Some(visitor) = &mut visitor {
453 visitor.visit_trait_item_mut(&mut item);
454 }
455 items.push(item);
456 })
457 })?;
458
459 let mut attrs = item.attrs.clone();
460 find_remove(&mut item.attrs, AttributeKind::Doc); attrs.push(Attribute::new(vec![
462 TokenTree::Ident(Ident::new("allow", Span::call_site())),
463 TokenTree::Group(Group::new(
464 Delimiter::Parenthesis,
465 std::iter::once(TokenTree::Ident(Ident::new(
466 "patterns_in_fns_without_body",
467 Span::call_site(),
468 )))
469 .collect(),
470 )),
471 ])); Ok(ItemTrait {
474 attrs,
475 vis: impl_vis.unwrap_or_else(|| assoc_vis.unwrap_or(Visibility::Inherited)),
477 unsafety: item.unsafety.clone(),
478 trait_token: Ident::new("trait", item.impl_token.span()),
479 ident: trait_name,
480 generics,
481 brace_token: item.brace_token,
482 items,
483 })
484}
485
486fn trait_item_from_impl_item(
487 impl_item: &mut ImplItem,
488 prev_vis: &mut Option<Visibility>,
489 impl_vis: &Option<Visibility>,
490) -> Result<TraitItem> {
491 fn check_visibility(
492 current: Visibility,
493 prev: &mut Option<Visibility>,
494 impl_vis: &Option<Visibility>,
495 span: &dyn ToTokens,
496 ) -> Result<()> {
497 if impl_vis.is_some() {
498 if current.is_inherited() {
499 return Ok(());
500 }
501 bail!(current, "all associated items must have inherited visibility");
502 }
503 match prev {
504 None => *prev = Some(current),
505 Some(prev) if *prev == current => {}
506 Some(prev) => {
507 if prev.is_inherited() {
508 bail!(current, "all associated items must have inherited visibility");
509 }
510 bail!(
511 if current.is_inherited() { span } else { ¤t },
512 "all associated items must have a visibility of `{}`",
513 prev,
514 );
515 }
516 }
517 Ok(())
518 }
519
520 match impl_item {
521 ImplItem::Const(impl_const) => {
522 let vis = mem::replace(&mut impl_const.vis, Visibility::Inherited);
523 check_visibility(vis, prev_vis, impl_vis, &impl_const.ident)?;
524
525 let attrs = impl_const.attrs.clone();
526 find_remove(&mut impl_const.attrs, AttributeKind::Doc); Ok(TraitItem::Const(TraitItemConst {
528 attrs,
529 const_token: impl_const.const_token.clone(),
530 ident: impl_const.ident.clone(),
531 colon_token: impl_const.colon_token.clone(),
532 ty: impl_const.ty.clone(),
533 semi_token: impl_const.semi_token.clone(),
534 }))
535 }
536 ImplItem::Type(impl_type) => {
537 let vis = mem::replace(&mut impl_type.vis, Visibility::Inherited);
538 check_visibility(vis, prev_vis, impl_vis, &impl_type.ident)?;
539
540 let attrs = impl_type.attrs.clone();
541 find_remove(&mut impl_type.attrs, AttributeKind::Doc); Ok(TraitItem::Type(TraitItemType {
543 attrs,
544 type_token: impl_type.type_token.clone(),
545 ident: impl_type.ident.clone(),
546 generics: impl_type.generics.clone(),
547 semi_token: impl_type.semi_token.clone(),
548 }))
549 }
550 ImplItem::Method(impl_method) => {
551 let vis = mem::replace(&mut impl_method.vis, Visibility::Inherited);
552 check_visibility(vis, prev_vis, impl_vis, &impl_method.sig.ident)?;
553
554 let mut attrs = impl_method.attrs.clone();
555 find_remove(&mut impl_method.attrs, AttributeKind::Doc); find_remove(&mut attrs, AttributeKind::Inline); Ok(TraitItem::Method(TraitItemMethod {
558 attrs,
559 sig: {
560 let mut sig = impl_method.sig.clone();
561 for arg in &mut sig.inputs {
562 if let FnArg::Typed(pat, ..) = arg {
563 if pat.to_string() != "self" {
564 *pat = std::iter::once(TokenTree::Ident(Ident::new(
565 "_",
566 pat.clone().into_iter().next().unwrap().span(),
567 )))
568 .collect();
569 }
570 }
571 }
572 sig
573 },
574 semi_token: punct(';', impl_method.body.span()),
575 }))
576 }
577 }
578}
579
580fn find_remove(attrs: &mut Vec<Attribute>, kind: AttributeKind) {
581 while let Some(i) = attrs.iter().position(|attr| attr.kind == kind) {
582 attrs.remove(i);
583 }
584}
585
586fn hash(input: &TokenStream) -> u64 {
588 let mut hasher = DefaultHasher::new();
589 hasher.write(input.to_string().as_bytes());
590 hasher.finish()
591}