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

Lint uncallable function #54127

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
30 changes: 30 additions & 0 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1082,11 +1082,19 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
GatherLocalsVisitor { fcx: &fcx, parent_id: outer_node_id, }.visit_body(body);

// Add formal parameters.
let mut uninhabited_args = vec![];
for (arg_ty, arg) in fn_sig.inputs().iter().zip(&body.arguments) {
// Check the pattern.
fcx.check_pat_walk(&arg.pat, arg_ty,
ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), true);

// If any of a function's parameters have a type that is uninhabited, then it
// cannot be called (because its arguments cannot be constructed).
let module = fcx.tcx.hir().get_module_parent(fn_id);
if fcx.tcx.is_ty_uninhabited_from(module, arg_ty) {
uninhabited_args.push(arg);
}

// Check that argument is Sized.
// The check for a non-trivial pattern is a hack to avoid duplicate warnings
// for simple cases like `fn foo(x: Trait)`,
Expand All @@ -1098,6 +1106,28 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
fcx.write_ty(arg.hir_id, arg_ty);
}

if !uninhabited_args.is_empty() {
match fcx.tcx.hir().find(fcx.tcx.hir().get_parent(fn_id)) {
Some(Node::Item(&hir::Item { node: ItemKind::Impl(..), .. })) => {
// We only want to warn for functions with parameters of uninhabited types if they
// are not required (e.g. trait implementations). In the future, such
// implementations may be unnecessary, but for now they are required.
}
_ => {
let mut err = fcx.tcx.struct_span_lint_node(
lint::builtin::UNREACHABLE_CODE,
fn_id,
fcx.tcx.hir().span(fn_id),
"functions with parameters of uninhabited types are uncallable",
);
for arg in uninhabited_args {
err.span_label(arg.pat.span, format!("this parameter has an uninhabited type"));
}
err.emit();
}
}
}

let fn_hir_id = fcx.tcx.hir().node_to_hir_id(fn_id);
inherited.tables.borrow_mut().liberated_fn_sigs_mut().insert(fn_hir_id, fn_sig);

Expand Down
3 changes: 3 additions & 0 deletions src/libstd/sys/cloudabi/shims/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
unsupported()
}

// `FilePermissions` is uninhabited in CloudABI, so this function is
// uncallable (but necessary for the public interface).
#[allow(unreachable_code)]
pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
match perm.0 {}
}
Expand Down
3 changes: 3 additions & 0 deletions src/libstd/sys/cloudabi/shims/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ impl AnonPipe {
}
}

// `AnonPipe` is uninhabited in CloudABI, so this function is
// uncallable (but necessary for the public interface).
#[allow(unreachable_code)]
pub fn read2(p1: AnonPipe, _v1: &mut Vec<u8>, _p2: AnonPipe, _v2: &mut Vec<u8>) -> io::Result<()> {
match p1.0 {}
}
3 changes: 3 additions & 0 deletions src/libstd/sys/sgx/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
unsupported()
}

// `FilePermissions` is uninhabited in SGX, so this function is
// uncallable (but necessary for the public interface).
#[allow(unreachable_code)]
pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
match perm.0 {}
}
Expand Down
3 changes: 3 additions & 0 deletions src/libstd/sys/sgx/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ impl AnonPipe {
}
}

// `AnonPipe` is uninhabited in SGX, so this function is
// uncallable (but necessary for the public interface).
#[allow(unreachable_code)]
pub fn read2(p1: AnonPipe,
_v1: &mut Vec<u8>,
_p2: AnonPipe,
Expand Down
3 changes: 3 additions & 0 deletions src/libstd/sys/wasm/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
unsupported()
}

// `FilePermissions` is uninhabited in WASM, so this function is
// uncallable (but necessary for the public interface).
#[allow(unreachable_code)]
pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
match perm.0 {}
}
Expand Down
3 changes: 3 additions & 0 deletions src/libstd/sys/wasm/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ impl AnonPipe {
}
}

// `AnonPipe` is uninhabited in WASM, so this function is
// uncallable (but necessary for the public interface).
#[allow(unreachable_code)]
pub fn read2(p1: AnonPipe,
_v1: &mut Vec<u8>,
_p2: AnonPipe,
Expand Down
8 changes: 5 additions & 3 deletions src/test/run-pass/drop/drop-uninhabited-enum.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// run-pass
#![allow(dead_code)]
#![allow(unused_variables)]
// pretty-expanded FIXME #23616
#![allow(unreachable_code)]

enum Foo { }
enum Foo {}

impl Drop for Foo {
fn drop(&mut self) { }
}

fn foo(x: Foo) { }
fn foo() {
let _x: Foo = unimplemented!();
}

fn main() { }
16 changes: 8 additions & 8 deletions src/test/run-pass/issues/issue-3037.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// run-pass
#![allow(dead_code)]
#![allow(unused_variables)]
// pretty-expanded FIXME #23616
#![allow(non_camel_case_types)]

enum what { }
enum Void {}

fn what_to_string(x: what) -> String
{
match x {
}
fn void() -> Void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why change this?

Copy link
Member Author

@varkor varkor Dec 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In its previous form, it was taking an uninhabited type as an argument, so the new lint was kicking in. Since #3037 was about matching on uninhabited types, it felt clearer to rewrite it like this rather than ignore the lint (since eventually we could be more aggressive about functions taking uninhabited types).

unimplemented!()
}

pub fn main()
{
fn void_to_string() -> String {
match void() {}
}

pub fn main() {}
18 changes: 18 additions & 0 deletions src/test/run-pass/issues/issue-46855.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
warning: functions with parameters of uninhabited types are uncallable
--> $DIR/issue-46855.rs:15:1
|
LL | fn foo(xs: [(Never, u32); 1]) -> u32 { xs[0].1 }
| ^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| this parameter has an uninhabited type
|
= note: #[warn(unreachable_code)] on by default

warning: functions with parameters of uninhabited types are uncallable
--> $DIR/issue-46855.rs:17:1
|
LL | fn bar([(_, x)]: [(Never, u32); 1]) -> u32 { x }
| ^^^^^^^--------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| this parameter has an uninhabited type

3 changes: 3 additions & 0 deletions src/test/run-pass/issues/issue-50731.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// run-pass
enum Void {}

#[allow(unreachable_code)]
fn foo(_: Result<(Void, u32), (Void, String)>) {}

fn main() {
let _: fn(_) = foo;
}
1 change: 1 addition & 0 deletions src/test/run-pass/never-type-rvalues.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![allow(dead_code)]
#![allow(path_statements)]
#![allow(unreachable_patterns)]
#![allow(unreachable_code)]

fn never_direct(x: !) {
x;
Expand Down
11 changes: 7 additions & 4 deletions src/test/ui/match/match-no-arms-unreachable-after.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@

enum Void { }

fn foo(v: Void) {
match v { }
let x = 2; //~ ERROR unreachable
fn bar() -> Void {
unreachable!()
}

fn main() {
fn foo() {
match bar() { }
let x = 2; //~ ERROR unreachable
}

fn main() {}
2 changes: 1 addition & 1 deletion src/test/ui/match/match-no-arms-unreachable-after.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: unreachable statement
--> $DIR/match-no-arms-unreachable-after.rs:8:5
--> $DIR/match-no-arms-unreachable-after.rs:12:5
|
LL | let x = 2; //~ ERROR unreachable
| ^^^^^^^^^^
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/reachable/expr_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
#![allow(dead_code)]
#![deny(unreachable_code)]

fn foo(x: !, y: usize) { }
fn foo(x: (), y: usize) {}

fn bar(x: !) { }
fn bar(x: ()) {}

fn a() {
// the `22` is unreachable:
Expand Down
41 changes: 41 additions & 0 deletions src/test/ui/uninhabited/uninhabited-function-parameter-warning.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#![deny(unreachable_code)]

enum Void {}

mod hide {
pub struct PrivatelyUninhabited(::Void);

pub struct PubliclyUninhabited(pub ::Void);
}

// Check that functions with (publicly) uninhabited parameters trigger a lint.

fn foo(a: (), b: Void) { //~ ERROR functions with parameters of uninhabited types are uncallable
a
}

fn bar(a: (), b: hide::PrivatelyUninhabited) { // ok
a
}

fn baz(a: (), b: hide::PubliclyUninhabited) {
//~^ ERROR functions with parameters of uninhabited types are uncallable
a
}

// Check that trait methods with uninhabited parameters do not trigger a lint
// (at least for now).

trait Foo {
fn foo(a: Self);

fn bar(b: Void);
}

impl Foo for Void {
fn foo(a: Void) {} // ok

fn bar(b: Void) {} // ok
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
error: functions with parameters of uninhabited types are uncallable
--> $DIR/uninhabited-function-parameter-warning.rs:13:1
|
LL | fn foo(a: (), b: Void) { //~ ERROR functions with parameters of uninhabited types are uncallable
| ^ - this parameter has an uninhabited type
| _|
| |
LL | | a
LL | | }
| |_^
|
note: lint level defined here
--> $DIR/uninhabited-function-parameter-warning.rs:1:9
|
LL | #![deny(unreachable_code)]
| ^^^^^^^^^^^^^^^^

error: functions with parameters of uninhabited types are uncallable
--> $DIR/uninhabited-function-parameter-warning.rs:21:1
|
LL | fn baz(a: (), b: hide::PubliclyUninhabited) {
| ^ - this parameter has an uninhabited type
| _|
| |
LL | | //~^ ERROR functions with parameters of uninhabited types are uncallable
LL | | a
LL | | }
| |_^

error: aborting due to 2 previous errors

12 changes: 8 additions & 4 deletions src/test/ui/unreachable/unwarned-match-on-never.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
#![feature(never_type)]

#![deny(unreachable_code)]
#![allow(dead_code)]

#![feature(never_type)]
fn never() -> ! {
unimplemented!()
}

fn foo(x: !) -> bool {
fn foo() -> bool {
// Explicit matches on the never type are unwarned.
match x {}
match never() {}
// But matches in unreachable code are warned.
match x {} //~ ERROR unreachable expression
match never() {} //~ ERROR unreachable expression
}

fn bar() {
Expand Down
12 changes: 6 additions & 6 deletions src/test/ui/unreachable/unwarned-match-on-never.stderr
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
error: unreachable expression
--> $DIR/unwarned-match-on-never.rs:10:5
--> $DIR/unwarned-match-on-never.rs:14:5
|
LL | match x {} //~ ERROR unreachable expression
| ^^^^^^^^^^
LL | match never() {} //~ ERROR unreachable expression
| ^^^^^^^^^^^^^^^^
|
note: lint level defined here
--> $DIR/unwarned-match-on-never.rs:1:9
--> $DIR/unwarned-match-on-never.rs:3:9
|
LL | #![deny(unreachable_code)]
| ^^^^^^^^^^^^^^^^

error: unreachable arm
--> $DIR/unwarned-match-on-never.rs:15:15
--> $DIR/unwarned-match-on-never.rs:19:15
|
LL | () => () //~ ERROR unreachable arm
| ^^

error: unreachable expression
--> $DIR/unwarned-match-on-never.rs:21:5
--> $DIR/unwarned-match-on-never.rs:25:5
|
LL | / match () { //~ ERROR unreachable expression
LL | | () => (),
Expand Down