Skip to content

Commit dd7641f

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

File tree

5 files changed

+167
-39
lines changed

5 files changed

+167
-39
lines changed

src/patterns.rs

Lines changed: 104 additions & 39 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,19 @@ 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-
}
68-
rewrite_tuple(context,
69-
items.iter().map(|x| &**x),
70-
self.span,
71-
width,
72-
offset)
67+
rewrite_tuple_pat(items, dotdot_pos, None, self.span, context, width, offset)
7368
}
7469
PatKind::Path(ref path) => rewrite_path(context, true, None, path, width, offset),
7570
PatKind::TupleStruct(ref path, ref pat_vec, dotdot_pos) => {
7671
let path_str = try_opt!(rewrite_path(context, true, None, path, width, offset));
77-
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-
}
104-
}
105-
}
72+
rewrite_tuple_pat(pat_vec,
73+
dotdot_pos,
74+
Some(path_str),
75+
self.span,
76+
context,
77+
width,
78+
offset)
10679
}
10780
PatKind::Lit(ref expr) => expr.rewrite(context, width, offset),
10881
PatKind::Vec(ref prefix, ref slice_pat, ref suffix) => {
@@ -190,3 +163,95 @@ impl Rewrite for FieldPat {
190163
}
191164
}
192165
}
166+
167+
enum PatOrDotdot<'a> {
168+
Pat(&'a ptr::P<ast::Pat>),
169+
Dotdot(BytePos, BytePos),
170+
}
171+
172+
impl<'a> PatOrDotdot<'a> {
173+
fn is_dotdot(&self) -> bool {
174+
match *self {
175+
PatOrDotdot::Dotdot(..) => true,
176+
_ => false,
177+
}
178+
}
179+
}
180+
181+
impl<'a> Rewrite for PatOrDotdot<'a> {
182+
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
183+
match *self {
184+
PatOrDotdot::Pat(ref p) => p.rewrite(context, width, offset),
185+
PatOrDotdot::Dotdot(..) => Some("..".to_string()),
186+
}
187+
}
188+
}
189+
190+
impl<'a> Spanned for PatOrDotdot<'a> {
191+
fn span(&self) -> Span {
192+
match *self {
193+
PatOrDotdot::Pat(ref p) => p.span(),
194+
PatOrDotdot::Dotdot(lo, hi) => {
195+
Span {
196+
lo: lo,
197+
hi: hi,
198+
expn_id: codemap::NO_EXPANSION,
199+
}
200+
}
201+
}
202+
}
203+
}
204+
205+
fn rewrite_tuple_pat(pats: &[ptr::P<ast::Pat>],
206+
dotdot_pos: Option<usize>,
207+
path_str: Option<String>,
208+
span: Span,
209+
context: &RewriteContext,
210+
width: usize,
211+
offset: Indent)
212+
-> Option<String> {
213+
let mut pat_vec: Vec<_> = pats.into_iter().map(|x| PatOrDotdot::Pat(x)).collect();
214+
215+
if let Some(pos) = dotdot_pos {
216+
let snippet = context.snippet(span);
217+
let lo = span.lo + BytePos(snippet.find("..").unwrap() as u32);
218+
// 2 == "..".len()
219+
let dotdot = PatOrDotdot::Dotdot(lo, lo + BytePos(2));
220+
221+
if pat_vec.is_empty() {
222+
pat_vec = vec![dotdot];
223+
} else {
224+
pat_vec.insert(pos, dotdot);
225+
}
226+
}
227+
228+
if pat_vec.is_empty() {
229+
path_str
230+
} else if pat_vec.len() == 1 && pat_vec[0].is_dotdot() {
231+
match path_str {
232+
Some(p) => Some(format!("{}(..)", p)),
233+
None => Some("(..)".to_string()),
234+
}
235+
} else {
236+
let path_len = path_str.as_ref().map(|p| p.len()).unwrap_or(0);
237+
// 2 = "()".len()
238+
let width = try_opt!(width.checked_sub(path_len + 2));
239+
// 1 = "(".len()
240+
let offset = offset + path_len + 1;
241+
let items = itemize_list(context.codemap,
242+
pat_vec.iter(),
243+
")",
244+
|item| item.span().lo,
245+
|item| item.span().hi,
246+
|item| item.rewrite(context, width, offset),
247+
context.codemap.span_after(span, "("),
248+
span.hi - BytePos(1));
249+
250+
let list = try_opt!(format_item_list(items, width, offset, context.config));
251+
252+
match path_str {
253+
Some(path_str) => Some(format!("{}({})", path_str, list)),
254+
None => Some(format!("({})", list)),
255+
}
256+
}
257+
}

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/source/tuple.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Test tuple litterals
2+
3+
fn foo() {
4+
let a = (a, a, a, a, a);
5+
let aaaaaaaaaaaaaaaa = (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaa, aaaaaaaaaaaaaa);
6+
let aaaaaaaaaaaaaaaaaaaaaa = (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
7+
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
8+
aaaaaaaaaaaaaaaaaaaaaaaaa,
9+
aaaa);
10+
let a = (a,);
11+
12+
let b = (// This is a comment
13+
b, // Comment
14+
b /* Trailing comment */);
15+
16+
let range_full = (..,);
17+
}
18+
19+
fn a() {
20+
((aaaaaaaa,
21+
aaaaaaaaaaaaa,
22+
aaaaaaaaaaaaaaaaa,
23+
aaaaaaaaaaaaaa,
24+
aaaaaaaaaaaaaaaa,
25+
aaaaaaaaaaaaaa),)
26+
}
27+
28+
fn b() {
29+
((bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb),
30+
bbbbbbbbbbbbbbbbbb)
31+
}

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

tests/target/tuple.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ fn foo() {
1212
let b = (// This is a comment
1313
b, // Comment
1414
b /* Trailing comment */);
15+
16+
let range_full = (..,);
1517
}
1618

1719
fn a() {

0 commit comments

Comments
 (0)