From 9a4c0a1c72c7a1e94c4ef80460c6fe2cb85e9f28 Mon Sep 17 00:00:00 2001
From: Oliver Schneider <git-no-reply-9879165716479413131@oli-obk.de>
Date: Tue, 11 Apr 2017 14:09:58 +0200
Subject: [PATCH] Don't lint about unused lifetimes if the lifetimes are used
 in the body of the function

---
 clippy_lints/src/lifetimes.rs | 47 +++++++++++++++++++++++++++++------
 tests/ui/lifetimes.rs         | 24 ++++++++++++++++++
 tests/ui/lifetimes.stderr     | 25 ++++++++++++++++++-
 3 files changed, 87 insertions(+), 9 deletions(-)

diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs
index 1de7e9e47f7..6bfcc475841 100644
--- a/clippy_lints/src/lifetimes.rs
+++ b/clippy_lints/src/lifetimes.rs
@@ -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
+    }
+}
\ No newline at end of file
diff --git a/tests/ui/lifetimes.rs b/tests/ui/lifetimes.rs
index f953c0ad3d2..620ceafdba2 100644
--- a/tests/ui/lifetimes.rs
+++ b/tests/ui/lifetimes.rs
@@ -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() {
 }
diff --git a/tests/ui/lifetimes.stderr b/tests/ui/lifetimes.stderr
index 6d213ad45f2..aa12547bb20 100644
--- a/tests/ui/lifetimes.stderr
+++ b/tests/ui/lifetimes.stderr
@@ -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