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