Skip to content

Commit

Permalink
Unused variable suggestions on all patterns.
Browse files Browse the repository at this point in the history
This commit extends existing suggestions to prefix unused variable
bindings in match arms with an underscore so that it applies to all
patterns in a match arm.
  • Loading branch information
davidtwco committed Jan 28, 2019
1 parent c325155 commit 5f021e0
Show file tree
Hide file tree
Showing 17 changed files with 179 additions and 59 deletions.
84 changes: 50 additions & 34 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ use lint;
use errors::Applicability;
use util::nodemap::{NodeMap, HirIdMap, HirIdSet};

use std::collections::VecDeque;
use std::collections::{BTreeMap, VecDeque};
use std::{fmt, u32};
use std::io::prelude::*;
use std::io;
Expand Down Expand Up @@ -1446,7 +1446,7 @@ fn check_local<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, local: &'tcx hir::Local)
None => {
this.pat_bindings(&local.pat, |this, ln, var, sp, id| {
let span = local.pat.simple_ident().map_or(sp, |ident| ident.span);
this.warn_about_unused(span, id, ln, var);
this.warn_about_unused(vec![span], id, ln, var);
})
}
}
Expand All @@ -1455,12 +1455,29 @@ fn check_local<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, local: &'tcx hir::Local)
}

fn check_arm<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, arm: &'tcx hir::Arm) {
// only consider the first pattern; any later patterns must have
// the same bindings, and we also consider the first pattern to be
// the "authoritative" set of ids
this.arm_pats_bindings(arm.pats.first().map(|p| &**p), |this, ln, var, sp, id| {
this.warn_about_unused(sp, id, ln, var);
});
// Only consider the variable from the first pattern; any later patterns must have
// the same bindings, and we also consider the first pattern to be the "authoritative" set of
// ids. However, we should take the spans of variables with the same name from the later
// patterns so the suggestions to prefix with underscores will apply to those too.
let mut vars: BTreeMap<String, (LiveNode, Variable, HirId, Vec<Span>)> = Default::default();

for pat in &arm.pats {
this.arm_pats_bindings(Some(&*pat), |this, ln, var, sp, id| {
let name = this.ir.variable_name(var);
vars.entry(name)
.and_modify(|(.., spans)| {
spans.push(sp);
})
.or_insert_with(|| {
(ln, var, id, vec![sp])
});
});
}

for (_, (ln, var, id, spans)) in vars {
this.warn_about_unused(spans, id, ln, var);
}

intravisit::walk_arm(this, arm);
}

Expand Down Expand Up @@ -1551,7 +1568,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
let var = self.variable(hir_id, sp);
// Ignore unused self.
if ident.name != keywords::SelfLower.name() {
if !self.warn_about_unused(sp, hir_id, entry_ln, var) {
if !self.warn_about_unused(vec![sp], hir_id, entry_ln, var) {
if self.live_on_entry(entry_ln, var).is_none() {
self.report_dead_assign(hir_id, sp, var, true);
}
Expand All @@ -1563,14 +1580,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {

fn warn_about_unused_or_dead_vars_in_pat(&mut self, pat: &hir::Pat) {
self.pat_bindings(pat, |this, ln, var, sp, id| {
if !this.warn_about_unused(sp, id, ln, var) {
if !this.warn_about_unused(vec![sp], id, ln, var) {
this.warn_about_dead_assign(sp, id, ln, var);
}
})
}

fn warn_about_unused(&self,
sp: Span,
spans: Vec<Span>,
hir_id: HirId,
ln: LiveNode,
var: Variable)
Expand All @@ -1587,33 +1604,36 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.assigned_on_exit(ln, var).is_some()
};

let suggest_underscore_msg = format!("consider using `_{}` instead", name);

if is_assigned {
self.ir.tcx
.lint_hir_note(lint::builtin::UNUSED_VARIABLES, hir_id, sp,
&format!("variable `{}` is assigned to, but never used",
name),
&suggest_underscore_msg);
self.ir.tcx.lint_hir_note(
lint::builtin::UNUSED_VARIABLES,
hir_id,
spans.clone(),
&format!("variable `{}` is assigned to, but never used", name),
&format!("consider using `_{}` instead", name),
);
} else if name != "self" {
let msg = format!("unused variable: `{}`", name);
let mut err = self.ir.tcx
.struct_span_lint_hir(lint::builtin::UNUSED_VARIABLES, hir_id, sp, &msg);
let mut err = self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_VARIABLES,
hir_id,
spans.clone(),
&format!("unused variable: `{}`", name),
);

if self.ir.variable_is_shorthand(var) {
err.span_suggestion(
sp,
err.multipart_suggestion(
"try ignoring the field",
format!("{}: _", name),
Applicability::MachineApplicable,
spans.iter().map(|span| (*span, format!("{}: _", name))).collect(),
Applicability::MachineApplicable
);
} else {
err.span_suggestion_short(
sp,
&suggest_underscore_msg,
format!("_{}", name),
err.multipart_suggestion(
"consider prefixing with an underscore",
spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
Applicability::MachineApplicable,
);
}

err.emit()
}
}
Expand All @@ -1623,11 +1643,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}
}

fn warn_about_dead_assign(&self,
sp: Span,
hir_id: HirId,
ln: LiveNode,
var: Variable) {
fn warn_about_dead_assign(&self, sp: Span, hir_id: HirId, ln: LiveNode, var: Variable) {
if self.live_on_exit(ln, var).is_none() {
self.report_dead_assign(hir_id, sp, var, false);
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-17999.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: unused variable: `x`
--> $DIR/issue-17999.rs:5:13
|
LL | let x = (); //~ ERROR: unused variable: `x`
| ^ help: consider using `_x` instead
| ^ help: consider prefixing with an underscore: `_x`
|
note: lint level defined here
--> $DIR/issue-17999.rs:1:9
Expand All @@ -14,7 +14,7 @@ error: unused variable: `a`
--> $DIR/issue-17999.rs:7:13
|
LL | a => {} //~ ERROR: unused variable: `a`
| ^ help: consider using `_a` instead
| ^ help: consider prefixing with an underscore: `_a`

error: aborting due to 2 previous errors

2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-22599.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: unused variable: `a`
--> $DIR/issue-22599.rs:8:19
|
LL | v = match 0 { a => 0 }; //~ ERROR: unused variable: `a`
| ^ help: consider using `_a` instead
| ^ help: consider prefixing with an underscore: `_a`
|
note: lint level defined here
--> $DIR/issue-22599.rs:1:9
Expand Down
44 changes: 44 additions & 0 deletions src/test/ui/issues/issue-56685.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#![allow(dead_code)]
#![deny(unused_variables)]

// This test aims to check that unused variable suggestions update bindings in all
// match arms.

fn main() {
enum E {
A(i32,),
B(i32,),
}

match E::A(1) {
E::A(x) | E::B(x) => {}
//~^ ERROR unused variable: `x`
}

enum F {
A(i32, i32,),
B(i32, i32,),
C(i32, i32,),
}

let _ = match F::A(1, 2) {
F::A(x, y) | F::B(x, y) => { y },
//~^ ERROR unused variable: `x`
F::C(a, b) => { 3 }
//~^ ERROR unused variable: `a`
//~^^ ERROR unused variable: `b`
};

let _ = if let F::A(x, y) | F::B(x, y) = F::A(1, 2) {
//~^ ERROR unused variable: `x`
y
} else {
3
};

while let F::A(x, y) | F::B(x, y) = F::A(1, 2) {
//~^ ERROR unused variable: `x`
let _ = y;
break;
}
}
60 changes: 60 additions & 0 deletions src/test/ui/issues/issue-56685.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
error: unused variable: `x`
--> $DIR/issue-56685.rs:14:14
|
LL | E::A(x) | E::B(x) => {}
| ^ ^
|
note: lint level defined here
--> $DIR/issue-56685.rs:2:9
|
LL | #![deny(unused_variables)]
| ^^^^^^^^^^^^^^^^
help: consider prefixing with an underscore
|
LL | E::A(_x) | E::B(_x) => {}
| ^^ ^^

error: unused variable: `x`
--> $DIR/issue-56685.rs:25:14
|
LL | F::A(x, y) | F::B(x, y) => { y },
| ^ ^
help: consider prefixing with an underscore
|
LL | F::A(_x, y) | F::B(_x, y) => { y },
| ^^ ^^

error: unused variable: `a`
--> $DIR/issue-56685.rs:27:14
|
LL | F::C(a, b) => { 3 }
| ^ help: consider prefixing with an underscore: `_a`

error: unused variable: `b`
--> $DIR/issue-56685.rs:27:17
|
LL | F::C(a, b) => { 3 }
| ^ help: consider prefixing with an underscore: `_b`

error: unused variable: `x`
--> $DIR/issue-56685.rs:32:25
|
LL | let _ = if let F::A(x, y) | F::B(x, y) = F::A(1, 2) {
| ^ ^
help: consider prefixing with an underscore
|
LL | let _ = if let F::A(_x, y) | F::B(_x, y) = F::A(1, 2) {
| ^^ ^^

error: unused variable: `x`
--> $DIR/issue-56685.rs:39:20
|
LL | while let F::A(x, y) | F::B(x, y) = F::A(1, 2) {
| ^ ^
help: consider prefixing with an underscore
|
LL | while let F::A(_x, y) | F::B(_x, y) = F::A(1, 2) {
| ^^ ^^

error: aborting due to 6 previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ warning: unused variable: `i_think_continually`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:26:9
|
LL | let i_think_continually = 2;
| ^^^^^^^^^^^^^^^^^^^ help: consider using `_i_think_continually` instead
| ^^^^^^^^^^^^^^^^^^^ help: consider prefixing with an underscore: `_i_think_continually`
|
note: lint level defined here
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:5:9
Expand All @@ -15,19 +15,19 @@ warning: unused variable: `mut_unused_var`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:33:13
|
LL | let mut mut_unused_var = 1;
| ^^^^^^^^^^^^^^ help: consider using `_mut_unused_var` instead
| ^^^^^^^^^^^^^^ help: consider prefixing with an underscore: `_mut_unused_var`

warning: unused variable: `var`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:35:14
|
LL | let (mut var, unused_var) = (1, 2);
| ^^^ help: consider using `_var` instead
| ^^^ help: consider prefixing with an underscore: `_var`

warning: unused variable: `unused_var`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:35:19
|
LL | let (mut var, unused_var) = (1, 2);
| ^^^^^^^^^^ help: consider using `_unused_var` instead
| ^^^^^^^^^^ help: consider prefixing with an underscore: `_unused_var`

warning: unused variable: `corridors_of_light`
--> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:37:26
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/lint/lint-removed-allow.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: unused variable: `unused`
--> $DIR/lint-removed-allow.rs:8:17
|
LL | fn main() { let unused = (); } //~ ERROR unused
| ^^^^^^ help: consider using `_unused` instead
| ^^^^^^ help: consider prefixing with an underscore: `_unused`
|
note: lint level defined here
--> $DIR/lint-removed-allow.rs:7:8
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/lint/lint-removed-cmdline.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ error: unused variable: `unused`
--> $DIR/lint-removed-cmdline.rs:12:17
|
LL | fn main() { let unused = (); }
| ^^^^^^ help: consider using `_unused` instead
| ^^^^^^ help: consider prefixing with an underscore: `_unused`
|
note: lint level defined here
--> $DIR/lint-removed-cmdline.rs:11:8
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/lint/lint-removed.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ error: unused variable: `unused`
--> $DIR/lint-removed.rs:8:17
|
LL | fn main() { let unused = (); } //~ ERROR unused
| ^^^^^^ help: consider using `_unused` instead
| ^^^^^^ help: consider prefixing with an underscore: `_unused`
|
note: lint level defined here
--> $DIR/lint-removed.rs:7:8
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/lint/lint-renamed-allow.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: unused variable: `unused`
--> $DIR/lint-renamed-allow.rs:8:17
|
LL | fn main() { let unused = (); } //~ ERROR unused
| ^^^^^^ help: consider using `_unused` instead
| ^^^^^^ help: consider prefixing with an underscore: `_unused`
|
note: lint level defined here
--> $DIR/lint-renamed-allow.rs:7:8
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/lint/lint-renamed-cmdline.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ error: unused variable: `unused`
--> $DIR/lint-renamed-cmdline.rs:8:17
|
LL | fn main() { let unused = (); }
| ^^^^^^ help: consider using `_unused` instead
| ^^^^^^ help: consider prefixing with an underscore: `_unused`
|
note: lint level defined here
--> $DIR/lint-renamed-cmdline.rs:7:8
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/lint/lint-renamed.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ error: unused variable: `unused`
--> $DIR/lint-renamed.rs:4:17
|
LL | fn main() { let unused = (); } //~ ERROR unused
| ^^^^^^ help: consider using `_unused` instead
| ^^^^^^ help: consider prefixing with an underscore: `_unused`
|
note: lint level defined here
--> $DIR/lint-renamed.rs:3:8
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/lint/lint-uppercase-variables.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ warning: unused variable: `Foo`
--> $DIR/lint-uppercase-variables.rs:22:9
|
LL | Foo => {}
| ^^^ help: consider using `_Foo` instead
| ^^^ help: consider prefixing with an underscore: `_Foo`
|
note: lint level defined here
--> $DIR/lint-uppercase-variables.rs:1:9
Expand Down
Loading

0 comments on commit 5f021e0

Please sign in to comment.