Skip to content

Commit b950587

Browse files
committed
Auto merge of #47171 - estebank:numeric-literal-suggestion, r=nikomatsakis
Provide suggestion when trying to use method on numeric literal New output: ``` error[E0688]: can't call method `powi` on ambiguous numeric type `{float}` --> $DIR/method-on-ambiguous-numeric-type.rs:12:17 | 12 | let x = 2.0.powi(2); | ^^^^ help: you must specify a concrete type for this numeric value, like `f32` | 12 | let x = 2.0_f32.powi(2); | ^^^^^^^ ``` Previous output: ``` error[E0599]: no method named `powi` found for type `{float}` in the current scope --> src/main.rs:12:17 | 12 | let x = 2.0.powi(2); | ^^^^ | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope, perhaps add a `use` for it: | 11 | use core::num::Float; | ``` Fix #40985.
2 parents 2148bcd + f7aed3e commit b950587

9 files changed

+199
-34
lines changed

src/librustc/hir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ pub struct LifetimeDef {
241241
}
242242

243243
/// A "Path" is essentially Rust's notion of a name; for instance:
244-
/// std::cmp::PartialEq . It's represented as a sequence of identifiers,
244+
/// `std::cmp::PartialEq`. It's represented as a sequence of identifiers,
245245
/// along with a bunch of supporting information.
246246
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)]
247247
pub struct Path {

src/librustc_typeck/check/method/suggest.rs

+80-15
Original file line numberDiff line numberDiff line change
@@ -195,15 +195,76 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
195195
}
196196
};
197197
let mut err = if !actual.references_error() {
198-
struct_span_err!(
199-
tcx.sess,
200-
span,
201-
E0599,
202-
"no {} named `{}` found for type `{}` in the current scope",
203-
type_str,
204-
item_name,
205-
ty_string
206-
)
198+
// Suggest clamping down the type if the method that is being attempted to
199+
// be used exists at all, and the type is an ambiuous numeric type
200+
// ({integer}/{float}).
201+
let mut candidates = all_traits(self.tcx)
202+
.filter(|info| {
203+
self.associated_item(info.def_id, item_name, Namespace::Value).is_some()
204+
});
205+
if let (true, false, Some(expr), Some(_)) = (actual.is_numeric(),
206+
actual.has_concrete_skeleton(),
207+
rcvr_expr,
208+
candidates.next()) {
209+
let mut err = struct_span_err!(
210+
tcx.sess,
211+
span,
212+
E0689,
213+
"can't call {} `{}` on ambiguous numeric type `{}`",
214+
type_str,
215+
item_name,
216+
ty_string
217+
);
218+
let concrete_type = if actual.is_integral() {
219+
"i32"
220+
} else {
221+
"f32"
222+
};
223+
match expr.node {
224+
hir::ExprLit(_) => { // numeric literal
225+
let snippet = tcx.sess.codemap().span_to_snippet(expr.span)
226+
.unwrap_or("<numeric literal>".to_string());
227+
// FIXME: use the literal for missing snippet
228+
229+
err.span_suggestion(expr.span,
230+
&format!("you must specify a concrete type for \
231+
this numeric value, like `{}`",
232+
concrete_type),
233+
format!("{}_{}",
234+
snippet,
235+
concrete_type));
236+
}
237+
hir::ExprPath(ref qpath) => { // local binding
238+
if let &hir::QPath::Resolved(_, ref path) = &qpath {
239+
if let hir::def::Def::Local(node_id) = path.def {
240+
let span = tcx.hir.span(node_id);
241+
let snippet = tcx.sess.codemap().span_to_snippet(span)
242+
.unwrap();
243+
err.span_suggestion(span,
244+
&format!("you must specify a type for \
245+
this binding, like `{}`",
246+
concrete_type),
247+
format!("{}: {}",
248+
snippet,
249+
concrete_type));
250+
}
251+
}
252+
}
253+
_ => {}
254+
}
255+
err.emit();
256+
return;
257+
} else {
258+
struct_span_err!(
259+
tcx.sess,
260+
span,
261+
E0599,
262+
"no {} named `{}` found for type `{}` in the current scope",
263+
type_str,
264+
item_name,
265+
ty_string
266+
)
267+
}
207268
} else {
208269
tcx.sess.diagnostic().struct_dummy()
209270
};
@@ -305,12 +366,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
305366
bound_list));
306367
}
307368

308-
self.suggest_traits_to_import(&mut err,
309-
span,
310-
rcvr_ty,
311-
item_name,
312-
rcvr_expr,
313-
out_of_scope_traits);
369+
if actual.is_numeric() && actual.is_fresh() {
370+
371+
} else {
372+
self.suggest_traits_to_import(&mut err,
373+
span,
374+
rcvr_ty,
375+
item_name,
376+
rcvr_expr,
377+
out_of_scope_traits);
378+
}
314379

315380
if let Some(lev_candidate) = lev_candidate {
316381
err.help(&format!("did you mean `{}`?", lev_candidate.name));

src/librustc_typeck/diagnostics.rs

+26
Original file line numberDiff line numberDiff line change
@@ -4641,6 +4641,32 @@ impl Foo for () {
46414641
```
46424642
"##,
46434643

4644+
E0689: r##"
4645+
This error indicates that the numeric value for the method being passed exists
4646+
but the type of the numeric value or binding could not be identified.
4647+
4648+
The error happens on numeric literals:
4649+
4650+
```compile_fail,E0689
4651+
2.0.powi(2);
4652+
```
4653+
4654+
and on numeric bindings without an identified concrete type:
4655+
4656+
```compile_fail,E0689
4657+
let x = 2.0;
4658+
x.powi(2); // same error as above
4659+
```
4660+
4661+
Because of this, you must give the numeric literal or binding a type:
4662+
4663+
```
4664+
let _ = 2.0_f32.powi(2);
4665+
let x: f32 = 2.0;
4666+
let _ = x.powi(2);
4667+
let _ = (2.0 as f32).powi(2);
4668+
```
4669+
"##,
46444670
}
46454671

46464672
register_diagnostics! {

src/test/ui/issue-41652/issue_41652.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct S;
1717
impl issue_41652_b::Tr for S {
1818
fn f() {
1919
3.f()
20-
//~^ ERROR no method named `f` found for type `{integer}` in the current scope
20+
//~^ ERROR can't call method `f` on ambiguous numeric type `{integer}`
2121
}
2222
}
2323

+4-10
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
1-
error[E0599]: no method named `f` found for type `{integer}` in the current scope
1+
error[E0689]: can't call method `f` on ambiguous numeric type `{integer}`
22
--> $DIR/issue_41652.rs:19:11
33
|
44
19 | 3.f()
55
| ^
6+
help: you must specify a concrete type for this numeric value, like `i32`
67
|
7-
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
8-
= help: try with `{integer}::f`
9-
note: candidate #1 is defined in the trait `issue_41652_b::Tr`
10-
--> $DIR/auxiliary/issue_41652_b.rs:14:5
11-
|
12-
14 | / fn f()
13-
15 | | where Self: Sized;
14-
| |__________________________^
15-
= help: to disambiguate the method call, write `issue_41652_b::Tr::f(3)` instead
8+
19 | 3_i32.f()
9+
| ^^^^^
1610

1711
error: aborting due to previous error
1812

src/test/ui/macros/macro-backtrace-invalid-internals.rs

+14
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,26 @@ macro_rules! fake_anon_field_expr {
4646
}
4747
}
4848

49+
macro_rules! real_method_stmt {
50+
() => {
51+
2.0.powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
52+
}
53+
}
54+
55+
macro_rules! real_method_expr {
56+
() => {
57+
2.0.powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
58+
}
59+
}
60+
4961
fn main() {
5062
fake_method_stmt!();
5163
fake_field_stmt!();
5264
fake_anon_field_stmt!();
65+
real_method_stmt!();
5366

5467
let _ = fake_method_expr!();
5568
let _ = fake_field_expr!();
5669
let _ = fake_anon_field_expr!();
70+
let _ = real_method_expr!();
5771
}

src/test/ui/macros/macro-backtrace-invalid-internals.stderr

+33-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0599]: no method named `fake` found for type `{integer}` in the current s
44
15 | 1.fake() //~ ERROR no method
55
| ^^^^
66
...
7-
50 | fake_method_stmt!();
7+
62 | fake_method_stmt!();
88
| -------------------- in this macro invocation
99

1010
error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
@@ -13,7 +13,7 @@ error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
1313
21 | 1.fake //~ ERROR doesn't have fields
1414
| ^^^^
1515
...
16-
51 | fake_field_stmt!();
16+
63 | fake_field_stmt!();
1717
| ------------------- in this macro invocation
1818

1919
error[E0609]: no field `0` on type `{integer}`
@@ -22,16 +22,29 @@ error[E0609]: no field `0` on type `{integer}`
2222
27 | (1).0 //~ ERROR no field
2323
| ^^^^^
2424
...
25-
52 | fake_anon_field_stmt!();
25+
64 | fake_anon_field_stmt!();
2626
| ------------------------ in this macro invocation
2727

28+
error[E0689]: can't call method `powi` on ambiguous numeric type `{float}`
29+
--> $DIR/macro-backtrace-invalid-internals.rs:51:15
30+
|
31+
51 | 2.0.powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
32+
| ^^^^
33+
...
34+
65 | real_method_stmt!();
35+
| -------------------- in this macro invocation
36+
help: you must specify a concrete type for this numeric value, like `f32`
37+
|
38+
51 | 2.0_f32.powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
39+
| ^^^^^^^
40+
2841
error[E0599]: no method named `fake` found for type `{integer}` in the current scope
2942
--> $DIR/macro-backtrace-invalid-internals.rs:33:13
3043
|
3144
33 | 1.fake() //~ ERROR no method
3245
| ^^^^
3346
...
34-
54 | let _ = fake_method_expr!();
47+
67 | let _ = fake_method_expr!();
3548
| ------------------- in this macro invocation
3649

3750
error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
@@ -40,7 +53,7 @@ error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
4053
39 | 1.fake //~ ERROR doesn't have fields
4154
| ^^^^
4255
...
43-
55 | let _ = fake_field_expr!();
56+
68 | let _ = fake_field_expr!();
4457
| ------------------ in this macro invocation
4558

4659
error[E0609]: no field `0` on type `{integer}`
@@ -49,8 +62,21 @@ error[E0609]: no field `0` on type `{integer}`
4962
45 | (1).0 //~ ERROR no field
5063
| ^^^^^
5164
...
52-
56 | let _ = fake_anon_field_expr!();
65+
69 | let _ = fake_anon_field_expr!();
5366
| ----------------------- in this macro invocation
5467

55-
error: aborting due to 6 previous errors
68+
error[E0689]: can't call method `powi` on ambiguous numeric type `{float}`
69+
--> $DIR/macro-backtrace-invalid-internals.rs:57:15
70+
|
71+
57 | 2.0.powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
72+
| ^^^^
73+
...
74+
70 | let _ = real_method_expr!();
75+
| ------------------- in this macro invocation
76+
help: you must specify a concrete type for this numeric value, like `f32`
77+
|
78+
57 | 2.0_f32.powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
79+
| ^^^^^^^
80+
81+
error: aborting due to 8 previous errors
5682

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let x = 2.0.powi(2);
13+
//~^ ERROR can't call method `powi` on ambiguous numeric type `{float}`
14+
let y = 2.0;
15+
let x = y.powi(2);
16+
//~^ ERROR can't call method `powi` on ambiguous numeric type `{float}`
17+
println!("{:?}", x);
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0689]: can't call method `powi` on ambiguous numeric type `{float}`
2+
--> $DIR/method-on-ambiguous-numeric-type.rs:12:17
3+
|
4+
12 | let x = 2.0.powi(2);
5+
| ^^^^
6+
help: you must specify a concrete type for this numeric value, like `f32`
7+
|
8+
12 | let x = 2.0_f32.powi(2);
9+
| ^^^^^^^
10+
11+
error[E0689]: can't call method `powi` on ambiguous numeric type `{float}`
12+
--> $DIR/method-on-ambiguous-numeric-type.rs:15:15
13+
|
14+
15 | let x = y.powi(2);
15+
| ^^^^
16+
help: you must specify a type for this binding, like `f32`
17+
|
18+
14 | let y: f32 = 2.0;
19+
| ^^^^^^
20+
21+
error: aborting due to 2 previous errors
22+

0 commit comments

Comments
 (0)