Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix auto-application of associated generic functions with placeholders #101990

Merged
135 changes: 115 additions & 20 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, Node, QPath};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{
type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
RegionVariableOrigin,
};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::traits::util::supertraits;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
use rustc_middle::ty::print::with_crate_prefix;
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable};
use rustc_middle::ty::{
self, DefIdTree, GenericArg, GenericArgKind, ToPredicate, Ty, TyCtxt, TypeVisitable,
};
use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Symbol;
Expand Down Expand Up @@ -279,7 +285,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
return None;
}

span = item_name.span;

// Don't show generic arguments when the method can't be found in any implementation (#81576).
Expand Down Expand Up @@ -392,28 +397,118 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
custom_span_label = true;
}
if static_candidates.len() == 1 {
let ty_str =
if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) {
// When the "method" is resolved through dereferencing, we really want the
// original type that has the associated function for accurate suggestions.
// (#61411)
let ty = tcx.at(span).type_of(*impl_did);
match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) {
(ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => {
// Use `actual` as it will have more `substs` filled in.
self.ty_to_value_string(actual.peel_refs())
let mut has_unsuggestable_args = false;
let ty_str = if let Some(CandidateSource::Impl(impl_did)) =
static_candidates.get(0)
{
// When the "method" is resolved through dereferencing, we really want the
// original type that has the associated function for accurate suggestions.
// (#61411)
let ty = tcx.at(span).type_of(*impl_did);
match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) {
(ty::Adt(def, _), ty::Adt(def_actual, substs)) if def == def_actual => {
// If there are any inferred arguments, (`{integer}`), we should replace
// them with underscores to allow the compiler to infer them
let infer_substs: Vec<GenericArg<'_>> = substs
.into_iter()
.map(|arg| {
if !arg.is_suggestable(tcx, true) {
has_unsuggestable_args = true;
match arg.unpack() {
GenericArgKind::Lifetime(_) => self
.next_region_var(RegionVariableOrigin::MiscVariable(
rustc_span::DUMMY_SP,
))
.into(),
GenericArgKind::Type(_) => self
.next_ty_var(TypeVariableOrigin {
span: rustc_span::DUMMY_SP,
kind: TypeVariableOriginKind::MiscVariable,
})
.into(),
GenericArgKind::Const(arg) => self
.next_const_var(
arg.ty(),
ConstVariableOrigin {
span: rustc_span::DUMMY_SP,
kind: ConstVariableOriginKind::MiscVariable,
},
)
.into(),
}
} else {
arg
}
})
.collect::<Vec<_>>();

tcx.value_path_str_with_substs(
def_actual.did(),
tcx.intern_substs(&infer_substs),
)
}
_ => self.ty_to_value_string(ty.peel_refs()),
}
} else {
self.ty_to_value_string(actual.peel_refs())
};
if let SelfSource::MethodCall(_) = source {
let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) &&
let Some(assoc) = self.associated_value(*impl_did, item_name) {
let sig = self.tcx.fn_sig(assoc.def_id);
if let Some(first) = sig.inputs().skip_binder().get(0) {
if first.peel_refs() == rcvr_ty.peel_refs() {
None
} else {
Some(if first.is_region_ptr() {
if first.is_mutable_ptr() { "&mut " } else { "&" }
} else {
""
})
}
_ => self.ty_to_value_string(ty.peel_refs()),
} else {
None
}
} else {
self.ty_to_value_string(actual.peel_refs())
None
};
let mut applicability = Applicability::MachineApplicable;
let args = if let Some((receiver, args)) = args {
// The first arg is the same kind as the receiver
let explicit_args = if first_arg.is_some() {
std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
} else {
// There is no `Self` kind to infer the arguments from
if has_unsuggestable_args {
applicability = Applicability::HasPlaceholders;
}
args.iter().collect()
};
format!(
"({}{})",
first_arg.unwrap_or(""),
explicit_args
.iter()
.map(|arg| tcx
.sess
.source_map()
.span_to_snippet(arg.span)
.unwrap_or_else(|_| {
applicability = Applicability::HasPlaceholders;
"_".to_owned()
}))
.collect::<Vec<_>>()
.join(", "),
)
} else {
applicability = Applicability::HasPlaceholders;
"(...)".to_owned()
};
if let SelfSource::MethodCall(expr) = source {
err.span_suggestion(
expr.span.to(span),
sugg_span,
"use associated function syntax instead",
format!("{}::{}", ty_str, item_name),
Applicability::MachineApplicable,
format!("{}::{}{}", ty_str, item_name, args),
applicability,
);
} else {
err.help(&format!("try with `{}::{}`", ty_str, item_name,));
Expand Down Expand Up @@ -1826,7 +1921,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Print out the type for use in value namespace.
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
match ty.kind() {
ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did(), substs)),
ty::Adt(def, substs) => self.tcx.def_path_str_with_substs(def.did(), substs),
_ => self.ty_to_string(ty),
}
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1659,6 +1659,12 @@ impl<'t> TyCtxt<'t> {
debug!("def_path_str: def_id={:?}, ns={:?}", def_id, ns);
FmtPrinter::new(self, ns).print_def_path(def_id, substs).unwrap().into_buffer()
}

pub fn value_path_str_with_substs(self, def_id: DefId, substs: &'t [GenericArg<'t>]) -> String {
let ns = guess_def_namespace(self, def_id);
debug!("value_path_str: def_id={:?}, ns={:?}", def_id, ns);
FmtPrinter::new(self, ns).print_value_path(def_id, substs).unwrap().into_buffer()
}
}

impl fmt::Write for FmtPrinter<'_, '_> {
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-3707.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ error[E0599]: no method named `boom` found for reference `&Obj` in the current s
--> $DIR/issue-3707.rs:10:14
|
LL | self.boom();
| -----^^^^
| -----^^^^--
| | |
| | this is an associated function, not a method
| help: use associated function syntax instead: `Obj::boom`
| help: use associated function syntax instead: `Obj::boom()`
|
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
note: the candidate is defined in an impl for the type `Obj`
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/suggestions/inner_type2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ fn main() {
let item = std::mem::MaybeUninit::new(Struct { p: 42_u32 });
item.method();
//~^ ERROR no method named `method` found for union `MaybeUninit` in the current scope [E0599]
//~| HELP if this `MaybeUninit::<Struct<u32>>` has been initialized, use one of the `assume_init` methods to access the inner value
//~| HELP if this `MaybeUninit<Struct<u32>>` has been initialized, use one of the `assume_init` methods to access the inner value
}
2 changes: 1 addition & 1 deletion src/test/ui/suggestions/inner_type2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ error[E0599]: no method named `method` found for union `MaybeUninit` in the curr
LL | item.method();
| ^^^^^^ method not found in `MaybeUninit<Struct<u32>>`
|
= help: if this `MaybeUninit::<Struct<u32>>` has been initialized, use one of the `assume_init` methods to access the inner value
= help: if this `MaybeUninit<Struct<u32>>` has been initialized, use one of the `assume_init` methods to access the inner value
note: the method `method` exists on the type `Struct<u32>`
--> $DIR/inner_type2.rs:6:5
|
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/suggestions/issue-102354.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ LL | fn func() {}
help: use associated function syntax instead
|
LL | i32::func();
| ~~~~~~~~~
| ~~~~~~~~~~~
help: disambiguate the associated function for the candidate
|
LL | <i32 as Trait>::func(x);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
struct GenericAssocMethod<T>(T);

impl<T> GenericAssocMethod<T> {
fn default_hello() {}
}

fn main() {
let x = GenericAssocMethod(33);
x.default_hello();
//~^ ERROR no method named `default_hello` found
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error[E0599]: no method named `default_hello` found for struct `GenericAssocMethod<{integer}>` in the current scope
--> $DIR/suggest-assoc-fn-call-with-turbofish-placeholder.rs:9:7
|
LL | struct GenericAssocMethod<T>(T);
| ---------------------------- method `default_hello` not found for this struct
...
LL | x.default_hello();
| --^^^^^^^^^^^^^--
| | |
| | this is an associated function, not a method
| help: use associated function syntax instead: `GenericAssocMethod::<_>::default_hello()`
|
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
note: the candidate is defined in an impl for the type `GenericAssocMethod<T>`
--> $DIR/suggest-assoc-fn-call-with-turbofish-placeholder.rs:4:5
|
LL | fn default_hello() {}
| ^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ error[E0599]: no method named `hello` found for struct `RefMut<'_, HasAssocMetho
--> $DIR/suggest-assoc-fn-call-with-turbofish-through-deref.rs:11:11
|
LL | state.hello();
| ------^^^^^
| ------^^^^^--
| | |
| | this is an associated function, not a method
| help: use associated function syntax instead: `HasAssocMethod::hello`
| help: use associated function syntax instead: `HasAssocMethod::hello()`
|
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
note: the candidate is defined in an impl for the type `HasAssocMethod`
Expand Down
26 changes: 26 additions & 0 deletions src/test/ui/suggestions/suggest-assoc-fn-call-with-turbofish.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// run-rustfix

struct GenericAssocMethod<T>(T);

impl<T> GenericAssocMethod<T> {
fn default_hello() {}
fn self_ty_hello(_: Self) {}
fn self_ty_ref_hello(_: &Self) {}
}

fn main() {
// Test for inferred types
let x = GenericAssocMethod(33);
GenericAssocMethod::<_>::self_ty_ref_hello(&x);
//~^ ERROR no method named `self_ty_ref_hello` found
GenericAssocMethod::<_>::self_ty_hello(x);
//~^ ERROR no method named `self_ty_hello` found
// Test for known types
let y = GenericAssocMethod(33i32);
GenericAssocMethod::<i32>::default_hello();
//~^ ERROR no method named `default_hello` found
GenericAssocMethod::<i32>::self_ty_ref_hello(&y);
//~^ ERROR no method named `self_ty_ref_hello` found
GenericAssocMethod::<i32>::self_ty_hello(y);
//~^ ERROR no method named `self_ty_hello` found
}
19 changes: 17 additions & 2 deletions src/test/ui/suggestions/suggest-assoc-fn-call-with-turbofish.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
// run-rustfix

struct GenericAssocMethod<T>(T);

impl<T> GenericAssocMethod<T> {
fn default_hello() {}
fn self_ty_hello(_: Self) {}
fn self_ty_ref_hello(_: &Self) {}
}

fn main() {
let x = GenericAssocMethod(33i32);
x.default_hello();
// Test for inferred types
let x = GenericAssocMethod(33);
x.self_ty_ref_hello();
//~^ ERROR no method named `self_ty_ref_hello` found
x.self_ty_hello();
//~^ ERROR no method named `self_ty_hello` found
// Test for known types
let y = GenericAssocMethod(33i32);
y.default_hello();
//~^ ERROR no method named `default_hello` found
y.self_ty_ref_hello();
//~^ ERROR no method named `self_ty_ref_hello` found
y.self_ty_hello();
//~^ ERROR no method named `self_ty_hello` found
}
Loading