diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 9bbecf104e55e..e0a83ba8c0d4a 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -123,7 +123,7 @@ impl<'a> Resolver<'a> { let (span, found_use) = if let Some(def_id) = def_id.as_local() { UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id]) } else { - (None, false) + (None, FoundUse::No) }; if !candidates.is_empty() { show_candidates( @@ -132,8 +132,9 @@ impl<'a> Resolver<'a> { &mut err, span, &candidates, - instead, + if instead { Instead::Yes } else { Instead::No }, found_use, + IsPattern::No, ); } else if let Some((span, msg, sugg, appl)) = suggestion { err.span_suggestion(span, msg, sugg, appl); @@ -493,14 +494,14 @@ impl<'a> Resolver<'a> { /// /// This takes the error provided, combines it with the span and any additional spans inside the /// error and emits it. - crate fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) { + crate fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) { self.into_struct_error(span, resolution_error).emit(); } crate fn into_struct_error( - &self, + &mut self, span: Span, - resolution_error: ResolutionError<'_>, + resolution_error: ResolutionError<'a>, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { match resolution_error { ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => { @@ -650,7 +651,7 @@ impl<'a> Resolver<'a> { } err } - ResolutionError::VariableNotBoundInPattern(binding_error) => { + ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { let BindingError { name, target, origin, could_be_path } = binding_error; let target_sp = target.iter().copied().collect::>(); @@ -670,13 +671,41 @@ impl<'a> Resolver<'a> { for sp in origin_sp { err.span_label(sp, "variable not in all patterns"); } - if *could_be_path { - let help_msg = format!( - "if you meant to match on a variant or a `const` item, consider \ - making the path in the pattern qualified: `?::{}`", - name, + if could_be_path { + let import_suggestions = self.lookup_import_candidates( + Ident::with_dummy_span(name), + Namespace::ValueNS, + &parent_scope, + &|res: Res| match res { + Res::Def( + DefKind::Ctor(CtorOf::Variant, CtorKind::Const) + | DefKind::Ctor(CtorOf::Struct, CtorKind::Const) + | DefKind::Const + | DefKind::AssocConst, + _, + ) => true, + _ => false, + }, + ); + + if import_suggestions.is_empty() { + let help_msg = format!( + "if you meant to match on a variant or a `const` item, consider \ + making the path in the pattern qualified: `path::to::ModOrType::{}`", + name, + ); + err.span_help(span, &help_msg); + } + show_candidates( + &self.definitions, + self.session, + &mut err, + Some(span), + &import_suggestions, + Instead::No, + FoundUse::Yes, + IsPattern::Yes, ); - err.span_help(span, &help_msg); } err } @@ -1022,7 +1051,7 @@ impl<'a> Resolver<'a> { } crate fn report_vis_error( - &self, + &mut self, vis_resolution_error: VisResolutionError<'_>, ) -> ErrorGuaranteed { match vis_resolution_error { @@ -1453,8 +1482,9 @@ impl<'a> Resolver<'a> { err, None, &import_suggestions, - false, - true, + Instead::No, + FoundUse::Yes, + IsPattern::No, ); if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { @@ -2390,6 +2420,27 @@ fn find_span_immediately_after_crate_name( (next_left_bracket == after_second_colon, from_second_colon) } +/// A suggestion has already been emitted, change the wording slightly to clarify that both are +/// independent options. +enum Instead { + Yes, + No, +} + +/// Whether an existing place with an `use` item was found. +enum FoundUse { + Yes, + No, +} + +/// Whether a binding is part of a pattern or an expression. Used for diagnostics. +enum IsPattern { + /// The binding is part of a pattern + Yes, + /// The binding is part of an expression + No, +} + /// When an entity with a given name is not available in scope, we search for /// entities with that name in all crates. This method allows outputting the /// results of this search in a programmer-friendly way @@ -2400,8 +2451,9 @@ fn show_candidates( // This is `None` if all placement locations are inside expansions use_placement_span: Option, candidates: &[ImportSuggestion], - instead: bool, - found_use: bool, + instead: Instead, + found_use: FoundUse, + is_pattern: IsPattern, ) { if candidates.is_empty() { return; @@ -2428,24 +2480,38 @@ fn show_candidates( } if !accessible_path_strings.is_empty() { - let (determiner, kind) = if accessible_path_strings.len() == 1 { - ("this", accessible_path_strings[0].1) + let (determiner, kind, name) = if accessible_path_strings.len() == 1 { + ("this", accessible_path_strings[0].1, format!(" `{}`", accessible_path_strings[0].0)) } else { - ("one of these", "items") + ("one of these", "items", String::new()) }; - let instead = if instead { " instead" } else { "" }; - let mut msg = format!("consider importing {} {}{}", determiner, kind, instead); + let instead = if let Instead::Yes = instead { " instead" } else { "" }; + let mut msg = if let IsPattern::Yes = is_pattern { + format!( + "if you meant to match on {}{}{}, use the full path in the pattern", + kind, instead, name + ) + } else { + format!("consider importing {} {}{}", determiner, kind, instead) + }; for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { err.note(note); } - if let Some(span) = use_placement_span { + if let (IsPattern::Yes, Some(span)) = (is_pattern, use_placement_span) { + err.span_suggestions( + span, + &msg, + accessible_path_strings.into_iter().map(|a| a.0), + Applicability::MaybeIncorrect, + ); + } else if let Some(span) = use_placement_span { for candidate in &mut accessible_path_strings { // produce an additional newline to separate the new use statement // from the directly following item. - let additional_newline = if found_use { "" } else { "\n" }; + let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" }; candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline); } @@ -2453,7 +2519,7 @@ fn show_candidates( span, &msg, accessible_path_strings.into_iter().map(|a| a.0), - Applicability::Unspecified, + Applicability::MaybeIncorrect, ); } else { msg.push(':'); @@ -2468,9 +2534,17 @@ fn show_candidates( } else { assert!(!inaccessible_path_strings.is_empty()); + let prefix = + if let IsPattern::Yes = is_pattern { "you might have meant to match on " } else { "" }; if inaccessible_path_strings.len() == 1 { let (name, descr, def_id, note) = &inaccessible_path_strings[0]; - let msg = format!("{} `{}` exists but is inaccessible", descr, name); + let msg = format!( + "{}{} `{}`{} exists but is inaccessible", + prefix, + descr, + name, + if let IsPattern::Yes = is_pattern { ", which" } else { "" } + ); if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) { let span = definitions.def_span(local_def_id); @@ -2496,7 +2570,7 @@ fn show_candidates( "item".to_string() }; - let mut msg = format!("these {}s exist but are inaccessible", descr); + let mut msg = format!("{}these {}s exist but are inaccessible", prefix, descr); let mut has_colon = false; let mut spans = Vec::new(); @@ -2537,14 +2611,14 @@ struct UsePlacementFinder { } impl UsePlacementFinder { - fn check(krate: &Crate, target_module: NodeId) -> (Option, bool) { + fn check(krate: &Crate, target_module: NodeId) -> (Option, FoundUse) { let mut finder = UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None }; finder.visit_crate(krate); if let Some(use_span) = finder.first_use_span { - (Some(use_span), true) + (Some(use_span), FoundUse::Yes) } else { - (finder.first_legal_span, false) + (finder.first_legal_span, FoundUse::No) } } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 21b888c25c25f..aafef3e313711 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1422,7 +1422,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { /// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved /// label and reports an error if the label is not found or is unreachable. - fn resolve_label(&self, mut label: Ident) -> Option { + fn resolve_label(&mut self, mut label: Ident) -> Option { let mut suggestion = None; // Preserve the original span so that errors contain "in this macro invocation" @@ -1442,6 +1442,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let ident = label.normalize_to_macro_rules(); if let Some((ident, id)) = rib.bindings.get_key_value(&ident) { + let definition_span = ident.span; return if self.is_label_valid_from_rib(i) { Some(*id) } else { @@ -1449,7 +1450,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { original_span, ResolutionError::UnreachableLabel { name: label.name, - definition_span: ident.span, + definition_span, suggestion, }, ); @@ -2135,7 +2136,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { span: Span, err: F, ) where - F: FnOnce(Ident, &str, Option) -> ResolutionError<'_>, + F: FnOnce(Ident, String, Option) -> ResolutionError<'a>, { // If there is a TraitRef in scope for an impl, then the method must be in the trait. let Some((module, _)) = &self.current_trait_ref else { return; }; @@ -2159,7 +2160,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // We could not find the method: report an error. let candidate = self.find_similarly_named_assoc_item(ident.name, kind); let path = &self.current_trait_ref.as_ref().unwrap().1.path; - self.report_error(span, err(ident, &path_names_to_string(path), candidate)); + let path_names = path_names_to_string(path); + self.report_error(span, err(ident, path_names, candidate)); return; }; @@ -2183,13 +2185,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { AssocItemKind::TyAlias(..) => (rustc_errors::error_code!(E0325), "type"), AssocItemKind::MacCall(..) => span_bug!(span, "unexpanded macro"), }; + let trait_path = path_names_to_string(path); self.report_error( span, ResolutionError::TraitImplMismatch { name: ident.name, kind, code, - trait_path: path_names_to_string(path), + trait_path, trait_item_span: binding.span, }, ); @@ -2304,16 +2307,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } // 3) Report all missing variables we found. - let mut missing_vars = missing_vars.iter_mut().collect::>(); - missing_vars.sort_by_key(|(sym, _err)| sym.as_str()); + let mut missing_vars = missing_vars.into_iter().collect::>(); + missing_vars.sort_by_key(|&(sym, ref _err)| sym); - for (name, mut v) in missing_vars { - if inconsistent_vars.contains_key(name) { + for (name, mut v) in missing_vars.into_iter() { + if inconsistent_vars.contains_key(&name) { v.could_be_path = false; } self.report_error( *v.origin.iter().next().unwrap(), - ResolutionError::VariableNotBoundInPattern(v), + ResolutionError::VariableNotBoundInPattern(v, self.parent_scope), ); } @@ -2815,7 +2818,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { /// A wrapper around [`Resolver::report_error`]. /// /// This doesn't emit errors for function bodies if this is rustdoc. - fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) { + fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) { if self.should_report_errs() { self.r.report_error(span, resolution_error); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 64e228a88d97e..dbc4f337ad3b5 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -201,13 +201,13 @@ enum ResolutionError<'a> { /// parameter list. NameAlreadyUsedInParameterList(Symbol, Span), /// Error E0407: method is not a member of trait. - MethodNotMemberOfTrait(Ident, &'a str, Option), + MethodNotMemberOfTrait(Ident, String, Option), /// Error E0437: type is not a member of trait. - TypeNotMemberOfTrait(Ident, &'a str, Option), + TypeNotMemberOfTrait(Ident, String, Option), /// Error E0438: const is not a member of trait. - ConstNotMemberOfTrait(Ident, &'a str, Option), + ConstNotMemberOfTrait(Ident, String, Option), /// Error E0408: variable `{}` is not bound in all patterns. - VariableNotBoundInPattern(&'a BindingError), + VariableNotBoundInPattern(BindingError, ParentScope<'a>), /// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm. VariableBoundWithDifferentMode(Symbol, Span), /// Error E0415: identifier is bound more than once in this parameter list. diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index cf0c5703cd0ee..356763fab5e3b 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -643,6 +643,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let pre = if in_match { "in the same arm, " } else { "" }; err.note(&format!("{}a binding must have the same type in all alternatives", pre)); + // FIXME: check if `var_ty` and `ty` can be made the same type by adding or removing + // `ref` or `&` to the pattern. err.emit(); } } diff --git a/src/test/ui/or-patterns/missing-bindings.stderr b/src/test/ui/or-patterns/missing-bindings.stderr index c173a3a9aba20..8fafa275b5c27 100644 --- a/src/test/ui/or-patterns/missing-bindings.stderr +++ b/src/test/ui/or-patterns/missing-bindings.stderr @@ -103,6 +103,22 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | | | pattern doesn't bind `c` +error[E0408]: variable `d` is not bound in all patterns + --> $DIR/missing-bindings.rs:45:33 + | +LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; + | - ^^^^ pattern doesn't bind `d` + | | + | variable not in all patterns + +error[E0408]: variable `e` is not bound in all patterns + --> $DIR/missing-bindings.rs:45:10 + | +LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; + | ^^^^^^^^^^^^^^^^^^^^ - variable not in all patterns + | | + | pattern doesn't bind `e` + error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:45:33 | @@ -127,22 +143,6 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | | | variable not in all patterns -error[E0408]: variable `d` is not bound in all patterns - --> $DIR/missing-bindings.rs:45:33 - | -LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; - | - ^^^^ pattern doesn't bind `d` - | | - | variable not in all patterns - -error[E0408]: variable `e` is not bound in all patterns - --> $DIR/missing-bindings.rs:45:10 - | -LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; - | ^^^^^^^^^^^^^^^^^^^^ - variable not in all patterns - | | - | pattern doesn't bind `e` - error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:61:29 | diff --git a/src/test/ui/resolve/resolve-inconsistent-names.rs b/src/test/ui/resolve/resolve-inconsistent-names.rs index b9202f556d12b..989d2d4523099 100644 --- a/src/test/ui/resolve/resolve-inconsistent-names.rs +++ b/src/test/ui/resolve/resolve-inconsistent-names.rs @@ -2,9 +2,9 @@ enum E { A, B, c } -mod m { +pub mod m { const CONST1: usize = 10; - const Const2: usize = 20; + pub const Const2: usize = 20; } fn main() { @@ -22,15 +22,14 @@ fn main() { //~| ERROR variable `B` is bound inconsistently //~| ERROR mismatched types //~| ERROR variable `c` is not bound in all patterns - //~| HELP consider making the path in the pattern qualified: `?::A` + //~| HELP if you meant to match on unit variant `E::A`, use the full path in the pattern } let z = (10, 20); match z { (CONST1, _) | (_, Const2) => () //~^ ERROR variable `CONST1` is not bound in all patterns - //~| HELP consider making the path in the pattern qualified: `?::CONST1` //~| ERROR variable `Const2` is not bound in all patterns - //~| HELP consider making the path in the pattern qualified: `?::Const2` + //~| HELP if you meant to match on constant `m::Const2`, use the full path in the pattern } } diff --git a/src/test/ui/resolve/resolve-inconsistent-names.stderr b/src/test/ui/resolve/resolve-inconsistent-names.stderr index 70e9c2e5bf598..9de191f7d327a 100644 --- a/src/test/ui/resolve/resolve-inconsistent-names.stderr +++ b/src/test/ui/resolve/resolve-inconsistent-names.stderr @@ -23,11 +23,10 @@ LL | (A, B) | (ref B, c) | (c, A) => () | | pattern doesn't bind `A` | variable not in all patterns | -help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::A` - --> $DIR/resolve-inconsistent-names.rs:19:10 +help: if you meant to match on unit variant `E::A`, use the full path in the pattern | -LL | (A, B) | (ref B, c) | (c, A) => () - | ^ +LL | (E::A, B) | (ref B, c) | (c, A) => () + | ~~~~ error[E0408]: variable `B` is not bound in all patterns --> $DIR/resolve-inconsistent-names.rs:19:31 @@ -63,11 +62,11 @@ LL | (CONST1, _) | (_, Const2) => () | | | variable not in all patterns | -help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::CONST1` - --> $DIR/resolve-inconsistent-names.rs:30:10 +note: you might have meant to match on constant `m::CONST1`, which exists but is inaccessible + --> $DIR/resolve-inconsistent-names.rs:6:5 | -LL | (CONST1, _) | (_, Const2) => () - | ^^^^^^ +LL | const CONST1: usize = 10; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not accessible error[E0408]: variable `Const2` is not bound in all patterns --> $DIR/resolve-inconsistent-names.rs:30:9 @@ -77,11 +76,10 @@ LL | (CONST1, _) | (_, Const2) => () | | | pattern doesn't bind `Const2` | -help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::Const2` - --> $DIR/resolve-inconsistent-names.rs:30:27 +help: if you meant to match on constant `m::Const2`, use the full path in the pattern | -LL | (CONST1, _) | (_, Const2) => () - | ^^^^^^ +LL | (CONST1, _) | (_, m::Const2) => () + | ~~~~~~~~~ error[E0308]: mismatched types --> $DIR/resolve-inconsistent-names.rs:19:19 diff --git a/src/test/ui/span/issue-39698.stderr b/src/test/ui/span/issue-39698.stderr index 445df90d395ee..25c35fd5479f9 100644 --- a/src/test/ui/span/issue-39698.stderr +++ b/src/test/ui/span/issue-39698.stderr @@ -1,3 +1,13 @@ +error[E0408]: variable `d` is not bound in all patterns + --> $DIR/issue-39698.rs:10:37 + | +LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } + | - - ^^^^^^^^ ^^^^^^^^ pattern doesn't bind `d` + | | | | + | | | pattern doesn't bind `d` + | | variable not in all patterns + | variable not in all patterns + error[E0408]: variable `a` is not bound in all patterns --> $DIR/issue-39698.rs:10:23 | @@ -28,16 +38,6 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | pattern doesn't bind `c` | pattern doesn't bind `c` -error[E0408]: variable `d` is not bound in all patterns - --> $DIR/issue-39698.rs:10:37 - | -LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } - | - - ^^^^^^^^ ^^^^^^^^ pattern doesn't bind `d` - | | | | - | | | pattern doesn't bind `d` - | | variable not in all patterns - | variable not in all patterns - error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0408`.