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