use crate::algorithm::Printer; use crate::iter::IterDelimited; use crate::path::PathKind; use crate::INDENT; use proc_macro2::TokenStream; use std::ptr; use syn::{ BoundLifetimes, ConstParam, GenericParam, Generics, LifetimeParam, PredicateLifetime, PredicateType, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, WhereClause, WherePredicate, }; impl Printer { pub fn generics(&mut self, generics: &Generics) { if generics.params.is_empty() { return; } self.word("<"); self.cbox(0); self.zerobreak(); // Print lifetimes before types and consts, regardless of their // order in self.params. #[derive(Ord, PartialOrd, Eq, PartialEq)] enum Group { First, Second, } fn group(param: &GenericParam) -> Group { match param { GenericParam::Lifetime(_) => Group::First, GenericParam::Type(_) | GenericParam::Const(_) => Group::Second, } } let last = generics.params.iter().max_by_key(|param| group(param)); for current_group in [Group::First, Group::Second] { for param in &generics.params { if group(param) == current_group { self.generic_param(param); self.trailing_comma(ptr::eq(param, last.unwrap())); } } } self.offset(-INDENT); self.end(); self.word(">"); } fn generic_param(&mut self, generic_param: &GenericParam) { match generic_param { GenericParam::Type(type_param) => self.type_param(type_param), GenericParam::Lifetime(lifetime_param) => self.lifetime_param(lifetime_param), GenericParam::Const(const_param) => self.const_param(const_param), } } pub fn bound_lifetimes(&mut self, bound_lifetimes: &BoundLifetimes) { self.word("for<"); for param in bound_lifetimes.lifetimes.iter().delimited() { self.generic_param(¶m); if !param.is_last { self.word(", "); } } self.word("> "); } fn lifetime_param(&mut self, lifetime_param: &LifetimeParam) { self.outer_attrs(&lifetime_param.attrs); self.lifetime(&lifetime_param.lifetime); for lifetime in lifetime_param.bounds.iter().delimited() { if lifetime.is_first { self.word(": "); } else { self.word(" + "); } self.lifetime(&lifetime); } } fn type_param(&mut self, type_param: &TypeParam) { self.outer_attrs(&type_param.attrs); self.ident(&type_param.ident); self.ibox(INDENT); for type_param_bound in type_param.bounds.iter().delimited() { if type_param_bound.is_first { self.word(": "); } else { self.space(); self.word("+ "); } self.type_param_bound(&type_param_bound); } if let Some(default) = &type_param.default { self.space(); self.word("= "); self.ty(default); } self.end(); } pub fn type_param_bound(&mut self, type_param_bound: &TypeParamBound) { match type_param_bound { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] TypeParamBound::Trait(trait_bound) => { let tilde_const = false; self.trait_bound(trait_bound, tilde_const); } TypeParamBound::Lifetime(lifetime) => self.lifetime(lifetime), TypeParamBound::Verbatim(bound) => self.type_param_bound_verbatim(bound), _ => unimplemented!("unknown TypeParamBound"), } } fn trait_bound(&mut self, trait_bound: &TraitBound, tilde_const: bool) { if trait_bound.paren_token.is_some() { self.word("("); } if tilde_const { self.word("~const "); } self.trait_bound_modifier(&trait_bound.modifier); if let Some(bound_lifetimes) = &trait_bound.lifetimes { self.bound_lifetimes(bound_lifetimes); } for segment in trait_bound.path.segments.iter().delimited() { if !segment.is_first || trait_bound.path.leading_colon.is_some() { self.word("::"); } self.path_segment(&segment, PathKind::Type); } if trait_bound.paren_token.is_some() { self.word(")"); } } fn trait_bound_modifier(&mut self, trait_bound_modifier: &TraitBoundModifier) { match trait_bound_modifier { TraitBoundModifier::None => {} TraitBoundModifier::Maybe(_question_mark) => self.word("?"), } } #[cfg(not(feature = "verbatim"))] fn type_param_bound_verbatim(&mut self, bound: &TokenStream) { unimplemented!("TypeParamBound::Verbatim `{}`", bound); } #[cfg(feature = "verbatim")] fn type_param_bound_verbatim(&mut self, tokens: &TokenStream) { use syn::parse::{Parse, ParseStream, Result}; use syn::{parenthesized, token, Token}; enum TypeParamBoundVerbatim { Ellipsis, TildeConst(TraitBound), } impl Parse for TypeParamBoundVerbatim { fn parse(input: ParseStream) -> Result { let content; let (paren_token, content) = if input.peek(token::Paren) { (Some(parenthesized!(content in input)), &content) } else { (None, input) }; let lookahead = content.lookahead1(); if lookahead.peek(Token![~]) { content.parse::()?; content.parse::()?; let mut bound: TraitBound = content.parse()?; bound.paren_token = paren_token; Ok(TypeParamBoundVerbatim::TildeConst(bound)) } else if lookahead.peek(Token![...]) { content.parse::()?; Ok(TypeParamBoundVerbatim::Ellipsis) } else { Err(lookahead.error()) } } } let bound: TypeParamBoundVerbatim = match syn::parse2(tokens.clone()) { Ok(bound) => bound, Err(_) => unimplemented!("TypeParamBound::Verbatim `{}`", tokens), }; match bound { TypeParamBoundVerbatim::Ellipsis => { self.word("..."); } TypeParamBoundVerbatim::TildeConst(trait_bound) => { let tilde_const = true; self.trait_bound(&trait_bound, tilde_const); } } } fn const_param(&mut self, const_param: &ConstParam) { self.outer_attrs(&const_param.attrs); self.word("const "); self.ident(&const_param.ident); self.word(": "); self.ty(&const_param.ty); if let Some(default) = &const_param.default { self.word(" = "); self.expr(default); } } pub fn where_clause_for_body(&mut self, where_clause: &Option) { let hardbreaks = true; let semi = false; self.where_clause_impl(where_clause, hardbreaks, semi); } pub fn where_clause_semi(&mut self, where_clause: &Option) { let hardbreaks = true; let semi = true; self.where_clause_impl(where_clause, hardbreaks, semi); } pub fn where_clause_oneline(&mut self, where_clause: &Option) { let hardbreaks = false; let semi = false; self.where_clause_impl(where_clause, hardbreaks, semi); } pub fn where_clause_oneline_semi(&mut self, where_clause: &Option) { let hardbreaks = false; let semi = true; self.where_clause_impl(where_clause, hardbreaks, semi); } fn where_clause_impl( &mut self, where_clause: &Option, hardbreaks: bool, semi: bool, ) { let where_clause = match where_clause { Some(where_clause) if !where_clause.predicates.is_empty() => where_clause, _ => { if semi { self.word(";"); } else { self.nbsp(); } return; } }; if hardbreaks { self.hardbreak(); self.offset(-INDENT); self.word("where"); self.hardbreak(); for predicate in where_clause.predicates.iter().delimited() { self.where_predicate(&predicate); if predicate.is_last && semi { self.word(";"); } else { self.word(","); self.hardbreak(); } } if !semi { self.offset(-INDENT); } } else { self.space(); self.offset(-INDENT); self.word("where"); self.space(); for predicate in where_clause.predicates.iter().delimited() { self.where_predicate(&predicate); if predicate.is_last && semi { self.word(";"); } else { self.trailing_comma_or_space(predicate.is_last); } } if !semi { self.offset(-INDENT); } } } fn where_predicate(&mut self, predicate: &WherePredicate) { match predicate { #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] WherePredicate::Type(predicate) => self.predicate_type(predicate), WherePredicate::Lifetime(predicate) => self.predicate_lifetime(predicate), _ => unimplemented!("unknown WherePredicate"), } } fn predicate_type(&mut self, predicate: &PredicateType) { if let Some(bound_lifetimes) = &predicate.lifetimes { self.bound_lifetimes(bound_lifetimes); } self.ty(&predicate.bounded_ty); self.word(":"); if predicate.bounds.len() == 1 { self.ibox(0); } else { self.ibox(INDENT); } for type_param_bound in predicate.bounds.iter().delimited() { if type_param_bound.is_first { self.nbsp(); } else { self.space(); self.word("+ "); } self.type_param_bound(&type_param_bound); } self.end(); } fn predicate_lifetime(&mut self, predicate: &PredicateLifetime) { self.lifetime(&predicate.lifetime); self.word(":"); self.ibox(INDENT); for lifetime in predicate.bounds.iter().delimited() { if lifetime.is_first { self.nbsp(); } else { self.space(); self.word("+ "); } self.lifetime(&lifetime); } self.end(); } }