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