Don't lint about unused lifetimes if the lifetimes are used in the body of the function

This commit is contained in:
Oliver Schneider 2017-04-11 14:09:58 +02:00
parent 5ae57c55fd
commit 9a4c0a1c72
3 changed files with 87 additions and 9 deletions

View file

@ -6,6 +6,7 @@ use rustc::hir::intravisit::{Visitor, walk_ty, walk_ty_param_bound, walk_fn_decl
use std::collections::{HashSet, HashMap};
use syntax::codemap::Span;
use utils::{in_external_macro, span_lint, last_path_segment};
use syntax::symbol::keywords;
/// **What it does:** Checks for lifetime annotations which can be removed by
/// relying on lifetime elision.
@ -58,20 +59,24 @@ impl LintPass for LifetimePass {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LifetimePass {
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
if let ItemFn(ref decl, _, _, _, ref generics, _) = item.node {
check_fn_inner(cx, decl, generics, item.span);
if let ItemFn(ref decl, _, _, _, ref generics, id) = item.node {
check_fn_inner(cx, decl, Some(id), generics, item.span);
}
}
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
if let ImplItemKind::Method(ref sig, _) = item.node {
check_fn_inner(cx, &sig.decl, &sig.generics, item.span);
if let ImplItemKind::Method(ref sig, id) = item.node {
check_fn_inner(cx, &sig.decl, Some(id), &sig.generics, item.span);
}
}
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
if let TraitItemKind::Method(ref sig, _) = item.node {
check_fn_inner(cx, &sig.decl, &sig.generics, item.span);
if let TraitItemKind::Method(ref sig, ref body) = item.node {
let body = match *body {
TraitMethod::Required(_) => None,
TraitMethod::Provided(id) => Some(id),
};
check_fn_inner(cx, &sig.decl, body, &sig.generics, item.span);
}
}
}
@ -98,7 +103,7 @@ fn bound_lifetimes(bound: &TyParamBound) -> HirVec<&Lifetime> {
}
}
fn check_fn_inner<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, generics: &'tcx Generics, span: Span) {
fn check_fn_inner<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, body: Option<BodyId>, generics: &'tcx Generics, span: Span) {
if in_external_macro(cx, span) || has_where_lifetimes(cx, &generics.where_clause) {
return;
}
@ -107,7 +112,7 @@ fn check_fn_inner<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, gene
.iter()
.flat_map(|typ| typ.bounds.iter().flat_map(bound_lifetimes));
if could_use_elision(cx, decl, &generics.lifetimes, bounds_lts) {
if could_use_elision(cx, decl, body, &generics.lifetimes, bounds_lts) {
span_lint(cx,
NEEDLESS_LIFETIMES,
span,
@ -119,6 +124,7 @@ fn check_fn_inner<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, gene
fn could_use_elision<'a, 'tcx: 'a, T: Iterator<Item = &'tcx Lifetime>>(
cx: &LateContext<'a, 'tcx>,
func: &'tcx FnDecl,
body: Option<BodyId>,
named_lts: &'tcx [LifetimeDef],
bounds_lts: T
) -> bool {
@ -147,6 +153,14 @@ fn could_use_elision<'a, 'tcx: 'a, T: Iterator<Item = &'tcx Lifetime>>(
let input_lts = lts_from_bounds(input_visitor.into_vec(), bounds_lts);
let output_lts = output_visitor.into_vec();
if let Some(body_id) = body {
let mut checker = BodyLifetimeChecker { lifetimes_used_in_body: false };
checker.visit_expr(&cx.tcx.hir.body(body_id).value);
if checker.lifetimes_used_in_body {
return false;
}
}
// check for lifetimes from higher scopes
for lt in input_lts.iter().chain(output_lts.iter()) {
if !allowed_lts.contains(lt) {
@ -384,3 +398,20 @@ fn report_extra_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, func: &'tcx
span_lint(cx, UNUSED_LIFETIMES, v, "this lifetime isn't used in the function definition");
}
}
struct BodyLifetimeChecker {
lifetimes_used_in_body: bool,
}
impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
// for lifetimes as parameters of generics
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
if lifetime.name != keywords::Invalid.name() && lifetime.name != "'static" {
self.lifetimes_used_in_body = true;
}
}
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
}

View file

@ -128,5 +128,29 @@ fn elided_input_named_output<'a>(_arg: &str) -> &'a str { unimplemented!() }
fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { unimplemented!() }
fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) { unimplemented!() }
// don't warn on these, see #292
fn trait_bound_bug<'a, T: WithLifetime<'a>>() { unimplemented!() }
// #740
struct Test {
vec: Vec<usize>,
}
impl Test {
fn iter<'a>(&'a self) -> Box<Iterator<Item = usize> + 'a> {
unimplemented!()
}
}
trait LintContext<'a> {}
fn f<'a, T: LintContext<'a>>(cx: &T) {}
fn test<'a>(x: &'a [u8]) -> u8 {
let y: &'a u8 = &x[5];
*y
}
fn main() {
}

View file

@ -91,5 +91,28 @@ error: explicit lifetimes given in parameter types where they could be elided
128 | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { unimplemented!() }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 14 previous errors
error: explicit lifetimes given in parameter types where they could be elided
--> $DIR/lifetimes.rs:132:1
|
132 | fn trait_bound_bug<'a, T: WithLifetime<'a>>() { unimplemented!() }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided
--> $DIR/lifetimes.rs:140:5
|
140 | fn iter<'a>(&'a self) -> Box<Iterator<Item = usize> + 'a> {
| _____^ starting here...
141 | | unimplemented!()
142 | | }
| |_____^ ...ending here
warning: unused variable: `cx`
--> $DIR/lifetimes.rs:148:30
|
148 | fn f<'a, T: LintContext<'a>>(cx: &T) {}
| ^^
|
= note: #[warn(unused_variables)] on by default
error: aborting due to 16 previous errors