Skip to content

Commit

Permalink
Fix rust-lang#1021: Handle .. in tuple / tuple struct patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
sinkuu committed Sep 6, 2016
1 parent bce26d5 commit 669f158
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 37 deletions.
12 changes: 11 additions & 1 deletion src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1704,7 +1704,17 @@ pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
if items.len() == 1 {
// 3 = "(" + ",)"
let budget = try_opt!(width.checked_sub(3));
return items.next().unwrap().rewrite(context, budget, indent).map(|s| format!("({},)", s));
return items.next()
.unwrap()
.rewrite(context, budget, indent)
.map(|s| {
if s == ".." {
// ".." pattern (RFC 1492)
"(..)".to_string()
} else {
format!("({},)", s)
}
});
}

let list_lo = context.codemap.span_after(span, "(");
Expand Down
141 changes: 105 additions & 36 deletions src/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use lists::{format_item_list, itemize_list};
use expr::{rewrite_unary_prefix, rewrite_pair, rewrite_tuple};
use types::rewrite_path;

use syntax::ast::{BindingMode, Pat, PatKind, FieldPat};
use syntax::ast::{self, BindingMode, Pat, PatKind, FieldPat};
use syntax::ptr;
use syntax::codemap::{self, BytePos, Span};

impl Rewrite for Pat {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
Expand Down Expand Up @@ -60,49 +62,78 @@ impl Rewrite for Pat {
let prefix = format!("&{}", format_mutability(mutability));
rewrite_unary_prefix(context, &prefix, &**pat, width, offset)
}
// FIXME(#1021): Handle `..` in tuple / tuple struct patterns (RFC 1492)
PatKind::Tuple(ref items, dotdot_pos) => {
if dotdot_pos.is_some() {
return None;
let mut pat_vec: Vec<_> = items.into_iter().map(|x| PatOrDotdot::Pat(x)).collect();

if let Some(pos) = dotdot_pos {
let snippet = context.snippet(self.span);
let lo_pos = snippet.find("..").unwrap();
let lo = self.span.lo + BytePos(lo_pos as u32);
// 2 == "..".len()
let dotdot = PatOrDotdot::Dotdot(lo, lo + BytePos(2));

if pat_vec.is_empty() {
pat_vec = vec![dotdot];
} else {
pat_vec.insert(pos, dotdot);
}
}
rewrite_tuple(context,
items.iter().map(|x| &**x),
self.span,
width,
offset)

rewrite_tuple(context, pat_vec.iter(), self.span, width, offset)
}
PatKind::Path(ref path) => rewrite_path(context, true, None, path, width, offset),
PatKind::TupleStruct(ref path, ref pat_vec, dotdot_pos) => {
let path_str = try_opt!(rewrite_path(context, true, None, path, width, offset));
let mut pat_vec: Vec<_> =
pat_vec.into_iter().map(|x| PatOrDotdot::Pat(x)).collect();

// FIXME(#1021): Handle `..` in tuple / tuple struct patterns (RFC 1492)
match dotdot_pos {
Some(_) => Some(format!("{}(..)", path_str)),
None => {
if pat_vec.is_empty() {
Some(path_str)
} else {
// 2 = "()".len()
let width = try_opt!(width.checked_sub(path_str.len() + 2));
// 1 = "(".len()
let offset = offset + path_str.len() + 1;
let items = itemize_list(context.codemap,
pat_vec.iter(),
")",
|item| item.span.lo,
|item| item.span.hi,
|item| item.rewrite(context, width, offset),
context.codemap.span_after(self.span, "("),
self.span.hi);
Some(format!("{}({})",
path_str,
try_opt!(format_item_list(items,
width,
offset,
context.config))))
}
if let Some(pos) = dotdot_pos {
let snippet = context.snippet(self.span);
let lo_pos = snippet.find("..").unwrap();
let lo = self.span.lo + BytePos(lo_pos as u32);
// 2 == "..".len()
let dotdot = PatOrDotdot::Dotdot(lo, lo + BytePos(2));

if pat_vec.is_empty() {
pat_vec = vec![dotdot];
} else {
pat_vec.insert(pos, dotdot);
}
}

let path_str = try_opt!(rewrite_path(context, true, None, path, width, offset));

if pat_vec.is_empty() {
Some(path_str)
} else if pat_vec.len() == 1 && pat_vec[0].is_dotdot() {
Some(format!("{}(..)", path_str))
} else {
// 2 = "()".len()
let width = try_opt!(width.checked_sub(path_str.len() + 2));
// 1 = "(".len()
let offset = offset + path_str.len() + 1;
let items = itemize_list(context.codemap,
pat_vec.iter(),
")",
|item| match **item {
PatOrDotdot::Pat(ref pat) => pat.span.lo,
PatOrDotdot::Dotdot(lo, _) => lo,
},
|item| match **item {
PatOrDotdot::Pat(ref pat) => pat.span.hi,
PatOrDotdot::Dotdot(_, hi) => hi,
},
|item| match **item {
PatOrDotdot::Pat(ref pat) => {
pat.rewrite(context, width, offset)
}
PatOrDotdot::Dotdot(..) => Some("..".to_string()),
},
context.codemap.span_after(self.span, "("),
self.span.hi);
Some(format!("{}({})",
path_str,
try_opt!(format_item_list(items, width, offset, context.config))))
}
}
PatKind::Lit(ref expr) => expr.rewrite(context, width, offset),
PatKind::Vec(ref prefix, ref slice_pat, ref suffix) => {
Expand Down Expand Up @@ -190,3 +221,41 @@ impl Rewrite for FieldPat {
}
}
}

enum PatOrDotdot<'a> {
Pat(&'a ptr::P<ast::Pat>),
Dotdot(BytePos, BytePos),
}

impl<'a> PatOrDotdot<'a> {
fn is_dotdot(&self) -> bool {
match *self {
PatOrDotdot::Dotdot(..) => true,
_ => false,
}
}
}

impl<'a> Rewrite for PatOrDotdot<'a> {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
match *self {
PatOrDotdot::Pat(ref p) => p.rewrite(context, width, offset),
PatOrDotdot::Dotdot(..) => Some("..".to_string()),
}
}
}

impl<'a> super::Spanned for PatOrDotdot<'a> {
fn span(&self) -> Span {
match *self {
PatOrDotdot::Pat(ref p) => p.span(),
PatOrDotdot::Dotdot(lo, hi) => {
Span {
lo: lo,
hi: hi,
expn_id: codemap::NO_EXPANSION,
}
}
}
}
}
15 changes: 15 additions & 0 deletions tests/source/issue-1021.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
fn main() {
match x {
S(true , .., true ) => (),
S(true , .. ) => (),
S(.., true ) => (),
S( .. ) => (),
}

match y {
(true , .., true ) => (),
(true , .. ) => (),
(.., true ) => (),
( .. ) => (),
}
}
15 changes: 15 additions & 0 deletions tests/target/issue-1021.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
fn main() {
match x {
S(true, .., true) => (),
S(true, ..) => (),
S(.., true) => (),
S(..) => (),
}

match y {
(true, .., true) => (),
(true, ..) => (),
(.., true) => (),
(..) => (),
}
}

0 comments on commit 669f158

Please sign in to comment.