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

Provide suggestion when trying to use method on numeric literal #47171

Merged
merged 2 commits into from
Jan 8, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 57 additions & 15 deletions src/librustc_typeck/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,15 +195,53 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
};
let mut err = if !actual.references_error() {
struct_span_err!(
tcx.sess,
span,
E0599,
"no {} named `{}` found for type `{}` in the current scope",
type_str,
item_name,
ty_string
)
// Suggest clamping down the type if the method that is being attempted to
// be used exists at all, and the type is an ambiuous numeric type
// ({integer}/{float}).
let mut candidates = all_traits(self.tcx)
.filter(|info| {
self.associated_item(info.def_id, item_name, Namespace::Value).is_some()
});
if let (true, false, Some(expr), Some(_)) = (actual.is_numeric(),
actual.has_concrete_skeleton(),
rcvr_expr,
candidates.next()) {
let mut err = struct_span_err!(
tcx.sess,
span,
E0689,
"can't call {} `{}` on ambiguous numeric type `{}`",
type_str,
item_name,
ty_string
);
let snippet = tcx.sess.codemap().span_to_snippet(expr.span)
.unwrap_or("4".to_string());
let concrete_type = if actual.is_integral() {
"u32"
} else {
"f32"
};
err.span_suggestion(expr.span,
&format!("you must specify a concrete type for \
this numeric value, like `{}`",
concrete_type),
format!("({} as {})",
snippet,
concrete_type));
err.emit();
return;
} else {
struct_span_err!(
tcx.sess,
span,
E0599,
"no {} named `{}` found for type `{}` in the current scope",
type_str,
item_name,
ty_string
)
}
} else {
tcx.sess.diagnostic().struct_dummy()
};
Expand Down Expand Up @@ -305,12 +343,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
bound_list));
}

self.suggest_traits_to_import(&mut err,
span,
rcvr_ty,
item_name,
rcvr_expr,
out_of_scope_traits);
if actual.is_numeric() && actual.is_fresh() {

} else {
self.suggest_traits_to_import(&mut err,
span,
rcvr_ty,
item_name,
rcvr_expr,
out_of_scope_traits);
}

if let Some(lev_candidate) = lev_candidate {
err.help(&format!("did you mean `{}`?", lev_candidate.name));
Expand Down
25 changes: 25 additions & 0 deletions src/librustc_typeck/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4641,6 +4641,31 @@ impl Foo for () {
```
"##,

E0689: r##"
This error indicates that the numeric value for the method being passed exists
but the type of the numeric value or binding could not be identified.

The error happens on numeric literals:

```compile_fail,E0689
2.0.powi(2);
```

and on numeric bindings without an identified concrete type:

```compile_fail,E0689
let x = 2.0;
x.powi(2); // same error as above
```

Because of this, you must give the numeric literal or binding a type:

```
let _ = (2.0 as f32).powi(2);
let x: f32 = 2.0;
let _ = x.powi(2);
```
"##,
}

register_diagnostics! {
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issue-41652/issue_41652.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct S;
impl issue_41652_b::Tr for S {
fn f() {
3.f()
//~^ ERROR no method named `f` found for type `{integer}` in the current scope
//~^ ERROR can't call method `f` on ambiguous numeric type `{integer}`
}
}

Expand Down
14 changes: 4 additions & 10 deletions src/test/ui/issue-41652/issue_41652.stderr
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
error[E0599]: no method named `f` found for type `{integer}` in the current scope
error[E0689]: can't call method `f` on ambiguous numeric type `{integer}`
--> $DIR/issue_41652.rs:19:11
|
19 | 3.f()
| ^
help: you must specify a concrete type for this numeric value, like `u32`
|
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
= help: try with `{integer}::f`
note: candidate #1 is defined in the trait `issue_41652_b::Tr`
--> $DIR/auxiliary/issue_41652_b.rs:14:5
|
14 | / fn f()
15 | | where Self: Sized;
| |__________________________^
= help: to disambiguate the method call, write `issue_41652_b::Tr::f(3)` instead
19 | (3 as u32).f()
| ^^^^^^^^^^

error: aborting due to previous error

14 changes: 14 additions & 0 deletions src/test/ui/macros/macro-backtrace-invalid-internals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,26 @@ macro_rules! fake_anon_field_expr {
}
}

macro_rules! real_method_stmt {
() => {
2.0.powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
}
}

macro_rules! real_method_expr {
() => {
2.0.powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
}
}

fn main() {
fake_method_stmt!();
fake_field_stmt!();
fake_anon_field_stmt!();
real_method_stmt!();

let _ = fake_method_expr!();
let _ = fake_field_expr!();
let _ = fake_anon_field_expr!();
let _ = real_method_expr!();
}
40 changes: 33 additions & 7 deletions src/test/ui/macros/macro-backtrace-invalid-internals.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error[E0599]: no method named `fake` found for type `{integer}` in the current s
15 | 1.fake() //~ ERROR no method
| ^^^^
...
50 | fake_method_stmt!();
62 | fake_method_stmt!();
| -------------------- in this macro invocation

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

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

error[E0689]: can't call method `powi` on ambiguous numeric type `{float}`
--> $DIR/macro-backtrace-invalid-internals.rs:51:15
|
51 | 2.0.powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
| ^^^^
...
65 | real_method_stmt!();
| -------------------- in this macro invocation
help: you must specify a concrete type for this numeric value, like `f32`
|
51 | (2.0 as f32).powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
| ^^^^^^^^^^^^

error[E0599]: no method named `fake` found for type `{integer}` in the current scope
--> $DIR/macro-backtrace-invalid-internals.rs:33:13
|
33 | 1.fake() //~ ERROR no method
| ^^^^
...
54 | let _ = fake_method_expr!();
67 | let _ = fake_method_expr!();
| ------------------- in this macro invocation

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

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

error: aborting due to 6 previous errors
error[E0689]: can't call method `powi` on ambiguous numeric type `{float}`
--> $DIR/macro-backtrace-invalid-internals.rs:57:15
|
57 | 2.0.powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
| ^^^^
...
70 | let _ = real_method_expr!();
| ------------------- in this macro invocation
help: you must specify a concrete type for this numeric value, like `f32`
|
57 | (2.0 as f32).powi(2) //~ ERROR can't call method `powi` on ambiguous numeric type `{float}`
| ^^^^^^^^^^^^

error: aborting due to 8 previous errors

15 changes: 15 additions & 0 deletions src/test/ui/suggestions/method-on-ambiguous-numeric-type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn main() {
let x = 2.0.powi(2);
//~^ ERROR can't call method `powi` on ambiguous numeric type `{float}`
println!("{:?}", x);
}
12 changes: 12 additions & 0 deletions src/test/ui/suggestions/method-on-ambiguous-numeric-type.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0689]: 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 as f32).powi(2);
| ^^^^^^^^^^^^

error: aborting due to previous error