Skip to content

Commit edbd7d2

Browse files
committed
Auto merge of #46441 - gaurikholkar:single_lifetimes, r=nikomatsakis
Lint against single-use lifetime names This is a fix for #44752 TO-DO - [x] change lint message - [x] add ui tests r? @nikomatsakis
2 parents b39c4bc + e741dad commit edbd7d2

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(_)
@@ -499,9 +513,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
499513
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) {
500514
match item.node {
501515
hir::ForeignItemFn(ref decl, _, ref generics) => {
502-
self.visit_early_late(None, decl, generics, |this| {
503-
intravisit::walk_foreign_item(this, item);
504-
})
516+
self.visit_early_late(None,
517+
decl,
518+
generics,
519+
|this| {
520+
intravisit::walk_foreign_item(this, item);
521+
})
505522
}
506523
hir::ForeignItemStatic(..) => {
507524
intravisit::walk_foreign_item(self, item);
@@ -1190,12 +1207,41 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
11901207
is_in_fn_syntax: self.is_in_fn_syntax,
11911208
labels_in_fn,
11921209
xcrate_object_lifetime_defaults,
1210+
lifetime_uses: DefIdMap(),
11931211
};
11941212
debug!("entering scope {:?}", this.scope);
11951213
f(self.scope, &mut this);
11961214
debug!("exiting scope {:?}", this.scope);
11971215
self.labels_in_fn = this.labels_in_fn;
11981216
self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
1217+
1218+
for (def_id, lifetimeuseset) in &this.lifetime_uses {
1219+
match lifetimeuseset {
1220+
&LifetimeUseSet::One(_) => {
1221+
let node_id = this.tcx.hir.as_local_node_id(*def_id).unwrap();
1222+
debug!("node id first={:?}", node_id);
1223+
if let hir::map::NodeLifetime(hir_lifetime) = this.tcx.hir.get(node_id) {
1224+
let span = hir_lifetime.span;
1225+
let id = hir_lifetime.id;
1226+
debug!("id ={:?} span = {:?} hir_lifetime = {:?}",
1227+
node_id,
1228+
span,
1229+
hir_lifetime);
1230+
1231+
this.tcx
1232+
.struct_span_lint_node(lint::builtin::SINGLE_USE_LIFETIME,
1233+
id,
1234+
span,
1235+
&format!("lifetime name `{}` only used once",
1236+
hir_lifetime.name.name()))
1237+
.emit();
1238+
}
1239+
}
1240+
_ => {
1241+
debug!("Not one use lifetime");
1242+
}
1243+
}
1244+
}
11991245
}
12001246

12011247
/// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
@@ -1287,9 +1333,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
12871333
}
12881334
}
12891335

1290-
fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) {
1336+
fn resolve_lifetime_ref(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
12911337
debug!("resolve_lifetime_ref(lifetime_ref={:?})", lifetime_ref);
1292-
12931338
// Walk up the scope chain, tracking the number of fn scopes
12941339
// that we pass through, until we find a lifetime with the
12951340
// given name or we run out of scopes.
@@ -1581,8 +1626,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
15811626
}
15821627

15831628
// Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds).
1584-
hir::map::NodeForeignItem(_) | hir::map::NodeTy(_) | hir::map::NodeTraitRef(_) => None,
1585-
1629+
hir::map::NodeForeignItem(_) | hir::map::NodeTy(_) | hir::map::NodeTraitRef(_) =>
1630+
None,
15861631
// Everything else (only closures?) doesn't
15871632
// actually enjoy elision in return types.
15881633
_ => {
@@ -1758,7 +1803,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
17581803
}
17591804
}
17601805

1761-
fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[hir::Lifetime]) {
1806+
fn resolve_elided_lifetimes(&mut self, lifetime_refs: &'tcx [hir::Lifetime]) {
17621807
if lifetime_refs.is_empty() {
17631808
return;
17641809
}
@@ -1913,7 +1958,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
19131958
}
19141959
}
19151960

1916-
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &hir::Lifetime) {
1961+
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
19171962
let mut late_depth = 0;
19181963
let mut scope = self.scope;
19191964
let lifetime = loop {
@@ -1935,7 +1980,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
19351980
self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
19361981
}
19371982

1938-
fn check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &[hir::LifetimeDef]) {
1983+
fn check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &'tcx [hir::LifetimeDef]) {
19391984
for i in 0..lifetimes.len() {
19401985
let lifetime_i = &lifetimes[i];
19411986

@@ -2019,7 +2064,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20192064
}
20202065
}
20212066

2022-
fn check_lifetime_def_for_shadowing(&self, mut old_scope: ScopeRef, lifetime: &hir::Lifetime) {
2067+
fn check_lifetime_def_for_shadowing(&self,
2068+
mut old_scope: ScopeRef,
2069+
lifetime: &'tcx hir::Lifetime) {
20232070
for &(label, label_span) in &self.labels_in_fn {
20242071
// FIXME (#24278): non-hygienic comparison
20252072
if lifetime.name.name() == label {
@@ -2068,7 +2115,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20682115
}
20692116
}
20702117

2071-
fn insert_lifetime(&mut self, lifetime_ref: &hir::Lifetime, def: Region) {
2118+
fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) {
20722119
if lifetime_ref.id == ast::DUMMY_NODE_ID {
20732120
span_bug!(
20742121
lifetime_ref.span,
@@ -2084,6 +2131,25 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
20842131
self.tcx.sess.codemap().span_to_string(lifetime_ref.span)
20852132
);
20862133
self.map.defs.insert(lifetime_ref.id, def);
2134+
2135+
match def {
2136+
Region::LateBoundAnon(..) |
2137+
Region::Static => {
2138+
// These are anonymous lifetimes or lifetimes that are not declared.
2139+
}
2140+
2141+
Region::Free(_, def_id) |
2142+
Region::LateBound(_, def_id, _) |
2143+
Region::EarlyBound(_, def_id, _) => {
2144+
// A lifetime declared by the user.
2145+
if !self.lifetime_uses.contains_key(&def_id) {
2146+
self.lifetime_uses
2147+
.insert(def_id, LifetimeUseSet::One(lifetime_ref));
2148+
} else {
2149+
self.lifetime_uses.insert(def_id, LifetimeUseSet::Many);
2150+
}
2151+
}
2152+
}
20872153
}
20882154
}
20892155

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)