2015-09-03 14:42:17 +00:00
use reexport ::* ;
2015-08-16 06:54:43 +00:00
use rustc ::lint ::* ;
2016-04-07 15:46:48 +00:00
use rustc ::hir ::def ::Def ;
use rustc ::hir ::* ;
use rustc ::hir ::intravisit ::{ Visitor , walk_ty , walk_ty_param_bound , walk_fn_decl , walk_generics } ;
2015-12-06 21:36:22 +00:00
use std ::collections ::{ HashSet , HashMap } ;
2016-02-24 16:38:57 +00:00
use syntax ::codemap ::Span ;
2015-08-16 06:54:43 +00:00
use utils ::{ in_external_macro , span_lint } ;
2015-08-12 11:16:09 +00:00
2016-02-05 23:41:54 +00:00
/// **What it does:** This lint checks for lifetime annotations which can be removed by relying on lifetime elision.
2015-12-11 00:22:27 +00:00
///
/// **Why is this bad?** The additional lifetimes make the code look more complicated, while there is nothing out of the ordinary going on. Removing them leads to more readable code.
///
/// **Known problems:** Potential false negatives: we bail out if the function has a `where` clause where lifetimes are mentioned.
///
/// **Example:** `fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 { x }`
2016-02-05 23:13:29 +00:00
declare_lint! {
pub NEEDLESS_LIFETIMES ,
Warn ,
" using explicit lifetimes for references in function arguments when elision rules \
would allow omitting them "
}
2015-08-12 11:16:09 +00:00
2016-02-05 23:41:54 +00:00
/// **What it does:** This lint checks for lifetimes in generics that are never used anywhere else.
2015-12-14 21:16:56 +00:00
///
/// **Why is this bad?** The additional lifetimes make the code look more complicated, while there is nothing out of the ordinary going on. Removing them leads to more readable code.
///
/// **Known problems:** None
///
/// **Example:** `fn unused_lifetime<'a>(x: u8) { .. }`
2016-02-05 23:13:29 +00:00
declare_lint! {
pub UNUSED_LIFETIMES ,
Warn ,
" unused lifetimes in function definitions "
}
2015-12-06 21:36:22 +00:00
2015-08-12 11:16:09 +00:00
#[ derive(Copy,Clone) ]
pub struct LifetimePass ;
impl LintPass for LifetimePass {
fn get_lints ( & self ) -> LintArray {
2015-12-06 21:36:22 +00:00
lint_array! ( NEEDLESS_LIFETIMES , UNUSED_LIFETIMES )
2015-08-12 11:16:09 +00:00
}
2015-09-19 02:53:04 +00:00
}
2015-08-12 11:16:09 +00:00
2015-09-19 02:53:04 +00:00
impl LateLintPass for LifetimePass {
fn check_item ( & mut self , cx : & LateContext , item : & Item ) {
2015-08-13 15:24:47 +00:00
if let ItemFn ( ref decl , _ , _ , _ , ref generics , _ ) = item . node {
2016-05-17 21:25:20 +00:00
check_fn_inner ( cx , decl , generics , item . span ) ;
2015-08-12 11:16:09 +00:00
}
2015-08-13 15:24:47 +00:00
}
2015-09-19 02:53:04 +00:00
fn check_impl_item ( & mut self , cx : & LateContext , item : & ImplItem ) {
2015-11-19 14:51:30 +00:00
if let ImplItemKind ::Method ( ref sig , _ ) = item . node {
2016-05-17 21:25:20 +00:00
check_fn_inner ( cx , & sig . decl , & sig . generics , item . span ) ;
2015-08-13 15:24:47 +00:00
}
}
2015-09-19 02:53:04 +00:00
fn check_trait_item ( & mut self , cx : & LateContext , item : & TraitItem ) {
2015-08-13 15:24:47 +00:00
if let MethodTraitItem ( ref sig , _ ) = item . node {
2016-05-17 21:25:20 +00:00
check_fn_inner ( cx , & sig . decl , & sig . generics , item . span ) ;
2015-08-12 11:16:09 +00:00
}
}
}
2015-08-12 18:13:14 +00:00
/// The lifetime of a &-reference.
2015-08-12 11:16:09 +00:00
#[ derive(PartialEq, Eq, Hash, Debug) ]
enum RefLt {
Unnamed ,
Static ,
Named ( Name ) ,
}
2016-01-29 21:18:14 +00:00
2016-06-21 11:49:08 +00:00
fn bound_lifetimes ( bound : & TyParamBound ) -> HirVec < & Lifetime > {
2016-01-29 21:18:14 +00:00
if let TraitTyParamBound ( ref trait_ref , _ ) = * bound {
2016-06-21 11:49:08 +00:00
trait_ref . trait_ref
. path
. segments
. last ( )
. expect ( " a path must have at least one segment " )
. parameters
. lifetimes ( )
2016-01-29 21:18:14 +00:00
} else {
2016-06-21 11:49:08 +00:00
HirVec ::new ( )
2016-01-29 21:18:14 +00:00
}
}
2015-08-12 11:16:09 +00:00
2016-05-17 21:25:20 +00:00
fn check_fn_inner ( cx : & LateContext , decl : & FnDecl , generics : & Generics , span : Span ) {
2015-11-10 23:12:45 +00:00
if in_external_macro ( cx , span ) | | has_where_lifetimes ( cx , & generics . where_clause ) {
2015-08-13 15:24:47 +00:00
return ;
}
2016-01-29 21:18:14 +00:00
2016-01-30 12:48:39 +00:00
let bounds_lts = generics . ty_params
. iter ( )
2016-06-21 11:49:08 +00:00
. flat_map ( | typ | typ . bounds . iter ( ) . flat_map ( bound_lifetimes ) ) ;
2016-01-29 21:18:14 +00:00
2016-05-17 21:25:20 +00:00
if could_use_elision ( cx , decl , & generics . lifetimes , bounds_lts ) {
2016-01-04 04:26:12 +00:00
span_lint ( cx ,
NEEDLESS_LIFETIMES ,
span ,
2015-08-13 15:24:47 +00:00
" explicit lifetimes given in parameter types where they could be elided " ) ;
}
2016-05-17 21:25:20 +00:00
report_extra_lifetimes ( cx , decl , generics ) ;
2015-08-13 15:24:47 +00:00
}
2016-05-17 21:25:20 +00:00
fn could_use_elision < ' a , T : Iterator < Item = & ' a Lifetime > > ( cx : & LateContext , func : & FnDecl ,
2016-01-30 12:48:39 +00:00
named_lts : & [ LifetimeDef ] , bounds_lts : T )
-> bool {
2015-08-12 11:16:09 +00:00
// There are two scenarios where elision works:
// * no output references, all input references have different LT
// * output references, exactly one input reference with same LT
2015-08-13 15:24:47 +00:00
// All lifetimes must be unnamed, 'static or defined without bounds on the
// level of the current item.
// check named LTs
2015-08-31 09:11:51 +00:00
let allowed_lts = allowed_lts_from ( named_lts ) ;
2015-08-12 11:16:09 +00:00
2015-08-12 18:13:14 +00:00
// these will collect all the lifetimes for references in arg/return types
2015-11-10 23:12:45 +00:00
let mut input_visitor = RefVisitor ::new ( cx ) ;
let mut output_visitor = RefVisitor ::new ( cx ) ;
2015-08-12 11:16:09 +00:00
2015-08-12 18:13:14 +00:00
// extract lifetimes in input argument types
for arg in & func . inputs {
2015-11-10 23:12:45 +00:00
input_visitor . visit_ty ( & arg . ty ) ;
2015-08-12 18:13:14 +00:00
}
// extract lifetimes in output type
2015-08-12 11:16:09 +00:00
if let Return ( ref ty ) = func . output {
2015-11-10 23:12:45 +00:00
output_visitor . visit_ty ( ty ) ;
2015-08-12 11:16:09 +00:00
}
2016-01-29 21:18:14 +00:00
let input_lts = lts_from_bounds ( input_visitor . into_vec ( ) , bounds_lts ) ;
2015-08-12 11:16:09 +00:00
let output_lts = output_visitor . into_vec ( ) ;
2015-08-13 15:24:47 +00:00
// check for lifetimes from higher scopes
for lt in input_lts . iter ( ) . chain ( output_lts . iter ( ) ) {
if ! allowed_lts . contains ( lt ) {
return false ;
}
}
2015-08-12 11:16:09 +00:00
// no input lifetimes? easy case!
if input_lts . is_empty ( ) {
2015-11-18 11:35:18 +00:00
false
2015-08-12 11:16:09 +00:00
} else if output_lts . is_empty ( ) {
// no output lifetimes, check distinctness of input lifetimes
2015-08-15 07:36:02 +00:00
// only unnamed and static, ok
2016-01-29 21:18:14 +00:00
if input_lts . iter ( ) . all ( | lt | * lt = = RefLt ::Unnamed | | * lt = = RefLt ::Static ) {
2015-08-12 11:16:09 +00:00
return false ;
}
// we have no output reference, so we only need all distinct lifetimes
2015-11-18 11:35:18 +00:00
input_lts . len ( ) = = unique_lifetimes ( & input_lts )
2015-08-12 11:16:09 +00:00
} else {
// we have output references, so we need one input reference,
// and all output lifetimes must be the same
if unique_lifetimes ( & output_lts ) > 1 {
return false ;
}
if input_lts . len ( ) = = 1 {
match ( & input_lts [ 0 ] , & output_lts [ 0 ] ) {
2016-01-29 21:18:14 +00:00
( & RefLt ::Named ( n1 ) , & RefLt ::Named ( n2 ) ) if n1 = = n2 = > true ,
( & RefLt ::Named ( _ ) , & RefLt ::Unnamed ) = > true ,
2016-01-04 04:26:12 +00:00
_ = > false , // already elided, different named lifetimes
// or something static going on
2015-08-12 11:16:09 +00:00
}
2015-11-18 11:35:18 +00:00
} else {
false
2015-08-12 11:16:09 +00:00
}
}
}
2015-08-31 09:11:51 +00:00
fn allowed_lts_from ( named_lts : & [ LifetimeDef ] ) -> HashSet < RefLt > {
let mut allowed_lts = HashSet ::new ( ) ;
for lt in named_lts {
if lt . bounds . is_empty ( ) {
2016-01-29 21:18:14 +00:00
allowed_lts . insert ( RefLt ::Named ( lt . lifetime . name ) ) ;
2015-08-31 09:11:51 +00:00
}
}
2016-01-29 21:18:14 +00:00
allowed_lts . insert ( RefLt ::Unnamed ) ;
allowed_lts . insert ( RefLt ::Static ) ;
2015-08-31 09:11:51 +00:00
allowed_lts
}
2016-01-30 12:48:39 +00:00
fn lts_from_bounds < ' a , T : Iterator < Item = & ' a Lifetime > > ( mut vec : Vec < RefLt > , bounds_lts : T ) -> Vec < RefLt > {
2016-01-29 21:18:14 +00:00
for lt in bounds_lts {
if lt . name . as_str ( ) ! = " 'static " {
vec . push ( RefLt ::Named ( lt . name ) ) ;
}
}
vec
}
2015-08-12 18:13:14 +00:00
/// Number of unique lifetimes in the given vector.
2015-08-13 14:28:11 +00:00
fn unique_lifetimes ( lts : & [ RefLt ] ) -> usize {
2015-08-12 18:13:14 +00:00
lts . iter ( ) . collect ::< HashSet < _ > > ( ) . len ( )
2015-08-12 11:16:09 +00:00
}
2016-01-29 21:18:14 +00:00
/// A visitor usable for `rustc_front::visit::walk_ty()`.
2015-11-10 23:12:45 +00:00
struct RefVisitor < ' v , ' t : ' v > {
2016-01-29 21:18:14 +00:00
cx : & ' v LateContext < ' v , ' t > ,
2016-01-04 04:26:12 +00:00
lts : Vec < RefLt > ,
2015-11-10 23:12:45 +00:00
}
2016-01-04 04:26:12 +00:00
impl < ' v , ' t > RefVisitor < ' v , ' t > {
2015-11-10 23:12:45 +00:00
fn new ( cx : & ' v LateContext < ' v , ' t > ) -> RefVisitor < ' v , ' t > {
2016-01-04 04:26:12 +00:00
RefVisitor {
cx : cx ,
lts : Vec ::new ( ) ,
}
2015-11-10 23:12:45 +00:00
}
2015-08-12 11:16:09 +00:00
2015-08-12 18:13:14 +00:00
fn record ( & mut self , lifetime : & Option < Lifetime > ) {
2015-11-24 17:44:40 +00:00
if let Some ( ref lt ) = * lifetime {
2015-09-28 05:04:06 +00:00
if lt . name . as_str ( ) = = " 'static " {
2016-01-29 21:18:14 +00:00
self . lts . push ( RefLt ::Static ) ;
2015-08-12 11:16:09 +00:00
} else {
2016-01-29 21:18:14 +00:00
self . lts . push ( RefLt ::Named ( lt . name ) ) ;
2015-08-12 11:16:09 +00:00
}
} else {
2016-01-29 21:18:14 +00:00
self . lts . push ( RefLt ::Unnamed ) ;
2015-08-12 11:16:09 +00:00
}
}
2015-08-12 18:13:14 +00:00
fn into_vec ( self ) -> Vec < RefLt > {
2015-11-10 23:12:45 +00:00
self . lts
}
fn collect_anonymous_lifetimes ( & mut self , path : & Path , ty : & Ty ) {
let last_path_segment = path . segments . last ( ) . map ( | s | & s . parameters ) ;
if let Some ( & AngleBracketedParameters ( ref params ) ) = last_path_segment {
if params . lifetimes . is_empty ( ) {
2015-12-04 14:56:25 +00:00
if let Some ( def ) = self . cx . tcx . def_map . borrow ( ) . get ( & ty . id ) . map ( | r | r . full_def ( ) ) {
match def {
2016-04-14 18:14:03 +00:00
Def ::TyAlias ( def_id ) |
Def ::Struct ( def_id ) = > {
2015-12-04 14:56:25 +00:00
let type_scheme = self . cx . tcx . lookup_item_type ( def_id ) ;
for _ in type_scheme . generics . regions . as_slice ( ) {
2015-11-10 23:12:45 +00:00
self . record ( & None ) ;
}
2016-01-04 04:26:12 +00:00
}
2016-01-22 12:24:44 +00:00
Def ::Trait ( def_id ) = > {
2015-12-04 14:56:25 +00:00
let trait_def = self . cx . tcx . trait_defs . borrow ( ) [ & def_id ] ;
for _ in & trait_def . generics . regions {
self . record ( & None ) ;
}
2016-01-04 04:26:12 +00:00
}
2016-04-14 18:14:03 +00:00
_ = > ( ) ,
2015-11-17 04:39:42 +00:00
}
2015-11-10 23:12:45 +00:00
}
}
}
2015-08-12 18:13:14 +00:00
}
}
2015-11-10 23:12:45 +00:00
impl < ' v , ' t > Visitor < ' v > for RefVisitor < ' v , ' t > {
2015-08-13 04:43:25 +00:00
// for lifetimes as parameters of generics
2015-09-30 14:24:41 +00:00
fn visit_lifetime ( & mut self , lifetime : & ' v Lifetime ) {
2015-08-13 04:43:25 +00:00
self . record ( & Some ( * lifetime ) ) ;
}
2015-09-30 14:40:54 +00:00
fn visit_ty ( & mut self , ty : & ' v Ty ) {
2015-11-10 23:12:45 +00:00
match ty . node {
TyRptr ( None , _ ) = > {
self . record ( & None ) ;
2015-11-17 04:39:42 +00:00
}
2015-11-10 23:12:45 +00:00
TyPath ( _ , ref path ) = > {
self . collect_anonymous_lifetimes ( path , ty ) ;
2015-11-17 04:39:42 +00:00
}
2016-04-14 18:14:03 +00:00
_ = > ( ) ,
2015-09-30 14:40:54 +00:00
}
walk_ty ( self , ty ) ;
}
2015-08-12 11:16:09 +00:00
}
2015-08-30 07:57:52 +00:00
/// Are any lifetimes mentioned in the `where` clause? If yes, we don't try to
/// reason about elision.
2015-11-10 23:12:45 +00:00
fn has_where_lifetimes ( cx : & LateContext , where_clause : & WhereClause ) -> bool {
2015-08-30 07:57:52 +00:00
for predicate in & where_clause . predicates {
match * predicate {
WherePredicate ::RegionPredicate ( .. ) = > return true ,
WherePredicate ::BoundPredicate ( ref pred ) = > {
2015-08-31 09:11:51 +00:00
// a predicate like F: Trait or F: for<'a> Trait<'a>
2015-11-10 23:12:45 +00:00
let mut visitor = RefVisitor ::new ( cx ) ;
2015-08-31 09:11:51 +00:00
// walk the type F, it may not contain LT refs
walk_ty ( & mut visitor , & pred . bounded_ty ) ;
2016-01-04 04:26:12 +00:00
if ! visitor . lts . is_empty ( ) {
return true ;
}
2015-08-31 09:11:51 +00:00
// if the bounds define new lifetimes, they are fine to occur
let allowed_lts = allowed_lts_from ( & pred . bound_lifetimes ) ;
// now walk the bounds
for bound in pred . bounds . iter ( ) {
walk_ty_param_bound ( & mut visitor , bound ) ;
}
// and check that all lifetimes are allowed
for lt in visitor . into_vec ( ) {
if ! allowed_lts . contains ( & lt ) {
return true ;
}
}
2015-08-30 07:57:52 +00:00
}
WherePredicate ::EqPredicate ( ref pred ) = > {
2015-11-10 23:12:45 +00:00
let mut visitor = RefVisitor ::new ( cx ) ;
2015-08-31 09:11:51 +00:00
walk_ty ( & mut visitor , & pred . ty ) ;
2016-01-04 04:26:12 +00:00
if ! visitor . lts . is_empty ( ) {
return true ;
}
2015-08-30 07:57:52 +00:00
}
}
}
2015-08-31 09:11:51 +00:00
false
2015-08-30 07:57:52 +00:00
}
2015-12-06 21:36:22 +00:00
struct LifetimeChecker ( HashMap < Name , Span > ) ;
impl < ' v > Visitor < ' v > for LifetimeChecker {
// for lifetimes as parameters of generics
fn visit_lifetime ( & mut self , lifetime : & ' v Lifetime ) {
self . 0. remove ( & lifetime . name ) ;
}
2015-12-10 16:44:12 +00:00
fn visit_lifetime_def ( & mut self , _ : & ' v LifetimeDef ) {
// don't actually visit `<'a>` or `<'a: 'b>`
// we've already visited the `'a` declarations and
// don't want to spuriously remove them
// `'b` in `'a: 'b` is useless unless used elsewhere in
// a non-lifetime bound
}
2015-12-06 21:36:22 +00:00
}
2016-05-17 21:25:20 +00:00
fn report_extra_lifetimes ( cx : & LateContext , func : & FnDecl , generics : & Generics ) {
2016-01-04 04:26:12 +00:00
let hs = generics . lifetimes
. iter ( )
2015-12-10 16:44:12 +00:00
. map ( | lt | ( lt . lifetime . name , lt . lifetime . span ) )
. collect ( ) ;
2015-12-06 21:36:22 +00:00
let mut checker = LifetimeChecker ( hs ) ;
2016-01-14 18:27:24 +00:00
2015-12-10 16:44:12 +00:00
walk_generics ( & mut checker , generics ) ;
2015-12-06 21:36:22 +00:00
walk_fn_decl ( & mut checker , func ) ;
2016-01-14 18:27:24 +00:00
2016-01-19 20:10:00 +00:00
for & v in checker . 0. values ( ) {
2016-01-04 04:26:12 +00:00
span_lint ( cx , UNUSED_LIFETIMES , v , " this lifetime isn't used in the function definition " ) ;
2015-12-06 21:36:22 +00:00
}
}