Skip to content

Commit acf90a8

Browse files
E0229: Suggest Moving Type Constraints to Type Parameter Declaration
1 parent 6d19ac3 commit acf90a8

File tree

3 files changed

+94
-11
lines changed

3 files changed

+94
-11
lines changed

compiler/rustc_hir/src/hir.rs

+15
Original file line numberDiff line numberDiff line change
@@ -3735,6 +3735,21 @@ impl<'hir> Node<'hir> {
37353735
}
37363736
}
37373737

3738+
/// Get a `hir::Impl` if the node is an impl block for the given `trait_def_id`.
3739+
pub fn impl_block_of_trait(self, trait_def_id: DefId) -> Option<&'hir Impl<'hir>> {
3740+
match self {
3741+
Node::Item(Item { kind: ItemKind::Impl(impl_block), .. })
3742+
if impl_block
3743+
.of_trait
3744+
.and_then(|trait_ref| trait_ref.trait_def_id())
3745+
.is_some_and(|trait_id| trait_id == trait_def_id) =>
3746+
{
3747+
Some(impl_block)
3748+
}
3749+
_ => None,
3750+
}
3751+
}
3752+
37383753
pub fn fn_sig(self) -> Option<&'hir FnSig<'hir>> {
37393754
match self {
37403755
Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. })

compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs

+63-10
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,6 @@ pub fn prohibit_assoc_item_constraint(
12171217
// otherwise suggest the removal of the binding.
12181218
if let Some((def_id, segment, _)) = segment
12191219
&& segment.args().parenthesized == hir::GenericArgsParentheses::No
1220-
&& let hir::AssocItemConstraintKind::Equality { term } = constraint.kind
12211220
{
12221221
// Suggests removal of the offending binding
12231222
let suggest_removal = |e: &mut Diag<'_>| {
@@ -1286,20 +1285,74 @@ pub fn prohibit_assoc_item_constraint(
12861285
// Check if the type has a generic param with the same name
12871286
// as the assoc type name in the associated item binding.
12881287
let generics = tcx.generics_of(def_id);
1289-
let matching_param =
1290-
generics.own_params.iter().find(|p| p.name.as_str() == constraint.ident.as_str());
1288+
let matching_param = generics.own_params.iter().find(|p| p.name == constraint.ident.name);
12911289

12921290
// Now emit the appropriate suggestion
12931291
if let Some(matching_param) = matching_param {
1294-
match (&matching_param.kind, term) {
1295-
(GenericParamDefKind::Type { .. }, hir::Term::Ty(ty)) => {
1296-
suggest_direct_use(&mut err, ty.span);
1292+
match constraint.kind {
1293+
hir::AssocItemConstraintKind::Equality { term } => {
1294+
match (&matching_param.kind, term) {
1295+
(GenericParamDefKind::Type { .. }, hir::Term::Ty(ty)) => {
1296+
suggest_direct_use(&mut err, ty.span);
1297+
}
1298+
(GenericParamDefKind::Const { .. }, hir::Term::Const(c)) => {
1299+
let span = tcx.hir().span(c.hir_id);
1300+
suggest_direct_use(&mut err, span);
1301+
}
1302+
_ => suggest_removal(&mut err),
1303+
}
12971304
}
1298-
(GenericParamDefKind::Const { .. }, hir::Term::Const(c)) => {
1299-
let span = tcx.hir().span(c.hir_id);
1300-
suggest_direct_use(&mut err, span);
1305+
hir::AssocItemConstraintKind::Bound { bounds } => {
1306+
// Suggest `impl<T: Bound> Trait<T> for Foo` when finding
1307+
// `impl Trait<T: Bound> for Foo`
1308+
1309+
// Get the parent impl block based on the binding we have
1310+
// and the trait DefId
1311+
let impl_block = tcx
1312+
.hir()
1313+
.parent_iter(constraint.hir_id)
1314+
.find_map(|(_, node)| node.impl_block_of_trait(def_id));
1315+
1316+
let type_with_constraints =
1317+
tcx.sess.source_map().span_to_snippet(constraint.span);
1318+
1319+
if let Some(impl_block) = impl_block
1320+
&& let Ok(type_with_constraints) = type_with_constraints
1321+
{
1322+
// Filter out the lifetime parameters because
1323+
// they should be declared before the type parameter
1324+
let lifetimes: String = bounds
1325+
.iter()
1326+
.filter_map(|bound| {
1327+
if let hir::GenericBound::Outlives(lifetime) = bound {
1328+
Some(format!("{lifetime}, "))
1329+
} else {
1330+
None
1331+
}
1332+
})
1333+
.collect();
1334+
// Figure out a span and suggestion string based on
1335+
// whether there are any existing parameters
1336+
let param_decl = if let Some(param_span) =
1337+
impl_block.generics.span_for_param_suggestion()
1338+
{
1339+
(param_span, format!(", {lifetimes}{type_with_constraints}"))
1340+
} else {
1341+
(
1342+
impl_block.generics.span.shrink_to_lo(),
1343+
format!("<{lifetimes}{type_with_constraints}>"),
1344+
)
1345+
};
1346+
let suggestions =
1347+
vec![param_decl, (constraint.span, format!("{}", matching_param.name))];
1348+
1349+
err.multipart_suggestion_verbose(
1350+
format!("declare the type parameter right after the `impl` keyword"),
1351+
suggestions,
1352+
Applicability::MaybeIncorrect,
1353+
);
1354+
}
13011355
}
1302-
_ => suggest_removal(&mut err),
13031356
}
13041357
} else {
13051358
suggest_removal(&mut err);

compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ pub(crate) fn check_generic_arg_count(
531531

532532
let num_default_params = expected_max - expected_min;
533533

534+
let mut all_params_are_binded = false;
534535
let gen_args_info = if provided > expected_max {
535536
invalid_args.extend((expected_max..provided).map(|i| i + args_offset));
536537
let num_redundant_args = provided - expected_max;
@@ -547,6 +548,20 @@ pub(crate) fn check_generic_arg_count(
547548
} else {
548549
let num_missing_args = expected_max - provided;
549550

551+
let constraint_names: Vec<_> =
552+
gen_args.constraints.iter().map(|b| b.ident.name).collect();
553+
let param_names: Vec<_> = gen_params
554+
.own_params
555+
.iter()
556+
.filter(|param| !has_self || param.index != 0) // Assumes `Self` will always be the first parameter
557+
.map(|param| param.name)
558+
.collect();
559+
if constraint_names == param_names {
560+
// We set this to true and delay emitting `WrongNumberOfGenericArgs`
561+
// to provide a succinct error for cases like issue #113073
562+
all_params_are_binded = true;
563+
};
564+
550565
GenericArgsInfo::MissingTypesOrConsts {
551566
num_missing_args,
552567
num_default_params,
@@ -567,7 +582,7 @@ pub(crate) fn check_generic_arg_count(
567582
def_id,
568583
)
569584
.diagnostic()
570-
.emit()
585+
.emit_unless(all_params_are_binded)
571586
});
572587

573588
Err(reported)

0 commit comments

Comments
 (0)