// Format list-like macro invocations. These are invocations whose token trees // can be interpreted as expressions and separated by commas. // Note that these token trees do not actually have to be interpreted as // expressions by the compiler. An example of an invocation we would reformat is // foo!( x, y, z ). The token x may represent an identifier in the code, but we // interpreted as an expression. // Macro uses which are not-list like, such as bar!(key => val), will not be // reformatted. // List-like invocations with parentheses will be formatted as function calls, // and those with brackets will be formatted as array literals. use std::collections::HashMap; use syntax::parse::new_parser_from_tts; use syntax::parse::parser::Parser; use syntax::parse::token::{BinOpToken, DelimToken, Token}; use syntax::print::pprust; use syntax::source_map::{BytePos, Span}; use syntax::symbol::keywords; use syntax::tokenstream::{Cursor, TokenStream, TokenTree}; use syntax::ThinVec; use syntax::{ast, parse, ptr}; use crate::comment::{ contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses, }; use crate::config::lists::*; use crate::expr::rewrite_array; use crate::lists::{itemize_list, write_list, ListFormatting}; use crate::overflow; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; use crate::source_map::SpanUtils; use crate::spanned::Spanned; use crate::utils::{ format_visibility, indent_next_line, is_empty_line, mk_sp, remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, wrap_str, NodeIdExt, }; use crate::visitor::FmtVisitor; const FORCED_BRACKET_MACROS: &[&str] = &["vec!"]; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum MacroPosition { Item, Statement, Expression, Pat, } #[derive(Debug)] pub enum MacroArg { Expr(ptr::P), Ty(ptr::P), Pat(ptr::P), Item(ptr::P), Keyword(ast::Ident, Span), } impl MacroArg { fn is_item(&self) -> bool { match self { MacroArg::Item(..) => true, _ => false, } } } impl Rewrite for ast::Item { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { let mut visitor = crate::visitor::FmtVisitor::from_context(context); visitor.block_indent = shape.indent; visitor.last_pos = self.span().lo(); visitor.visit_item(self); Some(visitor.buffer.to_owned()) } } impl Rewrite for MacroArg { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { match *self { MacroArg::Expr(ref expr) => expr.rewrite(context, shape), MacroArg::Ty(ref ty) => ty.rewrite(context, shape), MacroArg::Pat(ref pat) => pat.rewrite(context, shape), MacroArg::Item(ref item) => item.rewrite(context, shape), MacroArg::Keyword(ident, _) => Some(ident.to_string()), } } } fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { macro_rules! parse_macro_arg { ($macro_arg:ident, $parser:expr, $f:expr) => { let mut cloned_parser = (*parser).clone(); match $parser(&mut cloned_parser) { Ok(x) => { if parser.sess.span_diagnostic.has_errors() { parser.sess.span_diagnostic.reset_err_count(); } else { // Parsing succeeded. *parser = cloned_parser; return Some(MacroArg::$macro_arg($f(x)?)); } } Err(mut e) => { e.cancel(); parser.sess.span_diagnostic.reset_err_count(); } } }; } parse_macro_arg!( Expr, |parser: &mut parse::parser::Parser<'b>| parser.parse_expr(), |x: ptr::P| Some(x) ); parse_macro_arg!( Ty, |parser: &mut parse::parser::Parser<'b>| parser.parse_ty(), |x: ptr::P| Some(x) ); parse_macro_arg!( Pat, |parser: &mut parse::parser::Parser<'b>| parser.parse_pat(None), |x: ptr::P| Some(x) ); // `parse_item` returns `Option>`. parse_macro_arg!( Item, |parser: &mut parse::parser::Parser<'b>| parser.parse_item(), |x: Option>| x ); None } /// Rewrite macro name without using pretty-printer if possible. fn rewrite_macro_name( context: &RewriteContext<'_>, path: &ast::Path, extra_ident: Option, ) -> String { let name = if path.segments.len() == 1 { // Avoid using pretty-printer in the common case. format!("{}!", rewrite_ident(context, path.segments[0].ident)) } else { format!("{}!", path) }; match extra_ident { Some(ident) if ident != keywords::Invalid.ident() => format!("{} {}", name, ident), _ => name, } } // Use this on failing to format the macro call. fn return_macro_parse_failure_fallback( context: &RewriteContext<'_>, indent: Indent, span: Span, ) -> Option { // Mark this as a failure however we format it context.macro_rewrite_failure.replace(true); // Heuristically determine whether the last line of the macro uses "Block" style // rather than using "Visual" style, or another indentation style. let is_like_block_indent_style = context .snippet(span) .lines() .last() .map(|closing_line| { closing_line.trim().chars().all(|ch| match ch { '}' | ')' | ']' => true, _ => false, }) }) .unwrap_or(false); if is_like_block_indent_style { return trim_left_preserve_layout(context.snippet(span), indent, &context.config); } // Return the snippet unmodified if the macro is not block-like Some(context.snippet(span).to_owned()) } struct InsideMacroGuard<'a> { context: &'a RewriteContext<'a>, is_nested: bool, } impl<'a> InsideMacroGuard<'a> { fn inside_macro_context(context: &'a RewriteContext<'_>) -> InsideMacroGuard<'a> { let is_nested = context.inside_macro.replace(true); InsideMacroGuard { context, is_nested } } } impl<'a> Drop for InsideMacroGuard<'a> { fn drop(&mut self) { self.context.inside_macro.replace(self.is_nested); } } pub fn rewrite_macro( mac: &ast::Mac, extra_ident: Option, context: &RewriteContext<'_>, shape: Shape, position: MacroPosition, ) -> Option { let guard = InsideMacroGuard::inside_macro_context(context); let result = rewrite_macro_inner(mac, extra_ident, context, shape, position, guard.is_nested); if result.is_none() { context.macro_rewrite_failure.replace(true); } result } fn check_keyword<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { for &keyword in RUST_KEYWORDS.iter() { if parser.token.is_keyword(keyword) && parser.look_ahead(1, |t| { *t == Token::Eof || *t == Token::Comma || *t == Token::CloseDelim(DelimToken::NoDelim) }) { let macro_arg = MacroArg::Keyword(keyword.ident(), parser.span); parser.bump(); return Some(macro_arg); } } None } pub fn rewrite_macro_inner( mac: &ast::Mac, extra_ident: Option, context: &RewriteContext<'_>, shape: Shape, position: MacroPosition, is_nested_macro: bool, ) -> Option { if context.config.use_try_shorthand() { if let Some(expr) = convert_try_mac(mac, context) { context.inside_macro.replace(false); return expr.rewrite(context, shape); } } let original_style = macro_style(mac, context); let macro_name = rewrite_macro_name(context, &mac.node.path, extra_ident); let style = if FORCED_BRACKET_MACROS.contains(&¯o_name[..]) && !is_nested_macro { DelimToken::Bracket } else { original_style }; let ts: TokenStream = mac.node.stream(); let has_comment = contains_comment(context.snippet(mac.span)); if ts.is_empty() && !has_comment { return match style { DelimToken::Paren if position == MacroPosition::Item => { Some(format!("{}();", macro_name)) } DelimToken::Paren => Some(format!("{}()", macro_name)), DelimToken::Bracket => Some(format!("{}[]", macro_name)), DelimToken::Brace => Some(format!("{} {{}}", macro_name)), _ => unreachable!(), }; } // Format well-known macros which cannot be parsed as a valid AST. if macro_name == "lazy_static!" && !has_comment { if let success @ Some(..) = format_lazy_static(context, shape, &ts) { return success; } } let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect()); let mut arg_vec = Vec::new(); let mut vec_with_semi = false; let mut trailing_comma = false; if DelimToken::Brace != style { loop { if let Some(arg) = parse_macro_arg(&mut parser) { arg_vec.push(arg); } else if let Some(arg) = check_keyword(&mut parser) { arg_vec.push(arg); } else { return return_macro_parse_failure_fallback(context, shape.indent, mac.span); } match parser.token { Token::Eof => break, Token::Comma => (), Token::Semi => { // Try to parse `vec![expr; expr]` if FORCED_BRACKET_MACROS.contains(&¯o_name[..]) { parser.bump(); if parser.token != Token::Eof { match parse_macro_arg(&mut parser) { Some(arg) => { arg_vec.push(arg); parser.bump(); if parser.token == Token::Eof && arg_vec.len() == 2 { vec_with_semi = true; break; } } None => { return return_macro_parse_failure_fallback( context, shape.indent, mac.span, ); } } } } return return_macro_parse_failure_fallback(context, shape.indent, mac.span); } _ if arg_vec.last().map_or(false, MacroArg::is_item) => continue, _ => return return_macro_parse_failure_fallback(context, shape.indent, mac.span), } parser.bump(); if parser.token == Token::Eof { trailing_comma = true; break; } } } if !arg_vec.is_empty() && arg_vec.iter().all(MacroArg::is_item) { return rewrite_macro_with_items( context, &arg_vec, ¯o_name, shape, style, position, mac.span, ); } match style { DelimToken::Paren => { // Format macro invocation as function call, preserve the trailing // comma because not all macros support them. overflow::rewrite_with_parens( context, ¯o_name, arg_vec.iter(), shape, mac.span, context.config.width_heuristics().fn_call_width, if trailing_comma { Some(SeparatorTactic::Always) } else { Some(SeparatorTactic::Never) }, ) .map(|rw| match position { MacroPosition::Item => format!("{};", rw), _ => rw, }) } DelimToken::Bracket => { // Handle special case: `vec![expr; expr]` if vec_with_semi { let mac_shape = shape.offset_left(macro_name.len())?; // 8 = `vec![]` + `; ` let total_overhead = 8; let nested_shape = mac_shape.block_indent(context.config.tab_spaces()); let lhs = arg_vec[0].rewrite(context, nested_shape)?; let rhs = arg_vec[1].rewrite(context, nested_shape)?; if !lhs.contains('\n') && !rhs.contains('\n') && lhs.len() + rhs.len() + total_overhead <= shape.width { Some(format!("{}[{}; {}]", macro_name, lhs, rhs)) } else { Some(format!( "{}[{}{};{}{}{}]", macro_name, nested_shape.indent.to_string_with_newline(context.config), lhs, nested_shape.indent.to_string_with_newline(context.config), rhs, shape.indent.to_string_with_newline(context.config), )) } } else { // If we are rewriting `vec!` macro or other special macros, // then we can rewrite this as an usual array literal. // Otherwise, we must preserve the original existence of trailing comma. let macro_name = ¯o_name.as_str(); let mut force_trailing_comma = if trailing_comma { Some(SeparatorTactic::Always) } else { Some(SeparatorTactic::Never) }; if FORCED_BRACKET_MACROS.contains(macro_name) && !is_nested_macro { context.inside_macro.replace(false); if context.use_block_indent() { force_trailing_comma = Some(SeparatorTactic::Vertical); }; } let rewrite = rewrite_array( macro_name, arg_vec.iter(), mac.span, context, shape, force_trailing_comma, Some(original_style), )?; let comma = match position { MacroPosition::Item => ";", _ => "", }; Some(format!("{}{}", rewrite, comma)) } } DelimToken::Brace => { // For macro invocations with braces, always put a space between // the `macro_name!` and `{ /* macro_body */ }` but skip modifying // anything in between the braces (for now). let snippet = context.snippet(mac.span); let macro_raw = snippet.split_at(snippet.find('!')? + 1).1.trim_start(); match trim_left_preserve_layout(macro_raw, shape.indent, &context.config) { Some(macro_body) => Some(format!("{} {}", macro_name, macro_body)), None => Some(format!("{} {}", macro_name, macro_raw)), } } _ => unreachable!(), } } pub fn rewrite_macro_def( context: &RewriteContext<'_>, shape: Shape, indent: Indent, def: &ast::MacroDef, ident: ast::Ident, vis: &ast::Visibility, span: Span, ) -> Option { let snippet = Some(remove_trailing_white_spaces(context.snippet(span))); if snippet.as_ref().map_or(true, |s| s.ends_with(';')) { return snippet; } let mut parser = MacroParser::new(def.stream().into_trees()); let parsed_def = match parser.parse() { Some(def) => def, None => return snippet, }; let mut result = if def.legacy { String::from("macro_rules!") } else { format!("{}macro", format_visibility(context, vis)) }; result += " "; result += rewrite_ident(context, ident); let multi_branch_style = def.legacy || parsed_def.branches.len() != 1; let arm_shape = if multi_branch_style { shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config) } else { shape }; let branch_items = itemize_list( context.snippet_provider, parsed_def.branches.iter(), "}", ";", |branch| branch.span.lo(), |branch| branch.span.hi(), |branch| match branch.rewrite(context, arm_shape, multi_branch_style) { Some(v) => Some(v), // if the rewrite returned None because a macro could not be rewritten, then return the // original body None if *context.macro_rewrite_failure.borrow() => { Some(context.snippet(branch.body).trim().to_string()) } None => None, }, context.snippet_provider.span_after(span, "{"), span.hi(), false, ) .collect::>(); let fmt = ListFormatting::new(arm_shape, context.config) .separator(if def.legacy { ";" } else { "" }) .trailing_separator(SeparatorTactic::Always) .preserve_newline(true); if multi_branch_style { result += " {"; result += &arm_shape.indent.to_string_with_newline(context.config); } match write_list(&branch_items, &fmt) { Some(ref s) => result += s, None => return snippet, } if multi_branch_style { result += &indent.to_string_with_newline(context.config); result += "}"; } Some(result) } fn register_metavariable( map: &mut HashMap, result: &mut String, name: &str, dollar_count: usize, ) { let mut new_name = String::new(); let mut old_name = String::new(); old_name.push('$'); for _ in 0..(dollar_count - 1) { new_name.push('$'); old_name.push('$'); } new_name.push('z'); new_name.push_str(&name); old_name.push_str(&name); result.push_str(&new_name); map.insert(old_name, new_name); } // Replaces `$foo` with `zfoo`. We must check for name overlap to ensure we // aren't causing problems. // This should also work for escaped `$` variables, where we leave earlier `$`s. fn replace_names(input: &str) -> Option<(String, HashMap)> { // Each substitution will require five or six extra bytes. let mut result = String::with_capacity(input.len() + 64); let mut substs = HashMap::new(); let mut dollar_count = 0; let mut cur_name = String::new(); for (kind, c) in CharClasses::new(input.chars()) { if kind != FullCodeCharKind::Normal { result.push(c); } else if c == '$' { dollar_count += 1; } else if dollar_count == 0 { result.push(c); } else if !c.is_alphanumeric() && !cur_name.is_empty() { // Terminates a name following one or more dollars. register_metavariable(&mut substs, &mut result, &cur_name, dollar_count); result.push(c); dollar_count = 0; cur_name.clear(); } else if c == '(' && cur_name.is_empty() { // FIXME: Support macro def with repeat. return None; } else if c.is_alphanumeric() || c == '_' { cur_name.push(c); } } if !cur_name.is_empty() { register_metavariable(&mut substs, &mut result, &cur_name, dollar_count); } debug!("replace_names `{}` {:?}", result, substs); Some((result, substs)) } #[derive(Debug, Clone)] enum MacroArgKind { /// e.g., `$x: expr`. MetaVariable(ast::Ident, String), /// e.g., `$($foo: expr),*` Repeat( /// `()`, `[]` or `{}`. DelimToken, /// Inner arguments inside delimiters. Vec, /// Something after the closing delimiter and the repeat token, if available. Option>, /// The repeat token. This could be one of `*`, `+` or `?`. Token, ), /// e.g., `[derive(Debug)]` Delimited(DelimToken, Vec), /// A possible separator. e.g., `,` or `;`. Separator(String, String), /// Other random stuff that does not fit to other kinds. /// e.g., `== foo` in `($x: expr == foo)`. Other(String, String), } fn delim_token_to_str( context: &RewriteContext<'_>, delim_token: DelimToken, shape: Shape, use_multiple_lines: bool, inner_is_empty: bool, ) -> (String, String) { let (lhs, rhs) = match delim_token { DelimToken::Paren => ("(", ")"), DelimToken::Bracket => ("[", "]"), DelimToken::Brace => { if inner_is_empty || use_multiple_lines { ("{", "}") } else { ("{ ", " }") } } DelimToken::NoDelim => ("", ""), }; if use_multiple_lines { let indent_str = shape.indent.to_string_with_newline(context.config); let nested_indent_str = shape .indent .block_indent(context.config) .to_string_with_newline(context.config); ( format!("{}{}", lhs, nested_indent_str), format!("{}{}", indent_str, rhs), ) } else { (lhs.to_owned(), rhs.to_owned()) } } impl MacroArgKind { fn starts_with_brace(&self) -> bool { match *self { MacroArgKind::Repeat(DelimToken::Brace, _, _, _) | MacroArgKind::Delimited(DelimToken::Brace, _) => true, _ => false, } } fn starts_with_dollar(&self) -> bool { match *self { MacroArgKind::Repeat(..) | MacroArgKind::MetaVariable(..) => true, _ => false, } } fn ends_with_space(&self) -> bool { match *self { MacroArgKind::Separator(..) => true, _ => false, } } fn has_meta_var(&self) -> bool { match *self { MacroArgKind::MetaVariable(..) => true, MacroArgKind::Repeat(_, ref args, _, _) => args.iter().any(|a| a.kind.has_meta_var()), _ => false, } } fn rewrite( &self, context: &RewriteContext<'_>, shape: Shape, use_multiple_lines: bool, ) -> Option { let rewrite_delimited_inner = |delim_tok, args| -> Option<(String, String, String)> { let inner = wrap_macro_args(context, args, shape)?; let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, false, inner.is_empty()); if lhs.len() + inner.len() + rhs.len() <= shape.width { return Some((lhs, inner, rhs)); } let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, true, false); let nested_shape = shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config); let inner = wrap_macro_args(context, args, nested_shape)?; Some((lhs, inner, rhs)) }; match *self { MacroArgKind::MetaVariable(ty, ref name) => { Some(format!("${}:{}", name, ty.name.as_str())) } MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => { let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?; let another = another .as_ref() .and_then(|a| a.rewrite(context, shape, use_multiple_lines)) .unwrap_or_else(|| "".to_owned()); let repeat_tok = pprust::token_to_string(tok); Some(format!("${}{}{}{}{}", lhs, inner, rhs, another, repeat_tok)) } MacroArgKind::Delimited(delim_tok, ref args) => { rewrite_delimited_inner(delim_tok, args) .map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)) } MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{}{} ", prefix, sep)), MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{}{}", prefix, inner)), } } } #[derive(Debug, Clone)] struct ParsedMacroArg { kind: MacroArgKind, span: Span, } impl ParsedMacroArg { pub fn rewrite( &self, context: &RewriteContext<'_>, shape: Shape, use_multiple_lines: bool, ) -> Option { self.kind.rewrite(context, shape, use_multiple_lines) } } /// Parses macro arguments on macro def. struct MacroArgParser { /// Either a name of the next metavariable, a separator, or junk. buf: String, /// The start position on the current buffer. lo: BytePos, /// The first token of the current buffer. start_tok: Token, /// `true` if we are parsing a metavariable or a repeat. is_meta_var: bool, /// The position of the last token. hi: BytePos, /// The last token parsed. last_tok: Token, /// Holds the parsed arguments. result: Vec, } fn last_tok(tt: &TokenTree) -> Token { match *tt { TokenTree::Token(_, ref t) => t.clone(), TokenTree::Delimited(_, delim, _) => Token::CloseDelim(delim), } } impl MacroArgParser { pub fn new() -> MacroArgParser { MacroArgParser { lo: BytePos(0), hi: BytePos(0), buf: String::new(), is_meta_var: false, last_tok: Token::Eof, start_tok: Token::Eof, result: vec![], } } fn set_last_tok(&mut self, tok: &TokenTree) { self.hi = tok.span().hi(); self.last_tok = last_tok(tok); } fn add_separator(&mut self) { let prefix = if self.need_space_prefix() { " ".to_owned() } else { "".to_owned() }; self.result.push(ParsedMacroArg { kind: MacroArgKind::Separator(self.buf.clone(), prefix), span: mk_sp(self.lo, self.hi), }); self.buf.clear(); } fn add_other(&mut self) { let prefix = if self.need_space_prefix() { " ".to_owned() } else { "".to_owned() }; self.result.push(ParsedMacroArg { kind: MacroArgKind::Other(self.buf.clone(), prefix), span: mk_sp(self.lo, self.hi), }); self.buf.clear(); } fn add_meta_variable(&mut self, iter: &mut Cursor) -> Option<()> { match iter.next() { Some(TokenTree::Token(sp, Token::Ident(ref ident, _))) => { self.result.push(ParsedMacroArg { kind: MacroArgKind::MetaVariable(*ident, self.buf.clone()), span: mk_sp(self.lo, sp.hi()), }); self.buf.clear(); self.is_meta_var = false; Some(()) } _ => None, } } fn add_delimited(&mut self, inner: Vec, delim: DelimToken, span: Span) { self.result.push(ParsedMacroArg { kind: MacroArgKind::Delimited(delim, inner), span, }); } // $($foo: expr),? fn add_repeat( &mut self, inner: Vec, delim: DelimToken, iter: &mut Cursor, span: Span, ) -> Option<()> { let mut buffer = String::new(); let mut first = false; let mut lo = span.lo(); let mut hi = span.hi(); // Parse '*', '+' or '?. for tok in iter { self.set_last_tok(&tok); if first { first = false; lo = tok.span().lo(); } match tok { TokenTree::Token(_, Token::BinOp(BinOpToken::Plus)) | TokenTree::Token(_, Token::Question) | TokenTree::Token(_, Token::BinOp(BinOpToken::Star)) => { break; } TokenTree::Token(sp, ref t) => { buffer.push_str(&pprust::token_to_string(t)); hi = sp.hi(); } _ => return None, } } // There could be some random stuff between ')' and '*', '+' or '?'. let another = if buffer.trim().is_empty() { None } else { Some(Box::new(ParsedMacroArg { kind: MacroArgKind::Other(buffer, "".to_owned()), span: mk_sp(lo, hi), })) }; self.result.push(ParsedMacroArg { kind: MacroArgKind::Repeat(delim, inner, another, self.last_tok.clone()), span: mk_sp(self.lo, self.hi), }); Some(()) } fn update_buffer(&mut self, lo: BytePos, t: &Token) { if self.buf.is_empty() { self.lo = lo; self.start_tok = t.clone(); } else { let needs_space = match next_space(&self.last_tok) { SpaceState::Ident => ident_like(t), SpaceState::Punctuation => !ident_like(t), SpaceState::Always => true, SpaceState::Never => false, }; if force_space_before(t) || needs_space { self.buf.push(' '); } } self.buf.push_str(&pprust::token_to_string(t)); } fn need_space_prefix(&self) -> bool { if self.result.is_empty() { return false; } let last_arg = self.result.last().unwrap(); if let MacroArgKind::MetaVariable(..) = last_arg.kind { if ident_like(&self.start_tok) { return true; } if self.start_tok == Token::Colon { return true; } } if force_space_before(&self.start_tok) { return true; } false } /// Returns a collection of parsed macro def's arguments. pub fn parse(mut self, tokens: TokenStream) -> Option> { let stream: TokenStream = tokens.into(); let mut iter = stream.trees(); while let Some(tok) = iter.next() { match tok { TokenTree::Token(sp, Token::Dollar) => { // We always want to add a separator before meta variables. if !self.buf.is_empty() { self.add_separator(); } // Start keeping the name of this metavariable in the buffer. self.is_meta_var = true; self.lo = sp.lo(); self.start_tok = Token::Dollar; } TokenTree::Token(_, Token::Colon) if self.is_meta_var => { self.add_meta_variable(&mut iter)?; } TokenTree::Token(sp, ref t) => self.update_buffer(sp.lo(), t), TokenTree::Delimited(delimited_span, delimited, ref tts) => { if !self.buf.is_empty() { if next_space(&self.last_tok) == SpaceState::Always { self.add_separator(); } else { self.add_other(); } } // Parse the stuff inside delimiters. let mut parser = MacroArgParser::new(); parser.lo = delimited_span.open.lo(); let delimited_arg = parser.parse(tts.clone())?; let span = delimited_span.entire(); if self.is_meta_var { self.add_repeat(delimited_arg, delimited, &mut iter, span)?; self.is_meta_var = false; } else { self.add_delimited(delimited_arg, delimited, span); } } } self.set_last_tok(&tok); } // We are left with some stuff in the buffer. Since there is nothing // left to separate, add this as `Other`. if !self.buf.is_empty() { self.add_other(); } Some(self.result) } } fn wrap_macro_args( context: &RewriteContext<'_>, args: &[ParsedMacroArg], shape: Shape, ) -> Option { wrap_macro_args_inner(context, args, shape, false) .or_else(|| wrap_macro_args_inner(context, args, shape, true)) } fn wrap_macro_args_inner( context: &RewriteContext<'_>, args: &[ParsedMacroArg], shape: Shape, use_multiple_lines: bool, ) -> Option { let mut result = String::with_capacity(128); let mut iter = args.iter().peekable(); let indent_str = shape.indent.to_string_with_newline(context.config); while let Some(ref arg) = iter.next() { result.push_str(&arg.rewrite(context, shape, use_multiple_lines)?); if use_multiple_lines && (arg.kind.ends_with_space() || iter.peek().map_or(false, |a| a.kind.has_meta_var())) { if arg.kind.ends_with_space() { result.pop(); } result.push_str(&indent_str); } else if let Some(ref next_arg) = iter.peek() { let space_before_dollar = !arg.kind.ends_with_space() && next_arg.kind.starts_with_dollar(); let space_before_brace = next_arg.kind.starts_with_brace(); if space_before_dollar || space_before_brace { result.push(' '); } } } if !use_multiple_lines && result.len() >= shape.width { None } else { Some(result) } } // This is a bit sketchy. The token rules probably need tweaking, but it works // for some common cases. I hope the basic logic is sufficient. Note that the // meaning of some tokens is a bit different here from usual Rust, e.g., `*` // and `(`/`)` have special meaning. // // We always try and format on one line. // FIXME: Use multi-line when every thing does not fit on one line. fn format_macro_args( context: &RewriteContext<'_>, toks: TokenStream, shape: Shape, ) -> Option { if !context.config.format_macro_matchers() { let token_stream: TokenStream = toks.into(); let span = span_for_token_stream(&token_stream); return Some(match span { Some(span) => context.snippet(span).to_owned(), None => String::new(), }); } let parsed_args = MacroArgParser::new().parse(toks)?; wrap_macro_args(context, &parsed_args, shape) } fn span_for_token_stream(token_stream: &TokenStream) -> Option { token_stream.trees().next().map(|tt| tt.span()) } // We should insert a space if the next token is a: #[derive(Copy, Clone, PartialEq)] enum SpaceState { Never, Punctuation, Ident, // Or ident/literal-like thing. Always, } fn force_space_before(tok: &Token) -> bool { debug!("tok: force_space_before {:?}", tok); match tok { Token::Eq | Token::Lt | Token::Le | Token::EqEq | Token::Ne | Token::Ge | Token::Gt | Token::AndAnd | Token::OrOr | Token::Not | Token::Tilde | Token::BinOpEq(_) | Token::At | Token::RArrow | Token::LArrow | Token::FatArrow | Token::BinOp(_) | Token::Pound | Token::Dollar => true, _ => false, } } fn ident_like(tok: &Token) -> bool { match tok { Token::Ident(..) | Token::Literal(..) | Token::Lifetime(_) => true, _ => false, } } fn next_space(tok: &Token) -> SpaceState { debug!("next_space: {:?}", tok); match tok { Token::Not | Token::BinOp(BinOpToken::And) | Token::Tilde | Token::At | Token::Comma | Token::Dot | Token::DotDot | Token::DotDotDot | Token::DotDotEq | Token::Question => SpaceState::Punctuation, Token::ModSep | Token::Pound | Token::Dollar | Token::OpenDelim(_) | Token::CloseDelim(_) | Token::Whitespace => SpaceState::Never, Token::Literal(..) | Token::Ident(..) | Token::Lifetime(_) => SpaceState::Ident, _ => SpaceState::Always, } } /// Tries to convert a macro use into a short hand try expression. Returns `None` /// when the macro is not an instance of `try!` (or parsing the inner expression /// failed). pub fn convert_try_mac(mac: &ast::Mac, context: &RewriteContext<'_>) -> Option { if &mac.node.path.to_string() == "try" { let ts: TokenStream = mac.node.tts.clone().into(); let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect()); Some(ast::Expr { id: ast::NodeId::root(), // dummy value node: ast::ExprKind::Try(parser.parse_expr().ok()?), span: mac.span, // incorrect span, but shouldn't matter too much attrs: ThinVec::new(), }) } else { None } } fn macro_style(mac: &ast::Mac, context: &RewriteContext<'_>) -> DelimToken { let snippet = context.snippet(mac.span); let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value()); let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value()); let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value()); if paren_pos < bracket_pos && paren_pos < brace_pos { DelimToken::Paren } else if bracket_pos < brace_pos { DelimToken::Bracket } else { DelimToken::Brace } } // A very simple parser that just parses a macros 2.0 definition into its branches. // Currently we do not attempt to parse any further than that. #[derive(new)] struct MacroParser { toks: Cursor, } impl MacroParser { // (`(` ... `)` `=>` `{` ... `}`)* fn parse(&mut self) -> Option { let mut branches = vec![]; while self.toks.look_ahead(1).is_some() { branches.push(self.parse_branch()?); } Some(Macro { branches }) } // `(` ... `)` `=>` `{` ... `}` fn parse_branch(&mut self) -> Option { let tok = self.toks.next()?; let (lo, args_paren_kind) = match tok { TokenTree::Token(..) => return None, TokenTree::Delimited(delimited_span, d, _) => (delimited_span.open.lo(), d), }; let args = tok.joint().into(); match self.toks.next()? { TokenTree::Token(_, Token::FatArrow) => {} _ => return None, } let (mut hi, body, whole_body) = match self.toks.next()? { TokenTree::Token(..) => return None, TokenTree::Delimited(delimited_span, ..) => { let data = delimited_span.entire().data(); ( data.hi, Span::new(data.lo + BytePos(1), data.hi - BytePos(1), data.ctxt), delimited_span.entire(), ) } }; if let Some(TokenTree::Token(sp, Token::Semi)) = self.toks.look_ahead(0) { self.toks.next(); hi = sp.hi(); } Some(MacroBranch { span: mk_sp(lo, hi), args_paren_kind, args, body, whole_body, }) } } // A parsed macros 2.0 macro definition. struct Macro { branches: Vec, } // FIXME: it would be more efficient to use references to the token streams // rather than clone them, if we can make the borrowing work out. struct MacroBranch { span: Span, args_paren_kind: DelimToken, args: TokenStream, body: Span, whole_body: Span, } impl MacroBranch { fn rewrite( &self, context: &RewriteContext<'_>, shape: Shape, multi_branch_style: bool, ) -> Option { // Only attempt to format function-like macros. if self.args_paren_kind != DelimToken::Paren { // FIXME(#1539): implement for non-sugared macros. return None; } // 5 = " => {" let mut result = format_macro_args(context, self.args.clone(), shape.sub_width(5)?)?; if multi_branch_style { result += " =>"; } if !context.config.format_macro_bodies() { result += " "; result += context.snippet(self.whole_body); return Some(result); } // The macro body is the most interesting part. It might end up as various // AST nodes, but also has special variables (e.g, `$foo`) which can't be // parsed as regular Rust code (and note that these can be escaped using // `$$`). We'll try and format like an AST node, but we'll substitute // variables for new names with the same length first. let old_body = context.snippet(self.body).trim(); let (body_str, substs) = replace_names(old_body)?; let has_block_body = old_body.starts_with('{'); let mut config = context.config.clone(); config.set().hide_parse_errors(true); result += " {"; let body_indent = if has_block_body { shape.indent } else { shape.indent.block_indent(&config) }; let new_width = config.max_width() - body_indent.width(); config.set().max_width(new_width); // First try to format as items, then as statements. let new_body_snippet = match crate::format_snippet(&body_str, &config) { Some(new_body) => new_body, None => { let new_width = new_width + config.tab_spaces(); config.set().max_width(new_width); match crate::format_code_block(&body_str, &config) { Some(new_body) => new_body, None => return None, } } }; let new_body = wrap_str( new_body_snippet.snippet.to_string(), config.max_width(), shape, )?; // Indent the body since it is in a block. let indent_str = body_indent.to_string(&config); let mut new_body = LineClasses::new(new_body.trim_end()) .enumerate() .fold( (String::new(), true), |(mut s, need_indent), (i, (kind, ref l))| { if !is_empty_line(l) && need_indent && !new_body_snippet.is_line_non_formatted(i + 1) { s += &indent_str; } (s + l + "\n", indent_next_line(kind, &l, &config)) }, ) .0; // Undo our replacement of macro variables. // FIXME: this could be *much* more efficient. for (old, new) in &substs { if old_body.find(new).is_some() { debug!("rewrite_macro_def: bailing matching variable: `{}`", new); return None; } new_body = new_body.replace(new, old); } if has_block_body { result += new_body.trim(); } else if !new_body.is_empty() { result += "\n"; result += &new_body; result += &shape.indent.to_string(&config); } result += "}"; Some(result) } } /// Format `lazy_static!` from https://crates.io/crates/lazy_static. /// /// # Expected syntax /// /// ```ignore /// lazy_static! { /// [pub] static ref NAME_1: TYPE_1 = EXPR_1; /// [pub] static ref NAME_2: TYPE_2 = EXPR_2; /// ... /// [pub] static ref NAME_N: TYPE_N = EXPR_N; /// } /// ``` fn format_lazy_static( context: &RewriteContext<'_>, shape: Shape, ts: &TokenStream, ) -> Option { let mut result = String::with_capacity(1024); let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect()); let nested_shape = shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config); result.push_str("lazy_static! {"); result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); macro_rules! parse_or { ($method:ident $(,)* $($arg:expr),* $(,)*) => { match parser.$method($($arg,)*) { Ok(val) => { if parser.sess.span_diagnostic.has_errors() { parser.sess.span_diagnostic.reset_err_count(); return None; } else { val } } Err(mut err) => { err.cancel(); parser.sess.span_diagnostic.reset_err_count(); return None; } } } } while parser.token != Token::Eof { // Parse a `lazy_static!` item. let vis = crate::utils::format_visibility(context, &parse_or!(parse_visibility, false)); parser.eat_keyword(keywords::Static); parser.eat_keyword(keywords::Ref); let id = parse_or!(parse_ident); parser.eat(&Token::Colon); let ty = parse_or!(parse_ty); parser.eat(&Token::Eq); let expr = parse_or!(parse_expr); parser.eat(&Token::Semi); // Rewrite as a static item. let mut stmt = String::with_capacity(128); stmt.push_str(&format!( "{}static ref {}: {} =", vis, id, ty.rewrite(context, nested_shape)? )); result.push_str(&crate::expr::rewrite_assign_rhs( context, stmt, &*expr, nested_shape.sub_width(1)?, )?); result.push(';'); if parser.token != Token::Eof { result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); } } result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push('}'); Some(result) } fn rewrite_macro_with_items( context: &RewriteContext<'_>, items: &[MacroArg], macro_name: &str, shape: Shape, style: DelimToken, position: MacroPosition, span: Span, ) -> Option { let (opener, closer) = match style { DelimToken::Paren => ("(", ")"), DelimToken::Bracket => ("[", "]"), DelimToken::Brace => (" {", "}"), _ => return None, }; let trailing_semicolon = match style { DelimToken::Paren | DelimToken::Bracket if position == MacroPosition::Item => ";", _ => "", }; let mut visitor = FmtVisitor::from_context(context); visitor.block_indent = shape.indent.block_indent(context.config); visitor.last_pos = context.snippet_provider.span_after(span, opener.trim()); for item in items { let item = match item { MacroArg::Item(item) => item, _ => return None, }; visitor.visit_item(&item); } let mut result = String::with_capacity(256); result.push_str(¯o_name); result.push_str(opener); result.push_str(&visitor.block_indent.to_string_with_newline(context.config)); result.push_str(visitor.buffer.trim()); result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push_str(closer); result.push_str(trailing_semicolon); Some(result) } const RUST_KEYWORDS: [keywords::Keyword; 60] = [ keywords::PathRoot, keywords::DollarCrate, keywords::Underscore, keywords::As, keywords::Box, keywords::Break, keywords::Const, keywords::Continue, keywords::Crate, keywords::Else, keywords::Enum, keywords::Extern, keywords::False, keywords::Fn, keywords::For, keywords::If, keywords::Impl, keywords::In, keywords::Let, keywords::Loop, keywords::Match, keywords::Mod, keywords::Move, keywords::Mut, keywords::Pub, keywords::Ref, keywords::Return, keywords::SelfLower, keywords::SelfUpper, keywords::Static, keywords::Struct, keywords::Super, keywords::Trait, keywords::True, keywords::Type, keywords::Unsafe, keywords::Use, keywords::Where, keywords::While, keywords::Abstract, keywords::Become, keywords::Do, keywords::Final, keywords::Macro, keywords::Override, keywords::Priv, keywords::Typeof, keywords::Unsized, keywords::Virtual, keywords::Yield, keywords::Dyn, keywords::Async, keywords::Try, keywords::UnderscoreLifetime, keywords::StaticLifetime, keywords::Auto, keywords::Catch, keywords::Default, keywords::Existential, keywords::Union, ];