Skip to content

Commit 77a1a64

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 bace306 commit 77a1a64

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed

compiler/rustc_hir_analysis/src/impl_wf_check.rs

+95
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,14 @@ use std::assert_matches::debug_assert_matches;
1212

1313
use min_specialization::check_min_specialization;
1414
use rustc_data_structures::fx::FxHashSet;
15+
use rustc_errors::Applicability;
1516
use rustc_errors::codes::*;
1617
use rustc_hir::def::DefKind;
1718
use rustc_hir::def_id::LocalDefId;
19+
use rustc_hir::{
20+
GenericParam, GenericParamKind, LifetimeParamKind, Path, PredicateOrigin, QPath, Ty, TyKind,
21+
WhereBoundPredicate, WherePredicateKind,
22+
};
1823
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
1924
use rustc_span::ErrorGuaranteed;
2025

@@ -121,6 +126,14 @@ fn enforce_impl_params_are_constrained(
121126
})
122127
.collect();
123128

129+
let node = tcx.hir().get_if_local(impl_def_id.into()).expect("cannot get `Node`");
130+
let hir_impl = if let rustc_hir::Node::Item(item) = node {
131+
if let rustc_hir::ItemKind::Impl(imp) = item.kind { Some(imp) } else { None }
132+
} else {
133+
None
134+
}
135+
.expect("cannot take `Impl` in a impl block");
136+
124137
let mut res = Ok(());
125138
for param in &impl_generics.own_params {
126139
let err = match param.kind {
@@ -149,6 +162,79 @@ fn enforce_impl_params_are_constrained(
149162
const_param_note2: const_param_note,
150163
});
151164
diag.code(E0207);
165+
166+
let (index, hir_param) = hir_impl
167+
.generics
168+
.params
169+
.iter()
170+
.enumerate()
171+
.find(|(_, par)| par.name.ident().name == param.name)
172+
.unwrap();
173+
let mut suggestions = vec![];
174+
175+
let is_impl_generic = |par: &&GenericParam<'_>| match par.kind {
176+
GenericParamKind::Type { .. }
177+
| GenericParamKind::Const { .. }
178+
| GenericParamKind::Lifetime { kind: LifetimeParamKind::Explicit } => true,
179+
_ => false,
180+
};
181+
// Suggestion for removing the type parameter.
182+
suggestions.push(vec![(
183+
// Find the span of the type parameter.
184+
if let Some(prev) = hir_impl.generics.params[..index].iter().rfind(is_impl_generic)
185+
{
186+
let mut span = prev.span;
187+
188+
// Consider the span of the bounds with the generic parameter when there is.
189+
if let Some(predicate) = hir_impl.generics.predicates.iter().find(|pred| {
190+
if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
191+
origin: PredicateOrigin::GenericParam,
192+
bounded_ty,
193+
..
194+
}) = pred.kind
195+
{
196+
bounded_ty.span == prev.span
197+
} else {
198+
false
199+
}
200+
}) {
201+
span = span.to(predicate.span)
202+
};
203+
204+
span.shrink_to_hi().to(hir_param.span)
205+
} else if let Some(next) =
206+
hir_impl.generics.params[index + 1..].iter().find(is_impl_generic)
207+
{
208+
hir_param.span.until(next.span)
209+
} else {
210+
// Remove also angle brackets <> when there is just ONE generic parameter.
211+
hir_impl.generics.span
212+
},
213+
String::new(),
214+
)]);
215+
216+
// Suggestion for making use of the type parameter.
217+
if let Some(path) = extract_ty_as_path(hir_impl.self_ty) {
218+
let seg = path.segments.last().unwrap();
219+
if let Some(args) = seg.args {
220+
suggestions.push(vec![(
221+
args.span().unwrap().shrink_to_hi(),
222+
format!(", {}", param.name),
223+
)]);
224+
} else {
225+
suggestions
226+
.push(vec![(seg.ident.span.shrink_to_hi(), format!("<{}>", param.name))]);
227+
}
228+
}
229+
230+
diag.multipart_suggestions(
231+
format!(
232+
"either remove the type parameter {}, or make use of it, for example",
233+
param.name
234+
),
235+
suggestions,
236+
Applicability::MaybeIncorrect,
237+
);
152238
res = Err(diag.emit());
153239
}
154240
}
@@ -173,3 +259,12 @@ fn enforce_impl_params_are_constrained(
173259
// associated types. I believe this is sound, because lifetimes
174260
// used elsewhere are not projected back out.
175261
}
262+
263+
fn extract_ty_as_path<'hir>(ty: &Ty<'hir>) -> Option<&'hir Path<'hir>> {
264+
match ty.kind {
265+
TyKind::Path(QPath::Resolved(_, path)) => Some(path),
266+
TyKind::Slice(ty) | TyKind::Array(ty, _) => extract_ty_as_path(ty),
267+
TyKind::Ptr(ty) | TyKind::Ref(_, ty) => extract_ty_as_path(ty.ty),
268+
_ => None,
269+
}
270+
}

0 commit comments

Comments
 (0)