Code change:
Given the example code: I get a really nice warning message about what I did wrong: However, if I change the main function to match multiple patterns: The error message is really confusing with no hint how to fix it: Please expand the error message for the second case to look a bit more like the first case.
Triage: no change. We need to supply the line in E0408.
so I thought I would implement the change you've proposed but have been thinking how best to restrict the kinds of cases where this help note would be appended to the error as I figured it may be rather noisy and/or confusing within the context of some false negatives. So during resolution we of course cannot look at the types to see if it's possible we're matching against an enum. But what do you think about having the help note kick in only if the binding starts with an upper case letter? I know this is a convention that should not really be a concern of the compiler but seeing that using it would only make the diagnostic note a bit more fine-grained, perhaps it's not a terrible idea?
we already do that in some places, like the parser to infer intent, but in this case how would you figure out that you want to suggest and without resolving ? I believe a more holistic solution would be to detect "variable not bound in all patterns" errors where all bindings are single idents, record that fact while throwing the current error and recovering as if there had been a single variable, and later when we have access to the machinery uses emit a warning suggesting and . It is a bit more involved but if we recover as if only had been present the current diagnostic should kick in suggesting (leaving without a suggestion). Edit: that won't work as things stand, as the warning won't be emitted today if there are errors, I think. That would also need to be changed to make it always be emitted :-/
no, I didn't mean to say we need to suggest possible variants. I too concluded that it's non-trivial with the current ordering of passes and the early bailout (indeed, type checking is impossible without resolution succeeding). I was going to add the help note you proposed back in May, just that I would make it dependent on bindings having a Variant-alike naming. From what you're saying there's a precedent in , so I guess this is an acceptable solution? I'll make a PR then.
sorry, I misunderstood. Go ahead, it sounds good.
no, it's likely me who wasn't clear enough. :) I submitted a PR.

Related discussion:
text: // Checks that all of the arms in an or-pattern have exactly the // same set of bindings, with the same binding modes for each. fn check_consistent_bindings(&mut self, pats: &[P<Pat>]) { <del> if pats.is_empty() { return; } </del> let mut missing_vars = FxHashMap::default(); let mut inconsistent_vars = FxHashMap::default(); <del> for (i, p) in pats.iter().enumerate() { let map_i = self.binding_mode_map(&p); for (j, q) in pats.iter().enumerate() { if i == j { continue; } let map_j = self.binding_mode_map(&q); for (&key, &binding_i) in &map_i { if map_j.is_empty() {                   // Account for missing bindings when let binding_error = missing_vars    // `map_j` has none. .entry(key.name) .or_insert(BindingError { name: key.name, origin: BTreeSet::new(), target: BTreeSet::new(), }); binding_error.origin.insert(binding_i.span); binding_error.target.insert(q.span); } for (&key_j, &binding_j) in &map_j { match map_i.get(&key_j) { None => {  // missing binding let binding_error = missing_vars .entry(key_j.name) .or_insert(BindingError { name: key_j.name, origin: BTreeSet::new(), target: BTreeSet::new(), }); binding_error.origin.insert(binding_j.span); binding_error.target.insert(p.span); } Some(binding_i) => {  // check consistent binding if binding_i.binding_mode != binding_j.binding_mode { inconsistent_vars .entry(key.name) .or_insert((binding_j.span, binding_i.span)); } </del> <ins> for pat_outer in pats.iter() { let map_outer = self.binding_mode_map(&pat_outer); for pat_inner in pats.iter().filter(|pat| pat.id != pat_outer.id) { let map_inner = self.binding_mode_map(&pat_inner); for (&key_inner, &binding_inner) in map_inner.iter() { match map_outer.get(&key_inner) { None => {  // missing binding let binding_error = missing_vars .entry(key_inner.name) .or_insert(BindingError { name: key_inner.name, origin: BTreeSet::new(), target: BTreeSet::new(), could_be_path: key_inner.name.as_str().starts_with(char::is_uppercase) }); binding_error.origin.insert(binding_inner.span); binding_error.target.insert(pat_outer.span); } Some(binding_outer) => {  // check consistent binding if binding_outer.binding_mode != binding_inner.binding_mode { inconsistent_vars .entry(key_inner.name) .or_insert((binding_inner.span, binding_outer.span)); </ins> } } } } } } <del> let mut missing_vars = missing_vars.iter().collect::<Vec<_>>(); </del> <ins> let mut missing_vars = missing_vars.iter_mut().collect::<Vec<_>>(); </ins> missing_vars.sort(); <del> for (_, v) in missing_vars { </del> <ins> for (name, mut v) in missing_vars { if inconsistent_vars.contains_key(name) { v.could_be_path = false; } </ins> self.r.report_error( <del> *v.origin.iter().next().unwrap(), ResolutionError::VariableNotBoundInPattern(v) ); </del> <ins> *v.origin.iter().next().unwrap(), ResolutionError::VariableNotBoundInPattern(v)); </ins> } let mut inconsistent_vars = inconsistent_vars.iter().collect::<Vec<_>>(); inconsistent_vars.sort(); for (name, v) in inconsistent_vars {