Skip to content

Commit d85e1db

Browse files
authored
Merge pull request #2393 from RReverser/macro_rules
Format stable macro_rules
2 parents 18ba02e + 8691c64 commit d85e1db

File tree

10 files changed

+288
-109
lines changed

10 files changed

+288
-109
lines changed

src/bin/cargo-fmt.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,14 @@ fn execute() -> i32 {
9797
}
9898

9999
macro_rules! print_usage {
100-
($print:ident, $opts:ident, $reason:expr) => ({
100+
($print: ident, $opts: ident, $reason: expr) => {{
101101
let msg = format!("{}\nusage: cargo fmt [options]", $reason);
102102
$print!(
103103
"{}\nThis utility formats all bin and lib files of the current crate using rustfmt. \
104104
Arguments after `--` are passed to rustfmt.",
105105
$opts.usage(&msg)
106106
);
107-
})
107+
}};
108108
}
109109

110110
fn print_usage_to_stdout(opts: &Options, reason: &str) {

src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ macro_rules! is_nightly_channel {
3636
option_env!("CFG_RELEASE_CHANNEL")
3737
.map(|c| c == "nightly")
3838
.unwrap_or(true)
39-
}
39+
};
4040
}
4141

4242
macro_rules! configuration_option_enum{

src/macros.rs

Lines changed: 158 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ use syntax::util::ThinVec;
3333
use codemap::SpanUtils;
3434
use comment::{contains_comment, remove_trailing_white_spaces, FindUncommented};
3535
use expr::{rewrite_array, rewrite_call_inner};
36+
use lists::{itemize_list, write_list, DefinitiveListTactic, ListFormatting, SeparatorPlace,
37+
SeparatorTactic};
3638
use rewrite::{Rewrite, RewriteContext};
3739
use shape::{Indent, Shape};
3840
use utils::{format_visibility, mk_sp};
@@ -101,7 +103,7 @@ fn parse_macro_arg(parser: &mut Parser) -> Option<MacroArg> {
101103
parser.sess.span_diagnostic.reset_err_count();
102104
}
103105
}
104-
}
106+
};
105107
}
106108

107109
parse_macro_arg!(Expr, parse_expr);
@@ -283,6 +285,7 @@ pub fn rewrite_macro(
283285

284286
pub fn rewrite_macro_def(
285287
context: &RewriteContext,
288+
shape: Shape,
286289
indent: Indent,
287290
def: &ast::MacroDef,
288291
ident: ast::Ident,
@@ -291,88 +294,67 @@ pub fn rewrite_macro_def(
291294
) -> Option<String> {
292295
let snippet = Some(remove_trailing_white_spaces(context.snippet(span)));
293296

294-
if def.legacy {
295-
return snippet;
296-
}
297-
298297
let mut parser = MacroParser::new(def.stream().into_trees());
299-
let mut parsed_def = match parser.parse() {
298+
let parsed_def = match parser.parse() {
300299
Some(def) => def,
301300
None => return snippet,
302301
};
303302

304-
// Only attempt to format function-like macros.
305-
if parsed_def.branches.len() != 1 || parsed_def.branches[0].args_paren_kind != DelimToken::Paren
306-
{
307-
// FIXME(#1539): implement for non-sugared macros.
308-
return snippet;
309-
}
303+
let mut result = if def.legacy {
304+
String::from("macro_rules!")
305+
} else {
306+
format!("{}macro", format_visibility(vis))
307+
};
310308

311-
let branch = parsed_def.branches.remove(0);
312-
let args_str = format_macro_args(branch.args)?;
309+
result += " ";
310+
result += &ident.name.as_str();
313311

314-
// The macro body is the most interesting part. It might end up as various
315-
// AST nodes, but also has special variables (e.g, `$foo`) which can't be
316-
// parsed as regular Rust code (and note that these can be escaped using
317-
// `$$`). We'll try and format like an AST node, but we'll substitute
318-
// variables for new names with the same length first.
312+
let multi_branch_style = def.legacy || parsed_def.branches.len() != 1;
319313

320-
let old_body = context.snippet(branch.body).trim();
321-
let (body_str, substs) = match replace_names(old_body) {
322-
Some(result) => result,
323-
None => return snippet,
314+
let arm_shape = if multi_branch_style {
315+
shape
316+
.block_indent(context.config.tab_spaces())
317+
.with_max_width(context.config)
318+
} else {
319+
shape
324320
};
325321

326-
// We'll hack the indent below, take this into account when formatting,
327-
let mut config = context.config.clone();
328-
let new_width = config.max_width() - indent.block_indent(&config).width();
329-
config.set().max_width(new_width);
330-
config.set().hide_parse_errors(true);
331-
332-
// First try to format as items, then as statements.
333-
let new_body = match ::format_snippet(&body_str, &config) {
334-
Some(new_body) => new_body,
335-
None => match ::format_code_block(&body_str, &config) {
336-
Some(new_body) => new_body,
337-
None => return snippet,
338-
},
322+
let branch_items = itemize_list(
323+
context.codemap,
324+
parsed_def.branches.iter(),
325+
"}",
326+
";",
327+
|branch| branch.span.lo(),
328+
|branch| branch.span.hi(),
329+
|branch| branch.rewrite(context, arm_shape, multi_branch_style),
330+
context.codemap.span_after(span, "{"),
331+
span.hi(),
332+
false,
333+
).collect::<Vec<_>>();
334+
335+
let fmt = ListFormatting {
336+
tactic: DefinitiveListTactic::Vertical,
337+
separator: if def.legacy { ";" } else { "" },
338+
trailing_separator: SeparatorTactic::Always,
339+
separator_place: SeparatorPlace::Back,
340+
shape: arm_shape,
341+
ends_with_newline: true,
342+
preserve_newline: true,
343+
config: context.config,
339344
};
340345

341-
// Indent the body since it is in a block.
342-
let indent_str = indent.block_indent(&config).to_string(&config);
343-
let mut new_body = new_body
344-
.lines()
345-
.map(|l| {
346-
if l.is_empty() {
347-
l.to_owned()
348-
} else {
349-
format!("{}{}", indent_str, l)
350-
}
351-
})
352-
.collect::<Vec<_>>()
353-
.join("\n");
354-
355-
// Undo our replacement of macro variables.
356-
// FIXME: this could be *much* more efficient.
357-
for (old, new) in &substs {
358-
if old_body.find(new).is_some() {
359-
debug!(
360-
"rewrite_macro_def: bailing matching variable: `{}` in `{}`",
361-
new, ident
362-
);
363-
return snippet;
364-
}
365-
new_body = new_body.replace(new, old);
346+
if multi_branch_style {
347+
result += " {\n";
348+
result += &arm_shape.indent.to_string(context.config);
366349
}
367350

368-
let result = format!(
369-
"{}macro {}({}) {{\n{}\n{}}}",
370-
format_visibility(vis),
371-
ident,
372-
args_str,
373-
new_body,
374-
indent.to_string(context.config),
375-
);
351+
result += write_list(&branch_items, &fmt)?.as_str();
352+
353+
if multi_branch_style {
354+
result += "\n";
355+
result += &indent.to_string(context.config);
356+
result += "}";
357+
}
376358

377359
Some(result)
378360
}
@@ -714,24 +696,34 @@ impl MacroParser {
714696

715697
// `(` ... `)` `=>` `{` ... `}`
716698
fn parse_branch(&mut self) -> Option<MacroBranch> {
717-
let (args_paren_kind, args) = match self.toks.next()? {
699+
let tok = self.toks.next()?;
700+
let (lo, args_paren_kind) = match tok {
718701
TokenTree::Token(..) => return None,
719-
TokenTree::Delimited(_, ref d) => (d.delim, d.tts.clone()),
702+
TokenTree::Delimited(sp, ref d) => (sp.lo(), d.delim),
720703
};
704+
let args = tok.joint().into();
721705
match self.toks.next()? {
722706
TokenTree::Token(_, Token::FatArrow) => {}
723707
_ => return None,
724708
}
725-
let body = match self.toks.next()? {
709+
let (mut hi, body) = match self.toks.next()? {
726710
TokenTree::Token(..) => return None,
727711
TokenTree::Delimited(sp, _) => {
728712
let data = sp.data();
729-
Span::new(data.lo + BytePos(1), data.hi - BytePos(1), data.ctxt)
713+
(
714+
data.hi,
715+
Span::new(data.lo + BytePos(1), data.hi - BytePos(1), data.ctxt),
716+
)
730717
}
731718
};
719+
if let Some(TokenTree::Token(sp, Token::Semi)) = self.toks.look_ahead(0) {
720+
self.toks.next();
721+
hi = sp.hi();
722+
}
732723
Some(MacroBranch {
733-
args,
724+
span: mk_sp(lo, hi),
734725
args_paren_kind,
726+
args,
735727
body,
736728
})
737729
}
@@ -745,11 +737,102 @@ struct Macro {
745737
// FIXME: it would be more efficient to use references to the token streams
746738
// rather than clone them, if we can make the borrowing work out.
747739
struct MacroBranch {
748-
args: ThinTokenStream,
740+
span: Span,
749741
args_paren_kind: DelimToken,
742+
args: ThinTokenStream,
750743
body: Span,
751744
}
752745

746+
impl MacroBranch {
747+
fn rewrite(
748+
&self,
749+
context: &RewriteContext,
750+
shape: Shape,
751+
multi_branch_style: bool,
752+
) -> Option<String> {
753+
// Only attempt to format function-like macros.
754+
if self.args_paren_kind != DelimToken::Paren {
755+
// FIXME(#1539): implement for non-sugared macros.
756+
return None;
757+
}
758+
759+
let mut result = format_macro_args(self.args.clone())?;
760+
761+
if multi_branch_style {
762+
result += " =>";
763+
}
764+
765+
// The macro body is the most interesting part. It might end up as various
766+
// AST nodes, but also has special variables (e.g, `$foo`) which can't be
767+
// parsed as regular Rust code (and note that these can be escaped using
768+
// `$$`). We'll try and format like an AST node, but we'll substitute
769+
// variables for new names with the same length first.
770+
771+
let old_body = context.snippet(self.body).trim();
772+
let (body_str, substs) = replace_names(old_body)?;
773+
774+
let mut config = context.config.clone();
775+
config.set().hide_parse_errors(true);
776+
777+
result += " {";
778+
779+
let has_block_body = old_body.starts_with('{');
780+
781+
let body_indent = if has_block_body {
782+
shape.indent
783+
} else {
784+
// We'll hack the indent below, take this into account when formatting,
785+
let body_indent = shape.indent.block_indent(&config);
786+
let new_width = config.max_width() - body_indent.width();
787+
config.set().max_width(new_width);
788+
body_indent
789+
};
790+
791+
// First try to format as items, then as statements.
792+
let new_body = match ::format_snippet(&body_str, &config) {
793+
Some(new_body) => new_body,
794+
None => match ::format_code_block(&body_str, &config) {
795+
Some(new_body) => new_body,
796+
None => return None,
797+
},
798+
};
799+
800+
// Indent the body since it is in a block.
801+
let indent_str = body_indent.to_string(&config);
802+
let mut new_body = new_body
803+
.trim_right()
804+
.lines()
805+
.fold(String::new(), |mut s, l| {
806+
if !l.is_empty() {
807+
s += &indent_str;
808+
}
809+
s + l + "\n"
810+
});
811+
812+
// Undo our replacement of macro variables.
813+
// FIXME: this could be *much* more efficient.
814+
for (old, new) in &substs {
815+
if old_body.find(new).is_some() {
816+
debug!("rewrite_macro_def: bailing matching variable: `{}`", new);
817+
return None;
818+
}
819+
new_body = new_body.replace(new, old);
820+
}
821+
822+
if has_block_body {
823+
result += new_body.trim();
824+
} else if !new_body.is_empty() {
825+
result += "\n";
826+
result += &new_body;
827+
result += &shape.indent.to_string(&config);
828+
}
829+
830+
result += "}";
831+
832+
Some(result)
833+
}
834+
}
835+
753836
#[cfg(test)]
754837
mod test {
755838
use super::*;

src/spanned.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,30 @@ pub trait Spanned {
2020
}
2121

2222
macro_rules! span_with_attrs_lo_hi {
23-
($this:ident, $lo:expr, $hi:expr) => {
24-
{
25-
let attrs = outer_attributes(&$this.attrs);
26-
if attrs.is_empty() {
27-
mk_sp($lo, $hi)
28-
} else {
29-
mk_sp(attrs[0].span.lo(), $hi)
30-
}
23+
($this: ident, $lo: expr, $hi: expr) => {{
24+
let attrs = outer_attributes(&$this.attrs);
25+
if attrs.is_empty() {
26+
mk_sp($lo, $hi)
27+
} else {
28+
mk_sp(attrs[0].span.lo(), $hi)
3129
}
32-
}
30+
}};
3331
}
3432

3533
macro_rules! span_with_attrs {
36-
($this:ident) => {
34+
($this: ident) => {
3735
span_with_attrs_lo_hi!($this, $this.span.lo(), $this.span.hi())
38-
}
36+
};
3937
}
4038

4139
macro_rules! implement_spanned {
42-
($this:ty) => {
40+
($this: ty) => {
4341
impl Spanned for $this {
4442
fn span(&self) -> Span {
4543
span_with_attrs!(self)
4644
}
4745
}
48-
}
46+
};
4947
}
5048

5149
// Implement `Spanned` for structs with `attrs` field.

0 commit comments

Comments
 (0)