]> git.lizzy.rs Git - rust.git/blob - src/visitor.rs
Merge pull request #1776 from topecongiro/poor-formatting/index-budget
[rust.git] / src / visitor.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::cmp;
12
13 use syntax::{ast, ptr, visit};
14 use syntax::codemap::{CodeMap, Span, BytePos};
15 use syntax::parse::ParseSess;
16
17 use strings::string_buffer::StringBuffer;
18
19 use {Indent, Shape};
20 use expr::{format_expr, ExprType};
21 use utils::{self, mk_sp};
22 use codemap::{LineRangeUtils, SpanUtils};
23 use comment::{contains_comment, FindUncommented};
24 use config::Config;
25 use rewrite::{Rewrite, RewriteContext};
26 use comment::rewrite_comment;
27 use macros::{rewrite_macro, MacroPosition};
28 use items::{rewrite_static, rewrite_associated_type, rewrite_associated_impl_type,
29             rewrite_type_alias, format_impl, format_trait};
30 use lists::{itemize_list, write_list, DefinitiveListTactic, ListFormatting, SeparatorTactic};
31
32 fn is_use_item(item: &ast::Item) -> bool {
33     match item.node {
34         ast::ItemKind::Use(_) => true,
35         _ => false,
36     }
37 }
38
39 fn item_bound(item: &ast::Item) -> Span {
40     item.attrs.iter().map(|attr| attr.span).fold(
41         item.span,
42         |bound, span| {
43             Span {
44                 lo: cmp::min(bound.lo, span.lo),
45                 hi: cmp::max(bound.hi, span.hi),
46                 ctxt: span.ctxt,
47             }
48         },
49     )
50 }
51
52 pub struct FmtVisitor<'a> {
53     pub parse_session: &'a ParseSess,
54     pub codemap: &'a CodeMap,
55     pub buffer: StringBuffer,
56     pub last_pos: BytePos,
57     // FIXME: use an RAII util or closure for indenting
58     pub block_indent: Indent,
59     pub config: &'a Config,
60     pub failed: bool,
61     pub is_if_else_block: bool,
62 }
63
64 impl<'a> FmtVisitor<'a> {
65     fn visit_stmt(&mut self, stmt: &ast::Stmt) {
66         debug!(
67             "visit_stmt: {:?} {:?}",
68             self.codemap.lookup_char_pos(stmt.span.lo),
69             self.codemap.lookup_char_pos(stmt.span.hi)
70         );
71
72         // FIXME(#434): Move this check to somewhere more central, eg Rewrite.
73         if !self.config
74             .file_lines()
75             .intersects(&self.codemap.lookup_line_range(stmt.span))
76         {
77             return;
78         }
79
80         match stmt.node {
81             ast::StmtKind::Item(ref item) => {
82                 self.visit_item(item);
83             }
84             ast::StmtKind::Local(..) => {
85                 let rewrite = stmt.rewrite(
86                     &self.get_context(),
87                     Shape::indented(self.block_indent, self.config),
88                 );
89                 self.push_rewrite(stmt.span, rewrite);
90             }
91             ast::StmtKind::Expr(ref expr) => {
92                 let rewrite = format_expr(
93                     expr,
94                     ExprType::Statement,
95                     &self.get_context(),
96                     Shape::indented(self.block_indent, self.config),
97                 );
98                 let span = if expr.attrs.is_empty() {
99                     stmt.span
100                 } else {
101                     mk_sp(expr.attrs[0].span.lo, stmt.span.hi)
102                 };
103                 self.push_rewrite(span, rewrite)
104             }
105             ast::StmtKind::Semi(ref expr) => {
106                 let rewrite = stmt.rewrite(
107                     &self.get_context(),
108                     Shape::indented(self.block_indent, self.config),
109                 );
110                 let span = if expr.attrs.is_empty() {
111                     stmt.span
112                 } else {
113                     mk_sp(expr.attrs[0].span.lo, stmt.span.hi)
114                 };
115                 self.push_rewrite(span, rewrite)
116             }
117             ast::StmtKind::Mac(ref mac) => {
118                 let (ref mac, _macro_style, _) = **mac;
119                 self.visit_mac(mac, None, MacroPosition::Statement);
120                 self.format_missing(stmt.span.hi);
121             }
122         }
123     }
124
125     pub fn visit_block(&mut self, b: &ast::Block) {
126         debug!(
127             "visit_block: {:?} {:?}",
128             self.codemap.lookup_char_pos(b.span.lo),
129             self.codemap.lookup_char_pos(b.span.hi)
130         );
131
132         // Check if this block has braces.
133         let snippet = self.snippet(b.span);
134         let has_braces = snippet.starts_with('{') || snippet.starts_with("unsafe");
135         let brace_compensation = if has_braces { BytePos(1) } else { BytePos(0) };
136
137         self.last_pos = self.last_pos + brace_compensation;
138         self.block_indent = self.block_indent.block_indent(self.config);
139         self.buffer.push_str("{");
140
141         for stmt in &b.stmts {
142             self.visit_stmt(stmt)
143         }
144
145         if !b.stmts.is_empty() {
146             if let Some(expr) = utils::stmt_expr(&b.stmts[b.stmts.len() - 1]) {
147                 if utils::semicolon_for_expr(expr) {
148                     self.buffer.push_str(";");
149                 }
150             }
151         }
152
153         let mut unindent_comment = self.is_if_else_block && !b.stmts.is_empty();
154         if unindent_comment {
155             let end_pos = source!(self, b.span).hi - brace_compensation;
156             let snippet = self.get_context().snippet(mk_sp(self.last_pos, end_pos));
157             unindent_comment = snippet.contains("//") || snippet.contains("/*");
158         }
159         // FIXME: we should compress any newlines here to just one
160         if unindent_comment {
161             self.block_indent = self.block_indent.block_unindent(self.config);
162         }
163         self.format_missing_with_indent(source!(self, b.span).hi - brace_compensation);
164         if unindent_comment {
165             self.block_indent = self.block_indent.block_indent(self.config);
166         }
167         self.close_block(unindent_comment);
168         self.last_pos = source!(self, b.span).hi;
169     }
170
171     // FIXME: this is a terrible hack to indent the comments between the last
172     // item in the block and the closing brace to the block's level.
173     // The closing brace itself, however, should be indented at a shallower
174     // level.
175     fn close_block(&mut self, unindent_comment: bool) {
176         let total_len = self.buffer.len;
177         let chars_too_many = if unindent_comment {
178             0
179         } else if self.config.hard_tabs() {
180             1
181         } else {
182             self.config.tab_spaces()
183         };
184         self.buffer.truncate(total_len - chars_too_many);
185         self.buffer.push_str("}");
186         self.block_indent = self.block_indent.block_unindent(self.config);
187     }
188
189     // Note that this only gets called for function definitions. Required methods
190     // on traits do not get handled here.
191     fn visit_fn(
192         &mut self,
193         fk: visit::FnKind,
194         fd: &ast::FnDecl,
195         s: Span,
196         _: ast::NodeId,
197         defaultness: ast::Defaultness,
198     ) {
199         let indent = self.block_indent;
200         let block;
201         let rewrite = match fk {
202             visit::FnKind::ItemFn(ident, generics, unsafety, constness, abi, vis, b) => {
203                 block = b;
204                 self.rewrite_fn(
205                     indent,
206                     ident,
207                     fd,
208                     generics,
209                     unsafety,
210                     constness.node,
211                     defaultness,
212                     abi,
213                     vis,
214                     mk_sp(s.lo, b.span.lo),
215                     &b,
216                 )
217             }
218             visit::FnKind::Method(ident, sig, vis, b) => {
219                 block = b;
220                 self.rewrite_fn(
221                     indent,
222                     ident,
223                     fd,
224                     &sig.generics,
225                     sig.unsafety,
226                     sig.constness.node,
227                     defaultness,
228                     sig.abi,
229                     vis.unwrap_or(&ast::Visibility::Inherited),
230                     mk_sp(s.lo, b.span.lo),
231                     &b,
232                 )
233             }
234             visit::FnKind::Closure(_) => unreachable!(),
235         };
236
237         if let Some(fn_str) = rewrite {
238             self.format_missing_with_indent(source!(self, s).lo);
239             self.buffer.push_str(&fn_str);
240             if let Some(c) = fn_str.chars().last() {
241                 if c == '}' {
242                     self.last_pos = source!(self, block.span).hi;
243                     return;
244                 }
245             }
246         } else {
247             self.format_missing(source!(self, block.span).lo);
248         }
249
250         self.last_pos = source!(self, block.span).lo;
251         self.visit_block(block)
252     }
253
254     pub fn visit_item(&mut self, item: &ast::Item) {
255         // This is where we bail out if there is a skip attribute. This is only
256         // complex in the module case. It is complex because the module could be
257         // in a separate file and there might be attributes in both files, but
258         // the AST lumps them all together.
259         match item.node {
260             ast::ItemKind::Mod(ref m) => {
261                 let outer_file = self.codemap.lookup_char_pos(item.span.lo).file;
262                 let inner_file = self.codemap.lookup_char_pos(m.inner.lo).file;
263                 if outer_file.name == inner_file.name {
264                     // Module is inline, in this case we treat modules like any
265                     // other item.
266                     if self.visit_attrs(&item.attrs) {
267                         self.push_rewrite(item.span, None);
268                         return;
269                     }
270                 } else if utils::contains_skip(&item.attrs) {
271                     // Module is not inline, but should be skipped.
272                     return;
273                 } else {
274                     // Module is not inline and should not be skipped. We want
275                     // to process only the attributes in the current file.
276                     let attrs = item.attrs
277                         .iter()
278                         .filter_map(|a| {
279                             let attr_file = self.codemap.lookup_char_pos(a.span.lo).file;
280                             if attr_file.name == outer_file.name {
281                                 Some(a.clone())
282                             } else {
283                                 None
284                             }
285                         })
286                         .collect::<Vec<_>>();
287                     // Assert because if we should skip it should be caught by
288                     // the above case.
289                     assert!(!self.visit_attrs(&attrs));
290                 }
291             }
292             _ => {
293                 if self.visit_attrs(&item.attrs) {
294                     self.push_rewrite(item.span, None);
295                     return;
296                 }
297             }
298         }
299
300         match item.node {
301             ast::ItemKind::Use(ref vp) => {
302                 self.format_import(&item.vis, vp, item.span);
303             }
304             ast::ItemKind::Impl(..) => {
305                 self.format_missing_with_indent(source!(self, item.span).lo);
306                 let snippet = self.get_context().snippet(item.span);
307                 let where_span_end = snippet
308                     .find_uncommented("{")
309                     .map(|x| (BytePos(x as u32)) + source!(self, item.span).lo);
310                 if let Some(impl_str) =
311                     format_impl(&self.get_context(), item, self.block_indent, where_span_end)
312                 {
313                     self.buffer.push_str(&impl_str);
314                     self.last_pos = source!(self, item.span).hi;
315                 }
316             }
317             ast::ItemKind::Trait(..) => {
318                 self.format_missing_with_indent(item.span.lo);
319                 if let Some(trait_str) =
320                     format_trait(&self.get_context(), item, self.block_indent)
321                 {
322                     self.buffer.push_str(&trait_str);
323                     self.last_pos = source!(self, item.span).hi;
324                 }
325             }
326             ast::ItemKind::ExternCrate(_) => {
327                 self.format_missing_with_indent(source!(self, item.span).lo);
328                 let new_str = self.snippet(item.span);
329                 self.buffer.push_str(&new_str);
330                 self.last_pos = source!(self, item.span).hi;
331             }
332             ast::ItemKind::Struct(ref def, ref generics) => {
333                 let rewrite = {
334                     let indent = self.block_indent;
335                     let context = self.get_context();
336                     ::items::format_struct(
337                         &context,
338                         "struct ",
339                         item.ident,
340                         &item.vis,
341                         def,
342                         Some(generics),
343                         item.span,
344                         indent,
345                         None,
346                     ).map(|s| match *def {
347                         ast::VariantData::Tuple(..) => s + ";",
348                         _ => s,
349                     })
350                 };
351                 self.push_rewrite(item.span, rewrite);
352             }
353             ast::ItemKind::Enum(ref def, ref generics) => {
354                 self.format_missing_with_indent(source!(self, item.span).lo);
355                 self.visit_enum(item.ident, &item.vis, def, generics, item.span);
356                 self.last_pos = source!(self, item.span).hi;
357             }
358             ast::ItemKind::Mod(ref module) => {
359                 self.format_missing_with_indent(source!(self, item.span).lo);
360                 self.format_mod(module, &item.vis, item.span, item.ident);
361             }
362             ast::ItemKind::Mac(ref mac) => {
363                 self.visit_mac(mac, Some(item.ident), MacroPosition::Item);
364             }
365             ast::ItemKind::ForeignMod(ref foreign_mod) => {
366                 self.format_missing_with_indent(source!(self, item.span).lo);
367                 self.format_foreign_mod(foreign_mod, item.span);
368             }
369             ast::ItemKind::Static(ref ty, mutability, ref expr) => {
370                 let rewrite = rewrite_static(
371                     "static",
372                     &item.vis,
373                     item.ident,
374                     ty,
375                     mutability,
376                     Some(expr),
377                     self.block_indent,
378                     item.span,
379                     &self.get_context(),
380                 );
381                 self.push_rewrite(item.span, rewrite);
382             }
383             ast::ItemKind::Const(ref ty, ref expr) => {
384                 let rewrite = rewrite_static(
385                     "const",
386                     &item.vis,
387                     item.ident,
388                     ty,
389                     ast::Mutability::Immutable,
390                     Some(expr),
391                     self.block_indent,
392                     item.span,
393                     &self.get_context(),
394                 );
395                 self.push_rewrite(item.span, rewrite);
396             }
397             ast::ItemKind::DefaultImpl(..) => {
398                 // FIXME(#78): format impl definitions.
399             }
400             ast::ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
401                 self.visit_fn(
402                     visit::FnKind::ItemFn(
403                         item.ident,
404                         generics,
405                         unsafety,
406                         constness,
407                         abi,
408                         &item.vis,
409                         body,
410                     ),
411                     decl,
412                     item.span,
413                     item.id,
414                     ast::Defaultness::Final,
415                 )
416             }
417             ast::ItemKind::Ty(ref ty, ref generics) => {
418                 let rewrite = rewrite_type_alias(
419                     &self.get_context(),
420                     self.block_indent,
421                     item.ident,
422                     ty,
423                     generics,
424                     &item.vis,
425                     item.span,
426                 );
427                 self.push_rewrite(item.span, rewrite);
428             }
429             ast::ItemKind::Union(..) => {
430                 // FIXME(#1157): format union definitions.
431             }
432             ast::ItemKind::GlobalAsm(..) => {
433                 let snippet = Some(self.snippet(item.span));
434                 self.push_rewrite(item.span, snippet);
435             }
436             ast::ItemKind::MacroDef(..) => {
437                 // FIXME(#1539): macros 2.0
438                 let snippet = Some(self.snippet(item.span));
439                 self.push_rewrite(item.span, snippet);
440             }
441         }
442     }
443
444     pub fn visit_trait_item(&mut self, ti: &ast::TraitItem) {
445         if self.visit_attrs(&ti.attrs) {
446             self.push_rewrite(ti.span, None);
447             return;
448         }
449
450         match ti.node {
451             ast::TraitItemKind::Const(ref ty, ref expr_opt) => {
452                 let rewrite = rewrite_static(
453                     "const",
454                     &ast::Visibility::Inherited,
455                     ti.ident,
456                     ty,
457                     ast::Mutability::Immutable,
458                     expr_opt.as_ref(),
459                     self.block_indent,
460                     ti.span,
461                     &self.get_context(),
462                 );
463                 self.push_rewrite(ti.span, rewrite);
464             }
465             ast::TraitItemKind::Method(ref sig, None) => {
466                 let indent = self.block_indent;
467                 let rewrite = self.rewrite_required_fn(indent, ti.ident, sig, ti.span);
468                 self.push_rewrite(ti.span, rewrite);
469             }
470             ast::TraitItemKind::Method(ref sig, Some(ref body)) => {
471                 self.visit_fn(
472                     visit::FnKind::Method(ti.ident, sig, None, body),
473                     &sig.decl,
474                     ti.span,
475                     ti.id,
476                     ast::Defaultness::Final,
477                 );
478             }
479             ast::TraitItemKind::Type(ref type_param_bounds, ref type_default) => {
480                 let rewrite = rewrite_associated_type(
481                     ti.ident,
482                     type_default.as_ref(),
483                     Some(type_param_bounds),
484                     &self.get_context(),
485                     self.block_indent,
486                 );
487                 self.push_rewrite(ti.span, rewrite);
488             }
489             ast::TraitItemKind::Macro(ref mac) => {
490                 self.visit_mac(mac, Some(ti.ident), MacroPosition::Item);
491             }
492         }
493     }
494
495     pub fn visit_impl_item(&mut self, ii: &ast::ImplItem) {
496         if self.visit_attrs(&ii.attrs) {
497             self.push_rewrite(ii.span, None);
498             return;
499         }
500
501         match ii.node {
502             ast::ImplItemKind::Method(ref sig, ref body) => {
503                 self.visit_fn(
504                     visit::FnKind::Method(ii.ident, sig, Some(&ii.vis), body),
505                     &sig.decl,
506                     ii.span,
507                     ii.id,
508                     ii.defaultness,
509                 );
510             }
511             ast::ImplItemKind::Const(ref ty, ref expr) => {
512                 let rewrite = rewrite_static(
513                     "const",
514                     &ii.vis,
515                     ii.ident,
516                     ty,
517                     ast::Mutability::Immutable,
518                     Some(expr),
519                     self.block_indent,
520                     ii.span,
521                     &self.get_context(),
522                 );
523                 self.push_rewrite(ii.span, rewrite);
524             }
525             ast::ImplItemKind::Type(ref ty) => {
526                 let rewrite = rewrite_associated_impl_type(
527                     ii.ident,
528                     ii.defaultness,
529                     Some(ty),
530                     None,
531                     &self.get_context(),
532                     self.block_indent,
533                 );
534                 self.push_rewrite(ii.span, rewrite);
535             }
536             ast::ImplItemKind::Macro(ref mac) => {
537                 self.visit_mac(mac, Some(ii.ident), MacroPosition::Item);
538             }
539         }
540     }
541
542     fn visit_mac(&mut self, mac: &ast::Mac, ident: Option<ast::Ident>, pos: MacroPosition) {
543         // 1 = ;
544         let shape = Shape::indented(self.block_indent, self.config)
545             .sub_width(1)
546             .unwrap();
547         let rewrite = rewrite_macro(mac, ident, &self.get_context(), shape, pos);
548         self.push_rewrite(mac.span, rewrite);
549     }
550
551     fn push_rewrite(&mut self, span: Span, rewrite: Option<String>) {
552         self.format_missing_with_indent(source!(self, span).lo);
553         self.failed = match rewrite {
554             Some(ref s)
555                 if s.rewrite(
556                     &self.get_context(),
557                     Shape::indented(self.block_indent, self.config),
558                 ).is_none() => true,
559             None => true,
560             _ => self.failed,
561         };
562         let result = rewrite.unwrap_or_else(|| self.snippet(span));
563         self.buffer.push_str(&result);
564         self.last_pos = source!(self, span).hi;
565     }
566
567     pub fn from_codemap(parse_session: &'a ParseSess, config: &'a Config) -> FmtVisitor<'a> {
568         FmtVisitor {
569             parse_session: parse_session,
570             codemap: parse_session.codemap(),
571             buffer: StringBuffer::new(),
572             last_pos: BytePos(0),
573             block_indent: Indent::empty(),
574             config: config,
575             failed: false,
576             is_if_else_block: false,
577         }
578     }
579
580     pub fn snippet(&self, span: Span) -> String {
581         match self.codemap.span_to_snippet(span) {
582             Ok(s) => s,
583             Err(_) => {
584                 println!(
585                     "Couldn't make snippet for span {:?}->{:?}",
586                     self.codemap.lookup_char_pos(span.lo),
587                     self.codemap.lookup_char_pos(span.hi)
588                 );
589                 "".to_owned()
590             }
591         }
592     }
593
594     // Returns true if we should skip the following item.
595     pub fn visit_attrs(&mut self, attrs: &[ast::Attribute]) -> bool {
596         if utils::contains_skip(attrs) {
597             return true;
598         }
599
600         let outers: Vec<_> = attrs
601             .iter()
602             .filter(|a| a.style == ast::AttrStyle::Outer)
603             .cloned()
604             .collect();
605         if outers.is_empty() {
606             return false;
607         }
608
609         let first = &outers[0];
610         self.format_missing_with_indent(source!(self, first.span).lo);
611
612         let rewrite = outers
613             .rewrite(
614                 &self.get_context(),
615                 Shape::indented(self.block_indent, self.config),
616             )
617             .unwrap();
618         self.buffer.push_str(&rewrite);
619         let last = outers.last().unwrap();
620         self.last_pos = source!(self, last.span).hi;
621         false
622     }
623
624     fn walk_mod_items(&mut self, m: &ast::Mod) {
625         let mut items_left: &[ptr::P<ast::Item>] = &m.items;
626         while !items_left.is_empty() {
627             // If the next item is a `use` declaration, then extract it and any subsequent `use`s
628             // to be potentially reordered within `format_imports`. Otherwise, just format the
629             // next item for output.
630             if self.config.reorder_imports() && is_use_item(&*items_left[0]) {
631                 let reorder_imports_in_group = self.config.reorder_imports_in_group();
632                 let mut last = self.codemap.lookup_line_range(item_bound(&items_left[0]));
633                 let use_item_length = items_left
634                     .iter()
635                     .take_while(|ppi| {
636                         is_use_item(&***ppi) &&
637                             (!reorder_imports_in_group ||
638                                  {
639                                      let current = self.codemap.lookup_line_range(item_bound(&ppi));
640                                      let in_same_group = current.lo < last.hi + 2;
641                                      last = current;
642                                      in_same_group
643                                  })
644                     })
645                     .count();
646                 let (use_items, rest) = items_left.split_at(use_item_length);
647                 self.format_imports(use_items);
648                 items_left = rest;
649             } else {
650                 // `unwrap()` is safe here because we know `items_left`
651                 // has elements from the loop condition
652                 let (item, rest) = items_left.split_first().unwrap();
653                 self.visit_item(item);
654                 items_left = rest;
655             }
656         }
657     }
658
659     fn format_mod(&mut self, m: &ast::Mod, vis: &ast::Visibility, s: Span, ident: ast::Ident) {
660         // Decide whether this is an inline mod or an external mod.
661         let local_file_name = self.codemap.span_to_filename(s);
662         let inner_span = source!(self, m.inner);
663         let is_internal = !(inner_span.lo.0 == 0 && inner_span.hi.0 == 0) &&
664             local_file_name == self.codemap.span_to_filename(inner_span);
665
666         self.buffer.push_str(&*utils::format_visibility(vis));
667         self.buffer.push_str("mod ");
668         self.buffer.push_str(&ident.to_string());
669
670         if is_internal {
671             self.buffer.push_str(" {");
672             // Hackery to account for the closing }.
673             let mod_lo = self.codemap.span_after(source!(self, s), "{");
674             let body_snippet = self.snippet(mk_sp(mod_lo, source!(self, m.inner).hi - BytePos(1)));
675             let body_snippet = body_snippet.trim();
676             if body_snippet.is_empty() {
677                 self.buffer.push_str("}");
678             } else {
679                 self.last_pos = mod_lo;
680                 self.block_indent = self.block_indent.block_indent(self.config);
681                 self.walk_mod_items(m);
682                 self.format_missing_with_indent(source!(self, m.inner).hi - BytePos(1));
683                 self.close_block(false);
684             }
685             self.last_pos = source!(self, m.inner).hi;
686         } else {
687             self.buffer.push_str(";");
688             self.last_pos = source!(self, s).hi;
689         }
690     }
691
692     pub fn format_separate_mod(&mut self, m: &ast::Mod) {
693         let filemap = self.codemap.lookup_char_pos(m.inner.lo).file;
694         self.last_pos = filemap.start_pos;
695         self.block_indent = Indent::empty();
696         self.walk_mod_items(m);
697         self.format_missing_with_indent(filemap.end_pos);
698     }
699
700     pub fn get_context(&self) -> RewriteContext {
701         RewriteContext {
702             parse_session: self.parse_session,
703             codemap: self.codemap,
704             config: self.config,
705             inside_macro: false,
706             use_block: false,
707             is_if_else_block: false,
708             force_one_line_chain: false,
709         }
710     }
711 }
712
713 impl Rewrite for ast::NestedMetaItem {
714     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
715         match self.node {
716             ast::NestedMetaItemKind::MetaItem(ref meta_item) => meta_item.rewrite(context, shape),
717             ast::NestedMetaItemKind::Literal(..) => Some(context.snippet(self.span)),
718         }
719     }
720 }
721
722 fn count_missing_closing_parens(s: &str) -> u32 {
723     let op_parens = s.chars().filter(|c| *c == '(').count();
724     let cl_parens = s.chars().filter(|c| *c == ')').count();
725     op_parens.checked_sub(cl_parens).unwrap_or(0) as u32
726 }
727
728 impl Rewrite for ast::MetaItem {
729     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
730         Some(match self.node {
731             ast::MetaItemKind::Word => String::from(&*self.name.as_str()),
732             ast::MetaItemKind::List(ref list) => {
733                 let name = self.name.as_str();
734                 // 3 = `#[` and `(`, 2 = `]` and `)`
735                 let item_shape = try_opt!(
736                     shape
737                         .shrink_left(name.len() + 3)
738                         .and_then(|s| s.sub_width(2))
739                 );
740                 let hi = self.span.hi +
741                     BytePos(count_missing_closing_parens(&context.snippet(self.span)));
742                 let items = itemize_list(
743                     context.codemap,
744                     list.iter(),
745                     ")",
746                     |nested_meta_item| nested_meta_item.span.lo,
747                     // FIXME: Span from MetaItem is missing closing parens.
748                     |nested_meta_item| {
749                         let snippet = context.snippet(nested_meta_item.span);
750                         nested_meta_item.span.hi + BytePos(count_missing_closing_parens(&snippet))
751                     },
752                     |nested_meta_item| nested_meta_item.rewrite(context, item_shape),
753                     self.span.lo,
754                     hi,
755                 );
756                 let item_vec = items.collect::<Vec<_>>();
757                 let fmt = ListFormatting {
758                     tactic: DefinitiveListTactic::Mixed,
759                     separator: ",",
760                     trailing_separator: SeparatorTactic::Never,
761                     shape: item_shape,
762                     ends_with_newline: false,
763                     config: context.config,
764                 };
765                 format!("{}({})", name, try_opt!(write_list(&item_vec, &fmt)))
766             }
767             ast::MetaItemKind::NameValue(ref literal) => {
768                 let name = self.name.as_str();
769                 let value = context.snippet(literal.span);
770                 if &*name == "doc" && contains_comment(&value) {
771                     let doc_shape = Shape {
772                         width: cmp::min(shape.width, context.config.comment_width())
773                             .checked_sub(shape.indent.width())
774                             .unwrap_or(0),
775                         ..shape
776                     };
777                     format!(
778                         "{}",
779                         try_opt!(rewrite_comment(&value, false, doc_shape, context.config))
780                     )
781                 } else {
782                     format!("{} = {}", name, value)
783                 }
784             }
785         })
786     }
787 }
788
789 impl Rewrite for ast::Attribute {
790     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
791         try_opt!(self.meta()).rewrite(context, shape).map(
792             |rw| if rw.starts_with("///") {
793                 rw
794             } else {
795                 let original = context.snippet(self.span);
796                 if contains_comment(&original) {
797                     original
798                 } else {
799                     format!("#[{}]", rw)
800                 }
801             },
802         )
803     }
804 }
805
806 impl<'a> Rewrite for [ast::Attribute] {
807     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
808         let mut result = String::new();
809         if self.is_empty() {
810             return Some(result);
811         }
812         let indent = shape.indent.to_string(context.config);
813
814         for (i, a) in self.iter().enumerate() {
815             let a_str = try_opt!(a.rewrite(context, shape));
816
817             // Write comments and blank lines between attributes.
818             if i > 0 {
819                 let comment = context.snippet(mk_sp(self[i - 1].span.hi, a.span.lo));
820                 // This particular horror show is to preserve line breaks in between doc
821                 // comments. An alternative would be to force such line breaks to start
822                 // with the usual doc comment token.
823                 let multi_line = a_str.starts_with("//") && comment.matches('\n').count() > 1;
824                 let comment = comment.trim();
825                 if !comment.is_empty() {
826                     let comment = try_opt!(rewrite_comment(
827                         comment,
828                         false,
829                         Shape::legacy(
830                             context.config.comment_width() - shape.indent.width(),
831                             shape.indent,
832                         ),
833                         context.config,
834                     ));
835                     result.push_str(&indent);
836                     result.push_str(&comment);
837                     result.push('\n');
838                 } else if multi_line {
839                     result.push('\n');
840                 }
841                 result.push_str(&indent);
842             }
843
844             // Write the attribute itself.
845             result.push_str(&a_str);
846
847             if i < self.len() - 1 {
848                 result.push('\n');
849             }
850         }
851
852         Some(result)
853     }
854 }