mirror of https://github.com/rust-lang/rust.git
adding lint for single use lifetime names
This commit is contained in:
parent
75a02a919c
commit
e741dad629
|
@ -234,6 +234,12 @@ declare_lint! {
|
|||
"detect coercion to !"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
pub SINGLE_USE_LIFETIME,
|
||||
Allow,
|
||||
"detects single use lifetimes"
|
||||
}
|
||||
|
||||
/// Does nothing as a lint pass, but registers some `Lint`s
|
||||
/// which are used by other parts of the compiler.
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -277,7 +283,8 @@ impl LintPass for HardwiredLints {
|
|||
DEPRECATED,
|
||||
UNUSED_UNSAFE,
|
||||
UNUSED_MUT,
|
||||
COERCE_NEVER
|
||||
COERCE_NEVER,
|
||||
SINGLE_USE_LIFETIME
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ use syntax_pos::Span;
|
|||
use errors::DiagnosticBuilder;
|
||||
use util::nodemap::{DefIdMap, FxHashMap, FxHashSet, NodeMap, NodeSet};
|
||||
use std::slice;
|
||||
use rustc::lint;
|
||||
|
||||
use hir;
|
||||
use hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
|
@ -56,6 +57,13 @@ impl LifetimeDefOrigin {
|
|||
}
|
||||
}
|
||||
|
||||
// This counts the no of times a lifetime is used
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum LifetimeUseSet<'tcx> {
|
||||
One(&'tcx hir::Lifetime),
|
||||
Many,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
|
||||
pub enum Region {
|
||||
Static,
|
||||
|
@ -245,6 +253,8 @@ struct LifetimeContext<'a, 'tcx: 'a> {
|
|||
|
||||
// Cache for cross-crate per-definition object lifetime defaults.
|
||||
xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>,
|
||||
|
||||
lifetime_uses: DefIdMap<LifetimeUseSet<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -407,6 +417,7 @@ fn krate<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) -> NamedRegionMap {
|
|||
is_in_fn_syntax: false,
|
||||
labels_in_fn: vec![],
|
||||
xcrate_object_lifetime_defaults: DefIdMap(),
|
||||
lifetime_uses: DefIdMap(),
|
||||
};
|
||||
for (_, item) in &krate.items {
|
||||
visitor.visit_item(item);
|
||||
|
@ -443,8 +454,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
fn visit_item(&mut self, item: &'tcx hir::Item) {
|
||||
match item.node {
|
||||
hir::ItemFn(ref decl, _, _, _, ref generics, _) => {
|
||||
self.visit_early_late(None, decl, generics, |this| {
|
||||
intravisit::walk_item(this, item);
|
||||
self.visit_early_late(None,
|
||||
decl,
|
||||
generics,
|
||||
|this| {
|
||||
intravisit::walk_item(this, item);
|
||||
});
|
||||
}
|
||||
hir::ItemExternCrate(_)
|
||||
|
@ -498,9 +512,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) {
|
||||
match item.node {
|
||||
hir::ForeignItemFn(ref decl, _, ref generics) => {
|
||||
self.visit_early_late(None, decl, generics, |this| {
|
||||
intravisit::walk_foreign_item(this, item);
|
||||
})
|
||||
self.visit_early_late(None,
|
||||
decl,
|
||||
generics,
|
||||
|this| {
|
||||
intravisit::walk_foreign_item(this, item);
|
||||
})
|
||||
}
|
||||
hir::ForeignItemStatic(..) => {
|
||||
intravisit::walk_foreign_item(self, item);
|
||||
|
@ -1142,12 +1159,41 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
is_in_fn_syntax: self.is_in_fn_syntax,
|
||||
labels_in_fn,
|
||||
xcrate_object_lifetime_defaults,
|
||||
lifetime_uses: DefIdMap(),
|
||||
};
|
||||
debug!("entering scope {:?}", this.scope);
|
||||
f(self.scope, &mut this);
|
||||
debug!("exiting scope {:?}", this.scope);
|
||||
self.labels_in_fn = this.labels_in_fn;
|
||||
self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
|
||||
|
||||
for (def_id, lifetimeuseset) in &this.lifetime_uses {
|
||||
match lifetimeuseset {
|
||||
&LifetimeUseSet::One(_) => {
|
||||
let node_id = this.tcx.hir.as_local_node_id(*def_id).unwrap();
|
||||
debug!("node id first={:?}", node_id);
|
||||
if let hir::map::NodeLifetime(hir_lifetime) = this.tcx.hir.get(node_id) {
|
||||
let span = hir_lifetime.span;
|
||||
let id = hir_lifetime.id;
|
||||
debug!("id ={:?} span = {:?} hir_lifetime = {:?}",
|
||||
node_id,
|
||||
span,
|
||||
hir_lifetime);
|
||||
|
||||
this.tcx
|
||||
.struct_span_lint_node(lint::builtin::SINGLE_USE_LIFETIME,
|
||||
id,
|
||||
span,
|
||||
&format!("lifetime name `{}` only used once",
|
||||
hir_lifetime.name.name()))
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
debug!("Not one use lifetime");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) {
|
||||
fn resolve_lifetime_ref(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
|
||||
debug!("resolve_lifetime_ref(lifetime_ref={:?})", lifetime_ref);
|
||||
|
||||
// Walk up the scope chain, tracking the number of fn scopes
|
||||
// that we pass through, until we find a lifetime with the
|
||||
// given name or we run out of scopes.
|
||||
|
@ -1533,8 +1578,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
// Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds).
|
||||
hir::map::NodeForeignItem(_) | hir::map::NodeTy(_) | hir::map::NodeTraitRef(_) => None,
|
||||
|
||||
hir::map::NodeForeignItem(_) | hir::map::NodeTy(_) | hir::map::NodeTraitRef(_) =>
|
||||
None,
|
||||
// Everything else (only closures?) doesn't
|
||||
// actually enjoy elision in return types.
|
||||
_ => {
|
||||
|
@ -1710,7 +1755,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[hir::Lifetime]) {
|
||||
fn resolve_elided_lifetimes(&mut self, lifetime_refs: &'tcx [hir::Lifetime]) {
|
||||
if lifetime_refs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
@ -1865,7 +1910,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &hir::Lifetime) {
|
||||
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
|
||||
let mut late_depth = 0;
|
||||
let mut scope = self.scope;
|
||||
let lifetime = loop {
|
||||
|
@ -1887,7 +1932,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
|
||||
}
|
||||
|
||||
fn check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &[hir::LifetimeDef]) {
|
||||
fn check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &'tcx [hir::LifetimeDef]) {
|
||||
for i in 0..lifetimes.len() {
|
||||
let lifetime_i = &lifetimes[i];
|
||||
|
||||
|
@ -1971,7 +2016,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_lifetime_def_for_shadowing(&self, mut old_scope: ScopeRef, lifetime: &hir::Lifetime) {
|
||||
fn check_lifetime_def_for_shadowing(&self,
|
||||
mut old_scope: ScopeRef,
|
||||
lifetime: &'tcx hir::Lifetime) {
|
||||
for &(label, label_span) in &self.labels_in_fn {
|
||||
// FIXME (#24278): non-hygienic comparison
|
||||
if lifetime.name.name() == label {
|
||||
|
@ -2020,7 +2067,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn insert_lifetime(&mut self, lifetime_ref: &hir::Lifetime, def: Region) {
|
||||
fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) {
|
||||
if lifetime_ref.id == ast::DUMMY_NODE_ID {
|
||||
span_bug!(
|
||||
lifetime_ref.span,
|
||||
|
@ -2036,6 +2083,25 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
self.tcx.sess.codemap().span_to_string(lifetime_ref.span)
|
||||
);
|
||||
self.map.defs.insert(lifetime_ref.id, def);
|
||||
|
||||
match def {
|
||||
Region::LateBoundAnon(..) |
|
||||
Region::Static => {
|
||||
// These are anonymous lifetimes or lifetimes that are not declared.
|
||||
}
|
||||
|
||||
Region::Free(_, def_id) |
|
||||
Region::LateBound(_, def_id, _) |
|
||||
Region::EarlyBound(_, def_id, _) => {
|
||||
// A lifetime declared by the user.
|
||||
if !self.lifetime_uses.contains_key(&def_id) {
|
||||
self.lifetime_uses
|
||||
.insert(def_id, LifetimeUseSet::One(lifetime_ref));
|
||||
} else {
|
||||
self.lifetime_uses.insert(def_id, LifetimeUseSet::Many);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
#![deny(single_use_lifetime)]
|
||||
// FIXME(#44752) -- this scenario should not be warned
|
||||
fn deref<'x>() -> &'x u32 { //~ ERROR lifetime name `'x` only used once
|
||||
22
|
||||
}
|
||||
|
||||
fn main() { }
|
|
@ -0,0 +1,14 @@
|
|||
error: lifetime name `'x` only used once
|
||||
--> $DIR/single_use_lifetimes-2.rs:12:10
|
||||
|
|
||||
12 | fn deref<'x>() -> &'x u32 { //~ ERROR lifetime name `'x` only used once
|
||||
| ^^
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/single_use_lifetimes-2.rs:10:9
|
||||
|
|
||||
10 | #![deny(single_use_lifetime)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
#![deny(single_use_lifetime)]
|
||||
struct Foo<'x> { //~ ERROR lifetime name `'x` only used once
|
||||
x: &'x u32 // no warning!
|
||||
}
|
||||
|
||||
// Once #44524 is fixed, this should issue a warning.
|
||||
impl<'y> Foo<'y> { //~ ERROR lifetime name `'y` only used once
|
||||
fn method() { }
|
||||
}
|
||||
|
||||
fn main() { }
|
|
@ -0,0 +1,20 @@
|
|||
error: lifetime name `'x` only used once
|
||||
--> $DIR/single_use_lifetimes-3.rs:11:12
|
||||
|
|
||||
11 | struct Foo<'x> { //~ ERROR lifetime name `'x` only used once
|
||||
| ^^
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/single_use_lifetimes-3.rs:10:9
|
||||
|
|
||||
10 | #![deny(single_use_lifetime)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lifetime name `'y` only used once
|
||||
--> $DIR/single_use_lifetimes-3.rs:16:6
|
||||
|
|
||||
16 | impl<'y> Foo<'y> { //~ ERROR lifetime name `'y` only used once
|
||||
| ^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
#![deny(single_use_lifetime)]
|
||||
// Neither should issue a warning, as explicit lifetimes are mandatory in this case
|
||||
struct Foo<'x> { //~ ERROR lifetime name `'x` only used once
|
||||
x: &'x u32
|
||||
}
|
||||
|
||||
enum Bar<'x> { //~ ERROR lifetime name `'x` only used once
|
||||
Variant(&'x u32)
|
||||
}
|
||||
|
||||
fn main() { }
|
|
@ -0,0 +1,20 @@
|
|||
error: lifetime name `'x` only used once
|
||||
--> $DIR/single_use_lifetimes-4.rs:12:12
|
||||
|
|
||||
12 | struct Foo<'x> { //~ ERROR lifetime name `'x` only used once
|
||||
| ^^
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/single_use_lifetimes-4.rs:10:9
|
||||
|
|
||||
10 | #![deny(single_use_lifetime)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lifetime name `'x` only used once
|
||||
--> $DIR/single_use_lifetimes-4.rs:16:10
|
||||
|
|
||||
16 | enum Bar<'x> { //~ ERROR lifetime name `'x` only used once
|
||||
| ^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
#![deny(single_use_lifetime)]
|
||||
// Should not issue a warning, as explicit lifetimes are mandatory in this case:
|
||||
trait Foo<'x> { //~ ERROR lifetime name `'x` only used once
|
||||
fn foo(&self, arg: &'x u32);
|
||||
}
|
||||
|
||||
fn main() { }
|
|
@ -0,0 +1,14 @@
|
|||
error: lifetime name `'x` only used once
|
||||
--> $DIR/single_use_lifetimes-5.rs:12:11
|
||||
|
|
||||
12 | trait Foo<'x> { //~ ERROR lifetime name `'x` only used once
|
||||
| ^^
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/single_use_lifetimes-5.rs:10:9
|
||||
|
|
||||
10 | #![deny(single_use_lifetime)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
#![deny(single_use_lifetime)]
|
||||
|
||||
fn deref<'x>(v: &'x u32) -> u32 { //~ ERROR lifetime name `'x` only used once
|
||||
*v
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,14 @@
|
|||
error: lifetime name `'x` only used once
|
||||
--> $DIR/single_use_lifetimes.rs:12:10
|
||||
|
|
||||
12 | fn deref<'x>(v: &'x u32) -> u32 { //~ ERROR lifetime name `'x` only used once
|
||||
| ^^
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/single_use_lifetimes.rs:10:9
|
||||
|
|
||||
10 | #![deny(single_use_lifetime)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
Loading…
Reference in New Issue