]> git.lizzy.rs Git - rust.git/blob - src/overflow.rs
Move maybe_get_args_offset to overflow.rs
[rust.git] / src / overflow.rs
1 // Copyright 2018 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 //! Rewrite a list some items with overflow.
12
13 use config::lists::*;
14 use syntax::parse::token::DelimToken;
15 use syntax::source_map::Span;
16 use syntax::{ast, ptr};
17
18 use closures;
19 use expr::{
20     can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr,
21 };
22 use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator};
23 use macros::MacroArg;
24 use patterns::{can_be_overflowed_pat, TuplePatField};
25 use rewrite::{Rewrite, RewriteContext};
26 use shape::Shape;
27 use source_map::SpanUtils;
28 use spanned::Spanned;
29 use types::{can_be_overflowed_type, SegmentParam};
30 use utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
31
32 use std::cmp::min;
33
34 const SHORT_ITEM_THRESHOLD: usize = 10;
35
36 /// A list of `format!`-like macros, that take a long format string and a list of arguments to
37 /// format.
38 ///
39 /// Organized as a list of `(&str, usize)` tuples, giving the name of the macro and the number of
40 /// arguments before the format string (none for `format!("format", ...)`, one for `assert!(result,
41 /// "format", ...)`, two for `assert_eq!(left, right, "format", ...)`).
42 const SPECIAL_MACRO_WHITELIST: &[(&str, usize)] = &[
43     // format! like macros
44     // From the Rust Standard Library.
45     ("eprint!", 0),
46     ("eprintln!", 0),
47     ("format!", 0),
48     ("format_args!", 0),
49     ("print!", 0),
50     ("println!", 0),
51     ("panic!", 0),
52     ("unreachable!", 0),
53     // From the `log` crate.
54     ("debug!", 0),
55     ("error!", 0),
56     ("info!", 0),
57     ("warn!", 0),
58     // write! like macros
59     ("assert!", 1),
60     ("debug_assert!", 1),
61     ("write!", 1),
62     ("writeln!", 1),
63     // assert_eq! like macros
64     ("assert_eq!", 2),
65     ("assert_ne!", 2),
66     ("debug_assert_eq!", 2),
67     ("debug_assert_ne!", 2),
68 ];
69
70 pub enum OverflowableItem<'a> {
71     Expr(&'a ast::Expr),
72     GenericParam(&'a ast::GenericParam),
73     MacroArg(&'a MacroArg),
74     SegmentParam(&'a SegmentParam<'a>),
75     StructField(&'a ast::StructField),
76     TuplePatField(&'a TuplePatField<'a>),
77     Ty(&'a ast::Ty),
78 }
79
80 impl<'a> Rewrite for OverflowableItem<'a> {
81     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
82         self.map(|item| item.rewrite(context, shape))
83     }
84 }
85
86 impl<'a> Spanned for OverflowableItem<'a> {
87     fn span(&self) -> Span {
88         self.map(|item| item.span())
89     }
90 }
91
92 impl<'a> OverflowableItem<'a> {
93     pub fn map<F, T>(&self, f: F) -> T
94     where
95         F: Fn(&IntoOverflowableItem<'a>) -> T,
96     {
97         match self {
98             OverflowableItem::Expr(expr) => f(*expr),
99             OverflowableItem::GenericParam(gp) => f(*gp),
100             OverflowableItem::MacroArg(macro_arg) => f(*macro_arg),
101             OverflowableItem::SegmentParam(sp) => f(*sp),
102             OverflowableItem::StructField(sf) => f(*sf),
103             OverflowableItem::TuplePatField(pat) => f(*pat),
104             OverflowableItem::Ty(ty) => f(*ty),
105         }
106     }
107
108     pub fn is_simple(&self) -> bool {
109         match self {
110             OverflowableItem::Expr(expr) => is_simple_expr(expr),
111             OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr),
112             _ => false,
113         }
114     }
115
116     pub fn is_expr(&self) -> bool {
117         match self {
118             OverflowableItem::Expr(..) => true,
119             OverflowableItem::MacroArg(MacroArg::Expr(..)) => true,
120             _ => false,
121         }
122     }
123
124     pub fn is_nested_call(&self) -> bool {
125         match self {
126             OverflowableItem::Expr(expr) => is_nested_call(expr),
127             OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_nested_call(expr),
128             _ => false,
129         }
130     }
131
132     pub fn to_expr(&self) -> Option<&'a ast::Expr> {
133         match self {
134             OverflowableItem::Expr(expr) => Some(expr),
135             OverflowableItem::MacroArg(macro_arg) => match macro_arg {
136                 MacroArg::Expr(ref expr) => Some(expr),
137                 _ => None,
138             },
139             _ => None,
140         }
141     }
142
143     pub fn can_be_overflowed(&self, context: &RewriteContext, len: usize) -> bool {
144         match self {
145             OverflowableItem::Expr(expr) => can_be_overflowed_expr(context, expr, len),
146             OverflowableItem::MacroArg(macro_arg) => match macro_arg {
147                 MacroArg::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),
148                 MacroArg::Ty(ref ty) => can_be_overflowed_type(context, ty, len),
149                 MacroArg::Pat(..) => false,
150                 MacroArg::Item(..) => len == 1,
151             },
152             OverflowableItem::SegmentParam(seg) => match seg {
153                 SegmentParam::Type(ty) => can_be_overflowed_type(context, ty, len),
154                 _ => false,
155             },
156             OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len),
157             OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len),
158             _ => false,
159         }
160     }
161 }
162
163 pub trait IntoOverflowableItem<'a>: Rewrite + Spanned {
164     fn into_overflowable_item(&'a self) -> OverflowableItem<'a>;
165 }
166
167 impl<'a, T: 'a + IntoOverflowableItem<'a>> IntoOverflowableItem<'a> for ptr::P<T> {
168     fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
169         (**self).into_overflowable_item()
170     }
171 }
172
173 macro impl_into_overflowable_item_for_ast_node {
174     ($($ast_node:ident),*) => {
175         $(
176             impl<'a> IntoOverflowableItem<'a> for ast::$ast_node {
177                 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
178                     OverflowableItem::$ast_node(self)
179                 }
180             }
181         )*
182     }
183 }
184
185 macro impl_into_overflowable_item_for_rustfmt_types {
186     ([$($ty:ident),*], [$($ty_with_lifetime:ident),*]) => {
187         $(
188             impl<'a> IntoOverflowableItem<'a> for $ty {
189                 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
190                     OverflowableItem::$ty(self)
191                 }
192             }
193         )*
194         $(
195             impl<'a> IntoOverflowableItem<'a> for $ty_with_lifetime<'a> {
196                 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
197                     OverflowableItem::$ty_with_lifetime(self)
198                 }
199             }
200         )*
201     }
202 }
203
204 impl_into_overflowable_item_for_ast_node!(Expr, GenericParam, StructField, Ty);
205 impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]);
206
207 pub fn into_overflowable_list<'a, T>(
208     iter: impl Iterator<Item = &'a T>,
209 ) -> impl Iterator<Item = OverflowableItem<'a>>
210 where
211     T: 'a + IntoOverflowableItem<'a>,
212 {
213     iter.map(|x| IntoOverflowableItem::into_overflowable_item(x))
214 }
215
216 pub fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
217     context: &'a RewriteContext,
218     ident: &'a str,
219     items: impl Iterator<Item = &'a T>,
220     shape: Shape,
221     span: Span,
222     item_max_width: usize,
223     force_separator_tactic: Option<SeparatorTactic>,
224 ) -> Option<String> {
225     Context::new(
226         context,
227         items,
228         ident,
229         shape,
230         span,
231         "(",
232         ")",
233         item_max_width,
234         force_separator_tactic,
235         None,
236     )
237     .rewrite(shape)
238 }
239
240 pub fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
241     context: &'a RewriteContext,
242     ident: &'a str,
243     items: impl Iterator<Item = &'a T>,
244     shape: Shape,
245     span: Span,
246 ) -> Option<String> {
247     Context::new(
248         context,
249         items,
250         ident,
251         shape,
252         span,
253         "<",
254         ">",
255         context.config.max_width(),
256         None,
257         None,
258     )
259     .rewrite(shape)
260 }
261
262 pub fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
263     context: &'a RewriteContext,
264     name: &'a str,
265     items: impl Iterator<Item = &'a T>,
266     shape: Shape,
267     span: Span,
268     force_separator_tactic: Option<SeparatorTactic>,
269     delim_token: Option<DelimToken>,
270 ) -> Option<String> {
271     let (lhs, rhs) = match delim_token {
272         Some(DelimToken::Paren) => ("(", ")"),
273         Some(DelimToken::Brace) => ("{", "}"),
274         _ => ("[", "]"),
275     };
276     Context::new(
277         context,
278         items,
279         name,
280         shape,
281         span,
282         lhs,
283         rhs,
284         context.config.width_heuristics().array_width,
285         force_separator_tactic,
286         Some(("[", "]")),
287     )
288     .rewrite(shape)
289 }
290
291 struct Context<'a> {
292     context: &'a RewriteContext<'a>,
293     items: Vec<OverflowableItem<'a>>,
294     ident: &'a str,
295     prefix: &'static str,
296     suffix: &'static str,
297     one_line_shape: Shape,
298     nested_shape: Shape,
299     span: Span,
300     item_max_width: usize,
301     one_line_width: usize,
302     force_separator_tactic: Option<SeparatorTactic>,
303     custom_delims: Option<(&'a str, &'a str)>,
304 }
305
306 impl<'a> Context<'a> {
307     pub fn new<T: 'a + IntoOverflowableItem<'a>>(
308         context: &'a RewriteContext,
309         items: impl Iterator<Item = &'a T>,
310         ident: &'a str,
311         shape: Shape,
312         span: Span,
313         prefix: &'static str,
314         suffix: &'static str,
315         item_max_width: usize,
316         force_separator_tactic: Option<SeparatorTactic>,
317         custom_delims: Option<(&'a str, &'a str)>,
318     ) -> Context<'a> {
319         let used_width = extra_offset(ident, shape);
320         // 1 = `()`
321         let one_line_width = shape.width.saturating_sub(used_width + 2);
322
323         // 1 = "(" or ")"
324         let one_line_shape = shape
325             .offset_left(last_line_width(ident) + 1)
326             .and_then(|shape| shape.sub_width(1))
327             .unwrap_or(Shape { width: 0, ..shape });
328         let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1);
329         Context {
330             context,
331             items: into_overflowable_list(items).collect(),
332             ident,
333             one_line_shape,
334             nested_shape,
335             span,
336             prefix,
337             suffix,
338             item_max_width,
339             one_line_width,
340             force_separator_tactic,
341             custom_delims,
342         }
343     }
344
345     fn last_item(&self) -> Option<&OverflowableItem> {
346         self.items.last()
347     }
348
349     fn items_span(&self) -> Span {
350         let span_lo = self
351             .context
352             .snippet_provider
353             .span_after(self.span, self.prefix);
354         mk_sp(span_lo, self.span.hi())
355     }
356
357     fn rewrite_last_item_with_overflow(
358         &self,
359         last_list_item: &mut ListItem,
360         shape: Shape,
361     ) -> Option<String> {
362         let last_item = self.last_item()?;
363         let rewrite = match last_item {
364             OverflowableItem::Expr(ref expr) => {
365                 match expr.node {
366                     // When overflowing the closure which consists of a single control flow
367                     // expression, force to use block if its condition uses multi line.
368                     ast::ExprKind::Closure(..) => {
369                         // If the argument consists of multiple closures, we do not overflow
370                         // the last closure.
371                         if closures::args_have_many_closure(&self.items) {
372                             None
373                         } else {
374                             closures::rewrite_last_closure(self.context, expr, shape)
375                         }
376                     }
377                     _ => expr.rewrite(self.context, shape),
378                 }
379             }
380             item @ _ => item.rewrite(self.context, shape),
381         };
382
383         if let Some(rewrite) = rewrite {
384             let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned());
385             last_list_item.item = rewrite_first_line;
386             Some(rewrite)
387         } else {
388             None
389         }
390     }
391
392     fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
393         definitive_tactic(
394             list_items,
395             ListTactic::LimitedHorizontalVertical(self.item_max_width),
396             Separator::Comma,
397             self.one_line_width,
398         )
399     }
400
401     fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
402         // 1 = "("
403         let combine_arg_with_callee = self.items.len() == 1
404             && self.items[0].is_expr()
405             && self.ident.len() < self.context.config.tab_spaces();
406         let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, &self.items);
407
408         // Replace the last item with its first line to see if it fits with
409         // first arguments.
410         let placeholder = if overflow_last {
411             let old_value = *self.context.force_one_line_chain.borrow();
412             match self.last_item() {
413                 Some(OverflowableItem::Expr(expr))
414                     if !combine_arg_with_callee && is_method_call(expr) =>
415                 {
416                     self.context.force_one_line_chain.replace(true);
417                 }
418                 _ => (),
419             }
420             let result = last_item_shape(
421                 &self.items,
422                 list_items,
423                 self.one_line_shape,
424                 self.item_max_width,
425             )
426             .and_then(|arg_shape| {
427                 self.rewrite_last_item_with_overflow(
428                     &mut list_items[self.items.len() - 1],
429                     arg_shape,
430                 )
431             });
432             self.context.force_one_line_chain.replace(old_value);
433             result
434         } else {
435             None
436         };
437
438         let mut tactic = definitive_tactic(
439             &*list_items,
440             ListTactic::LimitedHorizontalVertical(self.item_max_width),
441             Separator::Comma,
442             self.one_line_width,
443         );
444
445         // Replace the stub with the full overflowing last argument if the rewrite
446         // succeeded and its first line fits with the other arguments.
447         match (overflow_last, tactic, placeholder) {
448             (true, DefinitiveListTactic::Horizontal, Some(ref overflowed))
449                 if self.items.len() == 1 =>
450             {
451                 // When we are rewriting a nested function call, we restrict the
452                 // budget for the inner function to avoid them being deeply nested.
453                 // However, when the inner function has a prefix or a suffix
454                 // (e.g. `foo() as u32`), this budget reduction may produce poorly
455                 // formatted code, where a prefix or a suffix being left on its own
456                 // line. Here we explicitlly check those cases.
457                 if count_newlines(overflowed) == 1 {
458                     let rw = self
459                         .items
460                         .last()
461                         .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
462                     let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
463                     if no_newline {
464                         list_items[self.items.len() - 1].item = rw;
465                     } else {
466                         list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
467                     }
468                 } else {
469                     list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
470                 }
471             }
472             (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
473                 list_items[self.items.len() - 1].item = placeholder;
474             }
475             _ if !self.items.is_empty() => {
476                 list_items[self.items.len() - 1].item = self
477                     .items
478                     .last()
479                     .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
480
481                 // Use horizontal layout for a function with a single argument as long as
482                 // everything fits in a single line.
483                 // `self.one_line_width == 0` means vertical layout is forced.
484                 if self.items.len() == 1
485                     && self.one_line_width != 0
486                     && !list_items[0].has_comment()
487                     && !list_items[0].inner_as_ref().contains('\n')
488                     && ::lists::total_item_width(&list_items[0]) <= self.one_line_width
489                 {
490                     tactic = DefinitiveListTactic::Horizontal;
491                 } else {
492                     tactic = self.default_tactic(list_items);
493
494                     if tactic == DefinitiveListTactic::Vertical {
495                         if let Some((all_simple, num_args_before)) =
496                             maybe_get_args_offset(self.ident, &self.items)
497                         {
498                             let one_line = all_simple
499                                 && definitive_tactic(
500                                     &list_items[..num_args_before],
501                                     ListTactic::HorizontalVertical,
502                                     Separator::Comma,
503                                     self.nested_shape.width,
504                                 ) == DefinitiveListTactic::Horizontal
505                                 && definitive_tactic(
506                                     &list_items[num_args_before + 1..],
507                                     ListTactic::HorizontalVertical,
508                                     Separator::Comma,
509                                     self.nested_shape.width,
510                                 ) == DefinitiveListTactic::Horizontal;
511
512                             if one_line {
513                                 tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
514                             };
515                         } else if is_every_expr_simple(&self.items) && no_long_items(list_items) {
516                             tactic = DefinitiveListTactic::Mixed;
517                         }
518                     }
519                 }
520             }
521             _ => (),
522         }
523
524         tactic
525     }
526
527     fn rewrite_items(&self) -> Option<(bool, String)> {
528         let span = self.items_span();
529         let items = itemize_list(
530             self.context.snippet_provider,
531             self.items.iter(),
532             self.suffix,
533             ",",
534             |item| item.span().lo(),
535             |item| item.span().hi(),
536             |item| item.rewrite(self.context, self.nested_shape),
537             span.lo(),
538             span.hi(),
539             true,
540         );
541         let mut list_items: Vec<_> = items.collect();
542
543         // Try letting the last argument overflow to the next line with block
544         // indentation. If its first line fits on one line with the other arguments,
545         // we format the function arguments horizontally.
546         let tactic = self.try_overflow_last_item(&mut list_items);
547         let trailing_separator = if let Some(tactic) = self.force_separator_tactic {
548             tactic
549         } else if !self.context.use_block_indent() {
550             SeparatorTactic::Never
551         } else if tactic == DefinitiveListTactic::Mixed {
552             // We are using mixed layout because everything did not fit within a single line.
553             SeparatorTactic::Always
554         } else {
555             self.context.config.trailing_comma()
556         };
557         let ends_with_newline = match tactic {
558             DefinitiveListTactic::Vertical | DefinitiveListTactic::Mixed => {
559                 self.context.use_block_indent()
560             }
561             _ => false,
562         };
563
564         let fmt = ListFormatting::new(self.nested_shape, self.context.config)
565             .tactic(tactic)
566             .trailing_separator(trailing_separator)
567             .ends_with_newline(ends_with_newline);
568
569         write_list(&list_items, &fmt)
570             .map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str))
571     }
572
573     fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String {
574         let shape = Shape {
575             width: shape.width.saturating_sub(last_line_width(self.ident)),
576             ..shape
577         };
578
579         let (prefix, suffix) = match self.custom_delims {
580             Some((lhs, rhs)) => (lhs, rhs),
581             _ => (self.prefix, self.suffix),
582         };
583
584         // 2 = `()`
585         let fits_one_line = items_str.len() + 2 <= shape.width;
586         let extend_width = if items_str.is_empty() {
587             2
588         } else {
589             first_line_width(items_str) + 1
590         };
591         let nested_indent_str = self
592             .nested_shape
593             .indent
594             .to_string_with_newline(self.context.config);
595         let indent_str = shape
596             .block()
597             .indent
598             .to_string_with_newline(self.context.config);
599         let mut result = String::with_capacity(
600             self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(),
601         );
602         result.push_str(self.ident);
603         result.push_str(prefix);
604         if !self.context.use_block_indent()
605             || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
606             || (is_extendable && extend_width <= shape.width)
607         {
608             result.push_str(items_str);
609         } else {
610             if !items_str.is_empty() {
611                 result.push_str(&nested_indent_str);
612                 result.push_str(items_str);
613             }
614             result.push_str(&indent_str);
615         }
616         result.push_str(suffix);
617         result
618     }
619
620     fn rewrite(&self, shape: Shape) -> Option<String> {
621         let (extendable, items_str) = self.rewrite_items()?;
622
623         // If we are using visual indent style and failed to format, retry with block indent.
624         if !self.context.use_block_indent()
625             && need_block_indent(&items_str, self.nested_shape)
626             && !extendable
627         {
628             self.context.use_block.replace(true);
629             let result = self.rewrite(shape);
630             self.context.use_block.replace(false);
631             return result;
632         }
633
634         Some(self.wrap_items(&items_str, shape, extendable))
635     }
636 }
637
638 fn need_block_indent(s: &str, shape: Shape) -> bool {
639     s.lines().skip(1).any(|s| {
640         s.find(|c| !char::is_whitespace(c))
641             .map_or(false, |w| w + 1 < shape.indent.width())
642     })
643 }
644
645 fn can_be_overflowed<'a>(context: &RewriteContext, items: &[OverflowableItem]) -> bool {
646     items
647         .last()
648         .map_or(false, |x| x.can_be_overflowed(context, items.len()))
649 }
650
651 /// Returns a shape for the last argument which is going to be overflowed.
652 fn last_item_shape(
653     lists: &[OverflowableItem],
654     items: &[ListItem],
655     shape: Shape,
656     args_max_width: usize,
657 ) -> Option<Shape> {
658     if items.len() == 1 && !lists.iter().next()?.is_nested_call() {
659         return Some(shape);
660     }
661     let offset = items.iter().rev().skip(1).fold(0, |acc, i| {
662         // 2 = ", "
663         acc + 2 + i.inner_as_ref().len()
664     });
665     Shape {
666         width: min(args_max_width, shape.width),
667         ..shape
668     }
669     .offset_left(offset)
670 }
671
672 fn shape_from_indent_style(
673     context: &RewriteContext,
674     shape: Shape,
675     overhead: usize,
676     offset: usize,
677 ) -> Shape {
678     let (shape, overhead) = if context.use_block_indent() {
679         let shape = shape
680             .block()
681             .block_indent(context.config.tab_spaces())
682             .with_max_width(context.config);
683         (shape, 1) // 1 = ","
684     } else {
685         (shape.visual_indent(offset), overhead)
686     };
687     Shape {
688         width: shape.width.saturating_sub(overhead),
689         ..shape
690     }
691 }
692
693 fn no_long_items(list: &[ListItem]) -> bool {
694     list.iter()
695         .all(|item| item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD)
696 }
697
698 /// In case special-case style is required, returns an offset from which we start horizontal layout.
699 pub fn maybe_get_args_offset(callee_str: &str, args: &[OverflowableItem]) -> Option<(bool, usize)> {
700     if let Some(&(_, num_args_before)) = args
701         .get(0)?
702         .whitelist()
703         .iter()
704         .find(|&&(s, _)| s == callee_str)
705     {
706         let all_simple = args.len() > num_args_before
707             && is_every_expr_simple(&args[0..num_args_before])
708             && is_every_expr_simple(&args[num_args_before + 1..]);
709
710         Some((all_simple, num_args_before))
711     } else {
712         None
713     }
714 }