Skip to content

Commit c1949f5

Browse files
committed
Merge pull request #893 from marcusklaas/try-shorthand
Try shorthand
2 parents fc2549b + eae2921 commit c1949f5

23 files changed

+270
-155
lines changed

src/chains.rs

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
/// Formatting of chained expressions, i.e. expressions which are chained by
12-
/// dots: struct and enum field access and method calls.
12+
/// dots: struct and enum field access, method calls, and try shorthand (?).
1313
///
1414
/// Instead of walking these subexpressions one-by-one, as is our usual strategy
1515
/// for expression formatting, we collect maximal sequences of these expressions
@@ -81,24 +81,23 @@
8181
/// true, then we allow the last method call to spill over multiple lines without
8282
/// forcing the rest of the chain to be split.
8383
84-
8584
use Indent;
8685
use rewrite::{Rewrite, RewriteContext};
8786
use utils::{wrap_str, first_line_width};
8887
use expr::rewrite_call;
8988
use config::BlockIndentStyle;
89+
use macros::convert_try_mac;
9090

9191
use syntax::{ast, ptr};
9292
use syntax::codemap::{mk_sp, Span};
9393

94-
9594
pub fn rewrite_chain(expr: &ast::Expr,
9695
context: &RewriteContext,
9796
width: usize,
9897
offset: Indent)
9998
-> Option<String> {
10099
let total_span = expr.span;
101-
let (parent, subexpr_list) = make_subexpr_list(expr);
100+
let (parent, subexpr_list) = make_subexpr_list(expr, context);
102101

103102
// Parent is the first item in the chain, e.g., `foo` in `foo.bar.baz()`.
104103
let parent_block_indent = chain_base_indent(context, offset);
@@ -107,11 +106,16 @@ pub fn rewrite_chain(expr: &ast::Expr,
107106

108107
// Decide how to layout the rest of the chain. `extend` is true if we can
109108
// put the first non-parent item on the same line as the parent.
110-
let (indent, extend) = if !parent_rewrite.contains('\n') && is_continuable(parent) ||
109+
let (indent, extend) = if !parent_rewrite.contains('\n') && is_continuable(&parent) ||
111110
parent_rewrite.len() <= context.config.tab_spaces {
112-
// Try and put at least the first two items on the same line.
113-
(chain_indent(context, offset + Indent::new(0, parent_rewrite.len())), true)
114-
} else if is_block_expr(parent, &parent_rewrite) {
111+
112+
let indent = if let ast::ExprKind::Try(..) = subexpr_list.last().unwrap().node {
113+
parent_block_indent.block_indent(context.config)
114+
} else {
115+
chain_indent(context, offset + Indent::new(0, parent_rewrite.len()))
116+
};
117+
(indent, true)
118+
} else if is_block_expr(&parent, &parent_rewrite) {
115119
// The parent is a block, so align the rest of the chain with the closing
116120
// brace.
117121
(parent_block_indent, false)
@@ -184,12 +188,29 @@ pub fn rewrite_chain(expr: &ast::Expr,
184188
wrap_str(format!("{}{}{}",
185189
parent_rewrite,
186190
first_connector,
187-
rewrites.join(&connector)),
191+
join_rewrites(&rewrites, &subexpr_list, &connector)),
188192
context.config.max_width,
189193
width,
190194
offset)
191195
}
192196

197+
fn join_rewrites(rewrites: &[String], subexps: &[ast::Expr], connector: &str) -> String {
198+
let mut rewrite_iter = rewrites.iter();
199+
let mut result = rewrite_iter.next().unwrap().clone();
200+
let mut subexpr_iter = subexps.iter().rev();
201+
subexpr_iter.next();
202+
203+
for (rewrite, expr) in rewrite_iter.zip(subexpr_iter) {
204+
match expr.node {
205+
ast::ExprKind::Try(_) => (),
206+
_ => result.push_str(connector),
207+
};
208+
result.push_str(&rewrite[..]);
209+
}
210+
211+
result
212+
}
213+
193214
// States whether an expression's last line exclusively consists of closing
194215
// parens, braces, and brackets in its idiomatic formatting.
195216
fn is_block_expr(expr: &ast::Expr, repr: &str) -> bool {
@@ -213,21 +234,11 @@ fn is_block_expr(expr: &ast::Expr, repr: &str) -> bool {
213234

214235
// Returns the root of the chain and a Vec of the prefixes of the rest of the chain.
215236
// E.g., for input `a.b.c` we return (`a`, [`a.b.c`, `a.b`])
216-
fn make_subexpr_list(mut expr: &ast::Expr) -> (&ast::Expr, Vec<&ast::Expr>) {
217-
fn pop_expr_chain(expr: &ast::Expr) -> Option<&ast::Expr> {
218-
match expr.node {
219-
ast::ExprKind::MethodCall(_, _, ref expressions) => Some(&expressions[0]),
220-
ast::ExprKind::TupField(ref subexpr, _) |
221-
ast::ExprKind::Field(ref subexpr, _) => Some(subexpr),
222-
_ => None,
223-
}
224-
}
225-
226-
let mut subexpr_list = vec![expr];
237+
fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext) -> (ast::Expr, Vec<ast::Expr>) {
238+
let mut subexpr_list = vec![expr.clone()];
227239

228-
while let Some(subexpr) = pop_expr_chain(expr) {
229-
subexpr_list.push(subexpr);
230-
expr = subexpr;
240+
while let Some(subexpr) = pop_expr_chain(subexpr_list.last().unwrap(), context) {
241+
subexpr_list.push(subexpr.clone());
231242
}
232243

233244
let parent = subexpr_list.pop().unwrap();
@@ -293,6 +304,33 @@ fn rewrite_method_call_with_overflow(expr_kind: &ast::ExprKind,
293304
}
294305
}
295306

307+
// Returns the expression's subexpression, if it exists. When the subexpr
308+
// is a try! macro, we'll convert it to shorthand when the option is set.
309+
fn pop_expr_chain(expr: &ast::Expr, context: &RewriteContext) -> Option<ast::Expr> {
310+
match expr.node {
311+
ast::ExprKind::MethodCall(_, _, ref expressions) => {
312+
Some(convert_try(&expressions[0], context))
313+
}
314+
ast::ExprKind::TupField(ref subexpr, _) |
315+
ast::ExprKind::Field(ref subexpr, _) |
316+
ast::ExprKind::Try(ref subexpr) => Some(convert_try(subexpr, context)),
317+
_ => None,
318+
}
319+
}
320+
321+
fn convert_try(expr: &ast::Expr, context: &RewriteContext) -> ast::Expr {
322+
match expr.node {
323+
ast::ExprKind::Mac(ref mac) if context.config.use_try_shorthand => {
324+
if let Some(subexpr) = convert_try_mac(mac, context) {
325+
subexpr
326+
} else {
327+
expr.clone()
328+
}
329+
}
330+
_ => expr.clone(),
331+
}
332+
}
333+
296334
// Rewrite the last element in the chain `expr`. E.g., given `a.b.c` we rewrite
297335
// `.c`.
298336
fn rewrite_chain_subexpr(expr: &ast::Expr,
@@ -328,6 +366,13 @@ fn rewrite_chain_subexpr(expr: &ast::Expr,
328366
None
329367
}
330368
}
369+
ast::ExprKind::Try(_) => {
370+
if width >= 1 {
371+
Some("?".into())
372+
} else {
373+
None
374+
}
375+
}
331376
_ => unreachable!(),
332377
}
333378
}

src/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ create_config! {
392392
match_wildcard_trailing_comma: bool, true, "Put a trailing comma after a wildcard arm";
393393
closure_block_indent_threshold: isize, 5, "How many lines a closure must have before it is \
394394
block indented. -1 means never use block indent.";
395+
use_try_shorthand: bool, false, "Replace uses of the try! macro by the ? shorthand";
395396
write_mode: WriteMode, WriteMode::Replace,
396397
"What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
397398
}

src/expr.rs

Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -136,18 +136,25 @@ impl Rewrite for ast::Expr {
136136
Some(ident) => format!(" {}", ident.node),
137137
None => String::new(),
138138
};
139-
wrap_str(format!("continue{}", id_str), context.config.max_width, width, offset)
139+
wrap_str(format!("continue{}", id_str),
140+
context.config.max_width,
141+
width,
142+
offset)
140143
}
141144
ast::ExprKind::Break(ref opt_ident) => {
142145
let id_str = match *opt_ident {
143146
Some(ident) => format!(" {}", ident.node),
144147
None => String::new(),
145148
};
146-
wrap_str(format!("break{}", id_str), context.config.max_width, width, offset)
149+
wrap_str(format!("break{}", id_str),
150+
context.config.max_width,
151+
width,
152+
offset)
147153
}
148154
ast::ExprKind::Closure(capture, ref fn_decl, ref body, _) => {
149155
rewrite_closure(capture, fn_decl, body, self.span, context, width, offset)
150156
}
157+
ast::ExprKind::Try(..) |
151158
ast::ExprKind::Field(..) |
152159
ast::ExprKind::TupField(..) |
153160
ast::ExprKind::MethodCall(..) => rewrite_chain(self, context, width, offset),
@@ -199,21 +206,15 @@ impl Rewrite for ast::Expr {
199206
rewrite_unary_prefix(context, delim, &**rhs, width, offset)
200207
}
201208
(Some(ref lhs), None) => {
202-
Some(format!("{}{}",
203-
try_opt!(lhs.rewrite(context,
204-
try_opt!(width.checked_sub(delim.len())),
205-
offset)),
206-
delim))
209+
rewrite_unary_suffix(context, delim, &**lhs, width, offset)
207210
}
208211
(None, None) => wrap_str(delim.into(), context.config.max_width, width, offset),
209212
}
210213
}
211214
// We do not format these expressions yet, but they should still
212215
// satisfy our width restrictions.
213216
ast::ExprKind::InPlace(..) |
214-
ast::ExprKind::InlineAsm(..) |
215-
// TODO(#867): Handle try shorthand
216-
ast::ExprKind::Try(_) => {
217+
ast::ExprKind::InlineAsm(..) => {
217218
wrap_str(context.snippet(self.span),
218219
context.config.max_width,
219220
width,
@@ -689,11 +690,8 @@ fn extract_comment(span: Span,
689690
-> Option<String> {
690691
let comment_str = context.snippet(span);
691692
if contains_comment(&comment_str) {
692-
let comment = try_opt!(rewrite_comment(comment_str.trim(),
693-
false,
694-
width,
695-
offset,
696-
context.config));
693+
let comment =
694+
try_opt!(rewrite_comment(comment_str.trim(), false, width, offset, context.config));
697695
Some(format!("\n{indent}{}\n{indent}",
698696
comment,
699697
indent = offset.to_string(context.config)))
@@ -793,14 +791,11 @@ fn rewrite_if_else(context: &RewriteContext,
793791
}
794792
};
795793

796-
let between_if_else_block = mk_sp(if_block.span.hi,
797-
context.codemap.span_before(mk_sp(if_block.span.hi,
798-
else_block.span.lo),
799-
"else"));
800-
let between_if_else_block_comment = extract_comment(between_if_else_block,
801-
&context,
802-
offset,
803-
width);
794+
let between_if_else_block =
795+
mk_sp(if_block.span.hi,
796+
context.codemap.span_before(mk_sp(if_block.span.hi, else_block.span.lo), "else"));
797+
let between_if_else_block_comment =
798+
extract_comment(between_if_else_block, &context, offset, width);
804799

805800
let after_else = mk_sp(context.codemap
806801
.span_after(mk_sp(if_block.span.hi, else_block.span.lo),
@@ -927,11 +922,8 @@ fn rewrite_match_arm_comment(context: &RewriteContext,
927922
}
928923
let missed_str = missed_str[first..].trim();
929924
if !missed_str.is_empty() {
930-
let comment = try_opt!(rewrite_comment(&missed_str,
931-
false,
932-
width,
933-
arm_indent,
934-
context.config));
925+
let comment =
926+
try_opt!(rewrite_comment(&missed_str, false, width, arm_indent, context.config));
935927
result.push('\n');
936928
result.push_str(arm_indent_str);
937929
result.push_str(&comment);
@@ -1155,10 +1147,9 @@ impl Rewrite for ast::Arm {
11551147
let body_budget = try_opt!(width.checked_sub(context.config.tab_spaces));
11561148
let indent = context.block_indent.block_indent(context.config);
11571149
let inner_context = &RewriteContext { block_indent: indent, ..*context };
1158-
let next_line_body = try_opt!(nop_block_collapse(body.rewrite(inner_context,
1159-
body_budget,
1160-
indent),
1161-
body_budget));
1150+
let next_line_body =
1151+
try_opt!(nop_block_collapse(body.rewrite(inner_context, body_budget, indent),
1152+
body_budget));
11621153
let indent_str = offset.block_indent(context.config).to_string(context.config);
11631154
let (body_prefix, body_suffix) = if context.config.wrap_match_arms {
11641155
if context.config.match_block_trailing_comma {
@@ -1762,6 +1753,21 @@ pub fn rewrite_unary_prefix<R: Rewrite>(context: &RewriteContext,
17621753
.map(|r| format!("{}{}", prefix, r))
17631754
}
17641755

1756+
// FIXME: this is probably not correct for multi-line Rewrites. we should
1757+
// subtract suffix.len() from the last line budget, not the first!
1758+
pub fn rewrite_unary_suffix<R: Rewrite>(context: &RewriteContext,
1759+
suffix: &str,
1760+
rewrite: &R,
1761+
width: usize,
1762+
offset: Indent)
1763+
-> Option<String> {
1764+
rewrite.rewrite(context, try_opt!(width.checked_sub(suffix.len())), offset)
1765+
.map(|mut r| {
1766+
r.push_str(suffix);
1767+
r
1768+
})
1769+
}
1770+
17651771
fn rewrite_unary_op(context: &RewriteContext,
17661772
op: &ast::UnOp,
17671773
expr: &ast::Expr,
@@ -1817,24 +1823,42 @@ pub fn rewrite_assign_rhs<S: Into<String>>(context: &RewriteContext,
18171823
let max_width = try_opt!(width.checked_sub(last_line_width + 1));
18181824
let rhs = ex.rewrite(&context, max_width, offset + last_line_width + 1);
18191825

1826+
fn count_line_breaks(src: &str) -> usize {
1827+
src.chars().filter(|&x| x == '\n').count()
1828+
}
1829+
18201830
match rhs {
1821-
Some(new_str) => {
1831+
Some(ref new_str) if count_line_breaks(new_str) < 2 => {
18221832
result.push(' ');
1823-
result.push_str(&new_str)
1833+
result.push_str(new_str);
18241834
}
1825-
None => {
1826-
// Expression did not fit on the same line as the identifier. Retry
1827-
// on the next line.
1835+
_ => {
1836+
// Expression did not fit on the same line as the identifier or is
1837+
// at least three lines big. Try splitting the line and see
1838+
// if that works better.
18281839
let new_offset = offset.block_indent(context.config);
1829-
result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
1830-
1831-
// FIXME: we probably should related max_width to width instead of
1832-
// config.max_width where is the 1 coming from anyway?
1833-
let max_width = try_opt!(context.config.max_width.checked_sub(new_offset.width() + 1));
1840+
let max_width = try_opt!((width + offset.width()).checked_sub(new_offset.width()));
18341841
let inner_context = context.nested_context();
1835-
let rhs = ex.rewrite(&inner_context, max_width, new_offset);
1836-
1837-
result.push_str(&&try_opt!(rhs));
1842+
let new_rhs = ex.rewrite(&inner_context, max_width, new_offset);
1843+
1844+
// FIXME: DRY!
1845+
match (rhs, new_rhs) {
1846+
(Some(ref orig_rhs), Some(ref replacement_rhs))
1847+
if count_line_breaks(orig_rhs) >
1848+
count_line_breaks(replacement_rhs) + 1 => {
1849+
result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
1850+
result.push_str(replacement_rhs);
1851+
}
1852+
(None, Some(ref final_rhs)) => {
1853+
result.push_str(&format!("\n{}", new_offset.to_string(context.config)));
1854+
result.push_str(final_rhs);
1855+
}
1856+
(None, None) => return None,
1857+
(Some(ref orig_rhs), _) => {
1858+
result.push(' ');
1859+
result.push_str(orig_rhs);
1860+
}
1861+
}
18381862
}
18391863
}
18401864

0 commit comments

Comments
 (0)