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