Create helper maybe_report_similar_assoc_fn

This commit is contained in:
trevyn 2024-02-05 18:53:28 -08:00
parent a4cb55f2ac
commit 0b6af718d8
4 changed files with 66 additions and 65 deletions

View File

@ -14,6 +14,7 @@
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::traits::FulfillmentError; use rustc_infer::traits::FulfillmentError;
use rustc_middle::query::Key;
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::parse::feature_err; use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edit_distance::find_best_match_for_name;
@ -859,6 +860,56 @@ pub(crate) fn complain_about_missing_associated_types(
self.set_tainted_by_errors(err.emit()); self.set_tainted_by_errors(err.emit());
} }
/// On ambiguous associated type, look for an associated function whose name matches the
/// extended path and, if found, emit an E0223 error with a structured suggestion.
/// e.g. for `String::from::utf8`, suggest `String::from_utf8` (#109195)
pub(crate) fn maybe_report_similar_assoc_fn(
&self,
span: Span,
qself_ty: Ty<'tcx>,
qself: &hir::Ty<'_>,
) -> Result<(), ErrorGuaranteed> {
let tcx = self.tcx();
if let Some((_, node)) = tcx.hir().parent_iter(qself.hir_id).skip(1).next()
&& let hir::Node::Expr(hir::Expr {
kind:
hir::ExprKind::Path(hir::QPath::TypeRelative(
hir::Ty {
kind:
hir::TyKind::Path(hir::QPath::TypeRelative(
_,
hir::PathSegment { ident: ident2, .. },
)),
..
},
hir::PathSegment { ident: ident3, .. },
)),
..
}) = node
&& let Some(ty_def_id) = qself_ty.ty_def_id()
&& let Ok([inherent_impl]) = tcx.inherent_impls(ty_def_id)
&& let name = format!("{ident2}_{ident3}")
&& let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx
.associated_items(inherent_impl)
.filter_by_name_unhygienic(Symbol::intern(&name))
.next()
{
let reported =
struct_span_code_err!(tcx.dcx(), span, E0223, "ambiguous associated type")
.with_span_suggestion_verbose(
ident2.span.to(ident3.span),
format!("there is an associated function with a similar name: `{name}`"),
name,
Applicability::MaybeIncorrect,
)
.emit();
self.set_tainted_by_errors(reported);
Err(reported)
} else {
Ok(())
}
}
} }
/// Emits an error regarding forbidden type binding associations /// Emits an error regarding forbidden type binding associations

View File

@ -29,7 +29,6 @@
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause; use rustc_infer::traits::ObligationCause;
use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::query::Key;
use rustc_middle::ty::{ use rustc_middle::ty::{
self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, IsSuggestable, ParamEnv, Ty, self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, IsSuggestable, ParamEnv, Ty,
TyCtxt, TypeVisitableExt, TyCtxt, TypeVisitableExt,
@ -1374,56 +1373,7 @@ pub fn associated_path_to_ty(
) )
.emit() // Already reported in an earlier stage. .emit() // Already reported in an earlier stage.
} else { } else {
// suggest methods that look similar to the path self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?;
// e.g. for `String::from::utf8`, suggest `String::from_utf8` (#109195)
for (_, node) in tcx.hir().parent_iter(qself.hir_id) {
if let hir::Node::Expr(hir::Expr {
kind:
hir::ExprKind::Path(hir::QPath::TypeRelative(
hir::Ty {
kind:
hir::TyKind::Path(hir::QPath::TypeRelative(
_,
hir::PathSegment { ident: ident2, .. },
)),
..
},
hir::PathSegment { ident: ident3, .. },
)),
..
}) = node
{
let name = format!("{ident2}_{ident3}");
if if let Some(ty_def_id) = qself_ty.ty_def_id()
&& let Ok([inherent_impl]) = tcx.inherent_impls(ty_def_id)
&& let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx
.associated_items(inherent_impl)
.filter_by_name_unhygienic(Symbol::intern(&name))
.next()
{
true
} else {
qself_ty.is_str()
&& ["from_utf8", "from_utf8_mut"].contains(&name.as_str())
} {
let reported = struct_span_code_err!(
tcx.dcx(),
span,
E0223,
"ambiguous associated type"
)
.with_span_suggestion_verbose(
ident2.span.to(ident3.span),
format!("you might have meant to use `{name}`"),
name,
Applicability::MaybeIncorrect,
)
.emit();
self.set_tainted_by_errors(reported);
return Err(reported);
}
}
}
let traits: Vec<_> = let traits: Vec<_> =
self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident); self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident);

View File

@ -1,20 +1,20 @@
fn main() { fn main() {
String::from::utf8; String::from::utf8;
//~^ ERROR ambiguous associated type [E0223] //~^ ERROR ambiguous associated type [E0223]
//~| HELP you might have meant to use `from_utf8` //~| HELP there is an associated function with a similar name: `from_utf8`
String::from::utf8(); String::from::utf8();
//~^ ERROR ambiguous associated type [E0223] //~^ ERROR ambiguous associated type [E0223]
//~| HELP you might have meant to use `from_utf8` //~| HELP there is an associated function with a similar name: `from_utf8`
String::from::utf16(); String::from::utf16();
//~^ ERROR ambiguous associated type [E0223] //~^ ERROR ambiguous associated type [E0223]
//~| HELP you might have meant to use `from_utf16` //~| HELP there is an associated function with a similar name: `from_utf16`
String::from::method_that_doesnt_exist(); String::from::method_that_doesnt_exist();
//~^ ERROR ambiguous associated type [E0223] //~^ ERROR ambiguous associated type [E0223]
//~| HELP if there were a trait named `Example` with associated type `from` //~| HELP if there were a trait named `Example` with associated type `from`
str::from::utf8(); str::from::utf8();
//~^ ERROR ambiguous associated type [E0223] //~^ ERROR ambiguous associated type [E0223]
//~| HELP you might have meant to use `from_utf8` //~| HELP if there were a trait named `Example` with associated type `from`
str::from::utf8_mut(); str::from::utf8_mut();
//~^ ERROR ambiguous associated type [E0223] //~^ ERROR ambiguous associated type [E0223]
//~| HELP you might have meant to use `from_utf8_mut` //~| HELP if there were a trait named `Example` with associated type `from`
} }

View File

@ -4,7 +4,7 @@ error[E0223]: ambiguous associated type
LL | String::from::utf8; LL | String::from::utf8;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
| |
help: you might have meant to use `from_utf8` help: there is an associated function with a similar name: `from_utf8`
| |
LL | String::from_utf8; LL | String::from_utf8;
| ~~~~~~~~~ | ~~~~~~~~~
@ -15,7 +15,7 @@ error[E0223]: ambiguous associated type
LL | String::from::utf8(); LL | String::from::utf8();
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
| |
help: you might have meant to use `from_utf8` help: there is an associated function with a similar name: `from_utf8`
| |
LL | String::from_utf8(); LL | String::from_utf8();
| ~~~~~~~~~ | ~~~~~~~~~
@ -26,7 +26,7 @@ error[E0223]: ambiguous associated type
LL | String::from::utf16(); LL | String::from::utf16();
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
| |
help: you might have meant to use `from_utf16` help: there is an associated function with a similar name: `from_utf16`
| |
LL | String::from_utf16(); LL | String::from_utf16();
| ~~~~~~~~~~ | ~~~~~~~~~~
@ -48,10 +48,10 @@ error[E0223]: ambiguous associated type
LL | str::from::utf8(); LL | str::from::utf8();
| ^^^^^^^^^ | ^^^^^^^^^
| |
help: you might have meant to use `from_utf8` help: if there were a trait named `Example` with associated type `from` implemented for `str`, you could use the fully-qualified path
| |
LL | str::from_utf8(); LL | <str as Example>::from::utf8();
| ~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~
error[E0223]: ambiguous associated type error[E0223]: ambiguous associated type
--> $DIR/issue-109195.rs:17:5 --> $DIR/issue-109195.rs:17:5
@ -59,10 +59,10 @@ error[E0223]: ambiguous associated type
LL | str::from::utf8_mut(); LL | str::from::utf8_mut();
| ^^^^^^^^^ | ^^^^^^^^^
| |
help: you might have meant to use `from_utf8_mut` help: if there were a trait named `Example` with associated type `from` implemented for `str`, you could use the fully-qualified path
| |
LL | str::from_utf8_mut(); LL | <str as Example>::from::utf8_mut();
| ~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 6 previous errors error: aborting due to 6 previous errors