Rollup merge of #55862 - zackmdavis:but_will_they_come_when_you_call_them, r=estebank

in which the E0618 "expected function" diagnostic gets a makeover

A woman of wisdom once told me, "Better late than never." (Can't reopen the previously-closed pull request from six months ago [due to GitHub limitations](https://github.com/rust-lang/rust/pull/51098#issuecomment-437647157).)

Now the main span focuses on the erroneous not-a-function callee, while showing the entire call expression is relegated to a secondary span. In the case where the erroneous callee is itself a call, we
point out the definition, and, if the call expression spans multiple lines, tentatively suggest a semicolon (because we suspect that the "outer" call is actually supposed to be a tuple).

![not_a_fn_1](https://user-images.githubusercontent.com/1076988/48309935-96755000-e538-11e8-9390-02a048abb0c2.png)

![not_a_fn_2](https://user-images.githubusercontent.com/1076988/48309936-98d7aa00-e538-11e8-8b9b-257bc77d6261.png)

The new `bug!` assertion is, in fact, safe (`confirm_builtin_call` is only called by `check_call`, which is only called with a first arg of kind `ExprKind::Call` in `check_expr_kind`).

Resolves #51055.

r? @estebank
This commit is contained in:
Pietro Albini 2018-11-18 23:24:37 +01:00 committed by GitHub
commit 9577734ff3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 160 additions and 60 deletions

View file

@ -219,35 +219,62 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} }
} }
let mut err = type_error_struct!( if let hir::ExprKind::Call(ref callee, _) = call_expr.node {
self.tcx.sess, let mut err = type_error_struct!(
call_expr.span, self.tcx.sess,
callee_ty, callee.span,
E0618, callee_ty,
"expected function, found {}", E0618,
match unit_variant { "expected function, found {}",
Some(ref path) => format!("enum variant `{}`", path), match unit_variant {
None => format!("`{}`", callee_ty), Some(ref path) => format!("enum variant `{}`", path),
}); None => format!("`{}`", callee_ty),
});
err.span_label(call_expr.span, "not a function"); if let Some(ref path) = unit_variant {
err.span_suggestion_with_applicability(
call_expr.span,
&format!("`{}` is a unit variant, you need to write it \
without the parenthesis", path),
path.to_string(),
Applicability::MachineApplicable
);
}
if let Some(ref path) = unit_variant { let mut inner_callee_path = None;
err.span_suggestion_with_applicability( let def = match callee.node {
call_expr.span, hir::ExprKind::Path(ref qpath) => {
&format!("`{}` is a unit variant, you need to write it \ self.tables.borrow().qpath_def(qpath, callee.hir_id)
without the parenthesis", path), },
path.to_string(), hir::ExprKind::Call(ref inner_callee, _) => {
Applicability::MachineApplicable // If the call spans more than one line and the callee kind is
); // itself another `ExprCall`, that's a clue that we might just be
} // missing a semicolon (Issue #51055)
let call_is_multiline = self.tcx.sess.source_map()
if let hir::ExprKind::Call(ref expr, _) = call_expr.node { .is_multiline(call_expr.span);
let def = if let hir::ExprKind::Path(ref qpath) = expr.node { if call_is_multiline {
self.tables.borrow().qpath_def(qpath, expr.hir_id) let span = self.tcx.sess.source_map().next_point(callee.span);
} else { err.span_suggestion_with_applicability(
Def::Err span,
"try adding a semicolon",
";".to_owned(),
Applicability::MaybeIncorrect
);
}
if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.node {
inner_callee_path = Some(inner_qpath);
self.tables.borrow().qpath_def(inner_qpath, inner_callee.hir_id)
} else {
Def::Err
}
},
_ => {
Def::Err
}
}; };
err.span_label(call_expr.span, "call expression requires function");
let def_span = match def { let def_span = match def {
Def::Err => None, Def::Err => None,
Def::Local(id) | Def::Upvar(id, ..) => { Def::Local(id) | Def::Upvar(id, ..) => {
@ -256,16 +283,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
_ => self.tcx.hir.span_if_local(def.def_id()) _ => self.tcx.hir.span_if_local(def.def_id())
}; };
if let Some(span) = def_span { if let Some(span) = def_span {
let name = match unit_variant { let label = match (unit_variant, inner_callee_path) {
Some(path) => path, (Some(path), _) => format!("`{}` defined here", path),
None => callee_ty.to_string(), (_, Some(hir::QPath::Resolved(_, path))) => format!(
"`{}` defined here returns `{}`", path, callee_ty.to_string()
),
_ => format!("`{}` defined here", callee_ty.to_string()),
}; };
err.span_label(span, format!("`{}` defined here", name)); err.span_label(span, label);
} }
err.emit();
} else {
bug!("call_expr.node should be an ExprKind::Call, got {:?}", call_expr.node);
} }
err.emit();
// This is the "default" function signature, used in case of error. // This is the "default" function signature, used in case of error.
// In that case, we check each argument against "error" in order to // In that case, we check each argument against "error" in order to
// set up all the node type bindings. // set up all the node type bindings.

View file

@ -12,8 +12,16 @@ LL | |y| x + y
error[E0618]: expected function, found `()` error[E0618]: expected function, found `()`
--> $DIR/issue-20862.rs:17:13 --> $DIR/issue-20862.rs:17:13
| |
LL | let x = foo(5)(2); LL | / fn foo(x: i32) {
| ^^^^^^^^^ not a function LL | | |y| x + y
LL | | //~^ ERROR: mismatched types
LL | | }
| |_- `foo` defined here returns `()`
...
LL | let x = foo(5)(2);
| ^^^^^^---
| |
| call expression requires function
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View file

@ -5,7 +5,9 @@ LL | struct Empty2;
| -------------- `Empty2` defined here | -------------- `Empty2` defined here
... ...
LL | let e2 = Empty2(); //~ ERROR expected function, found `Empty2` LL | let e2 = Empty2(); //~ ERROR expected function, found `Empty2`
| ^^^^^^^^ not a function | ^^^^^^--
| |
| call expression requires function
error[E0618]: expected function, found enum variant `E::Empty4` error[E0618]: expected function, found enum variant `E::Empty4`
--> $DIR/empty-struct-unit-expr.rs:26:14 --> $DIR/empty-struct-unit-expr.rs:26:14
@ -14,7 +16,9 @@ LL | Empty4
| ------ `E::Empty4` defined here | ------ `E::Empty4` defined here
... ...
LL | let e4 = E::Empty4(); LL | let e4 = E::Empty4();
| ^^^^^^^^^^^ not a function | ^^^^^^^^^--
| |
| call expression requires function
help: `E::Empty4` is a unit variant, you need to write it without the parenthesis help: `E::Empty4` is a unit variant, you need to write it without the parenthesis
| |
LL | let e4 = E::Empty4; LL | let e4 = E::Empty4;
@ -24,13 +28,17 @@ error[E0618]: expected function, found `empty_struct::XEmpty2`
--> $DIR/empty-struct-unit-expr.rs:28:15 --> $DIR/empty-struct-unit-expr.rs:28:15
| |
LL | let xe2 = XEmpty2(); //~ ERROR expected function, found `empty_struct::XEmpty2` LL | let xe2 = XEmpty2(); //~ ERROR expected function, found `empty_struct::XEmpty2`
| ^^^^^^^^^ not a function | ^^^^^^^--
| |
| call expression requires function
error[E0618]: expected function, found enum variant `XE::XEmpty4` error[E0618]: expected function, found enum variant `XE::XEmpty4`
--> $DIR/empty-struct-unit-expr.rs:29:15 --> $DIR/empty-struct-unit-expr.rs:29:15
| |
LL | let xe4 = XE::XEmpty4(); LL | let xe4 = XE::XEmpty4();
| ^^^^^^^^^^^^^ not a function | ^^^^^^^^^^^--
| |
| call expression requires function
help: `XE::XEmpty4` is a unit variant, you need to write it without the parenthesis help: `XE::XEmpty4` is a unit variant, you need to write it without the parenthesis
| |
LL | let xe4 = XE::XEmpty4; LL | let xe4 = XE::XEmpty4;

View file

@ -5,7 +5,9 @@ LL | Entry,
| ----- `X::Entry` defined here | ----- `X::Entry` defined here
... ...
LL | X::Entry(); LL | X::Entry();
| ^^^^^^^^^^ not a function | ^^^^^^^^--
| |
| call expression requires function
help: `X::Entry` is a unit variant, you need to write it without the parenthesis help: `X::Entry` is a unit variant, you need to write it without the parenthesis
| |
LL | X::Entry; LL | X::Entry;
@ -17,7 +19,9 @@ error[E0618]: expected function, found `i32`
LL | let x = 0i32; LL | let x = 0i32;
| - `i32` defined here | - `i32` defined here
LL | x(); LL | x();
| ^^^ not a function | ^--
| |
| call expression requires function
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View file

@ -4,7 +4,9 @@ error[E0618]: expected function, found `i32`
LL | fn func(i: i32) { LL | fn func(i: i32) {
| - `i32` defined here | - `i32` defined here
LL | i(); //~ERROR expected function, found `i32` LL | i(); //~ERROR expected function, found `i32`
| ^^^ not a function | ^--
| |
| call expression requires function
error[E0618]: expected function, found `i32` error[E0618]: expected function, found `i32`
--> $DIR/issue-10969.rs:16:5 --> $DIR/issue-10969.rs:16:5
@ -12,7 +14,9 @@ error[E0618]: expected function, found `i32`
LL | let i = 0i32; LL | let i = 0i32;
| - `i32` defined here | - `i32` defined here
LL | i(); //~ERROR expected function, found `i32` LL | i(); //~ERROR expected function, found `i32`
| ^^^ not a function | ^--
| |
| call expression requires function
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View file

@ -2,7 +2,9 @@ error[E0618]: expected function, found `!`
--> $DIR/issue-18532.rs:16:5 --> $DIR/issue-18532.rs:16:5
| |
LL | (return)((),()); //~ ERROR expected function, found `!` LL | (return)((),()); //~ ERROR expected function, found `!`
| ^^^^^^^^^^^^^^^ not a function | ^^^^^^^^-------
| |
| call expression requires function
error: aborting due to previous error error: aborting due to previous error

View file

@ -5,7 +5,9 @@ LL | struct G;
| --------- `G` defined here | --------- `G` defined here
... ...
LL | let g = G(); //~ ERROR: expected function, found `G` LL | let g = G(); //~ ERROR: expected function, found `G`
| ^^^ not a function | ^--
| |
| call expression requires function
error: aborting due to previous error error: aborting due to previous error

View file

@ -4,7 +4,9 @@ error[E0618]: expected function, found `U`
LL | fn foo<U>(t: U) { LL | fn foo<U>(t: U) {
| - `U` defined here | - `U` defined here
LL | let y = t(); LL | let y = t();
| ^^^ not a function | ^--
| |
| call expression requires function
error[E0618]: expected function, found `Bar` error[E0618]: expected function, found `Bar`
--> $DIR/issue-21701.rs:19:13 --> $DIR/issue-21701.rs:19:13
@ -13,7 +15,9 @@ LL | struct Bar;
| ----------- `Bar` defined here | ----------- `Bar` defined here
... ...
LL | let f = Bar(); LL | let f = Bar();
| ^^^^^ not a function | ^^^--
| |
| call expression requires function
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View file

@ -4,7 +4,9 @@ error[E0618]: expected function, found `&str`
LL | let foo = "bar"; LL | let foo = "bar";
| --- `&str` defined here | --- `&str` defined here
LL | let x = foo("baz"); LL | let x = foo("baz");
| ^^^^^^^^^^ not a function | ^^^-------
| |
| call expression requires function
error: aborting due to previous error error: aborting due to previous error

View file

@ -11,7 +11,6 @@
macro_rules! macro_panic { macro_rules! macro_panic {
($not_a_function:expr, $some_argument:ident) => { ($not_a_function:expr, $some_argument:ident) => {
$not_a_function($some_argument) $not_a_function($some_argument)
//~^ ERROR expected function, found `{integer}`
} }
} }
@ -19,5 +18,5 @@ fn main() {
let mut value_a = 0; let mut value_a = 0;
let mut value_b = 0; let mut value_b = 0;
macro_panic!(value_a, value_b); macro_panic!(value_a, value_b);
//~^ in this expansion of macro_panic! //~^ ERROR expected function, found `{integer}`
} }

View file

@ -1,14 +1,14 @@
error[E0618]: expected function, found `{integer}` error[E0618]: expected function, found `{integer}`
--> $DIR/issue-26237.rs:13:9 --> $DIR/issue-26237.rs:20:18
| |
LL | $not_a_function($some_argument) LL | $not_a_function($some_argument)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a function | ------------------------------- call expression requires function
... ...
LL | let mut value_a = 0; LL | let mut value_a = 0;
| ----------- `{integer}` defined here | ----------- `{integer}` defined here
LL | let mut value_b = 0; LL | let mut value_b = 0;
LL | macro_panic!(value_a, value_b); LL | macro_panic!(value_a, value_b);
| ------------------------------- in this macro invocation | ^^^^^^^
error: aborting due to previous error error: aborting due to previous error

View file

@ -2,7 +2,9 @@ error[E0618]: expected function, found `{float}`
--> $DIR/issue-45965.rs:12:30 --> $DIR/issue-45965.rs:12:30
| |
LL | let a = |r: f64| if r != 0.0(r != 0.0) { 1.0 } else { 0.0 }; LL | let a = |r: f64| if r != 0.0(r != 0.0) { 1.0 } else { 0.0 };
| ^^^^^^^^^^^^^ not a function | ^^^----------
| |
| call expression requires function
error: aborting due to previous error error: aborting due to previous error

View file

@ -4,7 +4,9 @@ error[E0618]: expected function, found `main::Foo`
LL | struct Foo; LL | struct Foo;
| ----------- `main::Foo` defined here | ----------- `main::Foo` defined here
LL | (1 .. 2).find(|_| Foo(0) == 0); //~ ERROR expected function, found `main::Foo` LL | (1 .. 2).find(|_| Foo(0) == 0); //~ ERROR expected function, found `main::Foo`
| ^^^^^^ not a function | ^^^---
| |
| call expression requires function
error: aborting due to previous error error: aborting due to previous error

View file

@ -47,9 +47,9 @@ error[E0618]: expected function, found `(char, char)`
--> $DIR/issue-5100.rs:58:14 --> $DIR/issue-5100.rs:58:14
| |
LL | let v = [('a', 'b') //~ ERROR expected function, found `(char, char)` LL | let v = [('a', 'b') //~ ERROR expected function, found `(char, char)`
| ______________^ | ______________-^^^^^^^^^
LL | | ('c', 'd'), LL | | ('c', 'd'),
| |_______________________^ not a function | |_______________________- call expression requires function
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/issue-5100.rs:65:19 --> $DIR/issue-5100.rs:65:19

View file

@ -17,7 +17,9 @@ LL | let y = 42;
| - `{integer}` defined here | - `{integer}` defined here
LL | let x = y.; //~ ERROR unexpected token LL | let x = y.; //~ ERROR unexpected token
LL | let x = y.(); //~ ERROR unexpected token LL | let x = y.(); //~ ERROR unexpected token
| ^^^^ not a function | ^---
| |
| call expression requires function
error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
--> $DIR/parse-error-correct.rs:21:15 --> $DIR/parse-error-correct.rs:21:15

View file

@ -171,7 +171,9 @@ LL | Unit,
| ---- `Z::Unit` defined here | ---- `Z::Unit` defined here
... ...
LL | let _ = Z::Unit(); LL | let _ = Z::Unit();
| ^^^^^^^^^ not a function | ^^^^^^^--
| |
| call expression requires function
help: `Z::Unit` is a unit variant, you need to write it without the parenthesis help: `Z::Unit` is a unit variant, you need to write it without the parenthesis
| |
LL | let _ = Z::Unit; LL | let _ = Z::Unit;
@ -193,7 +195,9 @@ LL | Unit,
| ---- `m::E::Unit` defined here | ---- `m::E::Unit` defined here
... ...
LL | let _: E = m::E::Unit(); LL | let _: E = m::E::Unit();
| ^^^^^^^^^^^^ not a function | ^^^^^^^^^^--
| |
| call expression requires function
help: `m::E::Unit` is a unit variant, you need to write it without the parenthesis help: `m::E::Unit` is a unit variant, you need to write it without the parenthesis
| |
LL | let _: E = m::E::Unit; LL | let _: E = m::E::Unit;
@ -215,7 +219,9 @@ LL | Unit,
| ---- `E::Unit` defined here | ---- `E::Unit` defined here
... ...
LL | let _: E = E::Unit(); LL | let _: E = E::Unit();
| ^^^^^^^^^ not a function | ^^^^^^^--
| |
| call expression requires function
help: `E::Unit` is a unit variant, you need to write it without the parenthesis help: `E::Unit` is a unit variant, you need to write it without the parenthesis
| |
LL | let _: E = E::Unit; LL | let _: E = E::Unit;

View file

@ -0,0 +1,8 @@
fn vindictive() -> bool { true }
fn perfidy() -> (i32, i32) {
vindictive() //~ ERROR expected function, found `bool`
(1, 2)
}
fn main() {}

View file

@ -0,0 +1,16 @@
error[E0618]: expected function, found `bool`
--> $DIR/issue-51055-missing-semicolon-between-call-and-tuple.rs:4:5
|
LL | fn vindictive() -> bool { true }
| -------------------------------- `vindictive` defined here returns `bool`
...
LL | vindictive() //~ ERROR expected function, found `bool`
| -^^^^^^^^^^^- help: try adding a semicolon: `;`
| _____|
| |
LL | | (1, 2)
| |__________- call expression requires function
error: aborting due to previous error
For more information about this error, try `rustc --explain E0618`.