Skip to content

Commit b4fc8c5

Browse files
committed
Warn unused type parameters
1 parent 7ad7232 commit b4fc8c5

19 files changed

+133
-16
lines changed

src/libcore/num/dec2flt/algorithm.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ fn power_of_ten(e: i16) -> Fp {
3535
// precision of the computation is determined on a per-operation basis.
3636
#[cfg(any(not(target_arch="x86"), target_feature="sse2"))]
3737
mod fpu_precision {
38-
pub fn set_precision<T>() { }
38+
pub fn set_precision<_T>() { }
3939
}
4040

4141
// On x86, the x87 FPU is used for float operations if the SSE/SSE2 extensions are not available.

src/librustc/lint/builtin.rs

+7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ declare_lint! {
2828
"imports that are never used"
2929
}
3030

31+
declare_lint! {
32+
pub UNUSED_TYPE_PARAMETERS,
33+
Warn,
34+
"type parameters that are never used"
35+
}
36+
3137
declare_lint! {
3238
pub UNUSED_EXTERN_CRATES,
3339
Allow,
@@ -226,6 +232,7 @@ impl LintPass for HardwiredLints {
226232
fn get_lints(&self) -> LintArray {
227233
lint_array!(
228234
UNUSED_IMPORTS,
235+
UNUSED_TYPE_PARAMETERS,
229236
UNUSED_EXTERN_CRATES,
230237
UNUSED_QUALIFICATIONS,
231238
UNKNOWN_LINTS,

src/librustc_lint/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
158158
add_lint_group!(sess,
159159
"unused",
160160
UNUSED_IMPORTS,
161+
UNUSED_TYPE_PARAMETERS,
161162
UNUSED_VARIABLES,
162163
UNUSED_ASSIGNMENTS,
163164
DEAD_CODE,

src/librustc_resolve/lib.rs

+69-9
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ use rustc::ty;
4949
use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
5050
use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet};
5151

52+
use syntax::abi::Abi;
5253
use syntax::ext::hygiene::{Mark, SyntaxContext};
5354
use syntax::ast::{self, FloatTy};
5455
use syntax::ast::{CRATE_NODE_ID, Name, NodeId, Ident, SpannedIdent, IntTy, UintTy};
@@ -559,7 +560,14 @@ impl<T> ::std::ops::IndexMut<Namespace> for PerNS<T> {
559560

560561
impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
561562
fn visit_item(&mut self, item: &'tcx Item) {
563+
let is_lang_item = item.attrs.iter().any(|attr| attr.name() == "lang");
564+
if is_lang_item {
565+
self.check_unused_type_parameters = false;
566+
}
567+
562568
self.resolve_item(item);
569+
570+
self.check_unused_type_parameters = true;
563571
}
564572
fn visit_arm(&mut self, arm: &'tcx Arm) {
565573
self.resolve_arm(arm);
@@ -722,15 +730,35 @@ enum RibKind<'a> {
722730
struct Rib<'a> {
723731
bindings: FxHashMap<Ident, Def>,
724732
kind: RibKind<'a>,
733+
sources: FxHashMap<Ident, (NodeId, Span)>,
734+
used_names: RefCell<FxHashSet<Ident>>,
725735
}
726736

727737
impl<'a> Rib<'a> {
728738
fn new(kind: RibKind<'a>) -> Rib<'a> {
729739
Rib {
730740
bindings: FxHashMap(),
731741
kind: kind,
742+
sources: FxHashMap(),
743+
used_names: RefCell::new(FxHashSet()),
732744
}
733745
}
746+
747+
fn insert(&mut self, ident: Ident, def: Def) {
748+
self.bindings.insert(ident, def);
749+
}
750+
751+
fn insert_with_source(&mut self, ident: Ident, id: NodeId, span: Span, def: Def) {
752+
self.bindings.insert(ident, def);
753+
self.sources.insert(ident, (id, span));
754+
}
755+
756+
fn get(&self, ident: Ident) -> Option<Def> {
757+
self.bindings.get(&ident).map(|def| {
758+
self.used_names.borrow_mut().insert(ident);
759+
def.clone()
760+
})
761+
}
734762
}
735763

736764
/// A definition along with the index of the rib it was found on
@@ -1114,6 +1142,10 @@ pub struct Resolver<'a> {
11141142

11151143
// Avoid duplicated errors for "name already defined".
11161144
name_already_seen: FxHashMap<Name, Span>,
1145+
1146+
// Whether unused type parameters should be warned. Default true,
1147+
// false for intrinsics and lang items.
1148+
check_unused_type_parameters: bool,
11171149
}
11181150

11191151
pub struct ResolverArenas<'a> {
@@ -1289,6 +1321,7 @@ impl<'a> Resolver<'a> {
12891321
macro_exports: Vec::new(),
12901322
invocations: invocations,
12911323
name_already_seen: FxHashMap(),
1324+
check_unused_type_parameters: true,
12921325
}
12931326
}
12941327

@@ -1394,7 +1427,7 @@ impl<'a> Resolver<'a> {
13941427

13951428
// Walk backwards up the ribs in scope.
13961429
for i in (0 .. self.ribs[ns].len()).rev() {
1397-
if let Some(def) = self.ribs[ns][i].bindings.get(&ident).cloned() {
1430+
if let Some(def) = self.ribs[ns][i].get(ident) {
13981431
// The ident resolves to a type parameter or local variable.
13991432
return Some(LexicalScopeBinding::Def(if let Some(span) = record_used {
14001433
self.adjust_local_def(LocalDef { ribs: Some((ns, i)), def: def }, span)
@@ -1502,7 +1535,7 @@ impl<'a> Resolver<'a> {
15021535
return None;
15031536
}
15041537
}
1505-
let result = rib.bindings.get(&ident).cloned();
1538+
let result = rib.get(ident);
15061539
if result.is_some() {
15071540
return result;
15081541
}
@@ -1577,10 +1610,22 @@ impl<'a> Resolver<'a> {
15771610
});
15781611
}
15791612

1580-
ItemKind::Mod(_) | ItemKind::ForeignMod(_) => {
1613+
ItemKind::Mod(_) => {
1614+
self.with_scope(item.id, |this| {
1615+
visit::walk_item(this, item);
1616+
});
1617+
}
1618+
1619+
ItemKind::ForeignMod(ref m) => {
1620+
if m.abi == Abi::RustIntrinsic {
1621+
self.check_unused_type_parameters = false;
1622+
}
1623+
15811624
self.with_scope(item.id, |this| {
15821625
visit::walk_item(this, item);
15831626
});
1627+
1628+
self.check_unused_type_parameters = true;
15841629
}
15851630

15861631
ItemKind::Const(..) | ItemKind::Static(..) => {
@@ -1637,7 +1682,7 @@ impl<'a> Resolver<'a> {
16371682
{
16381683
match type_parameters {
16391684
HasTypeParameters(generics, rib_kind) => {
1640-
let mut function_type_rib = Rib::new(rib_kind);
1685+
let mut type_rib = Rib::new(rib_kind);
16411686
let mut seen_bindings = FxHashMap();
16421687
for type_parameter in &generics.ty_params {
16431688
let name = type_parameter.ident.name;
@@ -1653,12 +1698,13 @@ impl<'a> Resolver<'a> {
16531698
seen_bindings.entry(name).or_insert(type_parameter.span);
16541699

16551700
// plain insert (no renaming)
1701+
let ident = Ident::with_empty_ctxt(name);
16561702
let def_id = self.definitions.local_def_id(type_parameter.id);
16571703
let def = Def::TyParam(def_id);
1658-
function_type_rib.bindings.insert(Ident::with_empty_ctxt(name), def);
1704+
type_rib.insert_with_source(ident, type_parameter.id, type_parameter.span, def);
16591705
self.record_def(type_parameter.id, PathResolution::new(def));
16601706
}
1661-
self.ribs[TypeNS].push(function_type_rib);
1707+
self.ribs[TypeNS].push(type_rib);
16621708
}
16631709

16641710
NoTypeParameters => {
@@ -1669,6 +1715,20 @@ impl<'a> Resolver<'a> {
16691715
f(self);
16701716

16711717
if let HasTypeParameters(..) = type_parameters {
1718+
if self.check_unused_type_parameters {
1719+
let type_rib = self.ribs[TypeNS].last().unwrap();
1720+
for (&name, &(id, span)) in &type_rib.sources {
1721+
if name.to_string().starts_with('_') {
1722+
continue;
1723+
}
1724+
if !type_rib.used_names.borrow().contains(&name) {
1725+
self.session.add_lint(lint::builtin::UNUSED_TYPE_PARAMETERS,
1726+
id, span,
1727+
"unused type parameter".to_string());
1728+
}
1729+
}
1730+
}
1731+
16721732
self.ribs[TypeNS].pop();
16731733
}
16741734
}
@@ -1784,7 +1844,7 @@ impl<'a> Resolver<'a> {
17841844
let mut self_type_rib = Rib::new(NormalRibKind);
17851845

17861846
// plain insert (no renaming, types are not currently hygienic....)
1787-
self_type_rib.bindings.insert(keywords::SelfType.ident(), self_def);
1847+
self_type_rib.insert(keywords::SelfType.ident(), self_def);
17881848
self.ribs[TypeNS].push(self_type_rib);
17891849
f(self);
17901850
self.ribs[TypeNS].pop();
@@ -2098,7 +2158,7 @@ impl<'a> Resolver<'a> {
20982158
// A completely fresh binding, add to the lists if it's valid.
20992159
if ident.node.name != keywords::Invalid.name() {
21002160
bindings.insert(ident.node, outer_pat_id);
2101-
self.ribs[ValueNS].last_mut().unwrap().bindings.insert(ident.node, def);
2161+
self.ribs[ValueNS].last_mut().unwrap().insert(ident.node, def);
21022162
}
21032163
}
21042164
}
@@ -2605,7 +2665,7 @@ impl<'a> Resolver<'a> {
26052665
if let Some(label) = label {
26062666
let def = Def::Label(id);
26072667
self.with_label_rib(|this| {
2608-
this.label_ribs.last_mut().unwrap().bindings.insert(label.node, def);
2668+
this.label_ribs.last_mut().unwrap().insert(label.node, def);
26092669
this.visit_block(block);
26102670
});
26112671
} else {

src/libstd/io/error.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ impl error::Error for Error {
545545
}
546546

547547
fn _assert_error_is_sync_send() {
548-
fn _is_sync_send<T: Sync+Send>() {}
548+
fn _is_sync_send<_T: Sync+Send>() {}
549549
_is_sync_send::<Error>();
550550
}
551551

src/libstd/thread/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,7 @@ impl<T> IntoInner<imp::Thread> for JoinHandle<T> {
789789
}
790790

791791
fn _assert_sync_and_send() {
792-
fn _assert_both<T: Send + Sync>() {}
792+
fn _assert_both<_T: Send + Sync>() {}
793793
_assert_both::<JoinHandle<()>>();
794794
_assert_both::<Thread>();
795795
}

src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// revisions: angle paren ok elision
1212

1313
#![allow(dead_code)]
14+
#![allow(unused_type_parameters)]
1415
#![feature(rustc_attrs)]
1516
#![feature(unboxed_closures)]
1617
#![deny(hr_lifetime_in_assoc_type)]

src/test/compile-fail/associated-types/cache/wasm-issue-32330.rs

-4
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616

1717
use std::str::Chars;
1818

19-
pub trait HasOutput<Ch, Str> {
20-
type Output;
21-
}
22-
2319
#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Debug)]
2420
pub enum Token<'a> {
2521
Begin(&'a str)

src/test/compile-fail/coherence-projection-ok-orphan.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#![feature(rustc_attrs)]
1212
#![allow(dead_code)]
13+
#![allow(unused_type_parameters)]
1314

1415
// Here we do not get a coherence conflict because `Baz: Iterator`
1516
// does not hold and (due to the orphan rules), we can rely on that.

src/test/compile-fail/coherence-projection-ok.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
#![feature(rustc_attrs)]
12+
#![allow(unused_type_parameters)]
1213

1314
pub trait Foo<P> {}
1415

src/test/compile-fail/generic-no-mangle.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![allow(unused_type_parameters)]
1112
#![deny(no_mangle_generic_items)]
1213

1314
#[no_mangle]

src/test/compile-fail/issue-29857.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111

1212
#![feature(rustc_attrs)]
13+
#![allow(unused_type_parameters)]
1314

1415
use std::marker::PhantomData;
1516

src/test/compile-fail/lint-dead-code-1.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
#![no_std]
12+
#![allow(unused_type_parameters)]
1213
#![allow(unused_variables)]
1314
#![allow(non_camel_case_types)]
1415
#![allow(non_upper_case_globals)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![deny(unused_type_parameters)]
12+
13+
pub fn f<T>() {} //~ ERROR unused type parameter
14+
15+
pub struct S;
16+
impl S {
17+
pub fn m<T>() {} //~ ERROR unused type parameter
18+
}
19+
20+
trait Trait {
21+
fn m<T>() {} //~ ERROR unused type parameter
22+
}
23+
24+
// Now test allow attributes
25+
26+
pub mod allow {
27+
#[allow(unused_type_parameters)]
28+
pub fn f<T>() {}
29+
30+
pub struct S;
31+
impl S {
32+
#[allow(unused_type_parameters)]
33+
pub fn m<T>() {}
34+
}
35+
36+
trait Trait {
37+
#[allow(unused_type_parameters)]
38+
fn m<T>() {}
39+
}
40+
}
41+
42+
fn main() {}

src/test/compile-fail/object-safety-phantom-fn.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#![feature(rustc_attrs)]
1414
#![allow(dead_code)]
15+
#![allow(unused_type_parameters)]
1516

1617
trait Baz {
1718
}

src/test/compile-fail/regions-implied-bounds-projection-gap-2.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#![feature(rustc_attrs)]
1616
#![allow(dead_code)]
17+
#![allow(unused_type_parameters)]
1718
#![allow(unused_variables)]
1819

1920
trait Trait1<'x> {

src/test/compile-fail/regions-implied-bounds-projection-gap-3.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#![feature(rustc_attrs)]
1616
#![allow(dead_code)]
17+
#![allow(unused_type_parameters)]
1718
#![allow(unused_variables)]
1819

1920
trait Trait1<'x> {

src/test/compile-fail/regions-implied-bounds-projection-gap-4.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#![feature(rustc_attrs)]
1616
#![allow(dead_code)]
17+
#![allow(unused_type_parameters)]
1718
#![allow(unused_variables)]
1819

1920
trait Trait1<'x> {

src/test/compile-fail/regions-outlives-projection-hrtype.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#![feature(rustc_attrs)]
1919
#![allow(dead_code)]
20+
#![allow(unused_type_parameters)]
2021

2122
trait TheTrait {
2223
type TheType;

0 commit comments

Comments
 (0)