1use crate::algorithm::Printer;
2use crate::path::PathKind;
3use crate::token::Token;
4use crate::INDENT;
5use proc_macro2::{Delimiter, Spacing, TokenStream};
6use syn::{Ident, Macro, MacroDelimiter};
7
8impl Printer {
9 pub fn mac(&mut self, mac: &Macro, ident: Option<&Ident>, semicolon: bool) {
10 if mac.path.is_ident("macro_rules") {
11 if let Some(ident) = ident {
12 self.macro_rules(ident, &mac.tokens);
13 return;
14 }
15 }
16 #[cfg(feature = "verbatim")]
17 if ident.is_none() && self.standard_library_macro(mac, semicolon) {
18 return;
19 }
20 self.path(&mac.path, PathKind::Simple);
21 self.word("!");
22 if let Some(ident) = ident {
23 self.nbsp();
24 self.ident(ident);
25 }
26 let (open, close, delimiter_break) = match mac.delimiter {
27 MacroDelimiter::Paren(_) => ("(", ")", Self::zerobreak as fn(&mut Self)),
28 MacroDelimiter::Brace(_) => (" {", "}", Self::hardbreak as fn(&mut Self)),
29 MacroDelimiter::Bracket(_) => ("[", "]", Self::zerobreak as fn(&mut Self)),
30 };
31 self.word(open);
32 if !mac.tokens.is_empty() {
33 self.cbox(INDENT);
34 delimiter_break(self);
35 self.ibox(0);
36 self.macro_rules_tokens(mac.tokens.clone(), false);
37 self.end();
38 delimiter_break(self);
39 self.offset(-INDENT);
40 self.end();
41 }
42 self.word(close);
43 if semicolon {
44 self.word(";");
45 }
46 }
47
48 fn macro_rules(&mut self, name: &Ident, rules: &TokenStream) {
49 enum State {
50 Start,
51 Matcher,
52 Equal,
53 Greater,
54 Expander,
55 }
56
57 use State::*;
58
59 self.word("macro_rules! ");
60 self.ident(name);
61 self.word(" {");
62 self.cbox(INDENT);
63 self.hardbreak_if_nonempty();
64 let mut state = State::Start;
65 for tt in rules.clone() {
66 let token = Token::from(tt);
67 match (state, token) {
68 (Start, Token::Group(delimiter, stream)) => {
69 self.delimiter_open(delimiter);
70 if !stream.is_empty() {
71 self.cbox(INDENT);
72 self.zerobreak();
73 self.ibox(0);
74 self.macro_rules_tokens(stream, true);
75 self.end();
76 self.zerobreak();
77 self.offset(-INDENT);
78 self.end();
79 }
80 self.delimiter_close(delimiter);
81 state = Matcher;
82 }
83 (Matcher, Token::Punct('=', Spacing::Joint)) => {
84 self.word(" =");
85 state = Equal;
86 }
87 (Equal, Token::Punct('>', Spacing::Alone)) => {
88 self.word(">");
89 state = Greater;
90 }
91 (Greater, Token::Group(_delimiter, stream)) => {
92 self.word(" {");
93 self.neverbreak();
94 if !stream.is_empty() {
95 self.cbox(INDENT);
96 self.hardbreak();
97 self.ibox(0);
98 self.macro_rules_tokens(stream, false);
99 self.end();
100 self.hardbreak();
101 self.offset(-INDENT);
102 self.end();
103 }
104 self.word("}");
105 state = Expander;
106 }
107 (Expander, Token::Punct(';', Spacing::Alone)) => {
108 self.word(";");
109 self.hardbreak();
110 state = Start;
111 }
112 _ => {
::core::panicking::panic_fmt(format_args!("not implemented: {0}",
format_args!("bad macro_rules syntax")));
}unimplemented!("bad macro_rules syntax"),
113 }
114 }
115 match state {
116 Start => {}
117 Expander => {
118 self.word(";");
119 self.hardbreak();
120 }
121 _ => self.hardbreak(),
122 }
123 self.offset(-INDENT);
124 self.end();
125 self.word("}");
126 }
127
128 pub fn macro_rules_tokens(&mut self, stream: TokenStream, matcher: bool) {
129 #[derive(#[automatically_derived]
impl ::core::cmp::PartialEq for State {
#[inline]
fn eq(&self, other: &State) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq)]
130 enum State {
131 Start,
132 Dollar,
133 DollarCrate,
134 DollarIdent,
135 DollarIdentColon,
136 DollarParen,
137 DollarParenSep,
138 Pound,
139 PoundBang,
140 Dot,
141 Colon,
142 Colon2,
143 Ident,
144 IdentBang,
145 Delim,
146 Other,
147 Amp,
148 Pipe,
149 PipeParam,
150 }
151
152 use State::*;
153
154 let mut state = Start;
155 let mut previous_is_joint = true;
156 for tt in stream {
157 let token = Token::from(tt);
158 let (needs_space, next_state) = match (&state, &token) {
159 (Dollar, Token::Ident(_)) if matcher => (false, DollarIdent),
160 (Dollar, Token::Ident(ident)) if ident == "crate" => (false, DollarCrate),
161 (Dollar, Token::Ident(_)) => (false, Other),
162 (DollarIdent, Token::Punct(':', Spacing::Alone)) => (false, DollarIdentColon),
163 (DollarIdentColon, Token::Ident(_)) => (false, Other),
164 (DollarParen, Token::Punct('+' | '*' | '?', Spacing::Alone)) => (false, Other),
165 (DollarParen, Token::Ident(_) | Token::Literal(_)) => (false, DollarParenSep),
166 (DollarParen, Token::Punct(_, Spacing::Joint)) => (false, DollarParen),
167 (DollarParen, Token::Punct(_, Spacing::Alone)) => (false, DollarParenSep),
168 (DollarParenSep, Token::Punct('+' | '*', _)) => (false, Other),
169 (Pound, Token::Punct('!', _)) => (false, PoundBang),
170 (Dollar, Token::Group(Delimiter::Parenthesis, _)) => (false, DollarParen),
171 (Pound | PoundBang, Token::Group(Delimiter::Bracket, _)) => (false, Other),
172 (Ident, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => {
173 (false, Delim)
174 }
175 (Ident, Token::Punct('!', Spacing::Alone)) => (false, IdentBang),
176 (IdentBang, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => {
177 (false, Other)
178 }
179 (Colon, Token::Punct(':', _)) => (false, Colon2),
180 (Amp, Token::Ident(_)) => (false, Ident),
182 (Start | Other | Amp, Token::Punct('|', Spacing::Alone)) => {
184 (state != Start, Pipe)
185 }
186 (Amp, _) => (false, Other),
187 (Pipe, Token::Punct('|', _)) => (false, Other),
189 (Pipe, Token::Ident(_)) => (false, PipeParam),
190 (Pipe, Token::Punct('&', _)) => (false, Pipe),
191 (Pipe, _) => (false, PipeParam),
192 (PipeParam, Token::Punct('|', _)) => (false, Other),
194 (PipeParam, Token::Punct(',', _)) => (false, Pipe),
195 (Colon2, Token::Punct('<', _)) => (false, Other),
197 (_, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => (true, Delim),
198 (_, Token::Group(Delimiter::Brace | Delimiter::None, _)) => (true, Other),
199 (_, Token::Ident(ident)) if !is_keyword(ident) => {
200 (state != Dot && state != Colon2, Ident)
201 }
202 (_, Token::Literal(lit)) if lit.to_string().ends_with('.') => (state != Dot, Other),
203 (_, Token::Literal(_)) => (state != Dot, Ident),
204 (_, Token::Punct(',' | ';', _)) => (false, Other),
205 (_, Token::Punct('.', _)) if !matcher => (state != Ident && state != Delim, Dot),
206 (_, Token::Punct(':', Spacing::Joint)) => {
207 (state != Ident && state != DollarCrate, Colon)
208 }
209 (_, Token::Punct('$', _)) => (true, Dollar),
210 (_, Token::Punct('#', _)) => (true, Pound),
211 (_, Token::Punct('&', _)) => (true, Amp),
212 (_, _) => (true, Other),
213 };
214 if !previous_is_joint {
215 if needs_space {
216 self.space();
217 } else if let Token::Punct('.', _) = token {
218 self.zerobreak();
219 }
220 }
221 previous_is_joint = match token {
222 Token::Punct(_, Spacing::Joint) | Token::Punct('$', _) => true,
223 _ => false,
224 };
225 self.single_token(
226 token,
227 if matcher {
228 |printer, stream| printer.macro_rules_tokens(stream, true)
229 } else {
230 |printer, stream| printer.macro_rules_tokens(stream, false)
231 },
232 );
233 state = next_state;
234 }
235 }
236}
237
238pub(crate) fn requires_semi(delimiter: &MacroDelimiter) -> bool {
239 match delimiter {
240 MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => true,
241 MacroDelimiter::Brace(_) => false,
242 }
243}
244
245fn is_keyword(ident: &Ident) -> bool {
246 match ident.to_string().as_str() {
247 "as" | "async" | "await" | "box" | "break" | "const" | "continue" | "crate" | "dyn"
248 | "else" | "enum" | "extern" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop"
249 | "macro" | "match" | "mod" | "move" | "mut" | "pub" | "ref" | "return" | "static"
250 | "struct" | "trait" | "type" | "unsafe" | "use" | "where" | "while" | "yield" => true,
251 _ => false,
252 }
253}
254
255#[cfg(feature = "verbatim")]
256mod standard_library {
257 use crate::algorithm::Printer;
258 use crate::expr;
259 use crate::fixup::FixupContext;
260 use crate::iter::IterDelimited;
261 use crate::path::PathKind;
262 use crate::INDENT;
263 use syn::ext::IdentExt;
264 use syn::parse::{Parse, ParseStream, Parser, Result};
265 use syn::punctuated::Punctuated;
266 use syn::{
267 parenthesized, token, Attribute, Expr, ExprAssign, ExprPath, Ident, Lit, Macro, Pat, Path,
268 Token, Type, Visibility,
269 };
270
271 enum KnownMacro {
272 Expr(Expr),
273 Exprs(Vec<Expr>),
274 Cfg(Cfg),
275 Matches(Matches),
276 ThreadLocal(Vec<ThreadLocal>),
277 VecArray(Punctuated<Expr, Token![,]>),
278 VecRepeat { elem: Expr, n: Expr },
279 }
280
281 enum Cfg {
282 Eq(Ident, Option<Lit>),
283 Call(Ident, Vec<Cfg>),
284 }
285
286 struct Matches {
287 expression: Expr,
288 pattern: Pat,
289 guard: Option<Expr>,
290 }
291
292 struct ThreadLocal {
293 attrs: Vec<Attribute>,
294 vis: Visibility,
295 name: Ident,
296 ty: Type,
297 init: Expr,
298 }
299
300 struct FormatArgs {
301 format_string: Expr,
302 args: Vec<Expr>,
303 }
304
305 impl Parse for FormatArgs {
306 fn parse(input: ParseStream) -> Result<Self> {
307 let format_string: Expr = input.parse()?;
308
309 let mut args = Vec::new();
310 while !input.is_empty() {
311 input.parse::<Token![,]>()?;
312 if input.is_empty() {
313 break;
314 }
315 let arg = if input.peek(Ident::peek_any)
316 && input.peek2(Token![=])
317 && !input.peek2(Token![==])
318 {
319 let key = input.call(Ident::parse_any)?;
320 let eq_token: Token![=] = input.parse()?;
321 let value: Expr = input.parse()?;
322 Expr::Assign(ExprAssign {
323 attrs: Vec::new(),
324 left: Box::new(Expr::Path(ExprPath {
325 attrs: Vec::new(),
326 qself: None,
327 path: Path::from(key),
328 })),
329 eq_token,
330 right: Box::new(value),
331 })
332 } else {
333 input.parse()?
334 };
335 args.push(arg);
336 }
337
338 Ok(FormatArgs {
339 format_string,
340 args,
341 })
342 }
343 }
344
345 impl KnownMacro {
346 fn parse_expr(input: ParseStream) -> Result<Self> {
347 let expr: Expr = input.parse()?;
348 Ok(KnownMacro::Expr(expr))
349 }
350
351 fn parse_expr_comma(input: ParseStream) -> Result<Self> {
352 let expr: Expr = input.parse()?;
353 input.parse::<Option<Token![,]>>()?;
354 Ok(KnownMacro::Exprs(vec![expr]))
355 }
356
357 fn parse_exprs(input: ParseStream) -> Result<Self> {
358 let exprs = input.parse_terminated(Expr::parse, Token![,])?;
359 Ok(KnownMacro::Exprs(Vec::from_iter(exprs)))
360 }
361
362 fn parse_assert(input: ParseStream) -> Result<Self> {
363 let mut exprs = Vec::new();
364 let cond: Expr = input.parse()?;
365 exprs.push(cond);
366 if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
367 let format_args: FormatArgs = input.parse()?;
368 exprs.push(format_args.format_string);
369 exprs.extend(format_args.args);
370 }
371 Ok(KnownMacro::Exprs(exprs))
372 }
373
374 fn parse_assert_cmp(input: ParseStream) -> Result<Self> {
375 let mut exprs = Vec::new();
376 let left: Expr = input.parse()?;
377 exprs.push(left);
378 input.parse::<Token![,]>()?;
379 let right: Expr = input.parse()?;
380 exprs.push(right);
381 if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
382 let format_args: FormatArgs = input.parse()?;
383 exprs.push(format_args.format_string);
384 exprs.extend(format_args.args);
385 }
386 Ok(KnownMacro::Exprs(exprs))
387 }
388
389 fn parse_cfg(input: ParseStream) -> Result<Self> {
390 fn parse_single(input: ParseStream) -> Result<Cfg> {
391 let ident: Ident = input.parse()?;
392 if input.peek(token::Paren) && (ident == "all" || ident == "any") {
393 let content;
394 parenthesized!(content in input);
395 let list = content.call(parse_multiple)?;
396 Ok(Cfg::Call(ident, list))
397 } else if input.peek(token::Paren) && ident == "not" {
398 let content;
399 parenthesized!(content in input);
400 let cfg = content.call(parse_single)?;
401 content.parse::<Option<Token![,]>>()?;
402 Ok(Cfg::Call(ident, vec![cfg]))
403 } else if input.peek(Token![=]) {
404 input.parse::<Token![=]>()?;
405 let string: Lit = input.parse()?;
406 Ok(Cfg::Eq(ident, Some(string)))
407 } else {
408 Ok(Cfg::Eq(ident, None))
409 }
410 }
411
412 fn parse_multiple(input: ParseStream) -> Result<Vec<Cfg>> {
413 let mut vec = Vec::new();
414 while !input.is_empty() {
415 let cfg = input.call(parse_single)?;
416 vec.push(cfg);
417 if input.is_empty() {
418 break;
419 }
420 input.parse::<Token![,]>()?;
421 }
422 Ok(vec)
423 }
424
425 let cfg = input.call(parse_single)?;
426 input.parse::<Option<Token![,]>>()?;
427 Ok(KnownMacro::Cfg(cfg))
428 }
429
430 fn parse_env(input: ParseStream) -> Result<Self> {
431 let mut exprs = Vec::new();
432 let name: Expr = input.parse()?;
433 exprs.push(name);
434 if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
435 let error_msg: Expr = input.parse()?;
436 exprs.push(error_msg);
437 input.parse::<Option<Token![,]>>()?;
438 }
439 Ok(KnownMacro::Exprs(exprs))
440 }
441
442 fn parse_format_args(input: ParseStream) -> Result<Self> {
443 let format_args: FormatArgs = input.parse()?;
444 let mut exprs = format_args.args;
445 exprs.insert(0, format_args.format_string);
446 Ok(KnownMacro::Exprs(exprs))
447 }
448
449 fn parse_matches(input: ParseStream) -> Result<Self> {
450 let expression: Expr = input.parse()?;
451 input.parse::<Token![,]>()?;
452 let pattern = input.call(Pat::parse_multi_with_leading_vert)?;
453 let guard = if input.parse::<Option<Token![if]>>()?.is_some() {
454 Some(input.parse()?)
455 } else {
456 None
457 };
458 input.parse::<Option<Token![,]>>()?;
459 Ok(KnownMacro::Matches(Matches {
460 expression,
461 pattern,
462 guard,
463 }))
464 }
465
466 fn parse_thread_local(input: ParseStream) -> Result<Self> {
467 let mut items = Vec::new();
468 while !input.is_empty() {
469 let attrs = input.call(Attribute::parse_outer)?;
470 let vis: Visibility = input.parse()?;
471 input.parse::<Token![static]>()?;
472 let name: Ident = input.parse()?;
473 input.parse::<Token![:]>()?;
474 let ty: Type = input.parse()?;
475 input.parse::<Token![=]>()?;
476 let init: Expr = input.parse()?;
477 if input.is_empty() {
478 break;
479 }
480 input.parse::<Token![;]>()?;
481 items.push(ThreadLocal {
482 attrs,
483 vis,
484 name,
485 ty,
486 init,
487 });
488 }
489 Ok(KnownMacro::ThreadLocal(items))
490 }
491
492 fn parse_vec(input: ParseStream) -> Result<Self> {
493 if input.is_empty() {
494 return Ok(KnownMacro::VecArray(Punctuated::new()));
495 }
496 let first: Expr = input.parse()?;
497 if input.parse::<Option<Token![;]>>()?.is_some() {
498 let len: Expr = input.parse()?;
499 Ok(KnownMacro::VecRepeat {
500 elem: first,
501 n: len,
502 })
503 } else {
504 let mut vec = Punctuated::new();
505 vec.push_value(first);
506 while !input.is_empty() {
507 let comma: Token![,] = input.parse()?;
508 vec.push_punct(comma);
509 if input.is_empty() {
510 break;
511 }
512 let next: Expr = input.parse()?;
513 vec.push_value(next);
514 }
515 Ok(KnownMacro::VecArray(vec))
516 }
517 }
518
519 fn parse_write(input: ParseStream) -> Result<Self> {
520 let mut exprs = Vec::new();
521 let dst: Expr = input.parse()?;
522 exprs.push(dst);
523 input.parse::<Token![,]>()?;
524 let format_args: FormatArgs = input.parse()?;
525 exprs.push(format_args.format_string);
526 exprs.extend(format_args.args);
527 Ok(KnownMacro::Exprs(exprs))
528 }
529
530 fn parse_writeln(input: ParseStream) -> Result<Self> {
531 let mut exprs = Vec::new();
532 let dst: Expr = input.parse()?;
533 exprs.push(dst);
534 if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
535 let format_args: FormatArgs = input.parse()?;
536 exprs.push(format_args.format_string);
537 exprs.extend(format_args.args);
538 }
539 Ok(KnownMacro::Exprs(exprs))
540 }
541 }
542
543 impl Printer {
544 pub fn standard_library_macro(&mut self, mac: &Macro, mut semicolon: bool) -> bool {
545 let name = mac.path.segments.last().unwrap().ident.to_string();
546 let parser = match name.as_str() {
547 "addr_of" | "addr_of_mut" => KnownMacro::parse_expr,
548 "assert" | "debug_assert" => KnownMacro::parse_assert,
549 "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne" => {
550 KnownMacro::parse_assert_cmp
551 }
552 "cfg" => KnownMacro::parse_cfg,
553 "compile_error" | "include" | "include_bytes" | "include_str" | "option_env" => {
554 KnownMacro::parse_expr_comma
555 }
556 "concat" | "concat_bytes" | "dbg" => KnownMacro::parse_exprs,
557 "const_format_args" | "eprint" | "eprintln" | "format" | "format_args"
558 | "format_args_nl" | "panic" | "print" | "println" | "todo" | "unimplemented"
559 | "unreachable" => KnownMacro::parse_format_args,
560 "env" => KnownMacro::parse_env,
561 "matches" => KnownMacro::parse_matches,
562 "thread_local" => KnownMacro::parse_thread_local,
563 "vec" => KnownMacro::parse_vec,
564 "write" => KnownMacro::parse_write,
565 "writeln" => KnownMacro::parse_writeln,
566 _ => return false,
567 };
568
569 let Ok(known_macro) = parser.parse2(mac.tokens.clone()) else {
570 return false;
571 };
572
573 self.path(&mac.path, PathKind::Simple);
574 self.word("!");
575
576 match &known_macro {
577 KnownMacro::Expr(expr) => {
578 self.word("(");
579 self.cbox(INDENT);
580 self.zerobreak();
581 self.expr(expr, FixupContext::NONE);
582 self.zerobreak();
583 self.offset(-INDENT);
584 self.end();
585 self.word(")");
586 }
587 KnownMacro::Exprs(exprs) => {
588 self.word("(");
589 self.cbox(INDENT);
590 self.zerobreak();
591 for elem in exprs.iter().delimited() {
592 self.expr(&elem, FixupContext::NONE);
593 self.trailing_comma(elem.is_last);
594 }
595 self.offset(-INDENT);
596 self.end();
597 self.word(")");
598 }
599 KnownMacro::Cfg(cfg) => {
600 self.word("(");
601 self.cfg(cfg);
602 self.word(")");
603 }
604 KnownMacro::Matches(matches) => {
605 self.word("(");
606 self.cbox(INDENT);
607 self.zerobreak();
608 self.expr(&matches.expression, FixupContext::NONE);
609 self.word(",");
610 self.space();
611 self.pat(&matches.pattern);
612 if let Some(guard) = &matches.guard {
613 self.space();
614 self.word("if ");
615 self.expr(guard, FixupContext::NONE);
616 }
617 self.zerobreak();
618 self.offset(-INDENT);
619 self.end();
620 self.word(")");
621 }
622 KnownMacro::ThreadLocal(items) => {
623 self.word(" {");
624 self.cbox(INDENT);
625 self.hardbreak_if_nonempty();
626 for item in items {
627 self.outer_attrs(&item.attrs);
628 self.cbox(0);
629 self.visibility(&item.vis);
630 self.word("static ");
631 self.ident(&item.name);
632 self.word(": ");
633 self.ty(&item.ty);
634 self.word(" = ");
635 self.neverbreak();
636 self.expr(&item.init, FixupContext::NONE);
637 self.word(";");
638 self.end();
639 self.hardbreak();
640 }
641 self.offset(-INDENT);
642 self.end();
643 self.word("}");
644 semicolon = false;
645 }
646 KnownMacro::VecArray(vec) => {
647 if vec.is_empty() {
648 self.word("[]");
649 } else if expr::simple_array(vec) {
650 self.cbox(INDENT);
651 self.word("[");
652 self.zerobreak();
653 self.ibox(0);
654 for elem in vec.iter().delimited() {
655 self.expr(&elem, FixupContext::NONE);
656 if !elem.is_last {
657 self.word(",");
658 self.space();
659 }
660 }
661 self.end();
662 self.trailing_comma(true);
663 self.offset(-INDENT);
664 self.word("]");
665 self.end();
666 } else {
667 self.word("[");
668 self.cbox(INDENT);
669 self.zerobreak();
670 for elem in vec.iter().delimited() {
671 self.expr(&elem, FixupContext::NONE);
672 self.trailing_comma(elem.is_last);
673 }
674 self.offset(-INDENT);
675 self.end();
676 self.word("]");
677 }
678 }
679 KnownMacro::VecRepeat { elem, n } => {
680 self.word("[");
681 self.cbox(INDENT);
682 self.zerobreak();
683 self.expr(elem, FixupContext::NONE);
684 self.word(";");
685 self.space();
686 self.expr(n, FixupContext::NONE);
687 self.zerobreak();
688 self.offset(-INDENT);
689 self.end();
690 self.word("]");
691 }
692 }
693
694 if semicolon {
695 self.word(";");
696 }
697
698 true
699 }
700
701 fn cfg(&mut self, cfg: &Cfg) {
702 match cfg {
703 Cfg::Eq(ident, value) => {
704 self.ident(ident);
705 if let Some(value) = value {
706 self.word(" = ");
707 self.lit(value);
708 }
709 }
710 Cfg::Call(ident, args) => {
711 self.ident(ident);
712 self.word("(");
713 self.cbox(INDENT);
714 self.zerobreak();
715 for arg in args.iter().delimited() {
716 self.cfg(&arg);
717 self.trailing_comma(arg.is_last);
718 }
719 self.offset(-INDENT);
720 self.end();
721 self.word(")");
722 }
723 }
724 }
725 }
726}