| use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; |
| use std::mem; |
| use syn::visit_mut::{self, VisitMut}; |
| use syn::{Expr, File, Generics, LifetimeParam, MacroDelimiter, Stmt, StmtMacro, TypeParam}; |
| |
| pub struct FlattenParens { |
| discard_paren_attrs: bool, |
| } |
| |
| impl FlattenParens { |
| pub fn discard_attrs() -> Self { |
| FlattenParens { |
| discard_paren_attrs: true, |
| } |
| } |
| |
| pub fn combine_attrs() -> Self { |
| FlattenParens { |
| discard_paren_attrs: false, |
| } |
| } |
| |
| pub fn visit_token_stream_mut(tokens: &mut TokenStream) { |
| *tokens = mem::take(tokens) |
| .into_iter() |
| .flat_map(|tt| { |
| if let TokenTree::Group(group) = tt { |
| let delimiter = group.delimiter(); |
| let mut content = group.stream(); |
| Self::visit_token_stream_mut(&mut content); |
| if let Delimiter::Parenthesis = delimiter { |
| content |
| } else { |
| TokenStream::from(TokenTree::Group(Group::new(delimiter, content))) |
| } |
| } else { |
| TokenStream::from(tt) |
| } |
| }) |
| .collect(); |
| } |
| } |
| |
| impl VisitMut for FlattenParens { |
| fn visit_expr_mut(&mut self, e: &mut Expr) { |
| while let Expr::Paren(paren) = e { |
| let paren_attrs = mem::take(&mut paren.attrs); |
| *e = mem::replace(&mut *paren.expr, Expr::PLACEHOLDER); |
| if !paren_attrs.is_empty() && !self.discard_paren_attrs { |
| let nested_attrs = match e { |
| Expr::Assign(e) => &mut e.attrs, |
| Expr::Binary(e) => &mut e.attrs, |
| Expr::Cast(e) => &mut e.attrs, |
| _ => unimplemented!(), |
| }; |
| assert!(nested_attrs.is_empty()); |
| *nested_attrs = paren_attrs; |
| } |
| } |
| visit_mut::visit_expr_mut(self, e); |
| } |
| } |
| |
| pub struct AsIfPrinted; |
| |
| impl VisitMut for AsIfPrinted { |
| fn visit_file_mut(&mut self, file: &mut File) { |
| file.shebang = None; |
| visit_mut::visit_file_mut(self, file); |
| } |
| |
| fn visit_generics_mut(&mut self, generics: &mut Generics) { |
| if generics.params.is_empty() { |
| generics.lt_token = None; |
| generics.gt_token = None; |
| } |
| if let Some(where_clause) = &generics.where_clause { |
| if where_clause.predicates.is_empty() { |
| generics.where_clause = None; |
| } |
| } |
| visit_mut::visit_generics_mut(self, generics); |
| } |
| |
| fn visit_lifetime_param_mut(&mut self, param: &mut LifetimeParam) { |
| if param.bounds.is_empty() { |
| param.colon_token = None; |
| } |
| visit_mut::visit_lifetime_param_mut(self, param); |
| } |
| |
| fn visit_stmt_mut(&mut self, stmt: &mut Stmt) { |
| if let Stmt::Expr(expr, semi) = stmt { |
| if let Expr::Macro(e) = expr { |
| if match e.mac.delimiter { |
| MacroDelimiter::Brace(_) => true, |
| MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => semi.is_some(), |
| } { |
| let Expr::Macro(expr) = mem::replace(expr, Expr::PLACEHOLDER) else { |
| unreachable!(); |
| }; |
| *stmt = Stmt::Macro(StmtMacro { |
| attrs: expr.attrs, |
| mac: expr.mac, |
| semi_token: *semi, |
| }); |
| } |
| } |
| } |
| visit_mut::visit_stmt_mut(self, stmt); |
| } |
| |
| fn visit_type_param_mut(&mut self, param: &mut TypeParam) { |
| if param.bounds.is_empty() { |
| param.colon_token = None; |
| } |
| visit_mut::visit_type_param_mut(self, param); |
| } |
| } |