]> git.lizzy.rs Git - rust.git/blob - src/functions.rs
e7f80922e8af2cce971c076a4f7179a5c622be19
[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         result.push('(');
72         result.push_str(&self.rewrite_args(&fd.inputs,
73                                            explicit_self,
74                                            one_line_budget,
75                                            multi_line_budget,
76                                            arg_indent,
77                                            span_for_return(&fd.output)));
78         result.push(')');
79
80         // Where clause.
81         result.push_str(&self.rewrite_where_clause(where_clause, indent, next_span));
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                     ReturnIndent::WithArgs => 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
105         // TODO any comments here?
106
107         // Prepare for the function body by possibly adding a newline and indent.
108         // FIXME we'll miss anything between the end of the signature and the start
109         // of the body, but we need more spans from the compiler to solve this.
110         if newline_brace {
111             result.push('\n');
112             result.push_str(&make_indent(self.block_indent));
113         } else {
114             result.push(' ');
115         }
116
117         result
118     }
119
120     fn rewrite_args(&self,
121                     args: &[ast::Arg],
122                     explicit_self: Option<&ast::ExplicitSelf>,
123                     one_line_budget: usize,
124                     multi_line_budget: usize,
125                     arg_indent: usize,
126                     ret_span: Span)
127         -> String
128     {
129         let mut arg_item_strs: Vec<_> = args.iter().map(|a| self.rewrite_fn_input(a)).collect();
130         // Account for sugary self.
131         let mut min_args = 1;
132         if let Some(explicit_self) = explicit_self {
133             match explicit_self.node {
134                 ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => {
135                     let lt_str = match lt {
136                         &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)),
137                         &None => String::new(),
138                     };
139                     let mut_str = match m {
140                         &ast::Mutability::MutMutable => "mut ".to_string(),
141                         &ast::Mutability::MutImmutable => String::new(),
142                     };
143                     arg_item_strs[0] = format!("&{}{}self", lt_str, mut_str);
144                     min_args = 2;
145                 }
146                 ast::ExplicitSelf_::SelfExplicit(ref ty, _) => {
147                     arg_item_strs[0] = format!("self: {}", pprust::ty_to_string(ty));
148                 }
149                 ast::ExplicitSelf_::SelfValue(_) => {
150                     arg_item_strs[0] = "self".to_string();
151                     min_args = 2;
152                 }
153                 _ => {}
154             }
155         }
156
157         // Comments between args
158         let mut arg_comments = Vec::new();
159         if min_args == 2 {
160             arg_comments.push("".to_string());
161         }
162         // TODO if there are no args, there might still be a comment, but without
163         // spans for the comment or parens, there is no chance of getting it right.
164         // You also don't get to put a comment on self, unless it is explicit.
165         if args.len() >= min_args {
166             arg_comments = self.make_comments_for_list(arg_comments,
167                                                        args[min_args-1..].iter(),
168                                                        ",",
169                                                        ")",
170                                                        |arg| arg.pat.span.lo,
171                                                        |arg| arg.ty.span.hi,
172                                                        ret_span.lo);
173         }
174
175         debug!("comments: {:?}", arg_comments);
176
177         // If there are // comments, keep them multi-line.
178         let mut list_tactic = ListTactic::HorizontalVertical;
179         if arg_comments.iter().any(|c| c.contains("//")) {
180             list_tactic = ListTactic::Vertical;
181         }
182
183         assert_eq!(arg_item_strs.len(), arg_comments.len());
184         let arg_strs: Vec<_> = arg_item_strs.into_iter().zip(arg_comments.into_iter()).collect();
185
186         let fmt = ListFormatting {
187             tactic: list_tactic,
188             separator: ",",
189             trailing_separator: SeparatorTactic::Never,
190             indent: arg_indent,
191             h_width: one_line_budget,
192             v_width: multi_line_budget,
193         };
194
195         write_list(&arg_strs, &fmt)
196     }
197
198     // Gets comments in between items of a list. 
199     fn make_comments_for_list<T, I, F1, F2>(&self,
200                                             prefix: Vec<String>,
201                                             mut it: I,
202                                             separator: &str,
203                                             terminator: &str,
204                                             get_lo: F1,
205                                             get_hi: F2,
206                                             next_span_start: BytePos)
207         -> Vec<String>
208         where I: Iterator<Item=T>,
209               F1: Fn(&T) -> BytePos,
210               F2: Fn(&T) -> BytePos
211     {
212         let mut result = prefix;
213
214         let mut prev_end = get_hi(&it.next().unwrap());
215         for item in it {
216             let cur_start = get_lo(&item);
217             let snippet = self.snippet(codemap::mk_sp(prev_end, cur_start));
218             let mut snippet = snippet.trim();
219             if snippet.starts_with(separator) {
220                 snippet = snippet[1..].trim();
221             } else if snippet.ends_with(separator) {
222                 snippet = snippet[..snippet.len()-1].trim();
223             }
224             result.push(snippet.to_string());
225             prev_end = get_hi(&item);
226         }
227         // Get the last commment.
228         // FIXME If you thought the crap with the commas was ugly, just wait.
229         // This is awful. We're going to look from the last item span to the
230         // start of the return type span, then we drop everything after the
231         // first closing paren. Obviously, this will break if there is a 
232         // closing paren in the comment.
233         // The fix is comments in the AST or a span for the closing paren.
234         let snippet = self.snippet(codemap::mk_sp(prev_end, next_span_start));
235         let snippet = snippet.trim();
236         let snippet = &snippet[..snippet.find(terminator).unwrap()];
237         let snippet = snippet.trim();
238         result.push(snippet.to_string());
239
240         result
241     }
242
243     fn compute_budgets_for_args(&self,
244                                 result: &mut String,
245                                 indent: usize,
246                                 ret_str_len: usize,
247                                 newline_brace: bool)
248         -> (usize, usize, usize)
249     {
250         let mut budgets = None;
251
252         // Try keeping everything on the same line
253         if !result.contains("\n") {
254             // 3 = `() `, space is before ret_string
255             let mut used_space = indent + result.len() + 3 + ret_str_len;
256             if newline_brace {
257                 used_space += 2;
258             }
259             let one_line_budget = if used_space > MAX_WIDTH {
260                 0
261             } else {
262                 MAX_WIDTH - used_space
263             };
264
265             let used_space = indent + result.len() + 2;
266             let max_space = IDEAL_WIDTH + LEEWAY;
267             if used_space < max_space {
268                 budgets = Some((one_line_budget,
269                                 // 2 = `()`
270                                 max_space - used_space,
271                                 indent + result.len() + 1));
272             }
273         }
274
275         // Didn't work. we must force vertical layout and put args on a newline.
276         if let None = budgets {
277             result.push('\n');
278             result.push_str(&make_indent(indent + 4));
279             // 6 = new indent + `()`
280             let used_space = indent + 6;
281             let max_space = IDEAL_WIDTH + LEEWAY;
282             if used_space > max_space {
283                 // Whoops! bankrupt.
284                 // TODO take evasive action, perhaps kill the indent or something.
285             } else {
286                 // 5 = new indent + `(`
287                 budgets = Some((0, max_space - used_space, indent + 5));
288             }
289         }
290
291         budgets.unwrap()
292     }
293
294     fn newline_for_brace(&self, where_clause: &ast::WhereClause) -> bool {
295         match FN_BRACE_STYLE {
296             BraceStyle::AlwaysNextLine => true,
297             BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true,
298             _ => false,
299         }
300     }
301
302     fn rewrite_generics(&self, generics: &ast::Generics, indent: usize, ret_span: Span) -> String {
303         // FIXME convert bounds to where clauses where they get too big or if
304         // there is a where clause at all.
305         let mut result = String::new();
306         let lifetimes: &[_] = &generics.lifetimes;
307         let tys: &[_] = &generics.ty_params;
308         if lifetimes.len() + tys.len() == 0 {
309             return result;
310         }
311
312         let budget = MAX_WIDTH - indent - 2;
313         // TODO might need to insert a newline if the generics are really long
314         result.push('<');
315
316         // Strings for the generics.
317         let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l));
318         let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty));
319
320         // Extract comments between generics.
321         let lt_spans = lifetimes.iter().map(|l| {
322             let hi = if l.bounds.len() == 0 {
323                 l.lifetime.span.hi
324             } else {
325                 l.bounds[l.bounds.len() - 1].span.hi
326             };
327             codemap::mk_sp(l.lifetime.span.lo, hi)
328         });
329         let ty_spans = tys.iter().map(span_for_ty_param);
330         let comments = self.make_comments_for_list(Vec::new(),
331                                                    lt_spans.chain(ty_spans),
332                                                    ",",
333                                                    ">",
334                                                    |sp| sp.lo,
335                                                    |sp| sp.hi,
336                                                    ret_span.lo);
337
338         // If there are // comments, keep them multi-line.
339         let mut list_tactic = ListTactic::HorizontalVertical;
340         if comments.iter().any(|c| c.contains("//")) {
341             list_tactic = ListTactic::Vertical;
342         }
343
344         let generics_strs: Vec<_> = lt_strs.chain(ty_strs).zip(comments.into_iter()).collect();
345         let fmt = ListFormatting {
346             tactic: list_tactic,
347             separator: ",",
348             trailing_separator: SeparatorTactic::Never,
349             indent: indent + 1,
350             h_width: budget,
351             v_width: budget,
352         };
353         result.push_str(&write_list(&generics_strs, &fmt));
354
355         result.push('>');
356
357         result
358     }
359
360     fn rewrite_where_clause(&self,
361                             where_clause: &ast::WhereClause,
362                             indent: usize,
363                             next_span: Span)
364         -> String
365     {
366         let mut result = String::new();
367         if where_clause.predicates.len() == 0 {
368             return result;
369         }
370
371         result.push('\n');
372         result.push_str(&make_indent(indent + 4));
373         result.push_str("where ");
374
375         // TODO uncomment when spans are fixed
376         //println!("{:?} {:?}", where_clause.predicates.iter().map(|p| self.snippet(span_for_where_pred(p))).collect::<Vec<_>>(), next_span.lo);
377         // let comments = self.make_comments_for_list(Vec::new(),
378         //                                            where_clause.predicates.iter(),
379         //                                            ",",
380         //                                            "{",
381         //                                            |pred| span_for_where_pred(pred).lo,
382         //                                            |pred| span_for_where_pred(pred).hi,
383         //                                            next_span.lo);
384         let comments = vec![String::new(); where_clause.predicates.len()];
385         let where_strs: Vec<_> = where_clause.predicates.iter()
386                                                         .map(|p| (self.rewrite_pred(p)))
387                                                         .zip(comments.into_iter())
388                                                         .collect();
389
390         let budget = IDEAL_WIDTH + LEEWAY - indent - 10;
391         let fmt = ListFormatting {
392             tactic: ListTactic::Vertical,
393             separator: ",",
394             trailing_separator: SeparatorTactic::Never,
395             indent: indent + 10,
396             h_width: budget,
397             v_width: budget,
398         };
399         result.push_str(&write_list(&where_strs, &fmt));
400
401         result
402     }
403
404     fn rewrite_return(&self, ret: &ast::FunctionRetTy) -> String {
405         match *ret {
406             ast::FunctionRetTy::DefaultReturn(_) => String::new(),
407             ast::FunctionRetTy::NoReturn(_) => "-> !".to_string(),
408             ast::FunctionRetTy::Return(ref ty) => "-> ".to_string() + &pprust::ty_to_string(ty),
409         }        
410     }
411
412     // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly
413     fn rewrite_fn_input(&self, arg: &ast::Arg) -> String {
414         format!("{}: {}",
415                 pprust::pat_to_string(&arg.pat),
416                 pprust::ty_to_string(&arg.ty))
417     }
418 }
419
420 fn span_for_return(ret: &ast::FunctionRetTy) -> Span {
421     match *ret {
422         ast::FunctionRetTy::NoReturn(ref span) |
423         ast::FunctionRetTy::DefaultReturn(ref span) => span.clone(),
424         ast::FunctionRetTy::Return(ref ty) => ty.span,
425     }
426 }
427
428 fn span_for_ty_param(ty: &ast::TyParam) -> Span {
429     // Note that ty.span is the span for ty.ident, not the whole item.
430     let lo = ty.span.lo;
431     if let Some(ref def) = ty.default {
432         return codemap::mk_sp(lo, def.span.hi);
433     }
434     if ty.bounds.len() == 0 {
435         return ty.span;
436     }
437     let hi = match ty.bounds[ty.bounds.len() - 1] {
438         ast::TyParamBound::TraitTyParamBound(ref ptr, _) => ptr.span.hi,
439         ast::TyParamBound::RegionTyParamBound(ref l) => l.span.hi,
440     };
441     codemap::mk_sp(lo, hi)
442 }
443
444 fn span_for_where_pred(pred: &ast::WherePredicate) -> Span {
445     match *pred {
446         ast::WherePredicate::BoundPredicate(ref p) => p.span,
447         ast::WherePredicate::RegionPredicate(ref p) => p.span,
448         ast::WherePredicate::EqPredicate(ref p) => p.span,
449     }
450 }