Skip to content

Commit ac4d9ba

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

File tree

4 files changed

+135
-37
lines changed

4 files changed

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

+94-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,66 @@ 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 = self.span.lo + BytePos(snippet.find("..").unwrap() as u32);
93+
// 2 == "..".len()
94+
let dotdot = PatOrDotdot::Dotdot(lo, lo + BytePos(2));
95+
96+
if pat_vec.is_empty() {
97+
pat_vec = vec![dotdot];
98+
} else {
99+
pat_vec.insert(pos, dotdot);
104100
}
105101
}
102+
103+
let path_str = try_opt!(rewrite_path(context, true, None, path, width, offset));
104+
105+
if pat_vec.is_empty() {
106+
Some(path_str)
107+
} else if pat_vec.len() == 1 && pat_vec[0].is_dotdot() {
108+
Some(format!("{}(..)", path_str))
109+
} else {
110+
// 2 = "()".len()
111+
let width = try_opt!(width.checked_sub(path_str.len() + 2));
112+
// 1 = "(".len()
113+
let offset = offset + path_str.len() + 1;
114+
let items = itemize_list(context.codemap,
115+
pat_vec.iter(),
116+
")",
117+
|item| item.span().lo,
118+
|item| item.span().hi,
119+
|item| item.rewrite(context, width, offset),
120+
context.codemap.span_after(self.span, "("),
121+
self.span.hi);
122+
Some(format!("{}({})",
123+
path_str,
124+
try_opt!(format_item_list(items, width, offset, context.config))))
125+
}
106126
}
107127
PatKind::Lit(ref expr) => expr.rewrite(context, width, offset),
108128
PatKind::Vec(ref prefix, ref slice_pat, ref suffix) => {
@@ -190,3 +210,41 @@ impl Rewrite for FieldPat {
190210
}
191211
}
192212
}
213+
214+
enum PatOrDotdot<'a> {
215+
Pat(&'a ptr::P<ast::Pat>),
216+
Dotdot(BytePos, BytePos),
217+
}
218+
219+
impl<'a> PatOrDotdot<'a> {
220+
fn is_dotdot(&self) -> bool {
221+
match *self {
222+
PatOrDotdot::Dotdot(..) => true,
223+
_ => false,
224+
}
225+
}
226+
}
227+
228+
impl<'a> Rewrite for PatOrDotdot<'a> {
229+
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
230+
match *self {
231+
PatOrDotdot::Pat(ref p) => p.rewrite(context, width, offset),
232+
PatOrDotdot::Dotdot(..) => Some("..".to_string()),
233+
}
234+
}
235+
}
236+
237+
impl<'a> Spanned for PatOrDotdot<'a> {
238+
fn span(&self) -> Span {
239+
match *self {
240+
PatOrDotdot::Pat(ref p) => p.span(),
241+
PatOrDotdot::Dotdot(lo, hi) => {
242+
Span {
243+
lo: lo,
244+
hi: hi,
245+
expn_id: codemap::NO_EXPANSION,
246+
}
247+
}
248+
}
249+
}
250+
}

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)