]> git.lizzy.rs Git - rust.git/blob - src/items.rs
Merge pull request #80 from cassiersg/master
[rust.git] / src / items.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 // Formatting top-level items - functions, structs, enums, traits, impls.
12
13 use {ReturnIndent, BraceStyle};
14 use utils::make_indent;
15 use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
16 use visitor::FmtVisitor;
17 use syntax::{ast, abi};
18 use syntax::codemap::{self, Span, BytePos};
19 use syntax::print::pprust;
20 use syntax::parse::token;
21
22 impl<'a> FmtVisitor<'a> {
23     pub fn rewrite_fn(&mut self,
24                       indent: usize,
25                       ident: ast::Ident,
26                       fd: &ast::FnDecl,
27                       explicit_self: Option<&ast::ExplicitSelf>,
28                       generics: &ast::Generics,
29                       unsafety: &ast::Unsafety,
30                       constness: &ast::Constness,
31                       abi: &abi::Abi,
32                       vis: ast::Visibility,
33                       span_end: BytePos)
34         -> String
35     {
36         let newline_brace = self.newline_for_brace(&generics.where_clause);
37
38         let mut result = self.rewrite_fn_base(indent,
39                                               ident,
40                                               fd,
41                                               explicit_self,
42                                               generics,
43                                               unsafety,
44                                               constness,
45                                               abi,
46                                               vis,
47                                               span_end,
48                                               newline_brace);
49
50         // Prepare for the function body by possibly adding a newline and indent.
51         // FIXME we'll miss anything between the end of the signature and the start
52         // of the body, but we need more spans from the compiler to solve this.
53         if newline_brace {
54             result.push('\n');
55             result.push_str(&make_indent(indent));
56         } else {
57             result.push(' ');
58         }
59
60         result
61     }
62
63     pub fn rewrite_required_fn(&mut self,
64                                indent: usize,
65                                ident: ast::Ident,
66                                sig: &ast::MethodSig,
67                                span: Span)
68         -> String
69     {
70         // Drop semicolon or it will be interpreted as comment
71         let span_end = span.hi - BytePos(1);
72
73         let mut result = self.rewrite_fn_base(indent,
74                                               ident,
75                                               &sig.decl,
76                                               Some(&sig.explicit_self),
77                                               &sig.generics,
78                                               &sig.unsafety,
79                                               &sig.constness,
80                                               &sig.abi,
81                                               ast::Visibility::Inherited,
82                                               span_end,
83                                               false);
84
85         // Re-attach semicolon
86         result.push(';');
87
88         result
89     }
90
91     fn rewrite_fn_base(&mut self,
92                        indent: usize,
93                        ident: ast::Ident,
94                        fd: &ast::FnDecl,
95                        explicit_self: Option<&ast::ExplicitSelf>,
96                        generics: &ast::Generics,
97                        unsafety: &ast::Unsafety,
98                        constness: &ast::Constness,
99                        abi: &abi::Abi,
100                        vis: ast::Visibility,
101                        span_end: BytePos,
102                        newline_brace: bool)
103         -> String
104     {
105         // FIXME we'll lose any comments in between parts of the function decl, but anyone
106         // who comments there probably deserves what they get.
107
108         let where_clause = &generics.where_clause;
109
110         let mut result = String::with_capacity(1024);
111         // Vis unsafety abi.
112         if vis == ast::Visibility::Public {
113             result.push_str("pub ");
114         }
115         if let &ast::Unsafety::Unsafe = unsafety {
116             result.push_str("unsafe ");
117         }
118         if let &ast::Constness::Const = constness {
119             result.push_str("const ");
120         }
121         if *abi != abi::Rust {
122             result.push_str("extern ");
123             result.push_str(&abi.to_string());
124             result.push(' ');
125         }
126
127         // fn foo
128         result.push_str("fn ");
129         result.push_str(&token::get_ident(ident));
130
131         // Generics.
132         let generics_indent = indent + result.len();
133         result.push_str(&self.rewrite_generics(generics,
134                                                generics_indent,
135                                                span_for_return(&fd.output).lo));
136
137         let ret_str = self.rewrite_return(&fd.output);
138
139         // Args.
140         let (one_line_budget, multi_line_budget, arg_indent) =
141             self.compute_budgets_for_args(&mut result, indent, ret_str.len(), newline_brace);
142
143         debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {}",
144                one_line_budget, multi_line_budget, arg_indent);
145
146         result.push('(');
147         result.push_str(&self.rewrite_args(&fd.inputs,
148                                            explicit_self,
149                                            one_line_budget,
150                                            multi_line_budget,
151                                            arg_indent,
152                                            span_for_return(&fd.output)));
153         result.push(')');
154
155         // Return type.
156         if ret_str.len() > 0 {
157             // If we've already gone multi-line, or the return type would push
158             // over the max width, then put the return type on a new line.
159             if result.contains("\n") ||
160                result.len() + indent + ret_str.len() > config!(max_width) {
161                 let indent = match config!(fn_return_indent) {
162                     ReturnIndent::WithWhereClause => indent + 4,
163                     // TODO we might want to check that using the arg indent doesn't
164                     // blow our budget, and if it does, then fallback to the where
165                     // clause indent.
166                     _ => arg_indent,
167                 };
168
169                 result.push('\n');
170                 result.push_str(&make_indent(indent));
171             } else {
172                 result.push(' ');
173             }
174             result.push_str(&ret_str);
175
176             // Comment between return type and the end of the decl.
177             let snippet_lo = fd.output.span().hi;
178             if where_clause.predicates.len() == 0 {
179                 let snippet_hi = span_end;
180                 let snippet = self.snippet(codemap::mk_sp(snippet_lo, snippet_hi));
181                 let snippet = snippet.trim();
182                 if snippet.len() > 0 {
183                     result.push(' ');
184                     result.push_str(snippet);
185                 }
186             } else {
187                 // FIXME it would be nice to catch comments between the return type
188                 // and the where clause, but we don't have a span for the where
189                 // clause.
190             }
191         }
192
193         // Where clause.
194         result.push_str(&self.rewrite_where_clause(where_clause, indent, span_end));
195
196         result
197     }
198
199     fn rewrite_args(&self,
200                     args: &[ast::Arg],
201                     explicit_self: Option<&ast::ExplicitSelf>,
202                     one_line_budget: usize,
203                     multi_line_budget: usize,
204                     arg_indent: usize,
205                     ret_span: Span)
206         -> String
207     {
208         let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
209         // Account for sugary self.
210         let mut min_args = 1;
211         if let Some(explicit_self) = explicit_self {
212             match explicit_self.node {
213                 ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => {
214                     let lt_str = match lt {
215                         &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)),
216                         &None => String::new(),
217                     };
218                     let mut_str = match m {
219                         &ast::Mutability::MutMutable => "mut ".to_owned(),
220                         &ast::Mutability::MutImmutable => String::new(),
221                     };
222                     arg_item_strs[0] = format!("&{}{}self", lt_str, mut_str);
223                     min_args = 2;
224                 }
225                 ast::ExplicitSelf_::SelfExplicit(ref ty, _) => {
226                     arg_item_strs[0] = format!("self: {}", pprust::ty_to_string(ty));
227                 }
228                 ast::ExplicitSelf_::SelfValue(_) => {
229                     assert!(args.len() >= 1, "&[ast::Arg] shouldn't be empty.");
230
231                     // this hacky solution caused by absence of `Mutability` in `SelfValue`.
232                     let mut_str = {
233                         if let ast::Pat_::PatIdent(ast::BindingMode::BindByValue(mutability), _, _)
234                                 = args[0].pat.node {
235                             match mutability {
236                                 ast::Mutability::MutMutable => "mut ",
237                                 ast::Mutability::MutImmutable => "",
238                             }
239                         } else {
240                             panic!("there is a bug or change in structure of AST, aborting.");
241                         }
242                     };
243
244                     arg_item_strs[0] = format!("{}self", mut_str);
245                     min_args = 2;
246                 }
247                 _ => {}
248             }
249         }
250
251         // Comments between args
252         let mut arg_comments = Vec::new();
253         if min_args == 2 {
254             arg_comments.push("".to_owned());
255         }
256         // TODO if there are no args, there might still be a comment, but without
257         // spans for the comment or parens, there is no chance of getting it right.
258         // You also don't get to put a comment on self, unless it is explicit.
259         if args.len() >= min_args {
260             arg_comments = self.make_comments_for_list(arg_comments,
261                                                        args[min_args-1..].iter(),
262                                                        ",",
263                                                        ")",
264                                                        |arg| arg.pat.span.lo,
265                                                        |arg| arg.ty.span.hi,
266                                                        ret_span.lo);
267         }
268
269         debug!("comments: {:?}", arg_comments);
270
271         // If there are // comments, keep them multi-line.
272         let mut list_tactic = ListTactic::HorizontalVertical;
273         if arg_comments.iter().any(|c| c.contains("//")) {
274             list_tactic = ListTactic::Vertical;
275         }
276
277         assert_eq!(arg_item_strs.len(), arg_comments.len());
278         let arg_strs: Vec<_> = arg_item_strs.into_iter().zip(arg_comments.into_iter()).collect();
279
280         let fmt = ListFormatting {
281             tactic: list_tactic,
282             separator: ",",
283             trailing_separator: SeparatorTactic::Never,
284             indent: arg_indent,
285             h_width: one_line_budget,
286             v_width: multi_line_budget,
287         };
288
289         write_list(&arg_strs, &fmt)
290     }
291
292     // Gets comments in between items of a list.
293     fn make_comments_for_list<T, I, F1, F2>(&self,
294                                             prefix: Vec<String>,
295                                             mut it: I,
296                                             separator: &str,
297                                             terminator: &str,
298                                             get_lo: F1,
299                                             get_hi: F2,
300                                             next_span_start: BytePos)
301         -> Vec<String>
302         where I: Iterator<Item=T>,
303               F1: Fn(&T) -> BytePos,
304               F2: Fn(&T) -> BytePos
305     {
306         let mut result = prefix;
307
308         let mut prev_end = get_hi(&it.next().unwrap());
309         for item in it {
310             let cur_start = get_lo(&item);
311             let snippet = self.snippet(codemap::mk_sp(prev_end, cur_start));
312             let mut snippet = snippet.trim();
313             let white_space: &[_] = &[' ', '\t'];
314             if snippet.starts_with(separator) {
315                 snippet = snippet[separator.len()..].trim_matches(white_space);
316             } else if snippet.ends_with(separator) {
317                 snippet = snippet[..snippet.len()-separator.len()].trim_matches(white_space);
318             }
319             result.push(snippet.to_owned());
320             prev_end = get_hi(&item);
321         }
322         // Get the last commment.
323         // FIXME If you thought the crap with the commas was ugly, just wait.
324         // This is awful. We're going to look from the last item span to the
325         // start of the return type span, then we drop everything after the
326         // first closing paren. Obviously, this will break if there is a
327         // closing paren in the comment.
328         // The fix is comments in the AST or a span for the closing paren.
329         let snippet = self.snippet(codemap::mk_sp(prev_end, next_span_start));
330         let snippet = snippet.trim();
331         let snippet = &snippet[..snippet.find(terminator)
332                                     .unwrap_or(snippet.find(separator).unwrap_or(snippet.len()))];
333         let snippet = snippet.trim();
334         result.push(snippet.to_owned());
335
336         result
337     }
338
339     fn compute_budgets_for_args(&self,
340                                 result: &mut String,
341                                 indent: usize,
342                                 ret_str_len: usize,
343                                 newline_brace: bool)
344         -> (usize, usize, usize)
345     {
346         let mut budgets = None;
347
348         // Try keeping everything on the same line
349         if !result.contains("\n") {
350             // 3 = `() `, space is before ret_string
351             let mut used_space = indent + result.len() + ret_str_len + 3;
352             if !newline_brace {
353                 used_space += 2;
354             }
355             let one_line_budget = if used_space > config!(max_width) {
356                 0
357             } else {
358                 config!(max_width) - used_space
359             };
360
361             // 2 = `()`
362             let used_space = indent + result.len() + 2;
363             let max_space = config!(ideal_width) + config!(leeway);
364             debug!("compute_budgets_for_args: used_space: {}, max_space: {}",
365                    used_space, max_space);
366             if used_space < max_space {
367                 budgets = Some((one_line_budget,
368                                 max_space - used_space,
369                                 indent + result.len() + 1));
370             }
371         }
372
373         // Didn't work. we must force vertical layout and put args on a newline.
374         if let None = budgets {
375             result.push('\n');
376             result.push_str(&make_indent(indent + 4));
377             // 6 = new indent + `()`
378             let used_space = indent + 6;
379             let max_space = config!(ideal_width) + config!(leeway);
380             if used_space > max_space {
381                 // Whoops! bankrupt.
382                 // TODO take evasive action, perhaps kill the indent or something.
383             } else {
384                 // 5 = new indent + `(`
385                 budgets = Some((0, max_space - used_space, indent + 5));
386             }
387         }
388
389         budgets.unwrap()
390     }
391
392     fn newline_for_brace(&self, where_clause: &ast::WhereClause) -> bool {
393         match config!(fn_brace_style) {
394             BraceStyle::AlwaysNextLine => true,
395             BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true,
396             _ => false,
397         }
398     }
399
400     pub fn visit_struct(&mut self,
401                         ident: ast::Ident,
402                         vis: ast::Visibility,
403                         struct_def: &ast::StructDef,
404                         generics: &ast::Generics,
405                         span: Span)
406     {
407         let header_str = self.struct_header(ident, vis);
408         self.changes.push_str_span(span, &header_str);
409
410         if struct_def.fields.len() == 0 {
411             assert!(generics.where_clause.predicates.len() == 0,
412                     "No-field struct with where clause?");
413             assert!(generics.lifetimes.len() == 0, "No-field struct with generics?");
414             assert!(generics.ty_params.len() == 0, "No-field struct with generics?");
415
416             self.changes.push_str_span(span, ";");
417             return;
418         }
419
420         let mut generics_buf = String::new();
421         let generics_str = self.rewrite_generics(generics, self.block_indent, struct_def.fields[0].span.lo);
422         generics_buf.push_str(&generics_str);
423
424         if generics.where_clause.predicates.len() > 0 {
425             generics_buf.push_str(&self.rewrite_where_clause(&generics.where_clause,
426                                                              self.block_indent,
427                                                              struct_def.fields[0].span.lo));
428             generics_buf.push_str(&make_indent(self.block_indent));
429             generics_buf.push_str("\n{");
430
431         } else {
432             generics_buf.push_str(" {");
433         }
434         self.changes.push_str_span(span, &generics_buf);
435
436         let struct_snippet = self.snippet(span);
437         // FIXME this will give incorrect results if there is a { in a commet.
438         self.last_pos = span.lo + BytePos(struct_snippet.find('{').unwrap() as u32 + 1);
439
440         self.block_indent += config!(tab_spaces);
441         for (i, f) in struct_def.fields.iter().enumerate() {
442             self.visit_field(f, i == struct_def.fields.len() - 1, span.lo, &struct_snippet);
443         }
444         self.block_indent -= config!(tab_spaces);
445
446         self.format_missing_with_indent(span.lo + BytePos(struct_snippet.rfind('}').unwrap() as u32));
447         self.changes.push_str_span(span, "}");
448     }
449
450     fn struct_header(&self,
451                      ident: ast::Ident,
452                      vis: ast::Visibility)
453         -> String
454     {
455         let vis = if vis == ast::Visibility::Public {
456             "pub "
457         } else {
458             ""
459         };
460
461         format!("{}struct {}", vis, &token::get_ident(ident))
462     }
463
464     // Field of a struct
465     fn visit_field(&mut self,
466                    field: &ast::StructField,
467                    last_field: bool,
468                    // These two args are for missing spans hacks.
469                    struct_start: BytePos,
470                    struct_snippet: &str)
471     {
472         if self.visit_attrs(&field.node.attrs) {
473             return;
474         }
475         self.format_missing_with_indent(field.span.lo);
476
477         let name = match field.node.kind {
478             ast::StructFieldKind::NamedField(ident, _) => Some(token::get_ident(ident)),
479             ast::StructFieldKind::UnnamedField(_) => None,
480         };
481         let vis = match field.node.kind {
482             ast::StructFieldKind::NamedField(_, vis) |
483             ast::StructFieldKind::UnnamedField(vis) => if vis == ast::Visibility::Public {
484                 "pub "
485             } else {
486                 ""
487             }
488         };
489         let typ = pprust::ty_to_string(&field.node.ty);
490
491         let mut field_str = match name {
492             Some(name) => {
493                 let budget = config!(ideal_width) - self.block_indent;
494                 // 3 is being conservative and assuming that there will be a trailing comma.
495                 if self.block_indent + vis.len() + name.len() + typ.len() + 3 > budget {
496                     format!("{}{}:\n{}{}",
497                             vis,
498                             name,
499                             &make_indent(self.block_indent + config!(tab_spaces)),
500                             typ)
501                 } else {
502                     format!("{}{}: {}", vis, name, typ)
503                 }
504             }
505             None => format!("{}{}", vis, typ),
506         };
507         if !last_field || config!(struct_trailing_comma) {
508             field_str.push(',');
509         }
510         self.changes.push_str_span(field.span, &field_str);
511
512         // This hack makes sure we only add comments etc. after the comma, and
513         // makes sure we don't repeat any commas.
514         let hi = field.span.hi;
515         // FIXME a comma in a comment will break this hack.
516         let comma_pos = match struct_snippet[(hi.0 - struct_start.0) as usize..].find(',') {
517             Some(i) => i,
518             None => 0,
519         };
520         self.last_pos = hi + BytePos(comma_pos as u32 + 1);
521     }
522
523     fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, span_end: BytePos) -> String {
524         // FIXME convert bounds to where clauses where they get too big or if
525         // there is a where clause at all.
526         let mut result = String::new();
527         let lifetimes: &[_] = &generics.lifetimes;
528         let tys: &[_] = &generics.ty_params;
529         if lifetimes.len() + tys.len() == 0 {
530             return result;
531         }
532
533         let budget = config!(max_width) - indent - 2;
534         // TODO might need to insert a newline if the generics are really long
535         result.push('<');
536
537         // Strings for the generics.
538         let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l));
539         let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty));
540
541         // Extract comments between generics.
542         let lt_spans = lifetimes.iter().map(|l| {
543             let hi = if l.bounds.len() == 0 {
544                 l.lifetime.span.hi
545             } else {
546                 l.bounds[l.bounds.len() - 1].span.hi
547             };
548             codemap::mk_sp(l.lifetime.span.lo, hi)
549         });
550         let ty_spans = tys.iter().map(span_for_ty_param);
551         let comments = self.make_comments_for_list(Vec::new(),
552                                                    lt_spans.chain(ty_spans),
553                                                    ",",
554                                                    ">",
555                                                    |sp| sp.lo,
556                                                    |sp| sp.hi,
557                                                    span_end);
558
559         // If there are // comments, keep them multi-line.
560         let mut list_tactic = ListTactic::HorizontalVertical;
561         if comments.iter().any(|c| c.contains("//")) {
562             list_tactic = ListTactic::Vertical;
563         }
564
565         let generics_strs: Vec<_> = lt_strs.chain(ty_strs).zip(comments.into_iter()).collect();
566         let fmt = ListFormatting {
567             tactic: list_tactic,
568             separator: ",",
569             trailing_separator: SeparatorTactic::Never,
570             indent: indent + 1,
571             h_width: budget,
572             v_width: budget,
573         };
574         result.push_str(&write_list(&generics_strs, &fmt));
575
576         result.push('>');
577
578         result
579     }
580
581     fn rewrite_where_clause(&self,
582                             where_clause: &ast::WhereClause,
583                             indent: usize,
584                             span_end: BytePos)
585         -> String
586     {
587         let mut result = String::new();
588         if where_clause.predicates.len() == 0 {
589             return result;
590         }
591
592         result.push('\n');
593         result.push_str(&make_indent(indent + 4));
594         result.push_str("where ");
595
596         let comments = self.make_comments_for_list(Vec::new(),
597                                                    where_clause.predicates.iter(),
598                                                    ",",
599                                                    "{",
600                                                    |pred| span_for_where_pred(pred).lo,
601                                                    |pred| span_for_where_pred(pred).hi,
602                                                    span_end);
603
604         let where_strs: Vec<_> = where_clause.predicates.iter()
605                                                         .map(|p| (self.rewrite_pred(p)))
606                                                         .zip(comments.into_iter())
607                                                         .collect();
608
609         let budget = config!(ideal_width) + config!(leeway) - indent - 10;
610         let fmt = ListFormatting {
611             tactic: ListTactic::Vertical,
612             separator: ",",
613             trailing_separator: SeparatorTactic::Never,
614             indent: indent + 10,
615             h_width: budget,
616             v_width: budget,
617         };
618         result.push_str(&write_list(&where_strs, &fmt));
619
620         result
621     }
622
623     fn rewrite_return(&self, ret: &ast::FunctionRetTy) -> String {
624         match *ret {
625             ast::FunctionRetTy::DefaultReturn(_) => String::new(),
626             ast::FunctionRetTy::NoReturn(_) => "-> !".to_owned(),
627             ast::FunctionRetTy::Return(ref ty) => "-> ".to_owned() + &pprust::ty_to_string(ty),
628         }
629     }
630
631     // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly
632     fn rewrite_fn_input(&self, arg: &ast::Arg) -> String {
633         format!("{}: {}",
634                 pprust::pat_to_string(&arg.pat),
635                 pprust::ty_to_string(&arg.ty))
636     }
637 }
638
639 fn span_for_return(ret: &ast::FunctionRetTy) -> Span {
640     match *ret {
641         ast::FunctionRetTy::NoReturn(ref span) |
642         ast::FunctionRetTy::DefaultReturn(ref span) => span.clone(),
643         ast::FunctionRetTy::Return(ref ty) => ty.span,
644     }
645 }
646
647 fn span_for_ty_param(ty: &ast::TyParam) -> Span {
648     // Note that ty.span is the span for ty.ident, not the whole item.
649     let lo = ty.span.lo;
650     if let Some(ref def) = ty.default {
651         return codemap::mk_sp(lo, def.span.hi);
652     }
653     if ty.bounds.len() == 0 {
654         return ty.span;
655     }
656     let hi = match ty.bounds[ty.bounds.len() - 1] {
657         ast::TyParamBound::TraitTyParamBound(ref ptr, _) => ptr.span.hi,
658         ast::TyParamBound::RegionTyParamBound(ref l) => l.span.hi,
659     };
660     codemap::mk_sp(lo, hi)
661 }
662
663 fn span_for_where_pred(pred: &ast::WherePredicate) -> Span {
664     match *pred {
665         ast::WherePredicate::BoundPredicate(ref p) => p.span,
666         ast::WherePredicate::RegionPredicate(ref p) => p.span,
667         ast::WherePredicate::EqPredicate(ref p) => p.span,
668     }
669 }