Skip to content

Commit

Permalink
in which the E0618 "expected function" diagnostic gets a makeover
Browse files Browse the repository at this point in the history
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).

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.
  • Loading branch information
zackmdavis committed Nov 11, 2018
1 parent 6408162 commit f3e9b1a
Show file tree
Hide file tree
Showing 18 changed files with 160 additions and 60 deletions.
95 changes: 63 additions & 32 deletions src/librustc_typeck/check/callee.rs
Expand Up @@ -218,35 +218,62 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}

let mut err = type_error_struct!(
self.tcx.sess,
call_expr.span,
callee_ty,
E0618,
"expected function, found {}",
match unit_variant {
Some(ref path) => format!("enum variant `{}`", path),
None => format!("`{}`", callee_ty),
});

err.span_label(call_expr.span, "not a function");
if let hir::ExprKind::Call(ref callee, _) = call_expr.node {
let mut err = type_error_struct!(
self.tcx.sess,
callee.span,
callee_ty,
E0618,
"expected function, found {}",
match unit_variant {
Some(ref path) => format!("enum variant `{}`", path),
None => format!("`{}`", callee_ty),
});

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 {
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 hir::ExprKind::Call(ref expr, _) = call_expr.node {
let def = if let hir::ExprKind::Path(ref qpath) = expr.node {
self.tables.borrow().qpath_def(qpath, expr.hir_id)
} else {
Def::Err
let mut inner_callee_path = None;
let def = match callee.node {
hir::ExprKind::Path(ref qpath) => {
self.tables.borrow().qpath_def(qpath, callee.hir_id)
},
hir::ExprKind::Call(ref inner_callee, _) => {
// 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()
.is_multiline(call_expr.span);
if call_is_multiline {
let span = self.tcx.sess.source_map().next_point(callee.span);
err.span_suggestion_with_applicability(
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 {
Def::Err => None,
Def::Local(id) | Def::Upvar(id, ..) => {
Expand All @@ -255,16 +282,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
_ => self.tcx.hir.span_if_local(def.def_id())
};
if let Some(span) = def_span {
let name = match unit_variant {
Some(path) => path,
None => callee_ty.to_string(),
let label = match (unit_variant, inner_callee_path) {
(Some(path), _) => format!("`{}` defined here", path),
(_, 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.
// In that case, we check each argument against "error" in order to
// set up all the node type bindings.
Expand Down
12 changes: 10 additions & 2 deletions src/test/ui/block-result/issue-20862.stderr
Expand Up @@ -12,8 +12,16 @@ LL | |y| x + y
error[E0618]: expected function, found `()`
--> $DIR/issue-20862.rs:17:13
|
LL | let x = foo(5)(2);
| ^^^^^^^^^ not a function
LL | / fn foo(x: i32) {
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

Expand Down
16 changes: 12 additions & 4 deletions src/test/ui/empty/empty-struct-unit-expr.stderr
Expand Up @@ -5,7 +5,9 @@ LL | struct Empty2;
| -------------- `Empty2` defined here
...
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`
--> $DIR/empty-struct-unit-expr.rs:26:14
Expand All @@ -14,7 +16,9 @@ LL | Empty4
| ------ `E::Empty4` defined here
...
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
|
LL | let e4 = E::Empty4;
Expand All @@ -24,13 +28,17 @@ error[E0618]: expected function, found `empty_struct::XEmpty2`
--> $DIR/empty-struct-unit-expr.rs:28:15
|
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`
--> $DIR/empty-struct-unit-expr.rs:29:15
|
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
|
LL | let xe4 = XE::XEmpty4;
Expand Down
8 changes: 6 additions & 2 deletions src/test/ui/error-codes/E0618.stderr
Expand Up @@ -5,7 +5,9 @@ LL | Entry,
| ----- `X::Entry` defined here
...
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
|
LL | X::Entry;
Expand All @@ -17,7 +19,9 @@ error[E0618]: expected function, found `i32`
LL | let x = 0i32;
| - `i32` defined here
LL | x();
| ^^^ not a function
| ^--
| |
| call expression requires function

error: aborting due to 2 previous errors

Expand Down
8 changes: 6 additions & 2 deletions src/test/ui/issues/issue-10969.stderr
Expand Up @@ -4,15 +4,19 @@ error[E0618]: expected function, found `i32`
LL | fn func(i: i32) {
| - `i32` defined here
LL | i(); //~ERROR expected function, found `i32`
| ^^^ not a function
| ^--
| |
| call expression requires function

error[E0618]: expected function, found `i32`
--> $DIR/issue-10969.rs:16:5
|
LL | let i = 0i32;
| - `i32` defined here
LL | i(); //~ERROR expected function, found `i32`
| ^^^ not a function
| ^--
| |
| call expression requires function

error: aborting due to 2 previous errors

Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/issues/issue-18532.stderr
Expand Up @@ -2,7 +2,9 @@ error[E0618]: expected function, found `!`
--> $DIR/issue-18532.rs:16:5
|
LL | (return)((),()); //~ ERROR expected function, found `!`
| ^^^^^^^^^^^^^^^ not a function
| ^^^^^^^^-------
| |
| call expression requires function

error: aborting due to previous error

Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/issues/issue-20714.stderr
Expand Up @@ -5,7 +5,9 @@ LL | struct G;
| --------- `G` defined here
...
LL | let g = G(); //~ ERROR: expected function, found `G`
| ^^^ not a function
| ^--
| |
| call expression requires function

error: aborting due to previous error

Expand Down
8 changes: 6 additions & 2 deletions src/test/ui/issues/issue-21701.stderr
Expand Up @@ -4,7 +4,9 @@ error[E0618]: expected function, found `U`
LL | fn foo<U>(t: U) {
| - `U` defined here
LL | let y = t();
| ^^^ not a function
| ^--
| |
| call expression requires function

error[E0618]: expected function, found `Bar`
--> $DIR/issue-21701.rs:19:13
Expand All @@ -13,7 +15,9 @@ LL | struct Bar;
| ----------- `Bar` defined here
...
LL | let f = Bar();
| ^^^^^ not a function
| ^^^--
| |
| call expression requires function

error: aborting due to 2 previous errors

Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/issues/issue-22468.stderr
Expand Up @@ -4,7 +4,9 @@ error[E0618]: expected function, found `&str`
LL | let foo = "bar";
| --- `&str` defined here
LL | let x = foo("baz");
| ^^^^^^^^^^ not a function
| ^^^-------
| |
| call expression requires function

error: aborting due to previous error

Expand Down
3 changes: 1 addition & 2 deletions src/test/ui/issues/issue-26237.rs
Expand Up @@ -11,13 +11,12 @@
macro_rules! macro_panic {
($not_a_function:expr, $some_argument:ident) => {
$not_a_function($some_argument)
//~^ ERROR expected function, found `{integer}`
}
}

fn main() {
let mut value_a = 0;
let mut value_b = 0;
macro_panic!(value_a, value_b);
//~^ in this expansion of macro_panic!
//~^ ERROR expected function, found `{integer}`
}
6 changes: 3 additions & 3 deletions src/test/ui/issues/issue-26237.stderr
@@ -1,14 +1,14 @@
error[E0618]: expected function, found `{integer}`
--> $DIR/issue-26237.rs:13:9
--> $DIR/issue-26237.rs:20:18
|
LL | $not_a_function($some_argument)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a function
| ------------------------------- call expression requires function
...
LL | let mut value_a = 0;
| ----------- `{integer}` defined here
LL | let mut value_b = 0;
LL | macro_panic!(value_a, value_b);
| ------------------------------- in this macro invocation
| ^^^^^^^

error: aborting due to previous error

Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/issues/issue-45965.stderr
Expand Up @@ -2,7 +2,9 @@ error[E0618]: expected function, found `{float}`
--> $DIR/issue-45965.rs:12:30
|
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

Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/issues/issue-46771.stderr
Expand Up @@ -4,7 +4,9 @@ error[E0618]: expected function, found `main::Foo`
LL | struct Foo;
| ----------- `main::Foo` defined here
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

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-5100.stderr
Expand Up @@ -47,9 +47,9 @@ error[E0618]: expected function, found `(char, char)`
--> $DIR/issue-5100.rs:58:14
|
LL | let v = [('a', 'b') //~ ERROR expected function, found `(char, char)`
| ______________^
| ______________-^^^^^^^^^
LL | | ('c', 'd'),
| |_______________________^ not a function
| |_______________________- call expression requires function

error[E0308]: mismatched types
--> $DIR/issue-5100.rs:65:19
Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/parse-error-correct.stderr
Expand Up @@ -17,7 +17,9 @@ LL | let y = 42;
| - `{integer}` defined here
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
--> $DIR/parse-error-correct.rs:21:15
Expand Down
12 changes: 9 additions & 3 deletions src/test/ui/resolve/privacy-enum-ctor.stderr
Expand Up @@ -171,7 +171,9 @@ LL | Unit,
| ---- `Z::Unit` defined here
...
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
|
LL | let _ = Z::Unit;
Expand All @@ -193,7 +195,9 @@ LL | Unit,
| ---- `m::E::Unit` defined here
...
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
|
LL | let _: E = m::E::Unit;
Expand All @@ -215,7 +219,9 @@ LL | Unit,
| ---- `E::Unit` defined here
...
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
|
LL | let _: E = E::Unit;
Expand Down
@@ -0,0 +1,8 @@
fn vindictive() -> bool { true }

fn perfidy() -> (i32, i32) {
vindictive() //~ ERROR expected function, found `bool`
(1, 2)
}

fn main() {}

0 comments on commit f3e9b1a

Please sign in to comment.