Skip to content

Commit d7c9df9

Browse files
authored
Rollup merge of rust-lang#153662 - estebank:suggest-fully-qualified-path, r=davidtwco
Suggest fully qualified path on method name collision Provide suggestion for using a fully qualified path when method names collide between traits and inherent impl. ``` error[E0061]: this method takes 0 arguments but 1 argument was supplied --> $DIR/shadowed-intrinsic-method.rs:20:7 | LL | a.borrow(()); | ^^^^^^ -- unexpected argument of type `()` | note: the `borrow` call is resolved to the method in `std::borrow::Borrow`, shadowing the method of the same name on the inherent impl for `A` --> $DIR/shadowed-intrinsic-method.rs:20:7 | LL | use std::borrow::Borrow; | ------------------- `std::borrow::Borrow` imported here ... LL | a.borrow(()); | ^^^^^^ refers to `std::borrow::Borrow::borrow` note: method defined here --> $SRC_DIR/core/src/borrow.rs:LL:COL help: you might have meant to call the other method; you can use the fully-qualified path to call it explicitly | LL - a.borrow(()); LL + A::borrow(&mut a, ()); | help: remove the extra argument | LL - a.borrow(()); LL + a.borrow(); | ``` Fix rust-lang#54103.
2 parents 7501b71 + 0bd2852 commit d7c9df9

7 files changed

Lines changed: 231 additions & 39 deletions

File tree

compiler/rustc_hir_typeck/src/demand.rs

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use rustc_errors::{Applicability, Diag, MultiSpan, listify};
2-
use rustc_hir::def::Res;
1+
use rustc_errors::{Applicability, Diag, MultiSpan, listify, pluralize};
2+
use rustc_hir::def::{DefKind, Res};
33
use rustc_hir::intravisit::Visitor;
44
use rustc_hir::{self as hir, find_attr};
55
use rustc_infer::infer::DefineOpaqueTypes;
@@ -29,7 +29,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2929
if expr_ty == expected {
3030
return;
3131
}
32-
self.annotate_alternative_method_deref(err, expr, error);
32+
self.annotate_alternative_method_deref_for_unop(err, expr, error);
3333
self.explain_self_literal(err, expr, expected, expr_ty);
3434

3535
// Use `||` to give these suggestions a precedence
@@ -723,8 +723,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
723723
hir::Path {
724724
res:
725725
hir::def::Res::Def(
726-
hir::def::DefKind::Static { .. }
727-
| hir::def::DefKind::Const { .. },
726+
DefKind::Static { .. } | DefKind::Const { .. },
728727
def_id,
729728
),
730729
..
@@ -899,7 +898,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
899898
false
900899
}
901900

902-
fn annotate_alternative_method_deref(
901+
fn annotate_alternative_method_deref_for_unop(
903902
&self,
904903
err: &mut Diag<'_>,
905904
expr: &hir::Expr<'_>,
@@ -919,7 +918,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
919918
let hir::ExprKind::Unary(hir::UnOp::Deref, deref) = lhs.kind else {
920919
return;
921920
};
922-
let hir::ExprKind::MethodCall(path, base, args, _) = deref.kind else {
921+
self.annotate_alternative_method_deref(err, deref, Some(expected))
922+
}
923+
924+
#[tracing::instrument(skip(self, err), level = "debug")]
925+
pub(crate) fn annotate_alternative_method_deref(
926+
&self,
927+
err: &mut Diag<'_>,
928+
expr: &hir::Expr<'_>,
929+
expected: Option<Ty<'tcx>>,
930+
) {
931+
let hir::ExprKind::MethodCall(path, base, args, _) = expr.kind else {
923932
return;
924933
};
925934
let Some(self_ty) = self.typeck_results.borrow().expr_ty_adjusted_opt(base) else {
@@ -929,7 +938,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
929938
let Ok(pick) = self.lookup_probe_for_diagnostic(
930939
path.ident,
931940
self_ty,
932-
deref,
941+
expr,
933942
probe::ProbeScope::TraitsInScope,
934943
None,
935944
) else {
@@ -939,10 +948,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
939948
let Ok(in_scope_methods) = self.probe_for_name_many(
940949
probe::Mode::MethodCall,
941950
path.ident,
942-
Some(expected),
951+
expected,
943952
probe::IsSuggestion(true),
944953
self_ty,
945-
deref.hir_id,
954+
expr.hir_id,
946955
probe::ProbeScope::TraitsInScope,
947956
) else {
948957
return;
@@ -954,45 +963,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
954963
let Ok(all_methods) = self.probe_for_name_many(
955964
probe::Mode::MethodCall,
956965
path.ident,
957-
Some(expected),
966+
expected,
958967
probe::IsSuggestion(true),
959968
self_ty,
960-
deref.hir_id,
969+
expr.hir_id,
961970
probe::ProbeScope::AllTraits,
962971
) else {
963972
return;
964973
};
965974

966975
let suggestions: Vec<_> = all_methods
967976
.into_iter()
968-
.filter(|c| c.item.def_id != pick.item.def_id)
969-
.map(|c| {
977+
.filter_map(|c| {
978+
if c.item.def_id == pick.item.def_id {
979+
return None;
980+
}
970981
let m = c.item;
971982
let generic_args = ty::GenericArgs::for_item(self.tcx, m.def_id, |param, _| {
972-
self.var_for_def(deref.span, param)
983+
self.var_for_def(expr.span, param)
973984
});
974-
let mutability =
975-
match self.tcx.fn_sig(m.def_id).skip_binder().input(0).skip_binder().kind() {
976-
ty::Ref(_, _, hir::Mutability::Mut) => "&mut ",
977-
ty::Ref(_, _, _) => "&",
978-
_ => "",
979-
};
980-
vec![
981-
(
982-
deref.span.until(base.span),
983-
format!(
984-
"{}({}",
985-
with_no_trimmed_paths!(
986-
self.tcx.def_path_str_with_args(m.def_id, generic_args,)
987-
),
988-
mutability,
989-
),
990-
),
985+
let fn_sig = self.tcx.fn_sig(m.def_id);
986+
if fn_sig.skip_binder().inputs().skip_binder().len() != args.len() + 1 {
987+
return None;
988+
}
989+
let rcvr_ty = fn_sig.skip_binder().input(0).skip_binder();
990+
let (mutability, ty) = match rcvr_ty.kind() {
991+
ty::Ref(_, ty, hir::Mutability::Mut) => ("&mut ", ty),
992+
ty::Ref(_, ty, _) => ("&", ty),
993+
_ => ("", &rcvr_ty),
994+
};
995+
let path = match self.tcx.assoc_parent(m.def_id) {
996+
Some((_, DefKind::Impl { of_trait: true })) => {
997+
// We have `impl Trait for T {}`, suggest `<T as Trait>::method`.
998+
self.tcx.def_path_str_with_args(m.def_id, generic_args).to_string()
999+
}
1000+
Some((_, DefKind::Impl { of_trait: false })) => {
1001+
if let ty::Adt(def, _) = ty.kind() {
1002+
// We have `impl T {}`, suggest `T::method`.
1003+
format!("{}::{}", self.tcx.def_path_str(def.did()), path.ident)
1004+
} else {
1005+
// This should be unreachable, as `impl &'a T {}` is invalid.
1006+
format!("{ty}::{}", path.ident)
1007+
}
1008+
}
1009+
// Fallback for arbitrary self types.
1010+
_ => with_no_trimmed_paths!(
1011+
self.tcx.def_path_str_with_args(m.def_id, generic_args)
1012+
)
1013+
.to_string(),
1014+
};
1015+
Some(vec![
1016+
(expr.span.until(base.span), format!("{path}({}", mutability)),
9911017
match &args {
992-
[] => (base.span.shrink_to_hi().with_hi(deref.span.hi()), ")".to_string()),
1018+
[] => (base.span.shrink_to_hi().with_hi(expr.span.hi()), ")".to_string()),
9931019
[first, ..] => (base.span.between(first.span), ", ".to_string()),
9941020
},
995-
]
1021+
])
9961022
})
9971023
.collect();
9981024
if suggestions.is_empty() {
@@ -1046,9 +1072,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10461072
),
10471073
);
10481074
if suggestions.len() > other_methods_in_scope.len() {
1075+
let n = suggestions.len() - other_methods_in_scope.len();
10491076
err.note(format!(
1050-
"additionally, there are {} other available methods that aren't in scope",
1051-
suggestions.len() - other_methods_in_scope.len()
1077+
"additionally, there {are} {n} other available method{s} that {are}n't in scope",
1078+
are = pluralize!("is", n),
1079+
s = pluralize!(n),
10521080
));
10531081
}
10541082
err.multipart_suggestions(
@@ -1263,7 +1291,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12631291
let hir::def::Res::Def(kind, def_id) = path.res else {
12641292
return;
12651293
};
1266-
let callable_kind = if matches!(kind, hir::def::DefKind::Ctor(_, _)) {
1294+
let callable_kind = if matches!(kind, DefKind::Ctor(_, _)) {
12671295
CallableKind::Constructor
12681296
} else {
12691297
CallableKind::Function

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2853,6 +2853,8 @@ impl<'a, 'b, 'tcx> ArgMatchingCtxt<'a, 'b, 'tcx> {
28532853
);
28542854
return;
28552855
}
2856+
2857+
self.annotate_alternative_method_deref(err, self.call_expr, None);
28562858
}
28572859

28582860
/// A "softer" version of the `demand_compatible`, which checks types without persisting them,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Can't use rustfix because we provide two suggestions:
2+
// to remove the arg for `Borrow::borrow` or to call `Type::borrow`.
3+
use std::borrow::Borrow;
4+
5+
struct A;
6+
7+
impl A { fn borrow(&mut self, _: ()) {} }
8+
9+
struct B;
10+
11+
fn main() {
12+
// The fully-qualified path for items within functions is unnameable from outside that function.
13+
impl B { fn borrow(&mut self, _: ()) {} }
14+
15+
struct C;
16+
// The fully-qualified path for items within functions is unnameable from outside that function.
17+
impl C { fn borrow(&mut self, _: ()) {} }
18+
19+
let mut a = A;
20+
a.borrow(()); //~ ERROR E0061
21+
// A::borrow(&mut a, ());
22+
let mut b = B;
23+
b.borrow(()); //~ ERROR E0061
24+
// This currently suggests `main::<impl B>::borrow`, which is not correct, it should be
25+
// B::borrow(&mut b, ());
26+
let mut c = C;
27+
c.borrow(()); //~ ERROR E0061
28+
// This currently suggests `main::C::borrow`, which is not correct, it should be
29+
// C::borrow(&mut c, ());
30+
}
31+
32+
fn foo() {
33+
let mut b = B;
34+
b.borrow(()); //~ ERROR E0061
35+
// This currently suggests `main::<impl B>::borrow`, which is not correct, it should be
36+
// B::borrow(&mut b, ());
37+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
error[E0061]: this method takes 0 arguments but 1 argument was supplied
2+
--> $DIR/shadowed-intrinsic-method.rs:20:7
3+
|
4+
LL | a.borrow(());
5+
| ^^^^^^ -- unexpected argument of type `()`
6+
|
7+
note: the `borrow` call is resolved to the method in `std::borrow::Borrow`, shadowing the method of the same name on the inherent impl for `A`
8+
--> $DIR/shadowed-intrinsic-method.rs:20:7
9+
|
10+
LL | use std::borrow::Borrow;
11+
| ------------------- `std::borrow::Borrow` imported here
12+
...
13+
LL | a.borrow(());
14+
| ^^^^^^ refers to `std::borrow::Borrow::borrow`
15+
note: method defined here
16+
--> $SRC_DIR/core/src/borrow.rs:LL:COL
17+
help: you might have meant to call the other method; you can use the fully-qualified path to call it explicitly
18+
|
19+
LL - a.borrow(());
20+
LL + A::borrow(&mut a, ());
21+
|
22+
help: remove the extra argument
23+
|
24+
LL - a.borrow(());
25+
LL + a.borrow();
26+
|
27+
28+
error[E0061]: this method takes 0 arguments but 1 argument was supplied
29+
--> $DIR/shadowed-intrinsic-method.rs:23:7
30+
|
31+
LL | b.borrow(());
32+
| ^^^^^^ -- unexpected argument of type `()`
33+
|
34+
note: the `borrow` call is resolved to the method in `std::borrow::Borrow`, shadowing the method of the same name on the inherent impl for `main::<impl B>`
35+
--> $DIR/shadowed-intrinsic-method.rs:23:7
36+
|
37+
LL | use std::borrow::Borrow;
38+
| ------------------- `std::borrow::Borrow` imported here
39+
...
40+
LL | b.borrow(());
41+
| ^^^^^^ refers to `std::borrow::Borrow::borrow`
42+
note: method defined here
43+
--> $SRC_DIR/core/src/borrow.rs:LL:COL
44+
help: you might have meant to call the other method; you can use the fully-qualified path to call it explicitly
45+
|
46+
LL - b.borrow(());
47+
LL + B::borrow(&mut b, ());
48+
|
49+
help: remove the extra argument
50+
|
51+
LL - b.borrow(());
52+
LL + b.borrow();
53+
|
54+
55+
error[E0061]: this method takes 0 arguments but 1 argument was supplied
56+
--> $DIR/shadowed-intrinsic-method.rs:27:7
57+
|
58+
LL | c.borrow(());
59+
| ^^^^^^ -- unexpected argument of type `()`
60+
|
61+
note: the `borrow` call is resolved to the method in `std::borrow::Borrow`, shadowing the method of the same name on the inherent impl for `main::C`
62+
--> $DIR/shadowed-intrinsic-method.rs:27:7
63+
|
64+
LL | use std::borrow::Borrow;
65+
| ------------------- `std::borrow::Borrow` imported here
66+
...
67+
LL | c.borrow(());
68+
| ^^^^^^ refers to `std::borrow::Borrow::borrow`
69+
note: method defined here
70+
--> $SRC_DIR/core/src/borrow.rs:LL:COL
71+
help: you might have meant to call the other method; you can use the fully-qualified path to call it explicitly
72+
|
73+
LL - c.borrow(());
74+
LL + C::borrow(&mut c, ());
75+
|
76+
help: remove the extra argument
77+
|
78+
LL - c.borrow(());
79+
LL + c.borrow();
80+
|
81+
82+
error[E0061]: this method takes 0 arguments but 1 argument was supplied
83+
--> $DIR/shadowed-intrinsic-method.rs:34:7
84+
|
85+
LL | b.borrow(());
86+
| ^^^^^^ -- unexpected argument of type `()`
87+
|
88+
note: the `borrow` call is resolved to the method in `std::borrow::Borrow`, shadowing the method of the same name on the inherent impl for `main::<impl B>`
89+
--> $DIR/shadowed-intrinsic-method.rs:34:7
90+
|
91+
LL | use std::borrow::Borrow;
92+
| ------------------- `std::borrow::Borrow` imported here
93+
...
94+
LL | b.borrow(());
95+
| ^^^^^^ refers to `std::borrow::Borrow::borrow`
96+
note: method defined here
97+
--> $SRC_DIR/core/src/borrow.rs:LL:COL
98+
help: you might have meant to call the other method; you can use the fully-qualified path to call it explicitly
99+
|
100+
LL - b.borrow(());
101+
LL + B::borrow(&mut b, ());
102+
|
103+
help: remove the extra argument
104+
|
105+
LL - b.borrow(());
106+
LL + b.borrow();
107+
|
108+
109+
error: aborting due to 4 previous errors
110+
111+
For more information about this error, try `rustc --explain E0061`.

tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.stderr

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ note: method defined here
1111
|
1212
LL | pub fn get(&self, data: i32) {
1313
| ^^^ ---------
14+
note: the `get` call is resolved to the method in `Target`, shadowing the method of the same name on trait `RandomTrait`
15+
--> $DIR/diagnostic-method-lookup-returns-sig-with-fewer-args.rs:4:12
16+
|
17+
LL | target.get(10.0); // (used to crash here)
18+
| ^^^ refers to `Target::get`
19+
= note: additionally, there is 1 other available method that isn't in scope
20+
help: you might have meant to call one of the other methods; you can use the fully-qualified path to call one of them explicitly
21+
|
22+
LL - target.get(10.0); // (used to crash here)
23+
LL + <_ as std::slice::SliceIndex<_>>::get(target, 10.0); // (used to crash here)
24+
|
25+
LL - target.get(10.0); // (used to crash here)
26+
LL + <_ as object::read::elf::relocation::Relr>::get(&target, 10.0); // (used to crash here)
27+
|
1428

1529
error: aborting due to 1 previous error
1630

tests/ui/suggestions/shadowed-lplace-method.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ use std::rc::Rc;
66

77
fn main() {
88
let rc = Rc::new(RefCell::new(true));
9-
*std::cell::RefCell::<_>::borrow_mut(&rc) = false; //~ ERROR E0308
9+
*RefCell::borrow_mut(&rc) = false; //~ ERROR E0308
1010
}

tests/ui/suggestions/shadowed-lplace-method.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ LL | *rc.borrow_mut() = false;
1919
help: you might have meant to call the other method; you can use the fully-qualified path to call it explicitly
2020
|
2121
LL - *rc.borrow_mut() = false;
22-
LL + *std::cell::RefCell::<_>::borrow_mut(&rc) = false;
22+
LL + *RefCell::borrow_mut(&rc) = false;
2323
|
2424

2525
error: aborting due to 1 previous error

0 commit comments

Comments
 (0)