Skip to content

Commit a5efefd

Browse files
committed
Properly infer tuple struct patterns when encountering ellipsis
1 parent 35dd62e commit a5efefd

File tree

2 files changed

+70
-9
lines changed

2 files changed

+70
-9
lines changed

crates/hir_ty/src/infer/pat.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ impl<'a> InferenceContext<'a> {
2323
expected: &Ty,
2424
default_bm: BindingMode,
2525
id: PatId,
26+
ellipsis: Option<usize>,
2627
) -> Ty {
2728
let (ty, def) = self.resolve_variant(path);
2829
let var_data = def.map(|it| variant_data(self.db.upcast(), it));
@@ -34,8 +35,15 @@ impl<'a> InferenceContext<'a> {
3435
let substs = ty.substs().unwrap_or_else(Substs::empty);
3536

3637
let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
38+
let (pre, post) = match ellipsis {
39+
Some(idx) => subpats.split_at(idx),
40+
None => (&subpats[..], &[][..]),
41+
};
42+
let post_idx_offset = field_tys.iter().count() - post.len();
3743

38-
for (i, &subpat) in subpats.iter().enumerate() {
44+
let pre_iter = pre.iter().enumerate();
45+
let post_iter = (post_idx_offset..).zip(post.iter());
46+
for (i, &subpat) in pre_iter.chain(post_iter) {
3947
let expected_ty = var_data
4048
.as_ref()
4149
.and_then(|d| d.field(&Name::new_tuple_field(i)))
@@ -111,23 +119,23 @@ impl<'a> InferenceContext<'a> {
111119
let expected = expected;
112120

113121
let ty = match &body[pat] {
114-
Pat::Tuple { ref args, ellipsis } => {
122+
&Pat::Tuple { ref args, ellipsis } => {
115123
let expectations = match expected.as_tuple() {
116124
Some(parameters) => &*parameters.0,
117125
_ => &[],
118126
};
119127

120128
let (pre, post) = match ellipsis {
121-
&Some(idx) => args.split_at(idx),
129+
Some(idx) => args.split_at(idx),
122130
None => (&args[..], &[][..]),
123131
};
124-
let uncovered_range = pre.len()..expectations.len().saturating_sub(post.len());
132+
let n_uncovered_patterns = expectations.len().saturating_sub(args.len());
125133
let mut expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown));
126134
let mut infer_pat = |(&pat, ty)| self.infer_pat(pat, ty, default_bm);
127135

128-
let mut inner_tys = Vec::with_capacity(expectations.len());
136+
let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len());
129137
inner_tys.extend(pre.iter().zip(expectations_iter.by_ref()).map(&mut infer_pat));
130-
inner_tys.extend(expectations_iter.by_ref().take(uncovered_range.len()).cloned());
138+
inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns).cloned());
131139
inner_tys.extend(post.iter().zip(expectations_iter).map(infer_pat));
132140

133141
Ty::apply(
@@ -159,9 +167,14 @@ impl<'a> InferenceContext<'a> {
159167
let subty = self.infer_pat(*pat, expectation, default_bm);
160168
Ty::apply_one(TypeCtor::Ref(*mutability), subty)
161169
}
162-
Pat::TupleStruct { path: p, args: subpats, .. } => {
163-
self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat)
164-
}
170+
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
171+
p.as_ref(),
172+
subpats,
173+
expected,
174+
default_bm,
175+
pat,
176+
*ellipsis,
177+
),
165178
Pat::Record { path: p, args: fields, ellipsis: _ } => {
166179
self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat)
167180
}

crates/hir_ty/src/tests/patterns.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,3 +726,51 @@ fn foo(tuple: (u8, i16, f32)) {
726726
"#]],
727727
);
728728
}
729+
730+
#[test]
731+
fn tuple_struct_ellipsis_pattern() {
732+
check_infer(
733+
r#"
734+
struct Tuple(u8, i16, f32);
735+
fn foo(tuple: Tuple) {
736+
match tuple {
737+
Tuple(.., b, c) => {},
738+
Tuple(a, .., c) => {},
739+
Tuple(a, b, ..) => {},
740+
Tuple(a, b) => {/*too short*/}
741+
Tuple(a, b, c, d) => {/*too long*/}
742+
_ => {}
743+
}
744+
}"#,
745+
expect![[r#"
746+
35..40 'tuple': Tuple
747+
49..268 '{ ... } }': ()
748+
55..266 'match ... }': ()
749+
61..66 'tuple': Tuple
750+
77..92 'Tuple(.., b, c)': Tuple
751+
87..88 'b': i16
752+
90..91 'c': f32
753+
96..98 '{}': ()
754+
108..123 'Tuple(a, .., c)': Tuple
755+
114..115 'a': u8
756+
121..122 'c': f32
757+
127..129 '{}': ()
758+
139..154 'Tuple(a, b, ..)': Tuple
759+
145..146 'a': u8
760+
148..149 'b': i16
761+
158..160 '{}': ()
762+
170..181 'Tuple(a, b)': Tuple
763+
176..177 'a': u8
764+
179..180 'b': i16
765+
185..200 '{/*too short*/}': ()
766+
209..226 'Tuple(... c, d)': Tuple
767+
215..216 'a': u8
768+
218..219 'b': i16
769+
221..222 'c': f32
770+
224..225 'd': {unknown}
771+
230..244 '{/*too long*/}': ()
772+
253..254 '_': Tuple
773+
258..260 '{}': ()
774+
"#]],
775+
);
776+
}

0 commit comments

Comments
 (0)