Skip to content

Commit 7f89fb7

Browse files
authored
Merge pull request #1672 from Manishearth/lifetime_sugg
Fix various false positives around needless_lifetime
2 parents 5ae57c5 + 5f85ea8 commit 7f89fb7

File tree

4 files changed

+111
-40
lines changed

4 files changed

+111
-40
lines changed

clippy_lints/src/format.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ pub fn get_argument_fmtstr_parts<'a, 'b>(cx: &LateContext<'a, 'b>, expr: &'a Exp
9898
}
9999

100100
/// Checks if the expressions matches
101-
/// ```rust
102-
/// { static __STATIC_FMTSTR: s = &["a", "b", c]; __STATIC_FMTSTR }
101+
/// ```rust, ignore
102+
/// { static __STATIC_FMTSTR: &'static[&'static str] = &["a", "b", c]; __STATIC_FMTSTR }
103103
/// ```
104104
fn check_static_str(cx: &LateContext, expr: &Expr) -> bool {
105105
if let Some(expr) = get_argument_fmtstr_parts(cx, expr) {

clippy_lints/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#![feature(conservative_impl_trait)]
1212

1313
#![allow(indexing_slicing, shadow_reuse, unknown_lints, missing_docs_in_private_items)]
14-
#![allow(needless_lifetimes)]
1514

1615
extern crate syntax;
1716
extern crate syntax_pos;

clippy_lints/src/lifetimes.rs

Lines changed: 85 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc::hir::intravisit::{Visitor, walk_ty, walk_ty_param_bound, walk_fn_decl
66
use std::collections::{HashSet, HashMap};
77
use syntax::codemap::Span;
88
use utils::{in_external_macro, span_lint, last_path_segment};
9+
use syntax::symbol::keywords;
910

1011
/// **What it does:** Checks for lifetime annotations which can be removed by
1112
/// relying on lifetime elision.
@@ -58,20 +59,24 @@ impl LintPass for LifetimePass {
5859

5960
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LifetimePass {
6061
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
61-
if let ItemFn(ref decl, _, _, _, ref generics, _) = item.node {
62-
check_fn_inner(cx, decl, generics, item.span);
62+
if let ItemFn(ref decl, _, _, _, ref generics, id) = item.node {
63+
check_fn_inner(cx, decl, Some(id), generics, item.span);
6364
}
6465
}
6566

6667
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
67-
if let ImplItemKind::Method(ref sig, _) = item.node {
68-
check_fn_inner(cx, &sig.decl, &sig.generics, item.span);
68+
if let ImplItemKind::Method(ref sig, id) = item.node {
69+
check_fn_inner(cx, &sig.decl, Some(id), &sig.generics, item.span);
6970
}
7071
}
7172

7273
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
73-
if let TraitItemKind::Method(ref sig, _) = item.node {
74-
check_fn_inner(cx, &sig.decl, &sig.generics, item.span);
74+
if let TraitItemKind::Method(ref sig, ref body) = item.node {
75+
let body = match *body {
76+
TraitMethod::Required(_) => None,
77+
TraitMethod::Provided(id) => Some(id),
78+
};
79+
check_fn_inner(cx, &sig.decl, body, &sig.generics, item.span);
7580
}
7681
}
7782
}
@@ -84,30 +89,32 @@ enum RefLt {
8489
Named(Name),
8590
}
8691

87-
fn bound_lifetimes(bound: &TyParamBound) -> HirVec<&Lifetime> {
88-
if let TraitTyParamBound(ref trait_ref, _) = *bound {
89-
trait_ref.trait_ref
90-
.path
91-
.segments
92-
.last()
93-
.expect("a path must have at least one segment")
94-
.parameters
95-
.lifetimes()
96-
} else {
97-
HirVec::new()
98-
}
99-
}
100-
101-
fn check_fn_inner<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, generics: &'tcx Generics, span: Span) {
92+
fn check_fn_inner<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, body: Option<BodyId>, generics: &'tcx Generics, span: Span) {
10293
if in_external_macro(cx, span) || has_where_lifetimes(cx, &generics.where_clause) {
10394
return;
10495
}
10596

106-
let bounds_lts = generics.ty_params
107-
.iter()
108-
.flat_map(|typ| typ.bounds.iter().flat_map(bound_lifetimes));
109-
110-
if could_use_elision(cx, decl, &generics.lifetimes, bounds_lts) {
97+
let mut bounds_lts = Vec::new();
98+
for typ in &generics.ty_params {
99+
for bound in &typ.bounds {
100+
if let TraitTyParamBound(ref trait_ref, _) = *bound {
101+
let bounds = trait_ref.trait_ref
102+
.path
103+
.segments
104+
.last()
105+
.expect("a path must have at least one segment")
106+
.parameters
107+
.lifetimes();
108+
for bound in bounds {
109+
if bound.name != "'static" && !bound.is_elided() {
110+
return;
111+
}
112+
bounds_lts.push(bound);
113+
}
114+
}
115+
}
116+
}
117+
if could_use_elision(cx, decl, body, &generics.lifetimes, bounds_lts) {
111118
span_lint(cx,
112119
NEEDLESS_LIFETIMES,
113120
span,
@@ -116,11 +123,12 @@ fn check_fn_inner<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, gene
116123
report_extra_lifetimes(cx, decl, generics);
117124
}
118125

119-
fn could_use_elision<'a, 'tcx: 'a, T: Iterator<Item = &'tcx Lifetime>>(
126+
fn could_use_elision<'a, 'tcx: 'a>(
120127
cx: &LateContext<'a, 'tcx>,
121128
func: &'tcx FnDecl,
129+
body: Option<BodyId>,
122130
named_lts: &'tcx [LifetimeDef],
123-
bounds_lts: T
131+
bounds_lts: Vec<&'tcx Lifetime>,
124132
) -> bool {
125133
// There are two scenarios where elision works:
126134
// * no output references, all input references have different LT
@@ -144,8 +152,22 @@ fn could_use_elision<'a, 'tcx: 'a, T: Iterator<Item = &'tcx Lifetime>>(
144152
output_visitor.visit_ty(ty);
145153
}
146154

147-
let input_lts = lts_from_bounds(input_visitor.into_vec(), bounds_lts);
148-
let output_lts = output_visitor.into_vec();
155+
let input_lts = match input_visitor.into_vec() {
156+
Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()),
157+
None => return false,
158+
};
159+
let output_lts = match output_visitor.into_vec() {
160+
Some(val) => val,
161+
None => return false,
162+
};
163+
164+
if let Some(body_id) = body {
165+
let mut checker = BodyLifetimeChecker { lifetimes_used_in_body: false };
166+
checker.visit_expr(&cx.tcx.hir.body(body_id).value);
167+
if checker.lifetimes_used_in_body {
168+
return false;
169+
}
170+
}
149171

150172
// check for lifetimes from higher scopes
151173
for lt in input_lts.iter().chain(output_lts.iter()) {
@@ -216,13 +238,15 @@ fn unique_lifetimes(lts: &[RefLt]) -> usize {
216238
struct RefVisitor<'a, 'tcx: 'a> {
217239
cx: &'a LateContext<'a, 'tcx>,
218240
lts: Vec<RefLt>,
241+
abort: bool,
219242
}
220243

221244
impl<'v, 't> RefVisitor<'v, 't> {
222245
fn new(cx: &'v LateContext<'v, 't>) -> RefVisitor<'v, 't> {
223246
RefVisitor {
224247
cx: cx,
225248
lts: Vec::new(),
249+
abort: false,
226250
}
227251
}
228252

@@ -240,8 +264,12 @@ impl<'v, 't> RefVisitor<'v, 't> {
240264
}
241265
}
242266

243-
fn into_vec(self) -> Vec<RefLt> {
244-
self.lts
267+
fn into_vec(self) -> Option<Vec<RefLt>> {
268+
if self.abort {
269+
None
270+
} else {
271+
Some(self.lts)
272+
}
245273
}
246274

247275
fn collect_anonymous_lifetimes(&mut self, qpath: &QPath, ty: &Ty) {
@@ -292,7 +320,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
292320
},
293321
TyTraitObject(ref bounds, ref lt) => {
294322
if !lt.is_elided() {
295-
self.record(&Some(*lt));
323+
self.abort = true;
296324
}
297325
for bound in bounds {
298326
self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
@@ -329,10 +357,13 @@ fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: &
329357
walk_ty_param_bound(&mut visitor, bound);
330358
}
331359
// and check that all lifetimes are allowed
332-
for lt in visitor.into_vec() {
333-
if !allowed_lts.contains(&lt) {
334-
return true;
335-
}
360+
match visitor.into_vec() {
361+
None => return false,
362+
Some(lts) => for lt in lts {
363+
if !allowed_lts.contains(&lt) {
364+
return true;
365+
}
366+
},
336367
}
337368
},
338369
WherePredicate::EqPredicate(ref pred) => {
@@ -384,3 +415,20 @@ fn report_extra_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, func: &'tcx
384415
span_lint(cx, UNUSED_LIFETIMES, v, "this lifetime isn't used in the function definition");
385416
}
386417
}
418+
419+
struct BodyLifetimeChecker {
420+
lifetimes_used_in_body: bool,
421+
}
422+
423+
impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
424+
// for lifetimes as parameters of generics
425+
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
426+
if lifetime.name != keywords::Invalid.name() && lifetime.name != "'static" {
427+
self.lifetimes_used_in_body = true;
428+
}
429+
}
430+
431+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
432+
NestedVisitorMap::None
433+
}
434+
}

tests/ui/lifetimes.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,29 @@ fn elided_input_named_output<'a>(_arg: &str) -> &'a str { unimplemented!() }
128128
fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { unimplemented!() }
129129
fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) { unimplemented!() }
130130

131+
// don't warn on these, see #292
132+
fn trait_bound_bug<'a, T: WithLifetime<'a>>() { unimplemented!() }
133+
134+
// #740
135+
struct Test {
136+
vec: Vec<usize>,
137+
}
138+
139+
impl Test {
140+
fn iter<'a>(&'a self) -> Box<Iterator<Item = usize> + 'a> {
141+
unimplemented!()
142+
}
143+
}
144+
145+
146+
trait LintContext<'a> {}
147+
148+
fn f<'a, T: LintContext<'a>>(_: &T) {}
149+
150+
fn test<'a>(x: &'a [u8]) -> u8 {
151+
let y: &'a u8 = &x[5];
152+
*y
153+
}
154+
131155
fn main() {
132156
}

0 commit comments

Comments
 (0)