Skip to content

Commit 84f4c7f

Browse files
committed
Suggest how to fix with unconstrained type parameters
Unconstrained type parameters made no suggestion and it makes difficult to know how to fix (#107295). To make fixing easier, we output suggestions how to fix.
1 parent f17cf74 commit 84f4c7f

File tree

1 file changed

+98
-1
lines changed

1 file changed

+98
-1
lines changed

compiler/rustc_hir_analysis/src/impl_wf_check.rs

+98-1
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@ use std::assert_matches::debug_assert_matches;
1313
use min_specialization::check_min_specialization;
1414
use rustc_data_structures::fx::FxHashSet;
1515
use rustc_errors::codes::*;
16+
use rustc_errors::{Applicability, Diag};
1617
use rustc_hir::def::DefKind;
1718
use rustc_hir::def_id::LocalDefId;
18-
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
19+
use rustc_hir::{
20+
GenericParam, GenericParamKind, LifetimeParamKind, Path, PredicateOrigin, QPath, Ty, TyKind,
21+
WhereBoundPredicate, WherePredicateKind,
22+
};
23+
use rustc_middle::ty::{self, GenericParamDef, TyCtxt, TypeVisitableExt};
1924
use rustc_span::ErrorGuaranteed;
2025

2126
use crate::constrained_generic_params as cgp;
@@ -140,6 +145,7 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained(
140145
const_param_note2: false,
141146
});
142147
diag.code(E0207);
148+
suggest_to_remove_or_use_generics(tcx, &mut diag, impl_def_id, param);
143149
res = Err(diag.emit());
144150
}
145151
// (*) This is a horrible concession to reality. I think it'd be
@@ -229,8 +235,99 @@ pub(crate) fn enforce_impl_non_lifetime_params_are_constrained(
229235
const_param_note2: const_param_note,
230236
});
231237
diag.code(E0207);
238+
suggest_to_remove_or_use_generics(tcx, &mut diag, impl_def_id, &param);
232239
res = Err(diag.emit());
233240
}
234241
}
235242
res
236243
}
244+
245+
fn suggest_to_remove_or_use_generics(
246+
tcx: TyCtxt<'_>,
247+
diag: &mut Diag<'_>,
248+
impl_def_id: LocalDefId,
249+
param: &GenericParamDef,
250+
) {
251+
let node = tcx.hir().get_if_local(impl_def_id.into()).expect("cannot get `Node`");
252+
let hir_impl = if let rustc_hir::Node::Item(item) = node {
253+
if let rustc_hir::ItemKind::Impl(imp) = item.kind { Some(imp) } else { None }
254+
} else {
255+
None
256+
}
257+
.expect("cannot take `Impl` in a impl block");
258+
259+
let (index, hir_param) = hir_impl
260+
.generics
261+
.params
262+
.iter()
263+
.enumerate()
264+
.find(|(_, par)| par.name.ident().name == param.name)
265+
.unwrap();
266+
let mut suggestions = vec![];
267+
268+
let is_impl_generic = |par: &&GenericParam<'_>| match par.kind {
269+
GenericParamKind::Type { .. }
270+
| GenericParamKind::Const { .. }
271+
| GenericParamKind::Lifetime { kind: LifetimeParamKind::Explicit } => true,
272+
_ => false,
273+
};
274+
// Suggestion for removing the type parameter.
275+
suggestions.push(vec![(
276+
// Find the span of the type parameter.
277+
if let Some(prev) = hir_impl.generics.params[..index].iter().rfind(is_impl_generic) {
278+
let mut span = prev.span;
279+
280+
// Consider the span of the bounds with the generic parameter when there is.
281+
if let Some(predicate) = hir_impl.generics.predicates.iter().find(|pred| {
282+
if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
283+
origin: PredicateOrigin::GenericParam,
284+
bounded_ty,
285+
..
286+
}) = pred.kind
287+
{
288+
bounded_ty.span == prev.span
289+
} else {
290+
false
291+
}
292+
}) {
293+
span = span.to(predicate.span)
294+
};
295+
296+
span.shrink_to_hi().to(hir_param.span)
297+
} else if let Some(next) =
298+
hir_impl.generics.params[index + 1..].iter().find(is_impl_generic)
299+
{
300+
hir_param.span.until(next.span)
301+
} else {
302+
// Remove also angle brackets <> when there is just ONE generic parameter.
303+
hir_impl.generics.span
304+
},
305+
String::new(),
306+
)]);
307+
308+
// Suggestion for making use of the type parameter.
309+
if let Some(path) = extract_ty_as_path(hir_impl.self_ty) {
310+
let seg = path.segments.last().unwrap();
311+
if let Some(args) = seg.args {
312+
suggestions
313+
.push(vec![(args.span().unwrap().shrink_to_hi(), format!(", {}", param.name))]);
314+
} else {
315+
suggestions.push(vec![(seg.ident.span.shrink_to_hi(), format!("<{}>", param.name))]);
316+
}
317+
}
318+
319+
diag.multipart_suggestions(
320+
format!("either remove the type parameter {}, or make use of it, for example", param.name),
321+
suggestions,
322+
Applicability::MaybeIncorrect,
323+
);
324+
}
325+
326+
fn extract_ty_as_path<'hir>(ty: &Ty<'hir>) -> Option<&'hir Path<'hir>> {
327+
match ty.kind {
328+
TyKind::Path(QPath::Resolved(_, path)) => Some(path),
329+
TyKind::Slice(ty) | TyKind::Array(ty, _) => extract_ty_as_path(ty),
330+
TyKind::Ptr(ty) | TyKind::Ref(_, ty) => extract_ty_as_path(ty.ty),
331+
_ => None,
332+
}
333+
}

0 commit comments

Comments
 (0)