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