]> git.lizzy.rs Git - rust.git/blob - src/functions.rs
Merge pull request #69 from oli-obk/patch-1
[rust.git] / src / functions.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 {ReturnIndent, MAX_WIDTH, BraceStyle,
12      IDEAL_WIDTH, LEEWAY, FN_BRACE_STYLE, FN_RETURN_INDENT};
13 use utils::make_indent;
14 use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
15 use visitor::FmtVisitor;
16 use syntax::{ast, abi};
17 use syntax::codemap::{self, Span, BytePos};
18 use syntax::print::pprust;
19 use syntax::parse::token;
20
21 impl<'a> FmtVisitor<'a> {
22     pub fn rewrite_fn(&mut self,
23                       indent: usize,
24                       ident: ast::Ident,
25                       fd: &ast::FnDecl,
26                       explicit_self: Option<&ast::ExplicitSelf>,
27                       generics: &ast::Generics,
28                       unsafety: &ast::Unsafety,
29                       abi: &abi::Abi,
30                       vis: ast::Visibility,
31                       next_span: Span) // next_span is a nasty hack, its a loose upper
32                                        // bound on any comments after the where clause.
33         -> String
34     {
35         // FIXME we'll lose any comments in between parts of the function decl, but anyone
36         // who comments there probably deserves what they get.
37
38         let where_clause = &generics.where_clause;
39         let newline_brace = self.newline_for_brace(where_clause);
40
41         let mut result = String::with_capacity(1024);
42         // Vis unsafety abi.
43         if vis == ast::Visibility::Public {
44             result.push_str("pub ");
45         }
46         if let &ast::Unsafety::Unsafe = unsafety {
47             result.push_str("unsafe ");
48         }
49         if *abi != abi::Rust {
50             result.push_str("extern ");
51             result.push_str(&abi.to_string());
52             result.push(' ');
53         }
54
55         // fn foo
56         result.push_str("fn ");
57         result.push_str(&token::get_ident(ident));
58
59         // Generics.
60         let generics_indent = indent + result.len();
61         result.push_str(&self.rewrite_generics(generics,
62                                                generics_indent,
63                                                span_for_return(&fd.output)));
64
65         let ret_str = self.rewrite_return(&fd.output);
66
67         // Args.
68         let (one_line_budget, multi_line_budget, arg_indent) =
69             self.compute_budgets_for_args(&mut result, indent, ret_str.len(), newline_brace);
70
71         debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {}",
72                one_line_budget, multi_line_budget, arg_indent);
73
74         result.push('(');
75         result.push_str(&self.rewrite_args(&fd.inputs,
76                                            explicit_self,
77                                            one_line_budget,
78                                            multi_line_budget,
79                                            arg_indent,
80                                            span_for_return(&fd.output)));
81         result.push(')');
82
83         // Return type.
84         if ret_str.len() > 0 {
85             // If we've already gone multi-line, or the return type would push
86             // over the max width, then put the return type on a new line.
87             if result.contains("\n") ||
88                result.len() + indent + ret_str.len() > MAX_WIDTH {
89                 let indent = match FN_RETURN_INDENT {
90                     ReturnIndent::WithWhereClause => indent + 4,
91                     // TODO we might want to check that using the arg indent doesn't
92                     // blow our budget, and if it does, then fallback to the where
93                     // clause indent.
94                     _ => arg_indent,
95                 };
96
97                 result.push('\n');
98                 result.push_str(&make_indent(indent));
99             } else {
100                 result.push(' ');
101             }
102             result.push_str(&ret_str);
103
104             // Comment between return type and the end of the decl.
105             let snippet_lo = fd.output.span().hi;
106             if where_clause.predicates.len() == 0 {
107                 let snippet_hi = next_span.lo;
108                 let snippet = self.snippet(codemap::mk_sp(snippet_lo, snippet_hi));
109                 let snippet = snippet.trim();
110                 if snippet.len() > 0 {
111                     result.push(' ');
112                     result.push_str(snippet);
113                 }
114             } else {
115                 // FIXME it would be nice to catch comments between the return type
116                 // and the where clause, but we don't have a span for the where
117                 // clause.
118             }
119         }
120
121         // Where clause.
122         result.push_str(&self.rewrite_where_clause(where_clause, indent, next_span));
123
124         // Prepare for the function body by possibly adding a newline and indent.
125         // FIXME we'll miss anything between the end of the signature and the start
126         // of the body, but we need more spans from the compiler to solve this.
127         if newline_brace {
128             result.push('\n');
129             result.push_str(&make_indent(self.block_indent));
130         } else {
131             result.push(' ');
132         }
133
134         result
135     }
136
137     fn rewrite_args(&self,
138                     args: &[ast::Arg],
139                     explicit_self: Option<&ast::ExplicitSelf>,
140                     one_line_budget: usize,
141                     multi_line_budget: usize,
142                     arg_indent: usize,
143                     ret_span: Span)
144         -> String
145     {
146         let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
147         // Account for sugary self.
148         let mut min_args = 1;
149         if let Some(explicit_self) = explicit_self {
150             match explicit_self.node {
151                 ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => {
152                     let lt_str = match lt {
153                         &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)),
154                         &None => String::new(),
155                     };
156                     let mut_str = match m {
157                         &ast::Mutability::MutMutable => "mut ".to_owned(),
158                         &ast::Mutability::MutImmutable => String::new(),
159                     };
160                     arg_item_strs[0] = format!("&{}{}self", lt_str, mut_str);
161                     min_args = 2;
162                 }
163                 ast::ExplicitSelf_::SelfExplicit(ref ty, _) => {
164                     arg_item_strs[0] = format!("self: {}", pprust::ty_to_string(ty));
165                 }
166                 ast::ExplicitSelf_::SelfValue(_) => {
167                     assert!(args.len() >= 1, "&[ast::Arg] shouldn't be empty.");
168
169                     // this hacky solution caused by absence of `Mutability` in `SelfValue`.
170                     let mut_str = {
171                         if let ast::Pat_::PatIdent(ast::BindingMode::BindByValue(mutability), _, _)
172                                 = args[0].pat.node {
173                             match mutability {
174                                 ast::Mutability::MutMutable => "mut ",
175                                 ast::Mutability::MutImmutable => "",
176                             }
177                         } else {
178                             panic!("there is a bug or change in structure of AST, aborting.");
179                         }
180                     };
181
182                     arg_item_strs[0] = format!("{}self", mut_str);
183                     min_args = 2;
184                 }
185                 _ => {}
186             }
187         }
188
189         // Comments between args
190         let mut arg_comments = Vec::new();
191         if min_args == 2 {
192             arg_comments.push("".to_owned());
193         }
194         // TODO if there are no args, there might still be a comment, but without
195         // spans for the comment or parens, there is no chance of getting it right.
196         // You also don't get to put a comment on self, unless it is explicit.
197         if args.len() >= min_args {
198             arg_comments = self.make_comments_for_list(arg_comments,
199                                                        args[min_args-1..].iter(),
200                                                        ",",
201                                                        ")",
202                                                        |arg| arg.pat.span.lo,
203                                                        |arg| arg.ty.span.hi,
204                                                        ret_span.lo);
205         }
206
207         debug!("comments: {:?}", arg_comments);
208
209         // If there are // comments, keep them multi-line.
210         let mut list_tactic = ListTactic::HorizontalVertical;
211         if arg_comments.iter().any(|c| c.contains("//")) {
212             list_tactic = ListTactic::Vertical;
213         }
214
215         assert_eq!(arg_item_strs.len(), arg_comments.len());
216         let arg_strs: Vec<_> = arg_item_strs.into_iter().zip(arg_comments.into_iter()).collect();
217
218         let fmt = ListFormatting {
219             tactic: list_tactic,
220             separator: ",",
221             trailing_separator: SeparatorTactic::Never,
222             indent: arg_indent,
223             h_width: one_line_budget,
224             v_width: multi_line_budget,
225         };
226
227         write_list(&arg_strs, &fmt)
228     }
229
230     // Gets comments in between items of a list.
231     fn make_comments_for_list<T, I, F1, F2>(&self,
232                                             prefix: Vec<String>,
233                                             mut it: I,
234                                             separator: &str,
235                                             terminator: &str,
236                                             get_lo: F1,
237                                             get_hi: F2,
238                                             next_span_start: BytePos)
239         -> Vec<String>
240         where I: Iterator<Item=T>,
241               F1: Fn(&T) -> BytePos,
242               F2: Fn(&T) -> BytePos
243     {
244         let mut result = prefix;
245
246         let mut prev_end = get_hi(&it.next().unwrap());
247         for item in it {
248             let cur_start = get_lo(&item);
249             let snippet = self.snippet(codemap::mk_sp(prev_end, cur_start));
250             let mut snippet = snippet.trim();
251             let white_space: &[_] = &[' ', '\t'];
252             if snippet.starts_with(separator) {
253                 snippet = snippet[separator.len()..].trim_matches(white_space);
254             } else if snippet.ends_with(separator) {
255                 snippet = snippet[..snippet.len()-separator.len()].trim_matches(white_space);
256             }
257             result.push(snippet.to_owned());
258             prev_end = get_hi(&item);
259         }
260         // Get the last commment.
261         // FIXME If you thought the crap with the commas was ugly, just wait.
262         // This is awful. We're going to look from the last item span to the
263         // start of the return type span, then we drop everything after the
264         // first closing paren. Obviously, this will break if there is a
265         // closing paren in the comment.
266         // The fix is comments in the AST or a span for the closing paren.
267         let snippet = self.snippet(codemap::mk_sp(prev_end, next_span_start));
268         let snippet = snippet.trim();
269         let snippet = &snippet[..snippet.find(terminator)
270                                     .unwrap_or(snippet.find(separator).unwrap_or(snippet.len()))];
271         let snippet = snippet.trim();
272         result.push(snippet.to_owned());
273
274         result
275     }
276
277     fn compute_budgets_for_args(&self,
278                                 result: &mut String,
279                                 indent: usize,
280                                 ret_str_len: usize,
281                                 newline_brace: bool)
282         -> (usize, usize, usize)
283     {
284         let mut budgets = None;
285
286         // Try keeping everything on the same line
287         if !result.contains("\n") {
288             // 3 = `() `, space is before ret_string
289             let mut used_space = indent + result.len() + ret_str_len + 3;
290             if !newline_brace {
291                 used_space += 2;
292             }
293             let one_line_budget = if used_space > MAX_WIDTH {
294                 0
295             } else {
296                 MAX_WIDTH - used_space
297             };
298
299             // 2 = `()`
300             let used_space = indent + result.len() + 2;
301             let max_space = IDEAL_WIDTH + LEEWAY;
302             debug!("compute_budgets_for_args: used_space: {}, max_space: {}",
303                    used_space, max_space);
304             if used_space < max_space {
305                 budgets = Some((one_line_budget,
306                                 max_space - used_space,
307                                 indent + result.len() + 1));
308             }
309         }
310
311         // Didn't work. we must force vertical layout and put args on a newline.
312         if let None = budgets {
313             result.push('\n');
314             result.push_str(&make_indent(indent + 4));
315             // 6 = new indent + `()`
316             let used_space = indent + 6;
317             let max_space = IDEAL_WIDTH + LEEWAY;
318             if used_space > max_space {
319                 // Whoops! bankrupt.
320                 // TODO take evasive action, perhaps kill the indent or something.
321             } else {
322                 // 5 = new indent + `(`
323                 budgets = Some((0, max_space - used_space, indent + 5));
324             }
325         }
326
327         budgets.unwrap()
328     }
329
330     fn newline_for_brace(&self, where_clause: &ast::WhereClause) -> bool {
331         match FN_BRACE_STYLE {
332             BraceStyle::AlwaysNextLine => true,
333             BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true,
334             _ => false,
335         }
336     }
337
338     fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, ret_span: Span) -> String {
339         // FIXME convert bounds to where clauses where they get too big or if
340         // there is a where clause at all.
341         let mut result = String::new();
342         let lifetimes: &[_] = &generics.lifetimes;
343         let tys: &[_] = &generics.ty_params;
344         if lifetimes.len() + tys.len() == 0 {
345             return result;
346         }
347
348         let budget = MAX_WIDTH - indent - 2;
349         // TODO might need to insert a newline if the generics are really long
350         result.push('<');
351
352         // Strings for the generics.
353         let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l));
354         let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty));
355
356         // Extract comments between generics.
357         let lt_spans = lifetimes.iter().map(|l| {
358             let hi = if l.bounds.len() == 0 {
359                 l.lifetime.span.hi
360             } else {
361                 l.bounds[l.bounds.len() - 1].span.hi
362             };
363             codemap::mk_sp(l.lifetime.span.lo, hi)
364         });
365         let ty_spans = tys.iter().map(span_for_ty_param);
366         let comments = self.make_comments_for_list(Vec::new(),
367                                                    lt_spans.chain(ty_spans),
368                                                    ",",
369                                                    ">",
370                                                    |sp| sp.lo,
371                                                    |sp| sp.hi,
372                                                    ret_span.lo);
373
374         // If there are // comments, keep them multi-line.
375         let mut list_tactic = ListTactic::HorizontalVertical;
376         if comments.iter().any(|c| c.contains("//")) {
377             list_tactic = ListTactic::Vertical;
378         }
379
380         let generics_strs: Vec<_> = lt_strs.chain(ty_strs).zip(comments.into_iter()).collect();
381         let fmt = ListFormatting {
382             tactic: list_tactic,
383             separator: ",",
384             trailing_separator: SeparatorTactic::Never,
385             indent: indent + 1,
386             h_width: budget,
387             v_width: budget,
388         };
389         result.push_str(&write_list(&generics_strs, &fmt));
390
391         result.push('>');
392
393         result
394     }
395
396     fn rewrite_where_clause(&self,
397                             where_clause: &ast::WhereClause,
398                             indent: usize,
399                             next_span: Span)
400         -> String
401     {
402         let mut result = String::new();
403         if where_clause.predicates.len() == 0 {
404             return result;
405         }
406
407         result.push('\n');
408         result.push_str(&make_indent(indent + 4));
409         result.push_str("where ");
410
411         let comments = self.make_comments_for_list(Vec::new(),
412                                                    where_clause.predicates.iter(),
413                                                    ",",
414                                                    "{",
415                                                    |pred| span_for_where_pred(pred).lo,
416                                                    |pred| span_for_where_pred(pred).hi,
417                                                    next_span.lo);
418         let where_strs: Vec<_> = where_clause.predicates.iter()
419                                                         .map(|p| (self.rewrite_pred(p)))
420                                                         .zip(comments.into_iter())
421                                                         .collect();
422
423         let budget = IDEAL_WIDTH + LEEWAY - indent - 10;
424         let fmt = ListFormatting {
425             tactic: ListTactic::Vertical,
426             separator: ",",
427             trailing_separator: SeparatorTactic::Never,
428             indent: indent + 10,
429             h_width: budget,
430             v_width: budget,
431         };
432         result.push_str(&write_list(&where_strs, &fmt));
433
434         result
435     }
436
437     fn rewrite_return(&self, ret: &ast::FunctionRetTy) -> String {
438         match *ret {
439             ast::FunctionRetTy::DefaultReturn(_) => String::new(),
440             ast::FunctionRetTy::NoReturn(_) => "-> !".to_owned(),
441             ast::FunctionRetTy::Return(ref ty) => "-> ".to_owned() + &pprust::ty_to_string(ty),
442         }
443     }
444
445     // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly
446     fn rewrite_fn_input(&self, arg: &ast::Arg) -> String {
447         format!("{}: {}",
448                 pprust::pat_to_string(&arg.pat),
449                 pprust::ty_to_string(&arg.ty))
450     }
451 }
452
453 fn span_for_return(ret: &ast::FunctionRetTy) -> Span {
454     match *ret {
455         ast::FunctionRetTy::NoReturn(ref span) |
456         ast::FunctionRetTy::DefaultReturn(ref span) => span.clone(),
457         ast::FunctionRetTy::Return(ref ty) => ty.span,
458     }
459 }
460
461 fn span_for_ty_param(ty: &ast::TyParam) -> Span {
462     // Note that ty.span is the span for ty.ident, not the whole item.
463     let lo = ty.span.lo;
464     if let Some(ref def) = ty.default {
465         return codemap::mk_sp(lo, def.span.hi);
466     }
467     if ty.bounds.len() == 0 {
468         return ty.span;
469     }
470     let hi = match ty.bounds[ty.bounds.len() - 1] {
471         ast::TyParamBound::TraitTyParamBound(ref ptr, _) => ptr.span.hi,
472         ast::TyParamBound::RegionTyParamBound(ref l) => l.span.hi,
473     };
474     codemap::mk_sp(lo, hi)
475 }
476
477 fn span_for_where_pred(pred: &ast::WherePredicate) -> Span {
478     match *pred {
479         ast::WherePredicate::BoundPredicate(ref p) => p.span,
480         ast::WherePredicate::RegionPredicate(ref p) => p.span,
481         ast::WherePredicate::EqPredicate(ref p) => p.span,
482     }
483 }