Skip to content

Commit 8a30e54

Browse files
committed
Fix rust-lang#1021: Handle .. in tuple / tuple struct patterns
1 parent bce26d5 commit 8a30e54

File tree

4 files changed

+136
-37
lines changed

4 files changed

+136
-37
lines changed

src/expr.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1704,7 +1704,17 @@ pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
17041704
if items.len() == 1 {
17051705
// 3 = "(" + ",)"
17061706
let budget = try_opt!(width.checked_sub(3));
1707-
return items.next().unwrap().rewrite(context, budget, indent).map(|s| format!("({},)", s));
1707+
return items.next()
1708+
.unwrap()
1709+
.rewrite(context, budget, indent)
1710+
.map(|s| {
1711+
if s == ".." {
1712+
// ".." pattern (RFC 1492)
1713+
"(..)".to_string()
1714+
} else {
1715+
format!("({},)", s)
1716+
}
1717+
});
17081718
}
17091719

17101720
let list_lo = context.codemap.span_after(span, "(");

src/patterns.rs

Lines changed: 95 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ use utils::{wrap_str, format_mutability};
1515
use lists::{format_item_list, itemize_list};
1616
use expr::{rewrite_unary_prefix, rewrite_pair, rewrite_tuple};
1717
use types::rewrite_path;
18+
use super::Spanned;
1819

19-
use syntax::ast::{BindingMode, Pat, PatKind, FieldPat};
20+
use syntax::ast::{self, BindingMode, Pat, PatKind, FieldPat};
21+
use syntax::ptr;
22+
use syntax::codemap::{self, BytePos, Span};
2023

2124
impl Rewrite for Pat {
2225
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
@@ -60,49 +63,67 @@ impl Rewrite for Pat {
6063
let prefix = format!("&{}", format_mutability(mutability));
6164
rewrite_unary_prefix(context, &prefix, &**pat, width, offset)
6265
}
63-
// FIXME(#1021): Handle `..` in tuple / tuple struct patterns (RFC 1492)
6466
PatKind::Tuple(ref items, dotdot_pos) => {
65-
if dotdot_pos.is_some() {
66-
return None;
67+
let mut pat_vec: Vec<_> = items.into_iter().map(|x| PatOrDotdot::Pat(x)).collect();
68+
69+
if let Some(pos) = dotdot_pos {
70+
let snippet = context.snippet(self.span);
71+
let lo_pos = snippet.find("..").unwrap();
72+
let lo = self.span.lo + BytePos(lo_pos as u32);
73+
// 2 == "..".len()
74+
let dotdot = PatOrDotdot::Dotdot(lo, lo + BytePos(2));
75+
76+
if pat_vec.is_empty() {
77+
pat_vec = vec![dotdot];
78+
} else {
79+
pat_vec.insert(pos, dotdot);
80+
}
6781
}
68-
rewrite_tuple(context,
69-
items.iter().map(|x| &**x),
70-
self.span,
71-
width,
72-
offset)
82+
83+
rewrite_tuple(context, pat_vec.iter(), self.span, width, offset)
7384
}
7485
PatKind::Path(ref path) => rewrite_path(context, true, None, path, width, offset),
7586
PatKind::TupleStruct(ref path, ref pat_vec, dotdot_pos) => {
76-
let path_str = try_opt!(rewrite_path(context, true, None, path, width, offset));
87+
let mut pat_vec: Vec<_> =
88+
pat_vec.into_iter().map(|x| PatOrDotdot::Pat(x)).collect();
7789

78-
// FIXME(#1021): Handle `..` in tuple / tuple struct patterns (RFC 1492)
79-
match dotdot_pos {
80-
Some(_) => Some(format!("{}(..)", path_str)),
81-
None => {
82-
if pat_vec.is_empty() {
83-
Some(path_str)
84-
} else {
85-
// 2 = "()".len()
86-
let width = try_opt!(width.checked_sub(path_str.len() + 2));
87-
// 1 = "(".len()
88-
let offset = offset + path_str.len() + 1;
89-
let items = itemize_list(context.codemap,
90-
pat_vec.iter(),
91-
")",
92-
|item| item.span.lo,
93-
|item| item.span.hi,
94-
|item| item.rewrite(context, width, offset),
95-
context.codemap.span_after(self.span, "("),
96-
self.span.hi);
97-
Some(format!("{}({})",
98-
path_str,
99-
try_opt!(format_item_list(items,
100-
width,
101-
offset,
102-
context.config))))
103-
}
90+
if let Some(pos) = dotdot_pos {
91+
let snippet = context.snippet(self.span);
92+
let lo_pos = snippet.find("..").unwrap();
93+
let lo = self.span.lo + BytePos(lo_pos as u32);
94+
// 2 == "..".len()
95+
let dotdot = PatOrDotdot::Dotdot(lo, lo + BytePos(2));
96+
97+
if pat_vec.is_empty() {
98+
pat_vec = vec![dotdot];
99+
} else {
100+
pat_vec.insert(pos, dotdot);
104101
}
105102
}
103+
104+
let path_str = try_opt!(rewrite_path(context, true, None, path, width, offset));
105+
106+
if pat_vec.is_empty() {
107+
Some(path_str)
108+
} else if pat_vec.len() == 1 && pat_vec[0].is_dotdot() {
109+
Some(format!("{}(..)", path_str))
110+
} else {
111+
// 2 = "()".len()
112+
let width = try_opt!(width.checked_sub(path_str.len() + 2));
113+
// 1 = "(".len()
114+
let offset = offset + path_str.len() + 1;
115+
let items = itemize_list(context.codemap,
116+
pat_vec.iter(),
117+
")",
118+
|item| item.span().lo,
119+
|item| item.span().hi,
120+
|item| item.rewrite(context, width, offset),
121+
context.codemap.span_after(self.span, "("),
122+
self.span.hi);
123+
Some(format!("{}({})",
124+
path_str,
125+
try_opt!(format_item_list(items, width, offset, context.config))))
126+
}
106127
}
107128
PatKind::Lit(ref expr) => expr.rewrite(context, width, offset),
108129
PatKind::Vec(ref prefix, ref slice_pat, ref suffix) => {
@@ -190,3 +211,41 @@ impl Rewrite for FieldPat {
190211
}
191212
}
192213
}
214+
215+
enum PatOrDotdot<'a> {
216+
Pat(&'a ptr::P<ast::Pat>),
217+
Dotdot(BytePos, BytePos),
218+
}
219+
220+
impl<'a> PatOrDotdot<'a> {
221+
fn is_dotdot(&self) -> bool {
222+
match *self {
223+
PatOrDotdot::Dotdot(..) => true,
224+
_ => false,
225+
}
226+
}
227+
}
228+
229+
impl<'a> Rewrite for PatOrDotdot<'a> {
230+
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
231+
match *self {
232+
PatOrDotdot::Pat(ref p) => p.rewrite(context, width, offset),
233+
PatOrDotdot::Dotdot(..) => Some("..".to_string()),
234+
}
235+
}
236+
}
237+
238+
impl<'a> Spanned for PatOrDotdot<'a> {
239+
fn span(&self) -> Span {
240+
match *self {
241+
PatOrDotdot::Pat(ref p) => p.span(),
242+
PatOrDotdot::Dotdot(lo, hi) => {
243+
Span {
244+
lo: lo,
245+
hi: hi,
246+
expn_id: codemap::NO_EXPANSION,
247+
}
248+
}
249+
}
250+
}
251+
}

tests/source/issue-1021.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fn main() {
2+
match x {
3+
S(true , .., true ) => (),
4+
S(true , .. ) => (),
5+
S(.., true ) => (),
6+
S( .. ) => (),
7+
}
8+
9+
match y {
10+
(true , .., true ) => (),
11+
(true , .. ) => (),
12+
(.., true ) => (),
13+
( .. ) => (),
14+
}
15+
}

tests/target/issue-1021.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fn main() {
2+
match x {
3+
S(true, .., true) => (),
4+
S(true, ..) => (),
5+
S(.., true) => (),
6+
S(..) => (),
7+
}
8+
9+
match y {
10+
(true, .., true) => (),
11+
(true, ..) => (),
12+
(.., true) => (),
13+
(..) => (),
14+
}
15+
}

0 commit comments

Comments
 (0)