Skip to content

Commit f0eb4b4

Browse files
authored
Rollup merge of rust-lang#67127 - estebank:disambiguate-suggestion, r=varkor
Use structured suggestion for disambiguating method calls Fix rust-lang#65635.
2 parents 57da9d3 + 8c4f1d5 commit f0eb4b4

18 files changed

+274
-82
lines changed

src/librustc/ty/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,17 @@ pub enum AssocKind {
212212
Type
213213
}
214214

215+
impl AssocKind {
216+
pub fn suggestion_descr(&self) -> &'static str {
217+
match self {
218+
ty::AssocKind::Method => "method call",
219+
ty::AssocKind::Type |
220+
ty::AssocKind::OpaqueTy => "associated type",
221+
ty::AssocKind::Const => "associated constant",
222+
}
223+
}
224+
}
225+
215226
impl AssocItem {
216227
pub fn def_kind(&self) -> DefKind {
217228
match self.kind {

src/librustc_typeck/check/expr.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1425,8 +1425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14251425
field: ast::Ident,
14261426
) -> Ty<'tcx> {
14271427
let expr_t = self.check_expr_with_needs(base, needs);
1428-
let expr_t = self.structurally_resolved_type(base.span,
1429-
expr_t);
1428+
let expr_t = self.structurally_resolved_type(base.span, expr_t);
14301429
let mut private_candidate = None;
14311430
let mut autoderef = self.autoderef(expr.span, expr_t);
14321431
while let Some((base_t, _)) = autoderef.next() {

src/librustc_typeck/check/method/suggest.rs

+118-43
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc::traits::Obligation;
1515
use rustc::ty::{self, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TypeFoldable};
1616
use rustc::ty::print::with_crate_prefix;
1717
use syntax_pos::{Span, FileName};
18-
use syntax::ast;
18+
use syntax::{ast, source_map};
1919
use syntax::util::lev_distance;
2020

2121
use rustc_error_codes::*;
@@ -79,37 +79,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7979
return None;
8080
}
8181

82-
let print_disambiguation_help = |
83-
err: &mut DiagnosticBuilder<'_>,
84-
trait_name: String,
85-
| {
86-
err.help(&format!(
87-
"to disambiguate the method call, write `{}::{}({}{})` instead",
88-
trait_name,
89-
item_name,
90-
if rcvr_ty.is_region_ptr() && args.is_some() {
91-
if rcvr_ty.is_mutable_ptr() {
92-
"&mut "
93-
} else {
94-
"&"
95-
}
96-
} else {
97-
""
98-
},
99-
args.map(|arg| arg
100-
.iter()
101-
.map(|arg| self.tcx.sess.source_map().span_to_snippet(arg.span)
102-
.unwrap_or_else(|_| "...".to_owned()))
103-
.collect::<Vec<_>>()
104-
.join(", ")
105-
).unwrap_or_else(|| "...".to_owned())
106-
));
107-
};
108-
10982
let report_candidates = |
11083
span: Span,
11184
err: &mut DiagnosticBuilder<'_>,
11285
mut sources: Vec<CandidateSource>,
86+
sugg_span: Span,
11387
| {
11488
sources.sort();
11589
sources.dedup();
@@ -150,15 +124,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
150124
}
151125
};
152126

153-
let note_str = if sources.len() > 1 {
154-
format!("candidate #{} is defined in an impl{} for the type `{}`",
155-
idx + 1,
156-
insertion,
157-
impl_ty)
127+
let (note_str, idx) = if sources.len() > 1 {
128+
(format!(
129+
"candidate #{} is defined in an impl{} for the type `{}`",
130+
idx + 1,
131+
insertion,
132+
impl_ty,
133+
), Some(idx + 1))
158134
} else {
159-
format!("the candidate is defined in an impl{} for the type `{}`",
160-
insertion,
161-
impl_ty)
135+
(format!(
136+
"the candidate is defined in an impl{} for the type `{}`",
137+
insertion,
138+
impl_ty,
139+
), None)
162140
};
163141
if let Some(note_span) = note_span {
164142
// We have a span pointing to the method. Show note with snippet.
@@ -168,7 +146,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
168146
err.note(&note_str);
169147
}
170148
if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) {
171-
print_disambiguation_help(err, self.tcx.def_path_str(trait_ref.def_id));
149+
let path = self.tcx.def_path_str(trait_ref.def_id);
150+
151+
let ty = match item.kind {
152+
ty::AssocKind::Const |
153+
ty::AssocKind::Type |
154+
ty::AssocKind::OpaqueTy => rcvr_ty,
155+
ty::AssocKind::Method => self.tcx.fn_sig(item.def_id)
156+
.inputs()
157+
.skip_binder()
158+
.get(0)
159+
.filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr())
160+
.map(|ty| *ty)
161+
.unwrap_or(rcvr_ty),
162+
};
163+
print_disambiguation_help(
164+
item_name,
165+
args,
166+
err,
167+
path,
168+
ty,
169+
item.kind,
170+
sugg_span,
171+
idx,
172+
self.tcx.sess.source_map(),
173+
);
172174
}
173175
}
174176
CandidateSource::TraitSource(trait_did) => {
@@ -182,19 +184,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
182184
};
183185
let item_span = self.tcx.sess.source_map()
184186
.def_span(self.tcx.def_span(item.def_id));
185-
if sources.len() > 1 {
187+
let idx = if sources.len() > 1 {
186188
span_note!(err,
187189
item_span,
188190
"candidate #{} is defined in the trait `{}`",
189191
idx + 1,
190192
self.tcx.def_path_str(trait_did));
193+
Some(idx + 1)
191194
} else {
192195
span_note!(err,
193196
item_span,
194197
"the candidate is defined in the trait `{}`",
195198
self.tcx.def_path_str(trait_did));
196-
}
197-
print_disambiguation_help(err, self.tcx.def_path_str(trait_did));
199+
None
200+
};
201+
let path = self.tcx.def_path_str(trait_did);
202+
print_disambiguation_help(
203+
item_name,
204+
args,
205+
err,
206+
path,
207+
rcvr_ty,
208+
item.kind,
209+
sugg_span,
210+
idx,
211+
self.tcx.sess.source_map(),
212+
);
198213
}
199214
}
200215
}
@@ -203,6 +218,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
203218
}
204219
};
205220

221+
let sugg_span = if let SelfSource::MethodCall(expr) = source {
222+
// Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing.
223+
self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)).span
224+
} else {
225+
span
226+
};
227+
206228
match error {
207229
MethodError::NoMatch(NoMatchData {
208230
static_candidates: static_sources,
@@ -495,9 +517,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
495517
));
496518
}
497519

498-
report_candidates(span, &mut err, static_sources);
520+
report_candidates(span, &mut err, static_sources, sugg_span);
499521
} else if static_sources.len() > 1 {
500-
report_candidates(span, &mut err, static_sources);
522+
report_candidates(span, &mut err, static_sources, sugg_span);
501523
}
502524

503525
if !unsatisfied_predicates.is_empty() {
@@ -584,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
584606
"multiple applicable items in scope");
585607
err.span_label(span, format!("multiple `{}` found", item_name));
586608

587-
report_candidates(span, &mut err, sources);
609+
report_candidates(span, &mut err, sources, sugg_span);
588610
err.emit();
589611
}
590612

@@ -1123,3 +1145,56 @@ impl hir::intravisit::Visitor<'tcx> for UsePlacementFinder<'tcx> {
11231145
hir::intravisit::NestedVisitorMap::None
11241146
}
11251147
}
1148+
1149+
fn print_disambiguation_help(
1150+
item_name: ast::Ident,
1151+
args: Option<&'tcx [hir::Expr]>,
1152+
err: &mut DiagnosticBuilder<'_>,
1153+
trait_name: String,
1154+
rcvr_ty: Ty<'_>,
1155+
kind: ty::AssocKind,
1156+
span: Span,
1157+
candidate: Option<usize>,
1158+
source_map: &source_map::SourceMap,
1159+
) {
1160+
let mut applicability = Applicability::MachineApplicable;
1161+
let sugg_args = if let (ty::AssocKind::Method, Some(args)) = (kind, args) {
1162+
format!(
1163+
"({}{})",
1164+
if rcvr_ty.is_region_ptr() {
1165+
if rcvr_ty.is_mutable_ptr() {
1166+
"&mut "
1167+
} else {
1168+
"&"
1169+
}
1170+
} else {
1171+
""
1172+
},
1173+
args.iter()
1174+
.map(|arg| source_map.span_to_snippet(arg.span)
1175+
.unwrap_or_else(|_| {
1176+
applicability = Applicability::HasPlaceholders;
1177+
"_".to_owned()
1178+
}))
1179+
.collect::<Vec<_>>()
1180+
.join(", "),
1181+
)
1182+
} else {
1183+
String::new()
1184+
};
1185+
let sugg = format!("{}::{}{}", trait_name, item_name, sugg_args);
1186+
err.span_suggestion(
1187+
span,
1188+
&format!(
1189+
"disambiguate the {} for {}",
1190+
kind.suggestion_descr(),
1191+
if let Some(candidate) = candidate {
1192+
format!("candidate #{}", candidate)
1193+
} else {
1194+
"the candidate".to_string()
1195+
},
1196+
),
1197+
sugg,
1198+
applicability,
1199+
);
1200+
}

src/test/ui/associated-const/associated-const-ambiguity-report.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `Foo` for the type `i32`
99
|
1010
LL | const ID: i32 = 1;
1111
| ^^^^^^^^^^^^^^^^^^
12-
= help: to disambiguate the method call, write `Foo::ID(...)` instead
1312
note: candidate #2 is defined in an impl of the trait `Bar` for the type `i32`
1413
--> $DIR/associated-const-ambiguity-report.rs:14:5
1514
|
1615
LL | const ID: i32 = 3;
1716
| ^^^^^^^^^^^^^^^^^^
18-
= help: to disambiguate the method call, write `Bar::ID(...)` instead
17+
help: disambiguate the associated constant for candidate #1
18+
|
19+
LL | const X: i32 = Foo::ID;
20+
| ^^^^^^^
21+
help: disambiguate the associated constant for candidate #2
22+
|
23+
LL | const X: i32 = Bar::ID;
24+
| ^^^^^^^
1925

2026
error: aborting due to previous error
2127

src/test/ui/error-codes/E0034.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `Trait1` for the type `Tes
99
|
1010
LL | fn foo() {}
1111
| ^^^^^^^^
12-
= help: to disambiguate the method call, write `Trait1::foo(...)` instead
1312
note: candidate #2 is defined in an impl of the trait `Trait2` for the type `Test`
1413
--> $DIR/E0034.rs:16:5
1514
|
1615
LL | fn foo() {}
1716
| ^^^^^^^^
18-
= help: to disambiguate the method call, write `Trait2::foo(...)` instead
17+
help: disambiguate the method call for candidate #1
18+
|
19+
LL | Trait1::foo()
20+
| ^^^^^^^^^^^
21+
help: disambiguate the method call for candidate #2
22+
|
23+
LL | Trait2::foo()
24+
| ^^^^^^^^^^^
1925

2026
error: aborting due to previous error
2127

src/test/ui/inference/inference_unstable_featured.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@ LL | assert_eq!('x'.ipu_flatten(), 0);
55
| ^^^^^^^^^^^ multiple `ipu_flatten` found
66
|
77
= note: candidate #1 is defined in an impl of the trait `inference_unstable_iterator::IpuIterator` for the type `char`
8-
= help: to disambiguate the method call, write `inference_unstable_iterator::IpuIterator::ipu_flatten('x')` instead
98
= note: candidate #2 is defined in an impl of the trait `inference_unstable_itertools::IpuItertools` for the type `char`
10-
= help: to disambiguate the method call, write `inference_unstable_itertools::IpuItertools::ipu_flatten('x')` instead
9+
help: disambiguate the method call for candidate #1
10+
|
11+
LL | assert_eq!(inference_unstable_iterator::IpuIterator::ipu_flatten(&'x'), 0);
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
help: disambiguate the method call for candidate #2
14+
|
15+
LL | assert_eq!(inference_unstable_itertools::IpuItertools::ipu_flatten(&'x'), 0);
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1117

1218
error: aborting due to previous error
1319

src/test/ui/issues/issue-18446.stderr

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ error[E0034]: multiple applicable items in scope
22
--> $DIR/issue-18446.rs:18:7
33
|
44
LL | x.foo();
5-
| ^^^ multiple `foo` found
5+
| --^^^--
6+
| | |
7+
| | multiple `foo` found
8+
| help: disambiguate the method call for candidate #2: `T::foo(&x)`
69
|
710
note: candidate #1 is defined in an impl for the type `dyn T`
811
--> $DIR/issue-18446.rs:9:5
@@ -14,7 +17,6 @@ note: candidate #2 is defined in the trait `T`
1417
|
1518
LL | fn foo(&self);
1619
| ^^^^^^^^^^^^^^
17-
= help: to disambiguate the method call, write `T::foo(&x)` instead
1820

1921
error: aborting due to previous error
2022

src/test/ui/issues/issue-3702-2.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `ToPrimitive` for the type
99
|
1010
LL | fn to_int(&self) -> isize { 0 }
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^
12-
= help: to disambiguate the method call, write `ToPrimitive::to_int(&self)` instead
1312
note: candidate #2 is defined in an impl of the trait `Add` for the type `isize`
1413
--> $DIR/issue-3702-2.rs:14:5
1514
|
1615
LL | fn to_int(&self) -> isize { *self }
1716
| ^^^^^^^^^^^^^^^^^^^^^^^^^
18-
= help: to disambiguate the method call, write `Add::to_int(&self)` instead
17+
help: disambiguate the method call for candidate #1
18+
|
19+
LL | ToPrimitive::to_int(&self) + other.to_int()
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
21+
help: disambiguate the method call for candidate #2
22+
|
23+
LL | Add::to_int(&self) + other.to_int()
24+
| ^^^^^^^^^^^^^^^^^^
1925

2026
error: aborting due to previous error
2127

src/test/ui/issues/issue-65634-raw-ident-suggestion.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ note: candidate #1 is defined in an impl of the trait `async` for the type `r#fn
99
|
1010
LL | fn r#struct(&self) {
1111
| ^^^^^^^^^^^^^^^^^^
12-
= help: to disambiguate the method call, write `async::r#struct(r#fn {})` instead
1312
note: candidate #2 is defined in an impl of the trait `await` for the type `r#fn`
1413
--> $DIR/issue-65634-raw-ident-suggestion.rs:10:5
1514
|
1615
LL | fn r#struct(&self) {
1716
| ^^^^^^^^^^^^^^^^^^
18-
= help: to disambiguate the method call, write `await::r#struct(r#fn {})` instead
17+
help: disambiguate the method call for candidate #1
18+
|
19+
LL | async::r#struct(&r#fn {});
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
21+
help: disambiguate the method call for candidate #2
22+
|
23+
LL | await::r#struct(&r#fn {});
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
1925

2026
error: aborting due to previous error
2127

0 commit comments

Comments
 (0)