Skip to content

Commit 2da1e1e

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

File tree

3 files changed

+146
-40
lines changed

3 files changed

+146
-40
lines changed

src/patterns.rs

+98-40
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ use codemap::SpanUtils;
1313
use rewrite::{Rewrite, RewriteContext};
1414
use utils::{wrap_str, format_mutability};
1515
use lists::{format_item_list, itemize_list};
16-
use expr::{rewrite_unary_prefix, rewrite_pair, rewrite_tuple};
16+
use expr::{rewrite_unary_prefix, rewrite_pair};
1717
use types::rewrite_path;
18+
use super::Spanned;
19+
use comment::FindUncommented;
1820

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

2125
impl Rewrite for Pat {
2226
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
@@ -60,49 +64,19 @@ impl Rewrite for Pat {
6064
let prefix = format!("&{}", format_mutability(mutability));
6165
rewrite_unary_prefix(context, &prefix, &**pat, width, offset)
6266
}
63-
// FIXME(#1021): Handle `..` in tuple / tuple struct patterns (RFC 1492)
6467
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)
68+
rewrite_tuple_pat(items, dotdot_pos, None, self.span, context, width, offset)
7369
}
7470
PatKind::Path(ref path) => rewrite_path(context, true, None, path, width, offset),
7571
PatKind::TupleStruct(ref path, ref pat_vec, dotdot_pos) => {
7672
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-
}
73+
rewrite_tuple_pat(pat_vec,
74+
dotdot_pos,
75+
Some(path_str),
76+
self.span,
77+
context,
78+
width,
79+
offset)
10680
}
10781
PatKind::Lit(ref expr) => expr.rewrite(context, width, offset),
10882
PatKind::Vec(ref prefix, ref slice_pat, ref suffix) => {
@@ -190,3 +164,87 @@ impl Rewrite for FieldPat {
190164
}
191165
}
192166
}
167+
168+
enum TaplePatField<'a> {
169+
Pat(&'a ptr::P<ast::Pat>),
170+
Dotdot(BytePos, BytePos),
171+
}
172+
173+
impl<'a> Rewrite for TaplePatField<'a> {
174+
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
175+
match *self {
176+
TaplePatField::Pat(ref p) => p.rewrite(context, width, offset),
177+
TaplePatField::Dotdot(..) => Some("..".to_string()),
178+
}
179+
}
180+
}
181+
182+
impl<'a> Spanned for TaplePatField<'a> {
183+
fn span(&self) -> Span {
184+
match *self {
185+
TaplePatField::Pat(ref p) => p.span(),
186+
TaplePatField::Dotdot(lo, hi) => {
187+
Span {
188+
lo: lo,
189+
hi: hi,
190+
expn_id: codemap::NO_EXPANSION,
191+
}
192+
}
193+
}
194+
}
195+
}
196+
197+
fn rewrite_tuple_pat(pats: &[ptr::P<ast::Pat>],
198+
dotdot_pos: Option<usize>,
199+
path_str: Option<String>,
200+
span: Span,
201+
context: &RewriteContext,
202+
width: usize,
203+
offset: Indent)
204+
-> Option<String> {
205+
let mut pat_vec: Vec<_> = pats.into_iter().map(|x| TaplePatField::Pat(x)).collect();
206+
207+
if let Some(pos) = dotdot_pos {
208+
let snippet = context.snippet(span);
209+
let lo = span.lo + BytePos(snippet.find_uncommented("..").unwrap() as u32);
210+
// 2 == "..".len()
211+
let dotdot = TaplePatField::Dotdot(lo, lo + BytePos(2));
212+
213+
if pat_vec.is_empty() {
214+
pat_vec = vec![dotdot];
215+
} else {
216+
pat_vec.insert(pos, dotdot);
217+
}
218+
}
219+
220+
if pat_vec.is_empty() {
221+
path_str
222+
} else {
223+
// add comma if `(x,)`
224+
let add_comma = path_str.is_none() && pat_vec.len() == 1 && dotdot_pos.is_none();
225+
226+
let path_len = path_str.as_ref().map(|p| p.len()).unwrap_or(0);
227+
// 2 = "()".len()
228+
let width = try_opt!(width.checked_sub(path_len + 2));
229+
// 1 = "(".len()
230+
let offset = offset + path_len + 1;
231+
let items = itemize_list(context.codemap,
232+
pat_vec.iter(),
233+
if add_comma { ",)" } else { ")" },
234+
|item| item.span().lo,
235+
|item| item.span().hi,
236+
|item| item.rewrite(context, width, offset),
237+
context.codemap.span_after(span, "("),
238+
span.hi - BytePos(1));
239+
240+
let list = try_opt!(format_item_list(items, width, offset, context.config));
241+
242+
match path_str {
243+
Some(path_str) => Some(format!("{}({})", path_str, list)),
244+
None => {
245+
let comma = if add_comma { "," } else { "" };
246+
Some(format!("({}{})", list, comma))
247+
}
248+
}
249+
}
250+
}

tests/source/issue-1021.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
fn main() {
2+
match x {
3+
S(true , .., true ) => (),
4+
S(true , .. ) => (),
5+
S(.., true ) => (),
6+
S( .. ) => (),
7+
S(_) => (),
8+
S(/* .. */ .. ) => (),
9+
S(/* .. */ .., true ) => (),
10+
}
11+
12+
match y {
13+
(true , .., true ) => (),
14+
(true , .. ) => (),
15+
(.., true ) => (),
16+
( .. ) => (),
17+
(_,) => (),
18+
(/* .. */ .. ) => (),
19+
(/* .. */ .., true ) => (),
20+
}
21+
}

tests/target/issue-1021.rs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
fn main() {
2+
match x {
3+
S(true, .., true) => (),
4+
S(true, ..) => (),
5+
S(.., true) => (),
6+
S(..) => (),
7+
S(_) => (),
8+
S(// ..
9+
..) => (),
10+
S(// ..
11+
..,
12+
true) => (),
13+
}
14+
15+
match y {
16+
(true, .., true) => (),
17+
(true, ..) => (),
18+
(.., true) => (),
19+
(..) => (),
20+
(_,) => (),
21+
(// ..
22+
..) => (),
23+
(// ..
24+
..,
25+
true) => (),
26+
}
27+
}

0 commit comments

Comments
 (0)