Skip to content

Commit 87242f3

Browse files
committed
Provide suggestion when trying to use method on numeric literal
1 parent 885011e commit 87242f3

8 files changed

+161
-33
lines changed

src/librustc_typeck/check/method/suggest.rs

+57-15
Original file line numberDiff line numberDiff line change
@@ -195,15 +195,53 @@ 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 snippet = tcx.sess.codemap().span_to_snippet(expr.span)
219+
.unwrap_or("4".to_string());
220+
let concrete_type = if actual.is_integral() {
221+
"u32"
222+
} else {
223+
"f32"
224+
};
225+
err.span_suggestion(expr.span,
226+
&format!("you must specify a concrete type for \
227+
this numeric value, like `{}`",
228+
concrete_type),
229+
format!("({} as {})",
230+
snippet,
231+
concrete_type));
232+
err.emit();
233+
return;
234+
} else {
235+
struct_span_err!(
236+
tcx.sess,
237+
span,
238+
E0599,
239+
"no {} named `{}` found for type `{}` in the current scope",
240+
type_str,
241+
item_name,
242+
ty_string
243+
)
244+
}
207245
} else {
208246
tcx.sess.diagnostic().struct_dummy()
209247
};
@@ -305,12 +343,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
305343
bound_list));
306344
}
307345

308-
self.suggest_traits_to_import(&mut err,
309-
span,
310-
rcvr_ty,
311-
item_name,
312-
rcvr_expr,
313-
out_of_scope_traits);
346+
if actual.is_numeric() && actual.is_fresh() {
347+
348+
} else {
349+
self.suggest_traits_to_import(&mut err,
350+
span,
351+
rcvr_ty,
352+
item_name,
353+
rcvr_expr,
354+
out_of_scope_traits);
355+
}
314356

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

src/librustc_typeck/diagnostics.rs

+25
Original file line numberDiff line numberDiff line change
@@ -4641,6 +4641,31 @@ 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 as f32).powi(2);
4665+
let x: f32 = 2.0;
4666+
let _ = x.powi(2);
4667+
```
4668+
"##,
46444669
}
46454670

46464671
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 `u32`
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 as u32).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 as 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 as 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,15 @@
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+
println!("{:?}", x);
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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 as f32).powi(2);
9+
| ^^^^^^^^^^^^
10+
11+
error: aborting due to previous error
12+

0 commit comments

Comments
 (0)