Skip to content

Commit e741dad

Browse files
nikomatsakisgaurikholkar
authored andcommitted
adding lint for single use lifetime names
1 parent 75a02a9 commit e741dad

12 files changed

+258
-15
lines changed

src/librustc/lint/builtin.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ declare_lint! {
234234
"detect coercion to !"
235235
}
236236

237+
declare_lint! {
238+
pub SINGLE_USE_LIFETIME,
239+
Allow,
240+
"detects single use lifetimes"
241+
}
242+
237243
/// Does nothing as a lint pass, but registers some `Lint`s
238244
/// which are used by other parts of the compiler.
239245
#[derive(Copy, Clone)]
@@ -277,7 +283,8 @@ impl LintPass for HardwiredLints {
277283
DEPRECATED,
278284
UNUSED_UNSAFE,
279285
UNUSED_MUT,
280-
COERCE_NEVER
286+
COERCE_NEVER,
287+
SINGLE_USE_LIFETIME
281288
)
282289
}
283290
}

src/librustc/middle/resolve_lifetime.rs

+80-14
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use syntax_pos::Span;
3131
use errors::DiagnosticBuilder;
3232
use util::nodemap::{DefIdMap, FxHashMap, FxHashSet, NodeMap, NodeSet};
3333
use std::slice;
34+
use rustc::lint;
3435

3536
use hir;
3637
use hir::intravisit::{self, NestedVisitorMap, Visitor};
@@ -56,6 +57,13 @@ impl LifetimeDefOrigin {
5657
}
5758
}
5859

60+
// This counts the no of times a lifetime is used
61+
#[derive(Clone, Copy, Debug)]
62+
pub enum LifetimeUseSet<'tcx> {
63+
One(&'tcx hir::Lifetime),
64+
Many,
65+
}
66+
5967
#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
6068
pub enum Region {
6169
Static,
@@ -245,6 +253,8 @@ struct LifetimeContext<'a, 'tcx: 'a> {
245253

246254
// Cache for cross-crate per-definition object lifetime defaults.
247255
xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>,
256+
257+
lifetime_uses: DefIdMap<LifetimeUseSet<'tcx>>,
248258
}
249259

250260
#[derive(Debug)]
@@ -407,6 +417,7 @@ fn krate<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) -> NamedRegionMap {
407417
is_in_fn_syntax: false,
408418
labels_in_fn: vec![],
409419
xcrate_object_lifetime_defaults: DefIdMap(),
420+
lifetime_uses: DefIdMap(),
410421
};
411422
for (_, item) in &krate.items {
412423
visitor.visit_item(item);
@@ -443,8 +454,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
443454
fn visit_item(&mut self, item: &'tcx hir::Item) {
444455
match item.node {
445456
hir::ItemFn(ref decl, _, _, _, ref generics, _) => {
446-
self.visit_early_late(None, decl, generics, |this| {
447-
intravisit::walk_item(this, item);
457+
self.visit_early_late(None,
458+
decl,
459+
generics,
460+
|this| {
461+
intravisit::walk_item(this, item);
448462
});
449463
}
450464
hir::ItemExternCrate(_)
@@ -498,9 +512,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
498512
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) {
499513
match item.node {
500514
hir::ForeignItemFn(ref decl, _, ref generics) => {
501-
self.visit_early_late(None, decl, generics, |this| {
502-
intravisit::walk_foreign_item(this, item);
503-
})
515+
self.visit_early_late(None,
516+
decl,
517+
generics,
518+
|this| {
519+
intravisit::walk_foreign_item(this, item);
520+
})
504521
}
505522
hir::ForeignItemStatic(..) => {
506523
intravisit::walk_foreign_item(self, item);
@@ -1142,12 +1159,41 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
11421159
is_in_fn_syntax: self.is_in_fn_syntax,
11431160
labels_in_fn,
11441161
xcrate_object_lifetime_defaults,
1162+
lifetime_uses: DefIdMap(),
11451163
};
11461164
debug!("entering scope {:?}", this.scope);
11471165
f(self.scope, &mut this);
11481166
debug!("exiting scope {:?}", this.scope);
11491167
self.labels_in_fn = this.labels_in_fn;
11501168
self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
1169+
1170+
for (def_id, lifetimeuseset) in &this.lifetime_uses {
1171+
match lifetimeuseset {
1172+
&LifetimeUseSet::One(_) => {
1173+
let node_id = this.tcx.hir.as_local_node_id(*def_id).unwrap();
1174+
debug!("node id first={:?}", node_id);
1175+
if let hir::map::NodeLifetime(hir_lifetime) = this.tcx.hir.get(node_id) {
1176+
let span = hir_lifetime.span;
1177+
let id = hir_lifetime.id;
1178+
debug!("id ={:?} span = {:?} hir_lifetime = {:?}",
1179+
node_id,
1180+
span,
1181+
hir_lifetime);
1182+
1183+
this.tcx
1184+
.struct_span_lint_node(lint::builtin::SINGLE_USE_LIFETIME,
1185+
id,
1186+
span,
1187+
&format!("lifetime name `{}` only used once",
1188+
hir_lifetime.name.name()))
1189+
.emit();
1190+
}
1191+
}
1192+
_ => {
1193+
debug!("Not one use lifetime");
1194+
}
1195+
}
1196+
}
11511197
}
11521198

11531199
/// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
@@ -1239,9 +1285,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
12391285
}
12401286
}
12411287

1242-
fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) {
1288+
fn resolve_lifetime_ref(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
12431289
debug!("resolve_lifetime_ref(lifetime_ref={:?})", lifetime_ref);
1244-
12451290
// Walk up the scope chain, tracking the number of fn scopes
12461291
// that we pass through, until we find a lifetime with the
12471292
// given name or we run out of scopes.
@@ -1533,8 +1578,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
15331578
}
15341579

15351580
// Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds).
1536-
hir::map::NodeForeignItem(_) | hir::map::NodeTy(_) | hir::map::NodeTraitRef(_) => None,
1537-
1581+
hir::map::NodeForeignItem(_) | hir::map::NodeTy(_) | hir::map::NodeTraitRef(_) =>
1582+
None,
15381583
// Everything else (only closures?) doesn't
15391584
// actually enjoy elision in return types.
15401585
_ => {
@@ -1710,7 +1755,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
17101755
}
17111756
}
17121757

1713-
fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[hir::Lifetime]) {
1758+
fn resolve_elided_lifetimes(&mut self, lifetime_refs: &'tcx [hir::Lifetime]) {
17141759
if lifetime_refs.is_empty() {
17151760
return;
17161761
}
@@ -1865,7 +1910,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
18651910
}
18661911
}
18671912

1868-
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &hir::Lifetime) {
1913+
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
18691914
let mut late_depth = 0;
18701915
let mut scope = self.scope;
18711916
let lifetime = loop {
@@ -1887,7 +1932,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
18871932
self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
18881933
}
18891934

1890-
fn check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &[hir::LifetimeDef]) {
1935+
fn check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &'tcx [hir::LifetimeDef]) {
18911936
for i in 0..lifetimes.len() {
18921937
let lifetime_i = &lifetimes[i];
18931938

@@ -1971,7 +2016,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
19712016
}
19722017
}
19732018

1974-
fn check_lifetime_def_for_shadowing(&self, mut old_scope: ScopeRef, lifetime: &hir::Lifetime) {
2019+
fn check_lifetime_def_for_shadowing(&self,
2020+
mut old_scope: ScopeRef,
2021+
lifetime: &'tcx hir::Lifetime) {
19752022
for &(label, label_span) in &self.labels_in_fn {
19762023
// FIXME (#24278): non-hygienic comparison
19772024
if lifetime.name.name() == label {
@@ -2020,7 +2067,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20202067
}
20212068
}
20222069

2023-
fn insert_lifetime(&mut self, lifetime_ref: &hir::Lifetime, def: Region) {
2070+
fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) {
20242071
if lifetime_ref.id == ast::DUMMY_NODE_ID {
20252072
span_bug!(
20262073
lifetime_ref.span,
@@ -2036,6 +2083,25 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20362083
self.tcx.sess.codemap().span_to_string(lifetime_ref.span)
20372084
);
20382085
self.map.defs.insert(lifetime_ref.id, def);
2086+
2087+
match def {
2088+
Region::LateBoundAnon(..) |
2089+
Region::Static => {
2090+
// These are anonymous lifetimes or lifetimes that are not declared.
2091+
}
2092+
2093+
Region::Free(_, def_id) |
2094+
Region::LateBound(_, def_id, _) |
2095+
Region::EarlyBound(_, def_id, _) => {
2096+
// A lifetime declared by the user.
2097+
if !self.lifetime_uses.contains_key(&def_id) {
2098+
self.lifetime_uses
2099+
.insert(def_id, LifetimeUseSet::One(lifetime_ref));
2100+
} else {
2101+
self.lifetime_uses.insert(def_id, LifetimeUseSet::Many);
2102+
}
2103+
}
2104+
}
20392105
}
20402106
}
20412107

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2017 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+
#![deny(single_use_lifetime)]
11+
// FIXME(#44752) -- this scenario should not be warned
12+
fn deref<'x>() -> &'x u32 { //~ ERROR lifetime name `'x` only used once
13+
22
14+
}
15+
16+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: lifetime name `'x` only used once
2+
--> $DIR/single_use_lifetimes-2.rs:12:10
3+
|
4+
12 | fn deref<'x>() -> &'x u32 { //~ ERROR lifetime name `'x` only used once
5+
| ^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/single_use_lifetimes-2.rs:10:9
9+
|
10+
10 | #![deny(single_use_lifetime)]
11+
| ^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2017 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+
#![deny(single_use_lifetime)]
11+
struct Foo<'x> { //~ ERROR lifetime name `'x` only used once
12+
x: &'x u32 // no warning!
13+
}
14+
15+
// Once #44524 is fixed, this should issue a warning.
16+
impl<'y> Foo<'y> { //~ ERROR lifetime name `'y` only used once
17+
fn method() { }
18+
}
19+
20+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: lifetime name `'x` only used once
2+
--> $DIR/single_use_lifetimes-3.rs:11:12
3+
|
4+
11 | struct Foo<'x> { //~ ERROR lifetime name `'x` only used once
5+
| ^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/single_use_lifetimes-3.rs:10:9
9+
|
10+
10 | #![deny(single_use_lifetime)]
11+
| ^^^^^^^^^^^^^^^^^^^
12+
13+
error: lifetime name `'y` only used once
14+
--> $DIR/single_use_lifetimes-3.rs:16:6
15+
|
16+
16 | impl<'y> Foo<'y> { //~ ERROR lifetime name `'y` only used once
17+
| ^^
18+
19+
error: aborting due to 2 previous errors
20+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2017 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+
#![deny(single_use_lifetime)]
11+
// Neither should issue a warning, as explicit lifetimes are mandatory in this case
12+
struct Foo<'x> { //~ ERROR lifetime name `'x` only used once
13+
x: &'x u32
14+
}
15+
16+
enum Bar<'x> { //~ ERROR lifetime name `'x` only used once
17+
Variant(&'x u32)
18+
}
19+
20+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: lifetime name `'x` only used once
2+
--> $DIR/single_use_lifetimes-4.rs:12:12
3+
|
4+
12 | struct Foo<'x> { //~ ERROR lifetime name `'x` only used once
5+
| ^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/single_use_lifetimes-4.rs:10:9
9+
|
10+
10 | #![deny(single_use_lifetime)]
11+
| ^^^^^^^^^^^^^^^^^^^
12+
13+
error: lifetime name `'x` only used once
14+
--> $DIR/single_use_lifetimes-4.rs:16:10
15+
|
16+
16 | enum Bar<'x> { //~ ERROR lifetime name `'x` only used once
17+
| ^^
18+
19+
error: aborting due to 2 previous errors
20+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2017 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+
#![deny(single_use_lifetime)]
11+
// Should not issue a warning, as explicit lifetimes are mandatory in this case:
12+
trait Foo<'x> { //~ ERROR lifetime name `'x` only used once
13+
fn foo(&self, arg: &'x u32);
14+
}
15+
16+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: lifetime name `'x` only used once
2+
--> $DIR/single_use_lifetimes-5.rs:12:11
3+
|
4+
12 | trait Foo<'x> { //~ ERROR lifetime name `'x` only used once
5+
| ^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/single_use_lifetimes-5.rs:10:9
9+
|
10+
10 | #![deny(single_use_lifetime)]
11+
| ^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2017 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+
#![deny(single_use_lifetime)]
11+
12+
fn deref<'x>(v: &'x u32) -> u32 { //~ ERROR lifetime name `'x` only used once
13+
*v
14+
}
15+
16+
fn main() {}

0 commit comments

Comments
 (0)