]> git.lizzy.rs Git - rust.git/blob - src/visitor.rs
Merge pull request #2860 from topecongiro/rustc-ap-syntax
[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 syntax::attr::HasAttrs;
12 use syntax::codemap::{self, BytePos, CodeMap, Pos, Span};
13 use syntax::parse::ParseSess;
14 use syntax::{ast, visit};
15
16 use attr::*;
17 use codemap::{LineRangeUtils, SpanUtils};
18 use comment::{CodeCharKind, CommentCodeSlices, FindUncommented};
19 use config::{BraceStyle, Config};
20 use items::{
21     format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item,
22     rewrite_associated_impl_type, rewrite_associated_type, rewrite_existential_impl_type,
23     rewrite_existential_type, rewrite_extern_crate, rewrite_type_alias, FnSig, StaticParts,
24     StructParts,
25 };
26 use macros::{rewrite_macro, rewrite_macro_def, MacroPosition};
27 use rewrite::{Rewrite, RewriteContext};
28 use shape::{Indent, Shape};
29 use spanned::Spanned;
30 use utils::{
31     self, contains_skip, count_newlines, inner_attributes, mk_sp, ptr_vec_to_ref_vec,
32     rewrite_ident, DEPR_SKIP_ANNOTATION,
33 };
34 use {ErrorKind, FormatReport, FormattingError};
35
36 use std::cell::RefCell;
37
38 /// Creates a string slice corresponding to the specified span.
39 pub struct SnippetProvider<'a> {
40     /// A pointer to the content of the file we are formatting.
41     big_snippet: &'a str,
42     /// A position of the start of `big_snippet`, used as an offset.
43     start_pos: usize,
44 }
45
46 impl<'a> SnippetProvider<'a> {
47     pub fn span_to_snippet(&self, span: Span) -> Option<&str> {
48         let start_index = span.lo().to_usize().checked_sub(self.start_pos)?;
49         let end_index = span.hi().to_usize().checked_sub(self.start_pos)?;
50         Some(&self.big_snippet[start_index..end_index])
51     }
52
53     pub fn new(start_pos: BytePos, big_snippet: &'a str) -> Self {
54         let start_pos = start_pos.to_usize();
55         SnippetProvider {
56             big_snippet,
57             start_pos,
58         }
59     }
60 }
61
62 pub struct FmtVisitor<'a> {
63     pub parse_session: &'a ParseSess,
64     pub codemap: &'a CodeMap,
65     pub buffer: String,
66     pub last_pos: BytePos,
67     // FIXME: use an RAII util or closure for indenting
68     pub block_indent: Indent,
69     pub config: &'a Config,
70     pub is_if_else_block: bool,
71     pub snippet_provider: &'a SnippetProvider<'a>,
72     pub line_number: usize,
73     pub skipped_range: Vec<(usize, usize)>,
74     pub macro_rewrite_failure: bool,
75     pub(crate) report: FormatReport,
76 }
77
78 impl<'b, 'a: 'b> FmtVisitor<'a> {
79     pub fn shape(&self) -> Shape {
80         Shape::indented(self.block_indent, self.config)
81     }
82
83     fn visit_stmt(&mut self, stmt: &ast::Stmt) {
84         debug!(
85             "visit_stmt: {:?} {:?}",
86             self.codemap.lookup_char_pos(stmt.span.lo()),
87             self.codemap.lookup_char_pos(stmt.span.hi())
88         );
89
90         match stmt.node {
91             ast::StmtKind::Item(ref item) => {
92                 self.visit_item(item);
93             }
94             ast::StmtKind::Local(..) | ast::StmtKind::Expr(..) | ast::StmtKind::Semi(..) => {
95                 if contains_skip(get_attrs_from_stmt(stmt)) {
96                     self.push_skipped_with_span(stmt.span());
97                 } else {
98                     let rewrite = stmt.rewrite(&self.get_context(), self.shape());
99                     self.push_rewrite(stmt.span(), rewrite)
100                 }
101             }
102             ast::StmtKind::Mac(ref mac) => {
103                 let (ref mac, _macro_style, ref attrs) = **mac;
104                 if self.visit_attrs(attrs, ast::AttrStyle::Outer) {
105                     self.push_skipped_with_span(stmt.span());
106                 } else {
107                     self.visit_mac(mac, None, MacroPosition::Statement);
108                 }
109                 self.format_missing(stmt.span.hi());
110             }
111         }
112     }
113
114     pub fn visit_block(
115         &mut self,
116         b: &ast::Block,
117         inner_attrs: Option<&[ast::Attribute]>,
118         has_braces: bool,
119     ) {
120         debug!(
121             "visit_block: {:?} {:?}",
122             self.codemap.lookup_char_pos(b.span.lo()),
123             self.codemap.lookup_char_pos(b.span.hi())
124         );
125
126         // Check if this block has braces.
127         let brace_compensation = BytePos(if has_braces { 1 } else { 0 });
128
129         self.last_pos = self.last_pos + brace_compensation;
130         self.block_indent = self.block_indent.block_indent(self.config);
131         self.push_str("{");
132
133         if let Some(first_stmt) = b.stmts.first() {
134             let attr_lo = inner_attrs
135                 .and_then(|attrs| inner_attributes(attrs).first().map(|attr| attr.span.lo()))
136                 .or_else(|| {
137                     // Attributes for an item in a statement position
138                     // do not belong to the statement. (rust-lang/rust#34459)
139                     if let ast::StmtKind::Item(ref item) = first_stmt.node {
140                         item.attrs.first()
141                     } else {
142                         first_stmt.attrs().first()
143                     }.and_then(|attr| {
144                         // Some stmts can have embedded attributes.
145                         // e.g. `match { #![attr] ... }`
146                         let attr_lo = attr.span.lo();
147                         if attr_lo < first_stmt.span.lo() {
148                             Some(attr_lo)
149                         } else {
150                             None
151                         }
152                     })
153                 });
154
155             let snippet = self.snippet(mk_sp(
156                 self.last_pos,
157                 attr_lo.unwrap_or_else(|| first_stmt.span.lo()),
158             ));
159             let len = CommentCodeSlices::new(snippet)
160                 .nth(0)
161                 .and_then(|(kind, _, s)| {
162                     if kind == CodeCharKind::Normal {
163                         s.rfind('\n')
164                     } else {
165                         None
166                     }
167                 });
168             if let Some(len) = len {
169                 self.last_pos = self.last_pos + BytePos::from_usize(len);
170             }
171         }
172
173         // Format inner attributes if available.
174         let skip_rewrite = if let Some(attrs) = inner_attrs {
175             self.visit_attrs(attrs, ast::AttrStyle::Inner)
176         } else {
177             false
178         };
179
180         if skip_rewrite {
181             self.push_rewrite(b.span, None);
182             self.close_block(false);
183             self.last_pos = source!(self, b.span).hi();
184             return;
185         }
186
187         self.walk_block_stmts(b);
188
189         if !b.stmts.is_empty() {
190             if let Some(expr) = utils::stmt_expr(&b.stmts[b.stmts.len() - 1]) {
191                 if utils::semicolon_for_expr(&self.get_context(), expr) {
192                     self.push_str(";");
193                 }
194             }
195         }
196
197         let mut remove_len = BytePos(0);
198         if let Some(stmt) = b.stmts.last() {
199             let snippet = self.snippet(mk_sp(
200                 stmt.span.hi(),
201                 source!(self, b.span).hi() - brace_compensation,
202             ));
203             let len = CommentCodeSlices::new(snippet)
204                 .last()
205                 .and_then(|(kind, _, s)| {
206                     if kind == CodeCharKind::Normal && s.trim().is_empty() {
207                         Some(s.len())
208                     } else {
209                         None
210                     }
211                 });
212             if let Some(len) = len {
213                 remove_len = BytePos::from_usize(len);
214             }
215         }
216
217         let unindent_comment = (self.is_if_else_block && !b.stmts.is_empty()) && {
218             let end_pos = source!(self, b.span).hi() - brace_compensation - remove_len;
219             let snippet = self.snippet(mk_sp(self.last_pos, end_pos));
220             snippet.contains("//") || snippet.contains("/*")
221         };
222         // FIXME: we should compress any newlines here to just one
223         if unindent_comment {
224             self.block_indent = self.block_indent.block_unindent(self.config);
225         }
226         self.format_missing_with_indent(
227             source!(self, b.span).hi() - brace_compensation - remove_len,
228         );
229         if unindent_comment {
230             self.block_indent = self.block_indent.block_indent(self.config);
231         }
232         self.close_block(unindent_comment);
233         self.last_pos = source!(self, b.span).hi();
234     }
235
236     // FIXME: this is a terrible hack to indent the comments between the last
237     // item in the block and the closing brace to the block's level.
238     // The closing brace itself, however, should be indented at a shallower
239     // level.
240     fn close_block(&mut self, unindent_comment: bool) {
241         let total_len = self.buffer.len();
242         let chars_too_many = if unindent_comment {
243             0
244         } else if self.config.hard_tabs() {
245             1
246         } else {
247             self.config.tab_spaces()
248         };
249         self.buffer.truncate(total_len - chars_too_many);
250         self.push_str("}");
251         self.block_indent = self.block_indent.block_unindent(self.config);
252     }
253
254     // Note that this only gets called for function definitions. Required methods
255     // on traits do not get handled here.
256     // FIXME(topecongiro) Format async fn (#2812).
257     fn visit_fn(
258         &mut self,
259         fk: visit::FnKind,
260         generics: &ast::Generics,
261         fd: &ast::FnDecl,
262         s: Span,
263         defaultness: ast::Defaultness,
264         inner_attrs: Option<&[ast::Attribute]>,
265     ) {
266         let indent = self.block_indent;
267         let block;
268         let rewrite = match fk {
269             visit::FnKind::ItemFn(ident, _, _, b) | visit::FnKind::Method(ident, _, _, b) => {
270                 block = b;
271                 self.rewrite_fn(
272                     indent,
273                     ident,
274                     &FnSig::from_fn_kind(&fk, generics, fd, defaultness),
275                     mk_sp(s.lo(), b.span.lo()),
276                     b,
277                     inner_attrs,
278                 )
279             }
280             visit::FnKind::Closure(_) => unreachable!(),
281         };
282
283         if let Some(fn_str) = rewrite {
284             self.format_missing_with_indent(source!(self, s).lo());
285             self.push_str(&fn_str);
286             if let Some(c) = fn_str.chars().last() {
287                 if c == '}' {
288                     self.last_pos = source!(self, block.span).hi();
289                     return;
290                 }
291             }
292         } else {
293             self.format_missing(source!(self, block.span).lo());
294         }
295
296         self.last_pos = source!(self, block.span).lo();
297         self.visit_block(block, inner_attrs, true)
298     }
299
300     pub fn visit_item(&mut self, item: &ast::Item) {
301         skip_out_of_file_lines_range_visitor!(self, item.span);
302
303         // This is where we bail out if there is a skip attribute. This is only
304         // complex in the module case. It is complex because the module could be
305         // in a separate file and there might be attributes in both files, but
306         // the AST lumps them all together.
307         let filtered_attrs;
308         let mut attrs = &item.attrs;
309         match item.node {
310             // For use items, skip rewriting attributes. Just check for a skip attribute.
311             ast::ItemKind::Use(..) => {
312                 if contains_skip(attrs) {
313                     self.push_skipped_with_span(item.span());
314                     return;
315                 }
316             }
317             // Module is inline, in this case we treat it like any other item.
318             _ if !is_mod_decl(item) => {
319                 if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
320                     self.push_skipped_with_span(item.span());
321                     return;
322                 }
323             }
324             // Module is not inline, but should be skipped.
325             ast::ItemKind::Mod(..) if contains_skip(&item.attrs) => {
326                 return;
327             }
328             // Module is not inline and should not be skipped. We want
329             // to process only the attributes in the current file.
330             ast::ItemKind::Mod(..) => {
331                 filtered_attrs = filter_inline_attrs(&item.attrs, item.span());
332                 // Assert because if we should skip it should be caught by
333                 // the above case.
334                 assert!(!self.visit_attrs(&filtered_attrs, ast::AttrStyle::Outer));
335                 attrs = &filtered_attrs;
336             }
337             _ => {
338                 if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
339                     self.push_skipped_with_span(item.span());
340                     return;
341                 }
342             }
343         }
344
345         match item.node {
346             ast::ItemKind::Use(ref tree) => self.format_import(item, tree),
347             ast::ItemKind::Impl(..) => {
348                 let snippet = self.snippet(item.span);
349                 let where_span_end = snippet
350                     .find_uncommented("{")
351                     .map(|x| BytePos(x as u32) + source!(self, item.span).lo());
352                 let rw = format_impl(&self.get_context(), item, self.block_indent, where_span_end);
353                 self.push_rewrite(item.span, rw);
354             }
355             ast::ItemKind::Trait(..) => {
356                 let rw = format_trait(&self.get_context(), item, self.block_indent);
357                 self.push_rewrite(item.span, rw);
358             }
359             ast::ItemKind::TraitAlias(ref generics, ref generic_bounds) => {
360                 let shape = Shape::indented(self.block_indent, self.config);
361                 let rw = format_trait_alias(
362                     &self.get_context(),
363                     item.ident,
364                     generics,
365                     generic_bounds,
366                     shape,
367                 );
368                 self.push_rewrite(item.span, rw);
369             }
370             ast::ItemKind::ExternCrate(_) => {
371                 let rw = rewrite_extern_crate(&self.get_context(), item);
372                 self.push_rewrite(item.span, rw);
373             }
374             ast::ItemKind::Struct(..) | ast::ItemKind::Union(..) => {
375                 self.visit_struct(&StructParts::from_item(item));
376             }
377             ast::ItemKind::Enum(ref def, ref generics) => {
378                 self.format_missing_with_indent(source!(self, item.span).lo());
379                 self.visit_enum(item.ident, &item.vis, def, generics, item.span);
380                 self.last_pos = source!(self, item.span).hi();
381             }
382             ast::ItemKind::Mod(ref module) => {
383                 let is_inline = !is_mod_decl(item);
384                 self.format_missing_with_indent(source!(self, item.span).lo());
385                 self.format_mod(module, &item.vis, item.span, item.ident, attrs, is_inline);
386             }
387             ast::ItemKind::Mac(ref mac) => {
388                 self.visit_mac(mac, Some(item.ident), MacroPosition::Item);
389             }
390             ast::ItemKind::ForeignMod(ref foreign_mod) => {
391                 self.format_missing_with_indent(source!(self, item.span).lo());
392                 self.format_foreign_mod(foreign_mod, item.span);
393             }
394             ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => {
395                 self.visit_static(&StaticParts::from_item(item));
396             }
397             ast::ItemKind::Fn(ref decl, fn_header, ref generics, ref body) => {
398                 let inner_attrs = inner_attributes(&item.attrs);
399                 self.visit_fn(
400                     visit::FnKind::ItemFn(item.ident, fn_header, &item.vis, body),
401                     generics,
402                     decl,
403                     item.span,
404                     ast::Defaultness::Final,
405                     Some(&inner_attrs),
406                 )
407             }
408             ast::ItemKind::Ty(ref ty, ref generics) => {
409                 let rewrite = rewrite_type_alias(
410                     &self.get_context(),
411                     self.block_indent,
412                     item.ident,
413                     ty,
414                     generics,
415                     &item.vis,
416                 );
417                 self.push_rewrite(item.span, rewrite);
418             }
419             ast::ItemKind::Existential(ref generic_bounds, ref generics) => {
420                 let rewrite = rewrite_existential_type(
421                     &self.get_context(),
422                     self.block_indent,
423                     item.ident,
424                     generic_bounds,
425                     generics,
426                     &item.vis,
427                 );
428                 self.push_rewrite(item.span, rewrite);
429             }
430             ast::ItemKind::GlobalAsm(..) => {
431                 let snippet = Some(self.snippet(item.span).to_owned());
432                 self.push_rewrite(item.span, snippet);
433             }
434             ast::ItemKind::MacroDef(ref def) => {
435                 let rewrite = rewrite_macro_def(
436                     &self.get_context(),
437                     self.shape(),
438                     self.block_indent,
439                     def,
440                     item.ident,
441                     &item.vis,
442                     item.span,
443                 );
444                 self.push_rewrite(item.span, rewrite);
445             }
446         }
447     }
448
449     pub fn visit_trait_item(&mut self, ti: &ast::TraitItem) {
450         skip_out_of_file_lines_range_visitor!(self, ti.span);
451
452         if self.visit_attrs(&ti.attrs, ast::AttrStyle::Outer) {
453             self.push_skipped_with_span(ti.span());
454             return;
455         }
456
457         match ti.node {
458             ast::TraitItemKind::Const(..) => self.visit_static(&StaticParts::from_trait_item(ti)),
459             ast::TraitItemKind::Method(ref sig, None) => {
460                 let indent = self.block_indent;
461                 let rewrite =
462                     self.rewrite_required_fn(indent, ti.ident, sig, &ti.generics, ti.span);
463                 self.push_rewrite(ti.span, rewrite);
464             }
465             ast::TraitItemKind::Method(ref sig, Some(ref body)) => {
466                 let inner_attrs = inner_attributes(&ti.attrs);
467                 self.visit_fn(
468                     visit::FnKind::Method(ti.ident, sig, None, body),
469                     &ti.generics,
470                     &sig.decl,
471                     ti.span,
472                     ast::Defaultness::Final,
473                     Some(&inner_attrs),
474                 );
475             }
476             ast::TraitItemKind::Type(ref generic_bounds, ref type_default) => {
477                 let rewrite = rewrite_associated_type(
478                     ti.ident,
479                     type_default.as_ref(),
480                     Some(generic_bounds),
481                     &self.get_context(),
482                     self.block_indent,
483                 );
484                 self.push_rewrite(ti.span, rewrite);
485             }
486             ast::TraitItemKind::Macro(ref mac) => {
487                 self.visit_mac(mac, Some(ti.ident), MacroPosition::Item);
488             }
489         }
490     }
491
492     pub fn visit_impl_item(&mut self, ii: &ast::ImplItem) {
493         skip_out_of_file_lines_range_visitor!(self, ii.span);
494
495         if self.visit_attrs(&ii.attrs, ast::AttrStyle::Outer) {
496             self.push_skipped_with_span(ii.span());
497             return;
498         }
499
500         match ii.node {
501             ast::ImplItemKind::Method(ref sig, ref body) => {
502                 let inner_attrs = inner_attributes(&ii.attrs);
503                 self.visit_fn(
504                     visit::FnKind::Method(ii.ident, sig, Some(&ii.vis), body),
505                     &ii.generics,
506                     &sig.decl,
507                     ii.span,
508                     ii.defaultness,
509                     Some(&inner_attrs),
510                 );
511             }
512             ast::ImplItemKind::Const(..) => self.visit_static(&StaticParts::from_impl_item(ii)),
513             ast::ImplItemKind::Type(ref ty) => {
514                 let rewrite = rewrite_associated_impl_type(
515                     ii.ident,
516                     ii.defaultness,
517                     Some(ty),
518                     None,
519                     &self.get_context(),
520                     self.block_indent,
521                 );
522                 self.push_rewrite(ii.span, rewrite);
523             }
524             ast::ImplItemKind::Existential(ref generic_bounds) => {
525                 let rewrite = rewrite_existential_impl_type(
526                     &self.get_context(),
527                     ii.ident,
528                     generic_bounds,
529                     self.block_indent,
530                 );
531                 self.push_rewrite(ii.span, rewrite);
532             }
533             ast::ImplItemKind::Macro(ref mac) => {
534                 self.visit_mac(mac, Some(ii.ident), MacroPosition::Item);
535             }
536         }
537     }
538
539     fn visit_mac(&mut self, mac: &ast::Mac, ident: Option<ast::Ident>, pos: MacroPosition) {
540         skip_out_of_file_lines_range_visitor!(self, mac.span);
541
542         // 1 = ;
543         let shape = self.shape().sub_width(1).unwrap();
544         let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos));
545         self.push_rewrite(mac.span, rewrite);
546     }
547
548     pub fn push_str(&mut self, s: &str) {
549         self.line_number += count_newlines(s);
550         self.buffer.push_str(s);
551     }
552
553     #[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
554     fn push_rewrite_inner(&mut self, span: Span, rewrite: Option<String>) {
555         if let Some(ref s) = rewrite {
556             self.push_str(s);
557         } else {
558             let snippet = self.snippet(span);
559             self.push_str(snippet);
560         }
561         self.last_pos = source!(self, span).hi();
562     }
563
564     pub fn push_rewrite(&mut self, span: Span, rewrite: Option<String>) {
565         self.format_missing_with_indent(source!(self, span).lo());
566         self.push_rewrite_inner(span, rewrite);
567     }
568
569     pub fn push_skipped_with_span(&mut self, span: Span) {
570         self.format_missing_with_indent(source!(self, span).lo());
571         let lo = self.line_number + 1;
572         self.push_rewrite_inner(span, None);
573         let hi = self.line_number + 1;
574         self.skipped_range.push((lo, hi));
575     }
576
577     pub fn from_context(ctx: &'a RewriteContext) -> FmtVisitor<'a> {
578         FmtVisitor::from_codemap(
579             ctx.parse_session,
580             ctx.config,
581             ctx.snippet_provider,
582             ctx.report.clone(),
583         )
584     }
585
586     pub(crate) fn from_codemap(
587         parse_session: &'a ParseSess,
588         config: &'a Config,
589         snippet_provider: &'a SnippetProvider,
590         report: FormatReport,
591     ) -> FmtVisitor<'a> {
592         FmtVisitor {
593             parse_session,
594             codemap: parse_session.codemap(),
595             buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2),
596             last_pos: BytePos(0),
597             block_indent: Indent::empty(),
598             config,
599             is_if_else_block: false,
600             snippet_provider,
601             line_number: 0,
602             skipped_range: vec![],
603             macro_rewrite_failure: false,
604             report,
605         }
606     }
607
608     pub fn opt_snippet(&'b self, span: Span) -> Option<&'a str> {
609         self.snippet_provider.span_to_snippet(span)
610     }
611
612     pub fn snippet(&'b self, span: Span) -> &'a str {
613         self.opt_snippet(span).unwrap()
614     }
615
616     // Returns true if we should skip the following item.
617     pub fn visit_attrs(&mut self, attrs: &[ast::Attribute], style: ast::AttrStyle) -> bool {
618         for attr in attrs {
619             if attr.name() == DEPR_SKIP_ANNOTATION {
620                 let file_name = self.codemap.span_to_filename(attr.span).into();
621                 self.report.append(
622                     file_name,
623                     vec![FormattingError::from_span(
624                         &attr.span,
625                         &self.codemap,
626                         ErrorKind::DeprecatedAttr,
627                     )],
628                 );
629             } else if attr.path.segments[0].ident.to_string() == "rustfmt" {
630                 if attr.path.segments.len() == 1
631                     || attr.path.segments[1].ident.to_string() != "skip"
632                 {
633                     let file_name = self.codemap.span_to_filename(attr.span).into();
634                     self.report.append(
635                         file_name,
636                         vec![FormattingError::from_span(
637                             &attr.span,
638                             &self.codemap,
639                             ErrorKind::BadAttr,
640                         )],
641                     );
642                 }
643             }
644         }
645         if contains_skip(attrs) {
646             return true;
647         }
648
649         let attrs: Vec<_> = attrs.iter().filter(|a| a.style == style).cloned().collect();
650         if attrs.is_empty() {
651             return false;
652         }
653
654         let rewrite = attrs.rewrite(&self.get_context(), self.shape());
655         let span = mk_sp(attrs[0].span.lo(), attrs[attrs.len() - 1].span.hi());
656         self.push_rewrite(span, rewrite);
657
658         false
659     }
660
661     fn walk_mod_items(&mut self, m: &ast::Mod) {
662         self.visit_items_with_reordering(&ptr_vec_to_ref_vec(&m.items));
663     }
664
665     fn walk_stmts(&mut self, stmts: &[ast::Stmt]) {
666         fn to_stmt_item(stmt: &ast::Stmt) -> Option<&ast::Item> {
667             match stmt.node {
668                 ast::StmtKind::Item(ref item) => Some(&**item),
669                 _ => None,
670             }
671         }
672
673         if stmts.is_empty() {
674             return;
675         }
676
677         // Extract leading `use ...;`.
678         let items: Vec<_> = stmts
679             .iter()
680             .take_while(|stmt| to_stmt_item(stmt).map_or(false, is_use_item))
681             .filter_map(|stmt| to_stmt_item(stmt))
682             .collect();
683
684         if items.is_empty() {
685             self.visit_stmt(&stmts[0]);
686             self.walk_stmts(&stmts[1..]);
687         } else {
688             self.visit_items_with_reordering(&items);
689             self.walk_stmts(&stmts[items.len()..]);
690         }
691     }
692
693     fn walk_block_stmts(&mut self, b: &ast::Block) {
694         self.walk_stmts(&b.stmts)
695     }
696
697     fn format_mod(
698         &mut self,
699         m: &ast::Mod,
700         vis: &ast::Visibility,
701         s: Span,
702         ident: ast::Ident,
703         attrs: &[ast::Attribute],
704         is_internal: bool,
705     ) {
706         let vis_str = utils::format_visibility(&self.get_context(), vis);
707         self.push_str(&*vis_str);
708         self.push_str("mod ");
709         // Calling `to_owned()` to work around borrow checker.
710         let ident_str = rewrite_ident(&self.get_context(), ident).to_owned();
711         self.push_str(&ident_str);
712
713         if is_internal {
714             match self.config.brace_style() {
715                 BraceStyle::AlwaysNextLine => {
716                     let indent_str = self.block_indent.to_string_with_newline(self.config);
717                     self.push_str(&indent_str);
718                     self.push_str("{");
719                 }
720                 _ => self.push_str(" {"),
721             }
722             // Hackery to account for the closing }.
723             let mod_lo = self.snippet_provider.span_after(source!(self, s), "{");
724             let body_snippet =
725                 self.snippet(mk_sp(mod_lo, source!(self, m.inner).hi() - BytePos(1)));
726             let body_snippet = body_snippet.trim();
727             if body_snippet.is_empty() {
728                 self.push_str("}");
729             } else {
730                 self.last_pos = mod_lo;
731                 self.block_indent = self.block_indent.block_indent(self.config);
732                 self.visit_attrs(attrs, ast::AttrStyle::Inner);
733                 self.walk_mod_items(m);
734                 self.format_missing_with_indent(source!(self, m.inner).hi() - BytePos(1));
735                 self.close_block(false);
736             }
737             self.last_pos = source!(self, m.inner).hi();
738         } else {
739             self.push_str(";");
740             self.last_pos = source!(self, s).hi();
741         }
742     }
743
744     pub fn format_separate_mod(&mut self, m: &ast::Mod, filemap: &codemap::FileMap) {
745         self.block_indent = Indent::empty();
746         self.walk_mod_items(m);
747         self.format_missing_with_indent(filemap.end_pos);
748     }
749
750     pub fn skip_empty_lines(&mut self, end_pos: BytePos) {
751         while let Some(pos) = self
752             .snippet_provider
753             .opt_span_after(mk_sp(self.last_pos, end_pos), "\n")
754         {
755             if let Some(snippet) = self.opt_snippet(mk_sp(self.last_pos, pos)) {
756                 if snippet.trim().is_empty() {
757                     self.last_pos = pos;
758                 } else {
759                     return;
760                 }
761             }
762         }
763     }
764
765     pub fn with_context<F>(&mut self, f: F) -> Option<String>
766     where
767         F: Fn(&RewriteContext) -> Option<String>,
768     {
769         let result;
770         let macro_rewrite_failure = {
771             let context = self.get_context();
772             result = f(&context);
773             unsafe { *context.macro_rewrite_failure.as_ptr() }
774         };
775         self.macro_rewrite_failure |= macro_rewrite_failure;
776         result
777     }
778
779     pub fn get_context(&self) -> RewriteContext {
780         RewriteContext {
781             parse_session: self.parse_session,
782             codemap: self.codemap,
783             config: self.config,
784             inside_macro: RefCell::new(false),
785             use_block: RefCell::new(false),
786             is_if_else_block: RefCell::new(false),
787             force_one_line_chain: RefCell::new(false),
788             snippet_provider: self.snippet_provider,
789             macro_rewrite_failure: RefCell::new(false),
790             report: self.report.clone(),
791         }
792     }
793 }