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