Skip to content

Commit f39477d

Browse files
committed
In resolve, check that an or-pattern has the same number of bindings in each disjunct
resolve3 wasn't checking this. Added test cases. Also added a helpful informational message in the case where you have a variable binding that you probably think refers to a variant that you forgot to import. This is easier to do in resolve than in typeck because there's code in typeck that assumes that each of the patterns binds the same number of variables.
1 parent 019d429 commit f39477d

File tree

4 files changed

+64
-5
lines changed

4 files changed

+64
-5
lines changed

src/rustc/middle/pat_util.rs

-3
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ fn pat_is_variant(dm: resolve::def_map, pat: @pat) -> bool {
3434
}
3535
}
3636

37-
// This does *not* normalize. The pattern should be already normalized
38-
// if you want to get a normalized pattern out of it.
39-
// Could return a constrained type in order to express that (future work)
4037
fn pat_bindings(dm: resolve::def_map, pat: @pat,
4138
it: fn(node_id, span, @path)) {
4239
do walk_pat(pat) |p| {

src/rustc/middle/resolve3.rs

+42-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ import syntax::ast::{ident, trait_ref, impure_fn, instance_var, item};
1717
import syntax::ast::{item_class, item_const, item_enum, item_fn, item_mac};
1818
import syntax::ast::{item_foreign_mod, item_trait, item_impl, item_mod};
1919
import syntax::ast::{item_ty, local, local_crate, method, node_id, pat};
20-
import syntax::ast::{pat_enum, pat_ident, pat_lit, pat_range, path, prim_ty};
21-
import syntax::ast::{stmt_decl, ty};
20+
import syntax::ast::{pat_enum, pat_ident, path, prim_ty, stmt_decl, ty,
21+
pat_box, pat_uniq, pat_lit, pat_range, pat_rec,
22+
pat_tup, pat_wild};
2223
import syntax::ast::{ty_bool, ty_char, ty_constr, ty_f, ty_f32, ty_f64};
2324
import syntax::ast::{ty_float, ty_i, ty_i16, ty_i32, ty_i64, ty_i8, ty_int};
2425
import syntax::ast::{ty_param, ty_path, ty_str, ty_u, ty_u16, ty_u32, ty_u64};
@@ -28,6 +29,7 @@ import syntax::ast::{view_path_list, view_path_simple};
2829
import syntax::ast_util::{def_id_of_def, dummy_sp, local_def, new_def_hash};
2930
import syntax::ast_util::{walk_pat};
3031
import syntax::attr::{attr_metas, contains_name};
32+
import syntax::print::pprust::path_to_str;
3133
import syntax::codemap::span;
3234
import syntax::visit::{default_visitor, fk_method, mk_vt, visit_block};
3335
import syntax::visit::{visit_crate, visit_expr, visit_expr_opt, visit_fn};
@@ -3384,6 +3386,40 @@ class Resolver {
33843386
none, visitor);
33853387
}
33863388

3389+
fn num_bindings(pat: @pat) -> uint {
3390+
pat_util::pat_binding_ids(self.def_map, pat).len()
3391+
}
3392+
3393+
fn warn_var_patterns(arm: arm) {
3394+
/*
3395+
The idea here is that an arm like:
3396+
alpha | beta
3397+
where alpha is a variant and beta is an identifier that
3398+
might refer to a variant that's not in scope will result
3399+
in a confusing error message. Showing that beta actually binds a
3400+
new variable might help.
3401+
*/
3402+
for arm.pats.each |p| {
3403+
do pat_util::pat_bindings(self.def_map, p) |_id, sp, pth| {
3404+
self.session.span_note(sp, #fmt("Treating %s as a variable \
3405+
binding, because it does not denote any variant in scope",
3406+
path_to_str(pth)));
3407+
}
3408+
};
3409+
}
3410+
fn check_consistent_bindings(arm: arm) {
3411+
if arm.pats.len() == 0 { ret; }
3412+
let good = self.num_bindings(arm.pats[0]);
3413+
for arm.pats.each() |p: @pat| {
3414+
if self.num_bindings(p) != good {
3415+
self.session.span_err(p.span,
3416+
"inconsistent number of bindings");
3417+
self.warn_var_patterns(arm);
3418+
break;
3419+
};
3420+
};
3421+
}
3422+
33873423
fn resolve_arm(arm: arm, visitor: ResolveVisitor) {
33883424
(*self.value_ribs).push(@Rib(NormalRibKind));
33893425

@@ -3393,6 +3429,10 @@ class Resolver {
33933429
some(bindings_list), visitor);
33943430
}
33953431

3432+
// This has to happen *after* we determine which
3433+
// pat_idents are variants
3434+
self.check_consistent_bindings(arm);
3435+
33963436
visit_expr_opt(arm.guard, (), visitor);
33973437
self.resolve_block(arm.body, visitor);
33983438

src/test/compile-fail/issue-2848.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
mod bar {
2+
enum foo {
3+
alpha,
4+
beta,
5+
charlie
6+
}
7+
}
8+
9+
fn main() {
10+
import bar::{alpha, charlie};
11+
alt alpha {
12+
alpha | beta {} //~ ERROR: inconsistent number of bindings
13+
charlie {}
14+
}
15+
}

src/test/compile-fail/issue-2849.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
enum foo { alpha, beta(int) }
2+
3+
fn main() {
4+
alt alpha {
5+
alpha | beta(i) {} //~ ERROR inconsistent number of bindings
6+
}
7+
}

0 commit comments

Comments
 (0)