]> git.lizzy.rs Git - rust.git/blob - src/visitor.rs
Merge pull request #2862 from topecongiro/issue-2852
[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                     &self.get_context(),
519                     self.block_indent,
520                 );
521                 self.push_rewrite(ii.span, rewrite);
522             }
523             ast::ImplItemKind::Existential(ref generic_bounds) => {
524                 let rewrite = rewrite_existential_impl_type(
525                     &self.get_context(),
526                     ii.ident,
527                     generic_bounds,
528                     self.block_indent,
529                 );
530                 self.push_rewrite(ii.span, rewrite);
531             }
532             ast::ImplItemKind::Macro(ref mac) => {
533                 self.visit_mac(mac, Some(ii.ident), MacroPosition::Item);
534             }
535         }
536     }
537
538     fn visit_mac(&mut self, mac: &ast::Mac, ident: Option<ast::Ident>, pos: MacroPosition) {
539         skip_out_of_file_lines_range_visitor!(self, mac.span);
540
541         // 1 = ;
542         let shape = self.shape().sub_width(1).unwrap();
543         let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos));
544         self.push_rewrite(mac.span, rewrite);
545     }
546
547     pub fn push_str(&mut self, s: &str) {
548         self.line_number += count_newlines(s);
549         self.buffer.push_str(s);
550     }
551
552     #[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
553     fn push_rewrite_inner(&mut self, span: Span, rewrite: Option<String>) {
554         if let Some(ref s) = rewrite {
555             self.push_str(s);
556         } else {
557             let snippet = self.snippet(span);
558             self.push_str(snippet);
559         }
560         self.last_pos = source!(self, span).hi();
561     }
562
563     pub fn push_rewrite(&mut self, span: Span, rewrite: Option<String>) {
564         self.format_missing_with_indent(source!(self, span).lo());
565         self.push_rewrite_inner(span, rewrite);
566     }
567
568     pub fn push_skipped_with_span(&mut self, span: Span) {
569         self.format_missing_with_indent(source!(self, span).lo());
570         let lo = self.line_number + 1;
571         self.push_rewrite_inner(span, None);
572         let hi = self.line_number + 1;
573         self.skipped_range.push((lo, hi));
574     }
575
576     pub fn from_context(ctx: &'a RewriteContext) -> FmtVisitor<'a> {
577         FmtVisitor::from_codemap(
578             ctx.parse_session,
579             ctx.config,
580             ctx.snippet_provider,
581             ctx.report.clone(),
582         )
583     }
584
585     pub(crate) fn from_codemap(
586         parse_session: &'a ParseSess,
587         config: &'a Config,
588         snippet_provider: &'a SnippetProvider,
589         report: FormatReport,
590     ) -> FmtVisitor<'a> {
591         FmtVisitor {
592             parse_session,
593             codemap: parse_session.codemap(),
594             buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2),
595             last_pos: BytePos(0),
596             block_indent: Indent::empty(),
597             config,
598             is_if_else_block: false,
599             snippet_provider,
600             line_number: 0,
601             skipped_range: vec![],
602             macro_rewrite_failure: false,
603             report,
604         }
605     }
606
607     pub fn opt_snippet(&'b self, span: Span) -> Option<&'a str> {
608         self.snippet_provider.span_to_snippet(span)
609     }
610
611     pub fn snippet(&'b self, span: Span) -> &'a str {
612         self.opt_snippet(span).unwrap()
613     }
614
615     // Returns true if we should skip the following item.
616     pub fn visit_attrs(&mut self, attrs: &[ast::Attribute], style: ast::AttrStyle) -> bool {
617         for attr in attrs {
618             if attr.name() == DEPR_SKIP_ANNOTATION {
619                 let file_name = self.codemap.span_to_filename(attr.span).into();
620                 self.report.append(
621                     file_name,
622                     vec![FormattingError::from_span(
623                         &attr.span,
624                         &self.codemap,
625                         ErrorKind::DeprecatedAttr,
626                     )],
627                 );
628             } else if attr.path.segments[0].ident.to_string() == "rustfmt" {
629                 if attr.path.segments.len() == 1
630                     || attr.path.segments[1].ident.to_string() != "skip"
631                 {
632                     let file_name = self.codemap.span_to_filename(attr.span).into();
633                     self.report.append(
634                         file_name,
635                         vec![FormattingError::from_span(
636                             &attr.span,
637                             &self.codemap,
638                             ErrorKind::BadAttr,
639                         )],
640                     );
641                 }
642             }
643         }
644         if contains_skip(attrs) {
645             return true;
646         }
647
648         let attrs: Vec<_> = attrs.iter().filter(|a| a.style == style).cloned().collect();
649         if attrs.is_empty() {
650             return false;
651         }
652
653         let rewrite = attrs.rewrite(&self.get_context(), self.shape());
654         let span = mk_sp(attrs[0].span.lo(), attrs[attrs.len() - 1].span.hi());
655         self.push_rewrite(span, rewrite);
656
657         false
658     }
659
660     fn walk_mod_items(&mut self, m: &ast::Mod) {
661         self.visit_items_with_reordering(&ptr_vec_to_ref_vec(&m.items));
662     }
663
664     fn walk_stmts(&mut self, stmts: &[ast::Stmt]) {
665         fn to_stmt_item(stmt: &ast::Stmt) -> Option<&ast::Item> {
666             match stmt.node {
667                 ast::StmtKind::Item(ref item) => Some(&**item),
668                 _ => None,
669             }
670         }
671
672         if stmts.is_empty() {
673             return;
674         }
675
676         // Extract leading `use ...;`.
677         let items: Vec<_> = stmts
678             .iter()
679             .take_while(|stmt| to_stmt_item(stmt).map_or(false, is_use_item))
680             .filter_map(|stmt| to_stmt_item(stmt))
681             .collect();
682
683         if items.is_empty() {
684             self.visit_stmt(&stmts[0]);
685             self.walk_stmts(&stmts[1..]);
686         } else {
687             self.visit_items_with_reordering(&items);
688             self.walk_stmts(&stmts[items.len()..]);
689         }
690     }
691
692     fn walk_block_stmts(&mut self, b: &ast::Block) {
693         self.walk_stmts(&b.stmts)
694     }
695
696     fn format_mod(
697         &mut self,
698         m: &ast::Mod,
699         vis: &ast::Visibility,
700         s: Span,
701         ident: ast::Ident,
702         attrs: &[ast::Attribute],
703         is_internal: bool,
704     ) {
705         let vis_str = utils::format_visibility(&self.get_context(), vis);
706         self.push_str(&*vis_str);
707         self.push_str("mod ");
708         // Calling `to_owned()` to work around borrow checker.
709         let ident_str = rewrite_ident(&self.get_context(), ident).to_owned();
710         self.push_str(&ident_str);
711
712         if is_internal {
713             match self.config.brace_style() {
714                 BraceStyle::AlwaysNextLine => {
715                     let indent_str = self.block_indent.to_string_with_newline(self.config);
716                     self.push_str(&indent_str);
717                     self.push_str("{");
718                 }
719                 _ => self.push_str(" {"),
720             }
721             // Hackery to account for the closing }.
722             let mod_lo = self.snippet_provider.span_after(source!(self, s), "{");
723             let body_snippet =
724                 self.snippet(mk_sp(mod_lo, source!(self, m.inner).hi() - BytePos(1)));
725             let body_snippet = body_snippet.trim();
726             if body_snippet.is_empty() {
727                 self.push_str("}");
728             } else {
729                 self.last_pos = mod_lo;
730                 self.block_indent = self.block_indent.block_indent(self.config);
731                 self.visit_attrs(attrs, ast::AttrStyle::Inner);
732                 self.walk_mod_items(m);
733                 self.format_missing_with_indent(source!(self, m.inner).hi() - BytePos(1));
734                 self.close_block(false);
735             }
736             self.last_pos = source!(self, m.inner).hi();
737         } else {
738             self.push_str(";");
739             self.last_pos = source!(self, s).hi();
740         }
741     }
742
743     pub fn format_separate_mod(&mut self, m: &ast::Mod, filemap: &codemap::FileMap) {
744         self.block_indent = Indent::empty();
745         self.walk_mod_items(m);
746         self.format_missing_with_indent(filemap.end_pos);
747     }
748
749     pub fn skip_empty_lines(&mut self, end_pos: BytePos) {
750         while let Some(pos) = self
751             .snippet_provider
752             .opt_span_after(mk_sp(self.last_pos, end_pos), "\n")
753         {
754             if let Some(snippet) = self.opt_snippet(mk_sp(self.last_pos, pos)) {
755                 if snippet.trim().is_empty() {
756                     self.last_pos = pos;
757                 } else {
758                     return;
759                 }
760             }
761         }
762     }
763
764     pub fn with_context<F>(&mut self, f: F) -> Option<String>
765     where
766         F: Fn(&RewriteContext) -> Option<String>,
767     {
768         let result;
769         let macro_rewrite_failure = {
770             let context = self.get_context();
771             result = f(&context);
772             unsafe { *context.macro_rewrite_failure.as_ptr() }
773         };
774         self.macro_rewrite_failure |= macro_rewrite_failure;
775         result
776     }
777
778     pub fn get_context(&self) -> RewriteContext {
779         RewriteContext {
780             parse_session: self.parse_session,
781             codemap: self.codemap,
782             config: self.config,
783             inside_macro: RefCell::new(false),
784             use_block: RefCell::new(false),
785             is_if_else_block: RefCell::new(false),
786             force_one_line_chain: RefCell::new(false),
787             snippet_provider: self.snippet_provider,
788             macro_rewrite_failure: RefCell::new(false),
789             report: self.report.clone(),
790         }
791     }
792 }