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