Skip to content

Commit 1555eed

Browse files
committed
Merge pull request #452 from fhartwig/lifetime-false-positives
Fix some false positive in needless_lifetimes lint
2 parents 9881f1b + 6046edb commit 1555eed

File tree

2 files changed

+83
-24
lines changed

2 files changed

+83
-24
lines changed

src/lifetimes.rs

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use reexport::*;
33
use rustc::lint::*;
44
use syntax::codemap::Span;
55
use rustc_front::visit::{Visitor, walk_ty, walk_ty_param_bound};
6+
use rustc::middle::def::Def::{DefTy, DefTrait};
67
use std::collections::HashSet;
78

89
use utils::{in_external_macro, span_lint};
@@ -53,16 +54,16 @@ use self::RefLt::*;
5354

5455
fn check_fn_inner(cx: &LateContext, decl: &FnDecl, slf: Option<&ExplicitSelf>,
5556
generics: &Generics, span: Span) {
56-
if in_external_macro(cx, span) || has_where_lifetimes(&generics.where_clause) {
57+
if in_external_macro(cx, span) || has_where_lifetimes(cx, &generics.where_clause) {
5758
return;
5859
}
59-
if could_use_elision(decl, slf, &generics.lifetimes) {
60+
if could_use_elision(cx, decl, slf, &generics.lifetimes) {
6061
span_lint(cx, NEEDLESS_LIFETIMES, span,
6162
"explicit lifetimes given in parameter types where they could be elided");
6263
}
6364
}
6465

65-
fn could_use_elision(func: &FnDecl, slf: Option<&ExplicitSelf>,
66+
fn could_use_elision(cx: &LateContext, func: &FnDecl, slf: Option<&ExplicitSelf>,
6667
named_lts: &[LifetimeDef]) -> bool {
6768
// There are two scenarios where elision works:
6869
// * no output references, all input references have different LT
@@ -74,8 +75,8 @@ fn could_use_elision(func: &FnDecl, slf: Option<&ExplicitSelf>,
7475
let allowed_lts = allowed_lts_from(named_lts);
7576

7677
// these will collect all the lifetimes for references in arg/return types
77-
let mut input_visitor = RefVisitor(Vec::new());
78-
let mut output_visitor = RefVisitor(Vec::new());
78+
let mut input_visitor = RefVisitor::new(cx);
79+
let mut output_visitor = RefVisitor::new(cx);
7980

8081
// extract lifetime in "self" argument for methods (there is a "self" argument
8182
// in func.inputs, but its type is TyInfer)
@@ -88,14 +89,11 @@ fn could_use_elision(func: &FnDecl, slf: Option<&ExplicitSelf>,
8889
}
8990
// extract lifetimes in input argument types
9091
for arg in &func.inputs {
91-
walk_ty(&mut input_visitor, &arg.ty);
92-
if let TyRptr(None, _) = arg.ty.node {
93-
input_visitor.record(&None);
94-
}
92+
input_visitor.visit_ty(&arg.ty);
9593
}
9694
// extract lifetimes in output type
9795
if let Return(ref ty) = func.output {
98-
walk_ty(&mut output_visitor, ty);
96+
output_visitor.visit_ty(ty);
9997
}
10098

10199
let input_lts = input_visitor.into_vec();
@@ -159,52 +157,92 @@ fn unique_lifetimes(lts: &[RefLt]) -> usize {
159157
}
160158

161159
/// A visitor usable for rustc_front::visit::walk_ty().
162-
struct RefVisitor(Vec<RefLt>);
160+
struct RefVisitor<'v, 't: 'v> {
161+
cx: &'v LateContext<'v, 't>, // context reference
162+
lts: Vec<RefLt>
163+
}
164+
165+
impl <'v, 't> RefVisitor<'v, 't> {
166+
fn new(cx: &'v LateContext<'v, 't>) -> RefVisitor<'v, 't> {
167+
RefVisitor { cx: cx, lts: Vec::new() }
168+
}
163169

164-
impl RefVisitor {
165170
fn record(&mut self, lifetime: &Option<Lifetime>) {
166171
if let &Some(ref lt) = lifetime {
167172
if lt.name.as_str() == "'static" {
168-
self.0.push(Static);
173+
self.lts.push(Static);
169174
} else {
170-
self.0.push(Named(lt.name));
175+
self.lts.push(Named(lt.name));
171176
}
172177
} else {
173-
self.0.push(Unnamed);
178+
self.lts.push(Unnamed);
174179
}
175180
}
176181

177182
fn into_vec(self) -> Vec<RefLt> {
178-
self.0
183+
self.lts
184+
}
185+
186+
fn collect_anonymous_lifetimes(&mut self, path: &Path, ty: &Ty) {
187+
let last_path_segment = path.segments.last().map(|s| &s.parameters);
188+
if let Some(&AngleBracketedParameters(ref params)) = last_path_segment {
189+
if params.lifetimes.is_empty() {
190+
let def = self.cx.tcx.def_map.borrow().get(&ty.id).map(|r| r.full_def());
191+
match def {
192+
Some(DefTy(def_id, _)) => {
193+
if let Some(ty_def) = self.cx.tcx.adt_defs.borrow().get(&def_id) {
194+
let scheme = ty_def.type_scheme(self.cx.tcx);
195+
for _ in scheme.generics.regions.as_slice() {
196+
self.record(&None);
197+
}
198+
}
199+
},
200+
Some(DefTrait(def_id)) => {
201+
let trait_def = self.cx.tcx.trait_defs.borrow()[&def_id];
202+
for _ in &trait_def.generics.regions {
203+
self.record(&None);
204+
}
205+
},
206+
_ => {}
207+
}
208+
}
209+
}
179210
}
180211
}
181212

182-
impl<'v> Visitor<'v> for RefVisitor {
213+
impl<'v, 't> Visitor<'v> for RefVisitor<'v, 't> {
214+
183215
// for lifetimes as parameters of generics
184216
fn visit_lifetime(&mut self, lifetime: &'v Lifetime) {
185217
self.record(&Some(*lifetime));
186218
}
187219

188220
fn visit_ty(&mut self, ty: &'v Ty) {
189-
if let TyRptr(None, _) = ty.node {
190-
self.record(&None);
221+
match ty.node {
222+
TyRptr(None, _) => {
223+
self.record(&None);
224+
},
225+
TyPath(_, ref path) => {
226+
self.collect_anonymous_lifetimes(path, ty);
227+
},
228+
_ => {}
191229
}
192230
walk_ty(self, ty);
193231
}
194232
}
195233

196234
/// Are any lifetimes mentioned in the `where` clause? If yes, we don't try to
197235
/// reason about elision.
198-
fn has_where_lifetimes(where_clause: &WhereClause) -> bool {
236+
fn has_where_lifetimes(cx: &LateContext, where_clause: &WhereClause) -> bool {
199237
for predicate in &where_clause.predicates {
200238
match *predicate {
201239
WherePredicate::RegionPredicate(..) => return true,
202240
WherePredicate::BoundPredicate(ref pred) => {
203241
// a predicate like F: Trait or F: for<'a> Trait<'a>
204-
let mut visitor = RefVisitor(Vec::new());
242+
let mut visitor = RefVisitor::new(cx);
205243
// walk the type F, it may not contain LT refs
206244
walk_ty(&mut visitor, &pred.bounded_ty);
207-
if !visitor.0.is_empty() { return true; }
245+
if !visitor.lts.is_empty() { return true; }
208246
// if the bounds define new lifetimes, they are fine to occur
209247
let allowed_lts = allowed_lts_from(&pred.bound_lifetimes);
210248
// now walk the bounds
@@ -219,9 +257,9 @@ fn has_where_lifetimes(where_clause: &WhereClause) -> bool {
219257
}
220258
}
221259
WherePredicate::EqPredicate(ref pred) => {
222-
let mut visitor = RefVisitor(Vec::new());
260+
let mut visitor = RefVisitor::new(cx);
223261
walk_ty(&mut visitor, &pred.ty);
224-
if !visitor.0.is_empty() { return true; }
262+
if !visitor.lts.is_empty() { return true; }
225263
}
226264
}
227265
}

tests/compile-fail/lifetimes.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,26 @@ fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 {
8585
unimplemented!()
8686
}
8787

88+
fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { unimplemented!() } //~ERROR explicit lifetimes given
89+
90+
// no warning, two input lifetimes (named on the reference, anonymous on Foo)
91+
fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str { unimplemented!() }
92+
93+
// no warning, two input lifetimes (anonymous on the reference, named on Foo)
94+
fn struct_with_lt3<'a>(_foo: &Foo<'a> ) -> &'a str { unimplemented!() }
95+
96+
// no warning, two input lifetimes
97+
fn struct_with_lt4<'a, 'b>(_foo: &'a Foo<'b> ) -> &'a str { unimplemented!() }
98+
99+
trait WithLifetime<'a> {}
100+
type WithLifetimeAlias<'a> = WithLifetime<'a>;
101+
102+
// should not warn because it won't build without the lifetime
103+
fn trait_obj_elided<'a>(_arg: &'a WithLifetime) -> &'a str { unimplemented!() }
104+
105+
// this should warn because there is no lifetime on Drop, so this would be
106+
// unambiguous if we elided the lifetime
107+
fn trait_obj_elided2<'a>(_arg: &'a Drop) -> &'a str { unimplemented!() } //~ERROR explicit lifetimes given
108+
88109
fn main() {
89110
}

0 commit comments

Comments
 (0)