Skip to content

Commit 0c8e520

Browse files
committed
Auto merge of #90988 - estebank:binding-supposed-to-be-const, r=davidtwco
When encountering a binding that could be a const or unit variant, suggest the right path
2 parents 05c0738 + bce5ab2 commit 0c8e520

File tree

8 files changed

+164
-88
lines changed

8 files changed

+164
-88
lines changed

compiler/rustc_resolve/src/diagnostics.rs

+104-30
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ impl<'a> Resolver<'a> {
123123
let (span, found_use) = if let Some(def_id) = def_id.as_local() {
124124
UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id])
125125
} else {
126-
(None, false)
126+
(None, FoundUse::No)
127127
};
128128
if !candidates.is_empty() {
129129
show_candidates(
@@ -132,8 +132,9 @@ impl<'a> Resolver<'a> {
132132
&mut err,
133133
span,
134134
&candidates,
135-
instead,
135+
if instead { Instead::Yes } else { Instead::No },
136136
found_use,
137+
IsPattern::No,
137138
);
138139
} else if let Some((span, msg, sugg, appl)) = suggestion {
139140
err.span_suggestion(span, msg, sugg, appl);
@@ -493,14 +494,14 @@ impl<'a> Resolver<'a> {
493494
///
494495
/// This takes the error provided, combines it with the span and any additional spans inside the
495496
/// error and emits it.
496-
crate fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) {
497+
crate fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
497498
self.into_struct_error(span, resolution_error).emit();
498499
}
499500

500501
crate fn into_struct_error(
501-
&self,
502+
&mut self,
502503
span: Span,
503-
resolution_error: ResolutionError<'_>,
504+
resolution_error: ResolutionError<'a>,
504505
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
505506
match resolution_error {
506507
ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => {
@@ -650,7 +651,7 @@ impl<'a> Resolver<'a> {
650651
}
651652
err
652653
}
653-
ResolutionError::VariableNotBoundInPattern(binding_error) => {
654+
ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => {
654655
let BindingError { name, target, origin, could_be_path } = binding_error;
655656

656657
let target_sp = target.iter().copied().collect::<Vec<_>>();
@@ -670,13 +671,41 @@ impl<'a> Resolver<'a> {
670671
for sp in origin_sp {
671672
err.span_label(sp, "variable not in all patterns");
672673
}
673-
if *could_be_path {
674-
let help_msg = format!(
675-
"if you meant to match on a variant or a `const` item, consider \
676-
making the path in the pattern qualified: `?::{}`",
677-
name,
674+
if could_be_path {
675+
let import_suggestions = self.lookup_import_candidates(
676+
Ident::with_dummy_span(name),
677+
Namespace::ValueNS,
678+
&parent_scope,
679+
&|res: Res| match res {
680+
Res::Def(
681+
DefKind::Ctor(CtorOf::Variant, CtorKind::Const)
682+
| DefKind::Ctor(CtorOf::Struct, CtorKind::Const)
683+
| DefKind::Const
684+
| DefKind::AssocConst,
685+
_,
686+
) => true,
687+
_ => false,
688+
},
689+
);
690+
691+
if import_suggestions.is_empty() {
692+
let help_msg = format!(
693+
"if you meant to match on a variant or a `const` item, consider \
694+
making the path in the pattern qualified: `path::to::ModOrType::{}`",
695+
name,
696+
);
697+
err.span_help(span, &help_msg);
698+
}
699+
show_candidates(
700+
&self.definitions,
701+
self.session,
702+
&mut err,
703+
Some(span),
704+
&import_suggestions,
705+
Instead::No,
706+
FoundUse::Yes,
707+
IsPattern::Yes,
678708
);
679-
err.span_help(span, &help_msg);
680709
}
681710
err
682711
}
@@ -1022,7 +1051,7 @@ impl<'a> Resolver<'a> {
10221051
}
10231052

10241053
crate fn report_vis_error(
1025-
&self,
1054+
&mut self,
10261055
vis_resolution_error: VisResolutionError<'_>,
10271056
) -> ErrorGuaranteed {
10281057
match vis_resolution_error {
@@ -1453,8 +1482,9 @@ impl<'a> Resolver<'a> {
14531482
err,
14541483
None,
14551484
&import_suggestions,
1456-
false,
1457-
true,
1485+
Instead::No,
1486+
FoundUse::Yes,
1487+
IsPattern::No,
14581488
);
14591489

14601490
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
@@ -2390,6 +2420,27 @@ fn find_span_immediately_after_crate_name(
23902420
(next_left_bracket == after_second_colon, from_second_colon)
23912421
}
23922422

2423+
/// A suggestion has already been emitted, change the wording slightly to clarify that both are
2424+
/// independent options.
2425+
enum Instead {
2426+
Yes,
2427+
No,
2428+
}
2429+
2430+
/// Whether an existing place with an `use` item was found.
2431+
enum FoundUse {
2432+
Yes,
2433+
No,
2434+
}
2435+
2436+
/// Whether a binding is part of a pattern or an expression. Used for diagnostics.
2437+
enum IsPattern {
2438+
/// The binding is part of a pattern
2439+
Yes,
2440+
/// The binding is part of an expression
2441+
No,
2442+
}
2443+
23932444
/// When an entity with a given name is not available in scope, we search for
23942445
/// entities with that name in all crates. This method allows outputting the
23952446
/// results of this search in a programmer-friendly way
@@ -2400,8 +2451,9 @@ fn show_candidates(
24002451
// This is `None` if all placement locations are inside expansions
24012452
use_placement_span: Option<Span>,
24022453
candidates: &[ImportSuggestion],
2403-
instead: bool,
2404-
found_use: bool,
2454+
instead: Instead,
2455+
found_use: FoundUse,
2456+
is_pattern: IsPattern,
24052457
) {
24062458
if candidates.is_empty() {
24072459
return;
@@ -2428,32 +2480,46 @@ fn show_candidates(
24282480
}
24292481

24302482
if !accessible_path_strings.is_empty() {
2431-
let (determiner, kind) = if accessible_path_strings.len() == 1 {
2432-
("this", accessible_path_strings[0].1)
2483+
let (determiner, kind, name) = if accessible_path_strings.len() == 1 {
2484+
("this", accessible_path_strings[0].1, format!(" `{}`", accessible_path_strings[0].0))
24332485
} else {
2434-
("one of these", "items")
2486+
("one of these", "items", String::new())
24352487
};
24362488

2437-
let instead = if instead { " instead" } else { "" };
2438-
let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
2489+
let instead = if let Instead::Yes = instead { " instead" } else { "" };
2490+
let mut msg = if let IsPattern::Yes = is_pattern {
2491+
format!(
2492+
"if you meant to match on {}{}{}, use the full path in the pattern",
2493+
kind, instead, name
2494+
)
2495+
} else {
2496+
format!("consider importing {} {}{}", determiner, kind, instead)
2497+
};
24392498

24402499
for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
24412500
err.note(note);
24422501
}
24432502

2444-
if let Some(span) = use_placement_span {
2503+
if let (IsPattern::Yes, Some(span)) = (is_pattern, use_placement_span) {
2504+
err.span_suggestions(
2505+
span,
2506+
&msg,
2507+
accessible_path_strings.into_iter().map(|a| a.0),
2508+
Applicability::MaybeIncorrect,
2509+
);
2510+
} else if let Some(span) = use_placement_span {
24452511
for candidate in &mut accessible_path_strings {
24462512
// produce an additional newline to separate the new use statement
24472513
// from the directly following item.
2448-
let additional_newline = if found_use { "" } else { "\n" };
2514+
let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
24492515
candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
24502516
}
24512517

24522518
err.span_suggestions(
24532519
span,
24542520
&msg,
24552521
accessible_path_strings.into_iter().map(|a| a.0),
2456-
Applicability::Unspecified,
2522+
Applicability::MaybeIncorrect,
24572523
);
24582524
} else {
24592525
msg.push(':');
@@ -2468,9 +2534,17 @@ fn show_candidates(
24682534
} else {
24692535
assert!(!inaccessible_path_strings.is_empty());
24702536

2537+
let prefix =
2538+
if let IsPattern::Yes = is_pattern { "you might have meant to match on " } else { "" };
24712539
if inaccessible_path_strings.len() == 1 {
24722540
let (name, descr, def_id, note) = &inaccessible_path_strings[0];
2473-
let msg = format!("{} `{}` exists but is inaccessible", descr, name);
2541+
let msg = format!(
2542+
"{}{} `{}`{} exists but is inaccessible",
2543+
prefix,
2544+
descr,
2545+
name,
2546+
if let IsPattern::Yes = is_pattern { ", which" } else { "" }
2547+
);
24742548

24752549
if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
24762550
let span = definitions.def_span(local_def_id);
@@ -2496,7 +2570,7 @@ fn show_candidates(
24962570
"item".to_string()
24972571
};
24982572

2499-
let mut msg = format!("these {}s exist but are inaccessible", descr);
2573+
let mut msg = format!("{}these {}s exist but are inaccessible", prefix, descr);
25002574
let mut has_colon = false;
25012575

25022576
let mut spans = Vec::new();
@@ -2537,14 +2611,14 @@ struct UsePlacementFinder {
25372611
}
25382612

25392613
impl UsePlacementFinder {
2540-
fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, bool) {
2614+
fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, FoundUse) {
25412615
let mut finder =
25422616
UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None };
25432617
finder.visit_crate(krate);
25442618
if let Some(use_span) = finder.first_use_span {
2545-
(Some(use_span), true)
2619+
(Some(use_span), FoundUse::Yes)
25462620
} else {
2547-
(finder.first_legal_span, false)
2621+
(finder.first_legal_span, FoundUse::No)
25482622
}
25492623
}
25502624
}

compiler/rustc_resolve/src/late.rs

+14-11
Original file line numberDiff line numberDiff line change
@@ -1422,7 +1422,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
14221422

14231423
/// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved
14241424
/// label and reports an error if the label is not found or is unreachable.
1425-
fn resolve_label(&self, mut label: Ident) -> Option<NodeId> {
1425+
fn resolve_label(&mut self, mut label: Ident) -> Option<NodeId> {
14261426
let mut suggestion = None;
14271427

14281428
// Preserve the original span so that errors contain "in this macro invocation"
@@ -1442,14 +1442,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
14421442

14431443
let ident = label.normalize_to_macro_rules();
14441444
if let Some((ident, id)) = rib.bindings.get_key_value(&ident) {
1445+
let definition_span = ident.span;
14451446
return if self.is_label_valid_from_rib(i) {
14461447
Some(*id)
14471448
} else {
14481449
self.report_error(
14491450
original_span,
14501451
ResolutionError::UnreachableLabel {
14511452
name: label.name,
1452-
definition_span: ident.span,
1453+
definition_span,
14531454
suggestion,
14541455
},
14551456
);
@@ -2135,7 +2136,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
21352136
span: Span,
21362137
err: F,
21372138
) where
2138-
F: FnOnce(Ident, &str, Option<Symbol>) -> ResolutionError<'_>,
2139+
F: FnOnce(Ident, String, Option<Symbol>) -> ResolutionError<'a>,
21392140
{
21402141
// If there is a TraitRef in scope for an impl, then the method must be in the trait.
21412142
let Some((module, _)) = &self.current_trait_ref else { return; };
@@ -2159,7 +2160,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
21592160
// We could not find the method: report an error.
21602161
let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
21612162
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
2162-
self.report_error(span, err(ident, &path_names_to_string(path), candidate));
2163+
let path_names = path_names_to_string(path);
2164+
self.report_error(span, err(ident, path_names, candidate));
21632165
return;
21642166
};
21652167

@@ -2183,13 +2185,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
21832185
AssocItemKind::TyAlias(..) => (rustc_errors::error_code!(E0325), "type"),
21842186
AssocItemKind::MacCall(..) => span_bug!(span, "unexpanded macro"),
21852187
};
2188+
let trait_path = path_names_to_string(path);
21862189
self.report_error(
21872190
span,
21882191
ResolutionError::TraitImplMismatch {
21892192
name: ident.name,
21902193
kind,
21912194
code,
2192-
trait_path: path_names_to_string(path),
2195+
trait_path,
21932196
trait_item_span: binding.span,
21942197
},
21952198
);
@@ -2304,16 +2307,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
23042307
}
23052308

23062309
// 3) Report all missing variables we found.
2307-
let mut missing_vars = missing_vars.iter_mut().collect::<Vec<_>>();
2308-
missing_vars.sort_by_key(|(sym, _err)| sym.as_str());
2310+
let mut missing_vars = missing_vars.into_iter().collect::<Vec<_>>();
2311+
missing_vars.sort_by_key(|&(sym, ref _err)| sym);
23092312

2310-
for (name, mut v) in missing_vars {
2311-
if inconsistent_vars.contains_key(name) {
2313+
for (name, mut v) in missing_vars.into_iter() {
2314+
if inconsistent_vars.contains_key(&name) {
23122315
v.could_be_path = false;
23132316
}
23142317
self.report_error(
23152318
*v.origin.iter().next().unwrap(),
2316-
ResolutionError::VariableNotBoundInPattern(v),
2319+
ResolutionError::VariableNotBoundInPattern(v, self.parent_scope),
23172320
);
23182321
}
23192322

@@ -2815,7 +2818,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
28152818
/// A wrapper around [`Resolver::report_error`].
28162819
///
28172820
/// This doesn't emit errors for function bodies if this is rustdoc.
2818-
fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) {
2821+
fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
28192822
if self.should_report_errs() {
28202823
self.r.report_error(span, resolution_error);
28212824
}

compiler/rustc_resolve/src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,13 @@ enum ResolutionError<'a> {
201201
/// parameter list.
202202
NameAlreadyUsedInParameterList(Symbol, Span),
203203
/// Error E0407: method is not a member of trait.
204-
MethodNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
204+
MethodNotMemberOfTrait(Ident, String, Option<Symbol>),
205205
/// Error E0437: type is not a member of trait.
206-
TypeNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
206+
TypeNotMemberOfTrait(Ident, String, Option<Symbol>),
207207
/// Error E0438: const is not a member of trait.
208-
ConstNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
208+
ConstNotMemberOfTrait(Ident, String, Option<Symbol>),
209209
/// Error E0408: variable `{}` is not bound in all patterns.
210-
VariableNotBoundInPattern(&'a BindingError),
210+
VariableNotBoundInPattern(BindingError, ParentScope<'a>),
211211
/// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm.
212212
VariableBoundWithDifferentMode(Symbol, Span),
213213
/// Error E0415: identifier is bound more than once in this parameter list.

compiler/rustc_typeck/src/check/pat.rs

+2
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
643643
});
644644
let pre = if in_match { "in the same arm, " } else { "" };
645645
err.note(&format!("{}a binding must have the same type in all alternatives", pre));
646+
// FIXME: check if `var_ty` and `ty` can be made the same type by adding or removing
647+
// `ref` or `&` to the pattern.
646648
err.emit();
647649
}
648650
}

0 commit comments

Comments
 (0)