Skip to content

Commit 584c71f

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

File tree

4 files changed

+141
-37
lines changed

4 files changed

+141
-37
lines changed

src/expr.rs

+11-1
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

+100-36
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,72 @@ 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| match **item {
121+
PatOrDotdot::Pat(ref pat) => {
122+
pat.rewrite(context, width, offset)
123+
}
124+
PatOrDotdot::Dotdot(..) => Some("..".to_string()),
125+
},
126+
context.codemap.span_after(self.span, "("),
127+
self.span.hi);
128+
Some(format!("{}({})",
129+
path_str,
130+
try_opt!(format_item_list(items, width, offset, context.config))))
131+
}
106132
}
107133
PatKind::Lit(ref expr) => expr.rewrite(context, width, offset),
108134
PatKind::Vec(ref prefix, ref slice_pat, ref suffix) => {
@@ -190,3 +216,41 @@ impl Rewrite for FieldPat {
190216
}
191217
}
192218
}
219+
220+
enum PatOrDotdot<'a> {
221+
Pat(&'a ptr::P<ast::Pat>),
222+
Dotdot(BytePos, BytePos),
223+
}
224+
225+
impl<'a> PatOrDotdot<'a> {
226+
fn is_dotdot(&self) -> bool {
227+
match *self {
228+
PatOrDotdot::Dotdot(..) => true,
229+
_ => false,
230+
}
231+
}
232+
}
233+
234+
impl<'a> Rewrite for PatOrDotdot<'a> {
235+
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
236+
match *self {
237+
PatOrDotdot::Pat(ref p) => p.rewrite(context, width, offset),
238+
PatOrDotdot::Dotdot(..) => Some("..".to_string()),
239+
}
240+
}
241+
}
242+
243+
impl<'a> Spanned for PatOrDotdot<'a> {
244+
fn span(&self) -> Span {
245+
match *self {
246+
PatOrDotdot::Pat(ref p) => p.span(),
247+
PatOrDotdot::Dotdot(lo, hi) => {
248+
Span {
249+
lo: lo,
250+
hi: hi,
251+
expn_id: codemap::NO_EXPANSION,
252+
}
253+
}
254+
}
255+
}
256+
}

tests/source/issue-1021.rs

+15
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

+15
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)