Add E0790 as more specific variant of E0283
This commit is contained in:
parent
96c2df810b
commit
1cbacc0c8a
21 changed files with 354 additions and 92 deletions
|
@ -2104,6 +2104,98 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) =
|
||||
(body_id, subst.map(|subst| subst.unpack()))
|
||||
{
|
||||
struct FindExprBySpan<'hir> {
|
||||
span: Span,
|
||||
result: Option<&'hir hir::Expr<'hir>>,
|
||||
}
|
||||
|
||||
impl<'v> hir::intravisit::Visitor<'v> for FindExprBySpan<'v> {
|
||||
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
|
||||
if self.span == ex.span {
|
||||
self.result = Some(ex);
|
||||
} else {
|
||||
hir::intravisit::walk_expr(self, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut expr_finder = FindExprBySpan { span, result: None };
|
||||
|
||||
expr_finder.visit_expr(&self.tcx.hir().body(body_id).value);
|
||||
|
||||
if let Some(hir::Expr {
|
||||
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), .. }
|
||||
) = expr_finder.result
|
||||
&& let [
|
||||
..,
|
||||
trait_path_segment @ hir::PathSegment {
|
||||
res: Some(rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id)),
|
||||
..
|
||||
},
|
||||
hir::PathSegment {
|
||||
ident: assoc_item_name,
|
||||
res: Some(rustc_hir::def::Res::Def(_, item_id)),
|
||||
..
|
||||
}
|
||||
] = path.segments
|
||||
&& data.trait_ref.def_id == *trait_id
|
||||
&& self.tcx.trait_of_item(item_id) == Some(*trait_id)
|
||||
&& !self.is_tainted_by_errors()
|
||||
{
|
||||
let (verb, noun) = match self.tcx.associated_item(item_id).kind {
|
||||
ty::AssocKind::Const => ("refer to the", "constant"),
|
||||
ty::AssocKind::Fn => ("call", "function"),
|
||||
ty::AssocKind::Type => ("refer to the", "type"), // this is already covered by E0223, but this single match arm doesn't hurt here
|
||||
};
|
||||
|
||||
// Replace the more general E0283 with a more specific error
|
||||
err.cancel();
|
||||
err = self.tcx.sess.struct_span_err_with_code(
|
||||
span,
|
||||
&format!(
|
||||
"cannot {verb} associated {noun} on trait without specifying the corresponding `impl` type",
|
||||
),
|
||||
rustc_errors::error_code!(E0790),
|
||||
);
|
||||
|
||||
if let Some(local_def_id) = data.trait_ref.def_id.as_local()
|
||||
&& let Some(hir::Node::Item(hir::Item { ident: trait_name, kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), .. })) = self.tcx.hir().find_by_def_id(local_def_id)
|
||||
&& let Some(method_ref) = trait_item_refs.iter().find(|item_ref| item_ref.ident == *assoc_item_name) {
|
||||
err.span_label(method_ref.span, format!("`{}::{}` defined here", trait_name, assoc_item_name));
|
||||
}
|
||||
|
||||
err.span_label(span, format!("cannot {verb} associated {noun} of trait"));
|
||||
|
||||
let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);
|
||||
|
||||
if trait_impls.blanket_impls().is_empty()
|
||||
&& let Some((impl_ty, _)) = trait_impls.non_blanket_impls().iter().next()
|
||||
&& let Some(impl_def_id) = impl_ty.def() {
|
||||
let message = if trait_impls.non_blanket_impls().len() == 1 {
|
||||
"use the fully-qualified path to the only available implementation".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"use a fully-qualified path to a specific available implementation ({} found)",
|
||||
trait_impls.non_blanket_impls().len()
|
||||
)
|
||||
};
|
||||
|
||||
err.multipart_suggestion(
|
||||
message,
|
||||
vec![
|
||||
(trait_path_segment.ident.span.shrink_to_lo(), format!("<{} as ", self.tcx.def_path(impl_def_id).to_string_no_crate_verbose())),
|
||||
(trait_path_segment.ident.span.shrink_to_hi(), format!(">"))
|
||||
],
|
||||
Applicability::MaybeIncorrect
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
err
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue