Skip to content

Commit fcdffd6

Browse files
committed
Emit suggestions when equality constraints are wronlgy used
1 parent eff958c commit fcdffd6

File tree

17 files changed

+710
-29
lines changed

17 files changed

+710
-29
lines changed

compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs

+112-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ use rustc_hir as hir;
1515
use rustc_hir::def_id::{DefId, LocalDefId};
1616
use rustc_infer::traits::FulfillmentError;
1717
use rustc_middle::query::Key;
18-
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt};
18+
use rustc_middle::ty::{
19+
self, suggest_constraining_type_param, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt,
20+
};
1921
use rustc_session::parse::feature_err;
2022
use rustc_span::edit_distance::find_best_match_for_name;
2123
use rustc_span::symbol::{sym, Ident};
@@ -1029,12 +1031,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
10291031
/// Emits an error regarding forbidden type binding associations
10301032
pub fn prohibit_assoc_item_binding(
10311033
tcx: TyCtxt<'_>,
1032-
span: Span,
1033-
segment: Option<(&hir::PathSegment<'_>, Span)>,
1034+
binding: &hir::TypeBinding<'_>,
1035+
segment: Option<(DefId, &hir::PathSegment<'_>, Span)>,
10341036
) {
1035-
tcx.dcx().emit_err(AssocTypeBindingNotAllowed {
1036-
span,
1037-
fn_trait_expansion: if let Some((segment, span)) = segment
1037+
let mut err = tcx.dcx().create_err(AssocTypeBindingNotAllowed {
1038+
span: binding.span,
1039+
fn_trait_expansion: if let Some((_, segment, span)) = segment
10381040
&& segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar
10391041
{
10401042
Some(ParenthesizedFnTraitExpansion {
@@ -1045,6 +1047,110 @@ pub fn prohibit_assoc_item_binding(
10451047
None
10461048
},
10471049
});
1050+
1051+
// Emit a suggestion to use the type arg directly
1052+
// if there's a generic param with a matching name
1053+
// otherwise suggest removal of type binding
1054+
if let Some((def_id, segment, _)) = segment
1055+
&& segment.args().parenthesized == hir::GenericArgsParentheses::No
1056+
&& let hir::TypeBindingKind::Equality { term } = binding.kind
1057+
{
1058+
// Suggests removal of the offending binding
1059+
let suggest_removal = |e: &mut Diag<'_>| {
1060+
let bindings = segment.args().bindings;
1061+
let args = segment.args().args;
1062+
let binding_span = binding.span;
1063+
1064+
// Compute the span to remove based on the the position
1065+
// of the binding. We do that as follows:
1066+
// 1. Find the index of the binding in the list of bindings
1067+
// 2. Locate the spans preceding and following the binding.
1068+
// If it's the first binding the preceding span would be
1069+
// that of the last arg
1070+
// 3. Using this information work out whether the span
1071+
// to remove will start from the end of the preceding span,
1072+
// end at the start of the next span or will simply be the
1073+
// span encomassing everything within the generics brackets
1074+
1075+
let Some(binding_index) = bindings.iter().position(|b| b.hir_id == binding.hir_id)
1076+
else {
1077+
bug!("a type binding exists but its HIR ID not found in generics");
1078+
};
1079+
1080+
let preceding_span = if binding_index > 0 {
1081+
Some(bindings[binding_index - 1].span)
1082+
} else {
1083+
args.last().map(|a| a.span())
1084+
};
1085+
1086+
let next_span = if binding_index < bindings.len() - 1 {
1087+
Some(bindings[binding_index + 1].span)
1088+
} else {
1089+
None
1090+
};
1091+
1092+
let removal_span = match (preceding_span, next_span) {
1093+
(Some(prec), _) => binding_span.with_lo(prec.hi()),
1094+
(None, Some(next)) => binding_span.with_hi(next.lo()),
1095+
(None, None) => {
1096+
let Some(generics_span) = segment.args().span_ext() else {
1097+
bug!("a type binding exists but generic span is empty");
1098+
};
1099+
1100+
generics_span
1101+
}
1102+
};
1103+
1104+
// Now emit the suggestion
1105+
if let Ok(suggestion) = tcx.sess.source_map().span_to_snippet(removal_span) {
1106+
e.span_suggestion_verbose(
1107+
removal_span,
1108+
"consider removing this type binding",
1109+
suggestion,
1110+
Applicability::MaybeIncorrect,
1111+
);
1112+
}
1113+
};
1114+
1115+
// Suggests replacing the type binding with normal
1116+
// type argument (i.e. replacing <T = A> with <T>)
1117+
let suggest_direct_use = |e: &mut Diag<'_>, sp: Span, is_ty: bool| {
1118+
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(sp) {
1119+
let ty_or_const = if is_ty { "generic" } else { "const" };
1120+
e.span_suggestion_verbose(
1121+
binding.span,
1122+
format!("to use `{snippet}` as a {ty_or_const} argument specify it directly"),
1123+
snippet,
1124+
Applicability::MaybeIncorrect,
1125+
);
1126+
}
1127+
};
1128+
1129+
// Check if the type has a generic param with the
1130+
// same name as the assoc type name in type binding
1131+
let generics = tcx.generics_of(def_id);
1132+
let binding_ident_lower = binding.ident.as_str().to_lowercase();
1133+
let matching_param =
1134+
generics.params.iter().find(|p| p.name.as_str().to_lowercase() == binding_ident_lower);
1135+
1136+
// Now emit the appropriate suggestion
1137+
if let Some(matching_param) = matching_param {
1138+
match (&matching_param.kind, term) {
1139+
(GenericParamDefKind::Type { .. }, hir::Term::Ty(ty)) => {
1140+
suggest_direct_use(&mut err, ty.span, true);
1141+
}
1142+
(GenericParamDefKind::Const { .. }, hir::Term::Const(c)) => {
1143+
let span = tcx.hir().span(c.hir_id);
1144+
suggest_direct_use(&mut err, span, false);
1145+
}
1146+
_ => suggest_removal(&mut err),
1147+
}
1148+
} else {
1149+
suggest_removal(&mut err);
1150+
}
1151+
}
1152+
1153+
err.emit();
10481154
}
10491155

10501156
pub(crate) fn fn_trait_to_string(

compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ pub(crate) fn check_generic_arg_count(
454454
if gen_pos != GenericArgPosition::Type
455455
&& let Some(b) = gen_args.bindings.first()
456456
{
457-
prohibit_assoc_item_binding(tcx, b.span, None);
457+
prohibit_assoc_item_binding(tcx, b, None);
458458
}
459459

460460
let explicit_late_bound =

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
322322
ty::BoundConstness::NotConst,
323323
);
324324
if let Some(b) = item_segment.args().bindings.first() {
325-
prohibit_assoc_item_binding(self.tcx(), b.span, Some((item_segment, span)));
325+
prohibit_assoc_item_binding(self.tcx(), b, Some((def_id, item_segment, span)));
326326
}
327327
args
328328
}
@@ -619,7 +619,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
619619
ty::BoundConstness::NotConst,
620620
);
621621
if let Some(b) = item_segment.args().bindings.first() {
622-
prohibit_assoc_item_binding(self.tcx(), b.span, Some((item_segment, span)));
622+
prohibit_assoc_item_binding(self.tcx(), b, Some((item_def_id, item_segment, span)));
623623
}
624624
args
625625
}
@@ -758,7 +758,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
758758
constness,
759759
);
760760
if let Some(b) = trait_segment.args().bindings.first() {
761-
prohibit_assoc_item_binding(self.tcx(), b.span, Some((trait_segment, span)));
761+
prohibit_assoc_item_binding(self.tcx(), b, Some((trait_def_id, trait_segment, span)));
762762
}
763763
ty::TraitRef::new(self.tcx(), trait_def_id, generic_args)
764764
}
@@ -1724,7 +1724,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
17241724
for segment in segments {
17251725
// Only emit the first error to avoid overloading the user with error messages.
17261726
if let Some(b) = segment.args().bindings.first() {
1727-
prohibit_assoc_item_binding(self.tcx(), b.span, None);
1727+
prohibit_assoc_item_binding(self.tcx(), b, None);
17281728
return true;
17291729
}
17301730
}

tests/rustdoc-ui/invalid_associated_const.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ error[E0229]: associated type bindings are not allowed here
33
|
44
LL | type A: S<C<X = 0i32> = 34>;
55
| ^^^^^^^^ associated type not allowed here
6+
|
7+
help: consider removing this type binding
8+
|
9+
LL | type A: S<C<X = 0i32> = 34>;
10+
| ~~~~~~~~~~
611

712
error[E0229]: associated type bindings are not allowed here
813
--> $DIR/invalid_associated_const.rs:4:17
@@ -11,6 +16,10 @@ LL | type A: S<C<X = 0i32> = 34>;
1116
| ^^^^^^^^ associated type not allowed here
1217
|
1318
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
19+
help: consider removing this type binding
20+
|
21+
LL | type A: S<C<X = 0i32> = 34>;
22+
| ~~~~~~~~~~
1423

1524
error: aborting due to 2 previous errors
1625

tests/rustdoc-ui/issue-102467.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ error[E0229]: associated type bindings are not allowed here
33
|
44
LL | type A: S<C<X = 0i32> = 34>;
55
| ^^^^^^^^ associated type not allowed here
6+
|
7+
help: consider removing this type binding
8+
|
9+
LL | type A: S<C<X = 0i32> = 34>;
10+
| ~~~~~~~~~~
611

712
error[E0229]: associated type bindings are not allowed here
813
--> $DIR/issue-102467.rs:7:17
@@ -11,6 +16,10 @@ LL | type A: S<C<X = 0i32> = 34>;
1116
| ^^^^^^^^ associated type not allowed here
1217
|
1318
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
19+
help: consider removing this type binding
20+
|
21+
LL | type A: S<C<X = 0i32> = 34>;
22+
| ~~~~~~~~~~
1423

1524
error: aborting due to 2 previous errors
1625

tests/ui/associated-consts/issue-102335-const.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ error[E0229]: associated type bindings are not allowed here
33
|
44
LL | type A: S<C<X = 0i32> = 34>;
55
| ^^^^^^^^ associated type not allowed here
6+
|
7+
help: consider removing this type binding
8+
|
9+
LL | type A: S<C<X = 0i32> = 34>;
10+
| ~~~~~~~~~~
611

712
error[E0229]: associated type bindings are not allowed here
813
--> $DIR/issue-102335-const.rs:4:17
@@ -11,6 +16,10 @@ LL | type A: S<C<X = 0i32> = 34>;
1116
| ^^^^^^^^ associated type not allowed here
1217
|
1318
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
19+
help: consider removing this type binding
20+
|
21+
LL | type A: S<C<X = 0i32> = 34>;
22+
| ~~~~~~~~~~
1423

1524
error: aborting due to 2 previous errors
1625

tests/ui/associated-type-bounds/issue-102335-ty.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
trait T {
2-
type A: S<C<i32 = u32> = ()>;
2+
type A: S<C<i32 = u32> = ()>; // Just one erroneous equality constraint
3+
//~^ ERROR associated type bindings are not allowed here
4+
//~| ERROR associated type bindings are not allowed here
5+
}
6+
7+
trait T2 {
8+
type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
39
//~^ ERROR associated type bindings are not allowed here
410
//~| ERROR associated type bindings are not allowed here
511
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,49 @@
11
error[E0229]: associated type bindings are not allowed here
22
--> $DIR/issue-102335-ty.rs:2:17
33
|
4-
LL | type A: S<C<i32 = u32> = ()>;
4+
LL | type A: S<C<i32 = u32> = ()>; // Just one erroneous equality constraint
55
| ^^^^^^^^^ associated type not allowed here
6+
|
7+
help: consider removing this type binding
8+
|
9+
LL | type A: S<C<i32 = u32> = ()>; // Just one erroneous equality constraint
10+
| ~~~~~~~~~~~
611

712
error[E0229]: associated type bindings are not allowed here
813
--> $DIR/issue-102335-ty.rs:2:17
914
|
10-
LL | type A: S<C<i32 = u32> = ()>;
15+
LL | type A: S<C<i32 = u32> = ()>; // Just one erroneous equality constraint
16+
| ^^^^^^^^^ associated type not allowed here
17+
|
18+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
19+
help: consider removing this type binding
20+
|
21+
LL | type A: S<C<i32 = u32> = ()>; // Just one erroneous equality constraint
22+
| ~~~~~~~~~~~
23+
24+
error[E0229]: associated type bindings are not allowed here
25+
--> $DIR/issue-102335-ty.rs:8:17
26+
|
27+
LL | type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
28+
| ^^^^^^^^^ associated type not allowed here
29+
|
30+
help: consider removing this type binding
31+
|
32+
LL | type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
33+
| ~~~~~~~~~~
34+
35+
error[E0229]: associated type bindings are not allowed here
36+
--> $DIR/issue-102335-ty.rs:8:17
37+
|
38+
LL | type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
1139
| ^^^^^^^^^ associated type not allowed here
1240
|
1341
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
42+
help: consider removing this type binding
43+
|
44+
LL | type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
45+
| ~~~~~~~~~~
1446

15-
error: aborting due to 2 previous errors
47+
error: aborting due to 4 previous errors
1648

1749
For more information about this error, try `rustc --explain E0229`.

0 commit comments

Comments
 (0)