+ format!("::<{}>", type_list.join(", "))
+ };
+ let callee_str = format!(".{}{}", rewrite_ident(context, method_name), type_str);
+ rewrite_call(context, &callee_str, &args[1..], span, shape)
+ }
+}
+
+#[derive(Debug)]
+struct Chain {
+ parent: ChainItem,
+ children: Vec<ChainItem>,
+}
+
+impl Chain {
+ fn from_ast(expr: &ast::Expr, context: &RewriteContext<'_>) -> Chain {
+ let subexpr_list = Self::make_subexpr_list(expr, context);
+
+ // Un-parse the expression tree into ChainItems
+ let mut rev_children = vec![];
+ let mut sub_tries = 0;
+ for subexpr in &subexpr_list {
+ match subexpr.node {
+ ast::ExprKind::Try(_) => sub_tries += 1,
+ _ => {
+ rev_children.push(ChainItem::new(context, subexpr, sub_tries));
+ sub_tries = 0;
+ }
+ }
+ }
+
+ fn is_tries(s: &str) -> bool {
+ s.chars().all(|c| c == '?')
+ }
+
+ fn is_post_comment(s: &str) -> bool {
+ let comment_start_index = s.chars().position(|c| c == '/');
+ if comment_start_index.is_none() {
+ return false;
+ }
+
+ let newline_index = s.chars().position(|c| c == '\n');
+ if newline_index.is_none() {
+ return true;
+ }
+
+ comment_start_index.unwrap() < newline_index.unwrap()
+ }
+
+ fn handle_post_comment(
+ post_comment_span: Span,
+ post_comment_snippet: &str,
+ prev_span_end: &mut BytePos,
+ children: &mut Vec<ChainItem>,
+ ) {
+ let white_spaces: &[_] = &[' ', '\t'];
+ if post_comment_snippet
+ .trim_matches(white_spaces)
+ .starts_with('\n')
+ {
+ // No post comment.
+ return;
+ }
+ let trimmed_snippet = trim_tries(post_comment_snippet);
+ if is_post_comment(&trimmed_snippet) {
+ children.push(ChainItem::comment(
+ post_comment_span,
+ trimmed_snippet.trim().to_owned(),
+ CommentPosition::Back,
+ ));
+ *prev_span_end = post_comment_span.hi();
+ }
+ }
+
+ let parent = rev_children.pop().unwrap();
+ let mut children = vec![];
+ let mut prev_span_end = parent.span.hi();
+ let mut iter = rev_children.into_iter().rev().peekable();
+ if let Some(first_chain_item) = iter.peek() {
+ let comment_span = mk_sp(prev_span_end, first_chain_item.span.lo());
+ let comment_snippet = context.snippet(comment_span);
+ if !is_tries(comment_snippet.trim()) {
+ handle_post_comment(
+ comment_span,
+ comment_snippet,
+ &mut prev_span_end,
+ &mut children,
+ );
+ }
+ }
+ while let Some(chain_item) = iter.next() {
+ let comment_snippet = context.snippet(chain_item.span);
+ // FIXME: Figure out the way to get a correct span when converting `try!` to `?`.
+ let handle_comment =
+ !(context.config.use_try_shorthand() || is_tries(comment_snippet.trim()));
+
+ // Pre-comment
+ if handle_comment {
+ let pre_comment_span = mk_sp(prev_span_end, chain_item.span.lo());
+ let pre_comment_snippet = trim_tries(context.snippet(pre_comment_span));
+ let (pre_comment, _) = extract_pre_comment(&pre_comment_snippet);
+ match pre_comment {
+ Some(ref comment) if !comment.is_empty() => {
+ children.push(ChainItem::comment(
+ pre_comment_span,
+ comment.to_owned(),
+ CommentPosition::Top,
+ ));
+ }
+ _ => (),
+ }
+ }
+
+ prev_span_end = chain_item.span.hi();
+ children.push(chain_item);
+
+ // Post-comment
+ if !handle_comment || iter.peek().is_none() {
+ continue;
+ }
+
+ let next_lo = iter.peek().unwrap().span.lo();
+ let post_comment_span = mk_sp(prev_span_end, next_lo);
+ let post_comment_snippet = context.snippet(post_comment_span);
+ handle_post_comment(
+ post_comment_span,
+ post_comment_snippet,
+ &mut prev_span_end,
+ &mut children,
+ );
+ }
+
+ Chain { parent, children }
+ }
+
+ // Returns a Vec of the prefixes of the chain.
+ // E.g., for input `a.b.c` we return [`a.b.c`, `a.b`, 'a']
+ fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext<'_>) -> Vec<ast::Expr> {
+ let mut subexpr_list = vec![expr.clone()];
+
+ while let Some(subexpr) = Self::pop_expr_chain(subexpr_list.last().unwrap(), context) {
+ subexpr_list.push(subexpr.clone());
+ }
+
+ subexpr_list
+ }
+
+ // Returns the expression's subexpression, if it exists. When the subexpr
+ // is a try! macro, we'll convert it to shorthand when the option is set.
+ fn pop_expr_chain(expr: &ast::Expr, context: &RewriteContext<'_>) -> Option<ast::Expr> {
+ match expr.node {
+ ast::ExprKind::MethodCall(_, ref expressions) => {
+ Some(Self::convert_try(&expressions[0], context))
+ }
+ ast::ExprKind::Field(ref subexpr, _)
+ | ast::ExprKind::Try(ref subexpr)
+ | ast::ExprKind::Await(ast::AwaitOrigin::FieldLike, ref subexpr) => {
+ Some(Self::convert_try(subexpr, context))
+ }
+ _ => None,
+ }
+ }
+
+ fn convert_try(expr: &ast::Expr, context: &RewriteContext<'_>) -> ast::Expr {
+ match expr.node {
+ ast::ExprKind::Mac(ref mac) if context.config.use_try_shorthand() => {
+ if let Some(subexpr) = convert_try_mac(mac, context) {
+ subexpr