From 5d973d2e8c1021ca8a7844f5d994c2e2be8b7a07 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Wed, 24 Jan 2018 18:21:01 +0000 Subject: [PATCH] Initial support for macros 1.1 --- src/macros.rs | 156 ++++++++++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 69 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 7df793ca79d..86ac93d19be 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -291,88 +291,103 @@ pub fn rewrite_macro_def( ) -> Option { let snippet = Some(remove_trailing_white_spaces(context.snippet(span))); - if def.legacy { - return snippet; - } - let mut parser = MacroParser::new(def.stream().into_trees()); - let mut parsed_def = match parser.parse() { + let parsed_def = match parser.parse() { Some(def) => def, None => return snippet, }; - // Only attempt to format function-like macros. - if parsed_def.branches.len() != 1 || parsed_def.branches[0].args_paren_kind != DelimToken::Paren - { - // FIXME(#1539): implement for non-sugared macros. - return snippet; - } - - let branch = parsed_def.branches.remove(0); - let args_str = format_macro_args(branch.args)?; - - // 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(branch.body).trim(); - let (body_str, substs) = match replace_names(old_body) { - Some(result) => result, - None => return snippet, + let mut result = if def.legacy { + String::from("macro_rules!") + } else { + format!("{}macro", format_visibility(vis)) }; - // We'll hack the indent below, take this into account when formatting, - let mut config = context.config.clone(); - let new_width = config.max_width() - indent.block_indent(&config).width(); - config.set().max_width(new_width); - config.set().hide_parse_errors(true); + result += " "; + result += &ident.name.as_str(); + result += " {"; - // First try to format as items, then as statements. - let new_body = match ::format_snippet(&body_str, &config) { - Some(new_body) => new_body, - None => match ::format_code_block(&body_str, &config) { - Some(new_body) => new_body, - None => return snippet, - }, - }; + let mac_indent = indent.block_indent(context.config); + let mac_indent_str = mac_indent.to_string(context.config); - // Indent the body since it is in a block. - let indent_str = indent.block_indent(&config).to_string(&config); - let mut new_body = new_body - .lines() - .map(|l| { - if l.is_empty() { - l.to_owned() - } else { - format!("{}{}", indent_str, l) - } - }) - .collect::>() - .join("\n"); - - // 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: `{}` in `{}`", - new, ident - ); + for branch in parsed_def.branches { + // Only attempt to format function-like macros. + if branch.args_paren_kind != DelimToken::Paren { + // FIXME(#1539): implement for non-sugared macros. return snippet; } - new_body = new_body.replace(new, old); + + result += "\n"; + result += &mac_indent_str; + result += "("; + result += &format_macro_args(branch.args)?; + result += ") => {\n"; + + // 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(branch.body).trim(); + let (body_str, substs) = match replace_names(old_body) { + Some(result) => result, + None => return snippet, + }; + + // We'll hack the indent below, take this into account when formatting, + let mut config = context.config.clone(); + let body_indent = mac_indent.block_indent(&config); + let new_width = config.max_width() - body_indent.width(); + config.set().max_width(new_width); + config.set().hide_parse_errors(true); + + // First try to format as items, then as statements. + let new_body = match ::format_snippet(&body_str, &config) { + Some(new_body) => new_body, + None => match ::format_code_block(&body_str, &config) { + Some(new_body) => new_body, + None => return snippet, + }, + }; + + // Indent the body since it is in a block. + let indent_str = body_indent.to_string(&config); + let mut new_body = new_body + .trim_right() + .lines() + .fold(String::new(), |mut s, l| { + if !l.is_empty() { + s += &indent_str; + } + s + l + "\n" + }); + + // 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: `{}` in `{}`", + new, ident + ); + return snippet; + } + new_body = new_body.replace(new, old); + } + + result += &new_body; + + result += &mac_indent_str; + result += "}"; + if def.legacy{ + result += ";"; + } + result += "\n"; } - let result = format!( - "{}macro {}({}) {{\n{}\n{}}}", - format_visibility(vis), - ident, - args_str, - new_body, - indent.to_string(context.config), - ); + result += &indent.to_string(context.config); + result += "}"; Some(result) } @@ -729,6 +744,9 @@ fn parse_branch(&mut self) -> Option { Span::new(data.lo + BytePos(1), data.hi - BytePos(1), data.ctxt) } }; + if let Some(TokenTree::Token(_, Token::Semi)) = self.toks.look_ahead(0) { + self.toks.next(); + } Some(MacroBranch { args, args_paren_kind,