From 6a143add9eb6a73a23e570fd3fb42c453497b29a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 3 May 2018 14:16:43 -0400 Subject: [PATCH 1/6] fix comments --- src/librustc/middle/resolve_lifetime.rs | 37 +++++++++++++------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index ceda72dcd7ae0..ee2e6b092a276 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -230,30 +230,31 @@ struct LifetimeContext<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, map: &'a mut NamedRegionMap, scope: ScopeRef<'a>, - // Deep breath. Our representation for poly trait refs contains a single - // binder and thus we only allow a single level of quantification. However, - // the syntax of Rust permits quantification in two places, e.g., `T: for <'a> Foo<'a>` - // and `for <'a, 'b> &'b T: Foo<'a>`. In order to get the de Bruijn indices - // correct when representing these constraints, we should only introduce one - // scope. However, we want to support both locations for the quantifier and - // during lifetime resolution we want precise information (so we can't - // desugar in an earlier phase). - - // SO, if we encounter a quantifier at the outer scope, we set - // trait_ref_hack to true (and introduce a scope), and then if we encounter - // a quantifier at the inner scope, we error. If trait_ref_hack is false, - // then we introduce the scope at the inner quantifier. - - // I'm sorry. + + /// Deep breath. Our representation for poly trait refs contains a single + /// binder and thus we only allow a single level of quantification. However, + /// the syntax of Rust permits quantification in two places, e.g., `T: for <'a> Foo<'a>` + /// and `for <'a, 'b> &'b T: Foo<'a>`. In order to get the de Bruijn indices + /// correct when representing these constraints, we should only introduce one + /// scope. However, we want to support both locations for the quantifier and + /// during lifetime resolution we want precise information (so we can't + /// desugar in an earlier phase). + /// + /// SO, if we encounter a quantifier at the outer scope, we set + /// trait_ref_hack to true (and introduce a scope), and then if we encounter + /// a quantifier at the inner scope, we error. If trait_ref_hack is false, + /// then we introduce the scope at the inner quantifier. + /// + /// I'm sorry. trait_ref_hack: bool, - // Used to disallow the use of in-band lifetimes in `fn` or `Fn` syntax. + /// Used to disallow the use of in-band lifetimes in `fn` or `Fn` syntax. is_in_fn_syntax: bool, - // List of labels in the function/method currently under analysis. + /// List of labels in the function/method currently under analysis. labels_in_fn: Vec<(ast::Name, Span)>, - // Cache for cross-crate per-definition object lifetime defaults. + /// Cache for cross-crate per-definition object lifetime defaults. xcrate_object_lifetime_defaults: DefIdMap>, lifetime_uses: DefIdMap>, From 63b2e01e85472cededf387e9dffbf2cbac8266e1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 3 May 2018 18:58:49 -0400 Subject: [PATCH 2/6] rustfmt --- src/librustc/middle/resolve_lifetime.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index ee2e6b092a276..ac4af1840385b 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -15,28 +15,28 @@ //! used between functions, and they operate in a purely top-down //! way. Therefore we break lifetime name resolution into a separate pass. -use hir::map::Map; use hir::def::Def; use hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; +use hir::map::Map; use hir::ItemLocalId; use hir::LifetimeName; use ty::{self, TyCtxt}; +use errors::DiagnosticBuilder; +use rustc::lint; +use rustc_data_structures::sync::Lrc; +use session::Session; use std::cell::Cell; use std::mem::replace; -use rustc_data_structures::sync::Lrc; +use std::slice; use syntax::ast; use syntax::attr; use syntax::ptr::P; use syntax_pos::Span; -use errors::DiagnosticBuilder; use util::nodemap::{DefIdMap, FxHashMap, FxHashSet, NodeMap, NodeSet}; -use session::Session; -use std::slice; -use rustc::lint; -use hir::{self, GenericParamsExt}; use hir::intravisit::{self, NestedVisitorMap, Visitor}; +use hir::{self, GenericParamsExt}; /// The origin of a named lifetime definition. /// @@ -2087,8 +2087,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { ); err.emit(); } - hir::LifetimeName::Fresh(_) | hir::LifetimeName::Implicit | - hir::LifetimeName::Name(_) => {} + hir::LifetimeName::Fresh(_) + | hir::LifetimeName::Implicit + | hir::LifetimeName::Name(_) => {} } } @@ -2140,8 +2141,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { )) .emit(); } - hir::LifetimeName::Fresh(_) | hir::LifetimeName::Implicit | - hir::LifetimeName::Name(_) => { + hir::LifetimeName::Fresh(_) + | hir::LifetimeName::Implicit + | hir::LifetimeName::Name(_) => { self.resolve_lifetime_ref(bound); } } From b406d0b268977a09bd9faf5266582e97c9a01f88 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 4 May 2018 04:59:57 -0400 Subject: [PATCH 3/6] handle relative paths again in `update-references.sh` --- src/test/ui/update-references.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/ui/update-references.sh b/src/test/ui/update-references.sh index 4fc11daaa3afa..6ff05b5ab047e 100755 --- a/src/test/ui/update-references.sh +++ b/src/test/ui/update-references.sh @@ -36,10 +36,11 @@ shopt -s nullglob while [[ "$1" != "" ]]; do for EXT in "stderr" "stdout"; do for OUT_NAME in $BUILD_DIR/${1%.rs}.*$EXT; do + OUT_DIR=`dirname "$1"` OUT_BASE=`basename "$OUT_NAME"` - if ! (diff $OUT_NAME $MYDIR/$OUT_BASE >& /dev/null); then - echo updating $MYDIR/$OUT_BASE - cp $OUT_NAME $MYDIR + if ! (diff $OUT_NAME $MYDIR/$OUT_DIR/$OUT_BASE >& /dev/null); then + echo updating $MYDIR/$OUT_DIR/$OUT_BASE + cp $OUT_NAME $MYDIR/$OUT_DIR fi done done From aef29a058370f2941bed3fec600a95343554c436 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 3 May 2018 18:43:28 -0400 Subject: [PATCH 4/6] fix single-use lint --- src/librustc/middle/resolve_lifetime.rs | 181 +++++++++++++++--- .../single_use_lifetimes-2.stderr | 14 -- .../single_use_lifetimes-3.stderr | 20 -- .../single_use_lifetimes-4.stderr | 20 -- .../single_use_lifetimes-5.stderr | 14 -- .../in-band-lifetimes/single_use_lifetimes.rs | 16 -- .../single_use_lifetimes.stderr | 14 -- src/test/ui/single-use-lifetime/fn-types.rs | 26 +++ .../ui/single-use-lifetime/fn-types.stderr | 21 ++ .../one-use-in-fn-argument-in-band.rs | 24 +++ .../one-use-in-fn-argument-in-band.stderr | 20 ++ .../one-use-in-fn-argument.rs} | 13 +- .../one-use-in-fn-argument.stderr | 14 ++ .../one-use-in-fn-return.rs | 30 +++ .../one-use-in-inherent-impl-header.rs | 29 +++ .../one-use-in-inherent-impl-header.stderr | 14 ++ .../one-use-in-inherent-method-argument.rs | 26 +++ ...one-use-in-inherent-method-argument.stderr | 20 ++ .../one-use-in-inherent-method-return.rs | 28 +++ .../one-use-in-inherent-method-return.stderr | 14 ++ .../single-use-lifetime/one-use-in-struct.rs | 31 +++ .../one-use-in-trait-method-argument.rs | 30 +++ .../one-use-in-trait-method-argument.stderr | 14 ++ .../two-uses-in-fn-argument-and-return.rs} | 16 +- .../two-uses-in-fn-arguments.rs} | 18 +- .../two-uses-in-inherent-impl-header.rs} | 20 +- ...-in-inherent-method-argument-and-return.rs | 28 +++ ...inherent-method-argument-and-return.stderr | 14 ++ .../two-uses-in-trait-impl.rs | 32 ++++ 29 files changed, 612 insertions(+), 149 deletions(-) delete mode 100644 src/test/ui/in-band-lifetimes/single_use_lifetimes-2.stderr delete mode 100644 src/test/ui/in-band-lifetimes/single_use_lifetimes-3.stderr delete mode 100644 src/test/ui/in-band-lifetimes/single_use_lifetimes-4.stderr delete mode 100644 src/test/ui/in-band-lifetimes/single_use_lifetimes-5.stderr delete mode 100644 src/test/ui/in-band-lifetimes/single_use_lifetimes.rs delete mode 100644 src/test/ui/in-band-lifetimes/single_use_lifetimes.stderr create mode 100644 src/test/ui/single-use-lifetime/fn-types.rs create mode 100644 src/test/ui/single-use-lifetime/fn-types.stderr create mode 100644 src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.rs create mode 100644 src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr rename src/test/ui/{in-band-lifetimes/single_use_lifetimes-2.rs => single-use-lifetime/one-use-in-fn-argument.rs} (64%) create mode 100644 src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr create mode 100644 src/test/ui/single-use-lifetime/one-use-in-fn-return.rs create mode 100644 src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.rs create mode 100644 src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr create mode 100644 src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs create mode 100644 src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr create mode 100644 src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.rs create mode 100644 src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.stderr create mode 100644 src/test/ui/single-use-lifetime/one-use-in-struct.rs create mode 100644 src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs create mode 100644 src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.stderr rename src/test/ui/{in-band-lifetimes/single_use_lifetimes-5.rs => single-use-lifetime/two-uses-in-fn-argument-and-return.rs} (61%) rename src/test/ui/{in-band-lifetimes/single_use_lifetimes-4.rs => single-use-lifetime/two-uses-in-fn-arguments.rs} (55%) rename src/test/ui/{in-band-lifetimes/single_use_lifetimes-3.rs => single-use-lifetime/two-uses-in-inherent-impl-header.rs} (59%) create mode 100644 src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.rs create mode 100644 src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.stderr create mode 100644 src/test/ui/single-use-lifetime/two-uses-in-trait-impl.rs diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index ac4af1840385b..eaabc333e5d9b 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -257,7 +257,7 @@ struct LifetimeContext<'a, 'tcx: 'a> { /// Cache for cross-crate per-definition object lifetime defaults. xcrate_object_lifetime_defaults: DefIdMap>, - lifetime_uses: DefIdMap>, + lifetime_uses: &'a mut DefIdMap>, } #[derive(Debug)] @@ -273,6 +273,11 @@ enum Scope<'a> { /// we should use for an early-bound region? next_early_index: u32, + /// Flag is set to true if, in this binder, `'_` would be + /// equivalent to a "single-use region". This is true on + /// impls, but not other kinds of items. + track_lifetime_uses: bool, + /// Whether or not this binder would serve as the parent /// binder for abstract types introduced within. For example: /// @@ -433,7 +438,7 @@ fn krate<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) -> NamedRegionMap { is_in_fn_syntax: false, labels_in_fn: vec![], xcrate_object_lifetime_defaults: DefIdMap(), - lifetime_uses: DefIdMap(), + lifetime_uses: &mut DefIdMap(), }; for (_, item) in &krate.items { visitor.visit_item(item); @@ -498,6 +503,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { | hir::ItemTrait(_, _, ref generics, ..) | hir::ItemTraitAlias(ref generics, ..) | hir::ItemImpl(_, _, _, ref generics, ..) => { + // Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name". + // This is not true for other kinds of items.x + let track_lifetime_uses = match item.node { + hir::ItemImpl(..) => true, + _ => false, + }; // These kinds of items have only early bound lifetime parameters. let mut index = if let hir::ItemTrait(..) = item.node { 1 // Self comes before lifetimes @@ -513,6 +524,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index, abstract_type_parent: true, + track_lifetime_uses, s: ROOT_SCOPE, }; self.with(scope, |old_scope, this| { @@ -540,7 +552,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } fn visit_ty(&mut self, ty: &'tcx hir::Ty) { - debug!("visit_ty: ty={:?}", ty); + debug!("visit_ty: id={:?} ty={:?}", ty.id, ty); match ty.node { hir::TyBareFn(ref c) => { let next_early_index = self.next_early_index(); @@ -553,6 +565,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .collect(), s: self.scope, next_early_index, + track_lifetime_uses: true, abstract_type_parent: false, }; self.with(scope, |old_scope, this| { @@ -674,6 +687,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index, s: this.scope, + track_lifetime_uses: true, abstract_type_parent: false, }; this.with(scope, |_old_scope, this| { @@ -688,6 +702,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index, s: self.scope, + track_lifetime_uses: true, abstract_type_parent: false, }; self.with(scope, |_old_scope, this| { @@ -728,6 +743,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index, s: self.scope, + track_lifetime_uses: true, abstract_type_parent: true, }; self.with(scope, |_old_scope, this| { @@ -774,6 +790,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index, s: self.scope, + track_lifetime_uses: true, abstract_type_parent: true, }; self.with(scope, |_old_scope, this| { @@ -847,6 +864,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .collect(), s: self.scope, next_early_index, + track_lifetime_uses: true, abstract_type_parent: false, }; let result = self.with(scope, |old_scope, this| { @@ -913,6 +931,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .collect(), s: self.scope, next_early_index, + track_lifetime_uses: true, abstract_type_parent: false, }; self.with(scope, |old_scope, this| { @@ -1104,10 +1123,7 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body) { } Scope::Binder { - ref lifetimes, - s, - next_early_index: _, - abstract_type_parent: _, + ref lifetimes, s, .. } => { // FIXME (#24278): non-hygienic comparison if let Some(def) = lifetimes.get(&hir::LifetimeName::Name(label)) { @@ -1255,33 +1271,70 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { F: for<'b> FnOnce(ScopeRef, &mut LifetimeContext<'b, 'tcx>), { let LifetimeContext { - tcx, ref mut map, .. - } = *self; + tcx, + map, + lifetime_uses, + .. + } = self; let labels_in_fn = replace(&mut self.labels_in_fn, vec![]); let xcrate_object_lifetime_defaults = replace(&mut self.xcrate_object_lifetime_defaults, DefIdMap()); let mut this = LifetimeContext { - tcx, - map: *map, + tcx: *tcx, + map: map, scope: &wrap_scope, trait_ref_hack: self.trait_ref_hack, is_in_fn_syntax: self.is_in_fn_syntax, labels_in_fn, xcrate_object_lifetime_defaults, - lifetime_uses: DefIdMap(), + lifetime_uses: lifetime_uses, }; debug!("entering scope {:?}", this.scope); f(self.scope, &mut this); + this.check_uses_for_lifetimes_defined_by_scope(); debug!("exiting scope {:?}", this.scope); self.labels_in_fn = this.labels_in_fn; self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults; + } + + fn check_uses_for_lifetimes_defined_by_scope(&mut self) { + let defined_by = match self.scope { + Scope::Binder { lifetimes, .. } => lifetimes, + _ => { + debug!("check_uses_for_lifetimes_defined_by_scope: not in a binder scope"); + return; + } + }; - for (def_id, lifetimeuseset) in &this.lifetime_uses { + for region in defined_by.values() { + debug!( + "check_uses_for_lifetimes_defined_by_scope: region = {:?}", + region + ); + + let def_id = match region { + Region::EarlyBound(_, def_id, _) + | Region::LateBound(_, def_id, _) + | Region::Free(_, def_id) => def_id, + + Region::LateBoundAnon(..) | Region::Static => continue, + }; + + debug!( + "check_uses_for_lifetimes_defined_by_scope: def_id = {:?}", + def_id + ); + + let lifetimeuseset = self.lifetime_uses.remove(def_id); + debug!( + "check_uses_for_lifetimes_defined_by_scope: lifetimeuseset = {:?}", + lifetimeuseset + ); match lifetimeuseset { - &LifetimeUseSet::One(_) => { - let node_id = this.tcx.hir.as_local_node_id(*def_id).unwrap(); + Some(LifetimeUseSet::One(_)) => { + let node_id = self.tcx.hir.as_local_node_id(*def_id).unwrap(); debug!("node id first={:?}", node_id); - if let hir::map::NodeLifetime(hir_lifetime) = this.tcx.hir.get(node_id) { + if let hir::map::NodeLifetime(hir_lifetime) = self.tcx.hir.get(node_id) { let span = hir_lifetime.span; let id = hir_lifetime.id; debug!( @@ -1289,22 +1342,23 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { node_id, span, hir_lifetime ); - this.tcx + self.tcx .struct_span_lint_node( lint::builtin::SINGLE_USE_LIFETIME, id, span, &format!( - "lifetime name `{}` only used once", + "lifetime parameter `{}` only used once", hir_lifetime.name.name() ), ) .emit(); } } - _ => { + Some(LifetimeUseSet::Many) => { debug!("Not one use lifetime"); } + None => {} } } } @@ -1372,6 +1426,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { next_early_index, s: self.scope, abstract_type_parent: true, + track_lifetime_uses: false, }; self.with(scope, move |old_scope, this| { this.check_lifetime_params(old_scope, &generics.params); @@ -1437,10 +1492,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } Scope::Binder { - ref lifetimes, - s, - next_early_index: _, - abstract_type_parent: _, + ref lifetimes, s, .. } => { if let Some(&def) = lifetimes.get(&lifetime_ref.name) { break Some(def.shifted(late_depth)); @@ -1631,6 +1683,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { inputs: &'tcx [P], output: Option<&'tcx P>, ) { + debug!("visit_fn_like_elision: enter"); let mut arg_elide = Elide::FreshLateAnon(Cell::new(0)); let arg_scope = Scope::Elision { elide: arg_elide.clone(), @@ -1653,6 +1706,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { None => return, }; + debug!("visit_fn_like_elision: determine output"); + // Figure out if there's a body we can get argument names from, // and whether there's a `self` argument (treated specially). let mut assoc_item_kind = None; @@ -1812,11 +1867,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Elide::Error(arg_lifetimes) }; + debug!("visit_fn_like_elision: elide={:?}", elide); + let scope = Scope::Elision { elide, s: self.scope, }; self.with(scope, |_, this| this.visit_ty(output)); + debug!("visit_fn_like_elision: exit"); struct GatherLifetimes<'a> { map: &'a NamedRegionMap, @@ -2182,10 +2240,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } Scope::Binder { - ref lifetimes, - s, - next_early_index: _, - abstract_type_parent: _, + ref lifetimes, s, .. } => { if let Some(&def) = lifetimes.get(&lifetime.name) { let node_id = self.tcx.hir.as_local_node_id(def.id().unwrap()).unwrap(); @@ -2205,6 +2260,50 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } + /// Returns true if, in the current scope, replacing `'_` would be + /// equivalent to a single-use lifetime. + fn track_lifetime_uses(&self) -> bool { + let mut scope = self.scope; + loop { + match *scope { + Scope::Root => break false, + + // Inside of items, it depends on the kind of item. + Scope::Binder { + track_lifetime_uses, + .. + } => break track_lifetime_uses, + + // Inside a body, `'_` will use an inference variable, + // should be fine. + Scope::Body { .. } => break true, + + // A lifetime only used in a fn argument could as well + // be replaced with `'_`, as that would generate a + // fresh name, too. + Scope::Elision { + elide: Elide::FreshLateAnon(_), + .. + } => break true, + + // In the return type or other such place, `'_` is not + // going to make a fresh name, so we cannot + // necessarily replace a single-use lifetime with + // `'_`. + Scope::Elision { + elide: Elide::Exact(_), + .. + } => break false, + Scope::Elision { + elide: Elide::Error(_), + .. + } => break false, + + Scope::ObjectLifetimeDefault { s, .. } => scope = s, + } + } + } + fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) { if lifetime_ref.id == ast::DUMMY_NODE_ID { span_bug!( @@ -2231,11 +2330,31 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { | Region::LateBound(_, def_id, _) | Region::EarlyBound(_, def_id, _) => { // A lifetime declared by the user. - if !self.lifetime_uses.contains_key(&def_id) { - self.lifetime_uses - .insert(def_id, LifetimeUseSet::One(lifetime_ref)); + let def_local_id = self.tcx.hir.as_local_node_id(def_id).unwrap(); + if def_local_id == lifetime_ref.id { + // This is weird. Because the HIR defines a + // lifetime *definition* as wrapping a Lifetime, + // we wind up invoking this method also for the + // definitions in some cases (notably + // higher-ranked types). This means that a + // lifetime with one use (e.g., `for<'a> fn(&'a + // u32)`) wind up being counted as two uses. To + // avoid that, we just ignore the lifetime that + // corresponds to the definition. } else { - self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); + let track_lifetime_uses = self.track_lifetime_uses(); + debug!( + "insert_lifetime: track_lifetime_uses={}", + track_lifetime_uses + ); + if track_lifetime_uses && !self.lifetime_uses.contains_key(&def_id) { + debug!("insert_lifetime: first use of {:?}", def_id); + self.lifetime_uses + .insert(def_id, LifetimeUseSet::One(lifetime_ref)); + } else { + debug!("insert_lifetime: many uses of {:?}", def_id); + self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); + } } } } diff --git a/src/test/ui/in-band-lifetimes/single_use_lifetimes-2.stderr b/src/test/ui/in-band-lifetimes/single_use_lifetimes-2.stderr deleted file mode 100644 index 38d05369fb845..0000000000000 --- a/src/test/ui/in-band-lifetimes/single_use_lifetimes-2.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: lifetime name `'x` only used once - --> $DIR/single_use_lifetimes-2.rs:12:10 - | -LL | fn deref<'x>() -> &'x u32 { //~ ERROR lifetime name `'x` only used once - | ^^ - | -note: lint level defined here - --> $DIR/single_use_lifetimes-2.rs:10:9 - | -LL | #![deny(single_use_lifetime)] - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/in-band-lifetimes/single_use_lifetimes-3.stderr b/src/test/ui/in-band-lifetimes/single_use_lifetimes-3.stderr deleted file mode 100644 index 49c06aafbe550..0000000000000 --- a/src/test/ui/in-band-lifetimes/single_use_lifetimes-3.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: lifetime name `'x` only used once - --> $DIR/single_use_lifetimes-3.rs:11:12 - | -LL | struct Foo<'x> { //~ ERROR lifetime name `'x` only used once - | ^^ - | -note: lint level defined here - --> $DIR/single_use_lifetimes-3.rs:10:9 - | -LL | #![deny(single_use_lifetime)] - | ^^^^^^^^^^^^^^^^^^^ - -error: lifetime name `'y` only used once - --> $DIR/single_use_lifetimes-3.rs:16:6 - | -LL | impl<'y> Foo<'y> { //~ ERROR lifetime name `'y` only used once - | ^^ - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/in-band-lifetimes/single_use_lifetimes-4.stderr b/src/test/ui/in-band-lifetimes/single_use_lifetimes-4.stderr deleted file mode 100644 index 2df370f5d0221..0000000000000 --- a/src/test/ui/in-band-lifetimes/single_use_lifetimes-4.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: lifetime name `'x` only used once - --> $DIR/single_use_lifetimes-4.rs:12:12 - | -LL | struct Foo<'x> { //~ ERROR lifetime name `'x` only used once - | ^^ - | -note: lint level defined here - --> $DIR/single_use_lifetimes-4.rs:10:9 - | -LL | #![deny(single_use_lifetime)] - | ^^^^^^^^^^^^^^^^^^^ - -error: lifetime name `'x` only used once - --> $DIR/single_use_lifetimes-4.rs:16:10 - | -LL | enum Bar<'x> { //~ ERROR lifetime name `'x` only used once - | ^^ - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/in-band-lifetimes/single_use_lifetimes-5.stderr b/src/test/ui/in-band-lifetimes/single_use_lifetimes-5.stderr deleted file mode 100644 index eec426e4e63ab..0000000000000 --- a/src/test/ui/in-band-lifetimes/single_use_lifetimes-5.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: lifetime name `'x` only used once - --> $DIR/single_use_lifetimes-5.rs:12:11 - | -LL | trait Foo<'x> { //~ ERROR lifetime name `'x` only used once - | ^^ - | -note: lint level defined here - --> $DIR/single_use_lifetimes-5.rs:10:9 - | -LL | #![deny(single_use_lifetime)] - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/in-band-lifetimes/single_use_lifetimes.rs b/src/test/ui/in-band-lifetimes/single_use_lifetimes.rs deleted file mode 100644 index a97056b6240ec..0000000000000 --- a/src/test/ui/in-band-lifetimes/single_use_lifetimes.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -#![deny(single_use_lifetime)] - -fn deref<'x>(v: &'x u32) -> u32 { //~ ERROR lifetime name `'x` only used once - *v -} - -fn main() {} diff --git a/src/test/ui/in-band-lifetimes/single_use_lifetimes.stderr b/src/test/ui/in-band-lifetimes/single_use_lifetimes.stderr deleted file mode 100644 index 15917d3c08562..0000000000000 --- a/src/test/ui/in-band-lifetimes/single_use_lifetimes.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: lifetime name `'x` only used once - --> $DIR/single_use_lifetimes.rs:12:10 - | -LL | fn deref<'x>(v: &'x u32) -> u32 { //~ ERROR lifetime name `'x` only used once - | ^^ - | -note: lint level defined here - --> $DIR/single_use_lifetimes.rs:10:9 - | -LL | #![deny(single_use_lifetime)] - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/single-use-lifetime/fn-types.rs b/src/test/ui/single-use-lifetime/fn-types.rs new file mode 100644 index 0000000000000..c5d98d2604844 --- /dev/null +++ b/src/test/ui/single-use-lifetime/fn-types.rs @@ -0,0 +1,26 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(single_use_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +// Test that we DO warn when lifetime name is used only +// once in a fn argument. + +struct Foo { + a: for<'a> fn(&'a u32), //~ ERROR `'a` only used once + b: for<'a> fn(&'a u32, &'a u32), // OK, used twice. + c: for<'a> fn(&'a u32) -> &'a u32, // OK, used twice. + d: for<'a> fn() -> &'a u32, // OK, used only in return type. + //~^ ERROR return type references lifetime `'a`, which is not constrained by the fn input types +} + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/fn-types.stderr b/src/test/ui/single-use-lifetime/fn-types.stderr new file mode 100644 index 0000000000000..bec712b004c3d --- /dev/null +++ b/src/test/ui/single-use-lifetime/fn-types.stderr @@ -0,0 +1,21 @@ +error: lifetime parameter `'a` only used once + --> $DIR/fn-types.rs:19:10 + | +LL | a: for<'a> fn(&'a u32), //~ ERROR `'a` only used once + | ^^ + | +note: lint level defined here + --> $DIR/fn-types.rs:11:9 + | +LL | #![deny(single_use_lifetime)] + | ^^^^^^^^^^^^^^^^^^^ + +error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types + --> $DIR/fn-types.rs:22:22 + | +LL | d: for<'a> fn() -> &'a u32, // OK, used only in return type. + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0581`. diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.rs b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.rs new file mode 100644 index 0000000000000..9001a8de29a43 --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(in_band_lifetimes)] +#![deny(single_use_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +// Test that we DO warn when lifetime name is used only +// once in a fn argument, even with in band lifetimes. + +fn a(x: &'a u32, y: &'b u32) { + //~^ ERROR `'a` only used once + //~| ERROR `'b` only used once +} + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr new file mode 100644 index 0000000000000..2011359a51120 --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr @@ -0,0 +1,20 @@ +error: lifetime parameter `'b` only used once + --> $DIR/one-use-in-fn-argument-in-band.rs:19:22 + | +LL | fn a(x: &'a u32, y: &'b u32) { + | ^^ + | +note: lint level defined here + --> $DIR/one-use-in-fn-argument-in-band.rs:12:9 + | +LL | #![deny(single_use_lifetime)] + | ^^^^^^^^^^^^^^^^^^^ + +error: lifetime parameter `'a` only used once + --> $DIR/one-use-in-fn-argument-in-band.rs:19:10 + | +LL | fn a(x: &'a u32, y: &'b u32) { + | ^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/in-band-lifetimes/single_use_lifetimes-2.rs b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs similarity index 64% rename from src/test/ui/in-band-lifetimes/single_use_lifetimes-2.rs rename to src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs index 005f1f033b6eb..2a9e80d56c31b 100644 --- a/src/test/ui/in-band-lifetimes/single_use_lifetimes-2.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs @@ -1,4 +1,4 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -7,10 +7,15 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. + #![deny(single_use_lifetime)] -// FIXME(#44752) -- this scenario should not be warned -fn deref<'x>() -> &'x u32 { //~ ERROR lifetime name `'x` only used once - 22 +#![allow(dead_code)] +#![allow(unused_variables)] + +// Test that we DO warn when lifetime name is used only +// once in a fn argument. + +fn a<'a>(x: &'a u32) { //~ ERROR `'a` only used once } fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr new file mode 100644 index 0000000000000..e9a3570b3fb4d --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.stderr @@ -0,0 +1,14 @@ +error: lifetime parameter `'a` only used once + --> $DIR/one-use-in-fn-argument.rs:18:6 + | +LL | fn a<'a>(x: &'a u32) { //~ ERROR `'a` only used once + | ^^ + | +note: lint level defined here + --> $DIR/one-use-in-fn-argument.rs:11:9 + | +LL | #![deny(single_use_lifetime)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-return.rs b/src/test/ui/single-use-lifetime/one-use-in-fn-return.rs new file mode 100644 index 0000000000000..d024094ae7122 --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-return.rs @@ -0,0 +1,30 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-pass + +#![deny(single_use_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +// Test that we DO NOT warn when lifetime name is used only +// once in a fn return type -- using `'_` is not legal there, +// as it must refer back to an argument. +// +// (Normally, using `'static` would be preferred, but there are +// times when that is not what you want.) +// +// run-pass + +fn b<'a>() -> &'a u32 { // OK: used only in return type + &22 +} + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.rs b/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.rs new file mode 100644 index 0000000000000..9cdad09e33d4e --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.rs @@ -0,0 +1,29 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(single_use_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +// Test that we DO warn for a lifetime used only once in an impl. +// +// (Actually, until #15872 is fixed, you can't use `'_` here, but +// hopefully that will come soon.) + +struct Foo<'f> { + data: &'f u32 +} + +impl<'f> Foo<'f> { //~ ERROR `'f` only used once + fn inherent_a(&self) { + } +} + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr b/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr new file mode 100644 index 0000000000000..1718f19318819 --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr @@ -0,0 +1,14 @@ +error: lifetime parameter `'f` only used once + --> $DIR/one-use-in-inherent-impl-header.rs:24:6 + | +LL | impl<'f> Foo<'f> { //~ ERROR `'f` only used once + | ^^ + | +note: lint level defined here + --> $DIR/one-use-in-inherent-impl-header.rs:11:9 + | +LL | #![deny(single_use_lifetime)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs new file mode 100644 index 0000000000000..1ca078cdb3bbb --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs @@ -0,0 +1,26 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(single_use_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +// Test that we DO warn for a lifetime used only once in an inherent method. + +struct Foo<'f> { + data: &'f u32 +} + +impl<'f> Foo<'f> { //~ ERROR `'f` only used once + fn inherent_a<'a>(&self, data: &'a u32) { //~ ERROR `'a` only used once + } +} + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr new file mode 100644 index 0000000000000..38e90e76f56e2 --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr @@ -0,0 +1,20 @@ +error: lifetime parameter `'a` only used once + --> $DIR/one-use-in-inherent-method-argument.rs:22:19 + | +LL | fn inherent_a<'a>(&self, data: &'a u32) { //~ ERROR `'a` only used once + | ^^ + | +note: lint level defined here + --> $DIR/one-use-in-inherent-method-argument.rs:11:9 + | +LL | #![deny(single_use_lifetime)] + | ^^^^^^^^^^^^^^^^^^^ + +error: lifetime parameter `'f` only used once + --> $DIR/one-use-in-inherent-method-argument.rs:21:6 + | +LL | impl<'f> Foo<'f> { //~ ERROR `'f` only used once + | ^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.rs b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.rs new file mode 100644 index 0000000000000..7d2ffa379adc4 --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.rs @@ -0,0 +1,28 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(single_use_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +// Test that we DO NOT warn for a lifetime used just once in a return type, +// where that return type is in an inherent method. + +struct Foo<'f> { + data: &'f u32 +} + +impl<'f> Foo<'f> { //~ ERROR `'f` only used once + fn inherent_a<'a>(&self) -> &'a u32 { // OK for 'a + &22 + } +} + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.stderr b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.stderr new file mode 100644 index 0000000000000..32fd284949e96 --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.stderr @@ -0,0 +1,14 @@ +error: lifetime parameter `'f` only used once + --> $DIR/one-use-in-inherent-method-return.rs:22:6 + | +LL | impl<'f> Foo<'f> { //~ ERROR `'f` only used once + | ^^ + | +note: lint level defined here + --> $DIR/one-use-in-inherent-method-return.rs:11:9 + | +LL | #![deny(single_use_lifetime)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/single-use-lifetime/one-use-in-struct.rs b/src/test/ui/single-use-lifetime/one-use-in-struct.rs new file mode 100644 index 0000000000000..5c758d9db0787 --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-struct.rs @@ -0,0 +1,31 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we do not warn for named lifetimes in structs, +// even when they are only used once (since to not use a named +// lifetime is illegal!) +// +// compile-pass + +#![deny(single_use_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +struct Foo<'f> { + data: &'f u32 +} + +enum Bar<'f> { + Data(&'f u32) +} + +trait Baz<'f> { } + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs b/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs new file mode 100644 index 0000000000000..99e9eaf98ad1f --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs @@ -0,0 +1,30 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we DO warn for a lifetime on an impl used only in `&self` +// in a trait method. + +#![deny(single_use_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +struct Foo<'f> { + data: &'f u32 +} + +impl<'f> Iterator for Foo<'f> { + type Item = &'f u32; + + fn next<'g>(&'g mut self) -> Option { //~ ERROR `'g` only used once + None + } +} + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.stderr b/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.stderr new file mode 100644 index 0000000000000..e5278671a1afa --- /dev/null +++ b/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.stderr @@ -0,0 +1,14 @@ +error: lifetime parameter `'g` only used once + --> $DIR/one-use-in-trait-method-argument.rs:25:13 + | +LL | fn next<'g>(&'g mut self) -> Option { //~ ERROR `'g` only used once + | ^^ + | +note: lint level defined here + --> $DIR/one-use-in-trait-method-argument.rs:14:9 + | +LL | #![deny(single_use_lifetime)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/in-band-lifetimes/single_use_lifetimes-5.rs b/src/test/ui/single-use-lifetime/two-uses-in-fn-argument-and-return.rs similarity index 61% rename from src/test/ui/in-band-lifetimes/single_use_lifetimes-5.rs rename to src/test/ui/single-use-lifetime/two-uses-in-fn-argument-and-return.rs index cef904c48962c..d210be4ba2a50 100644 --- a/src/test/ui/in-band-lifetimes/single_use_lifetimes-5.rs +++ b/src/test/ui/single-use-lifetime/two-uses-in-fn-argument-and-return.rs @@ -1,4 +1,4 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -7,10 +7,18 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. + +// Test that we DO NOT warn when lifetime name is used in +// both the argument and return. +// +// compile-pass + #![deny(single_use_lifetime)] -// Should not issue a warning, as explicit lifetimes are mandatory in this case: -trait Foo<'x> { //~ ERROR lifetime name `'x` only used once - fn foo(&self, arg: &'x u32); +#![allow(dead_code)] +#![allow(unused_variables)] + +fn c<'a>(x: &'a u32) -> &'a u32 { // OK: used twice + &22 } fn main() { } diff --git a/src/test/ui/in-band-lifetimes/single_use_lifetimes-4.rs b/src/test/ui/single-use-lifetime/two-uses-in-fn-arguments.rs similarity index 55% rename from src/test/ui/in-band-lifetimes/single_use_lifetimes-4.rs rename to src/test/ui/single-use-lifetime/two-uses-in-fn-arguments.rs index 4ac8f8c0d4e22..f43a3e116efe5 100644 --- a/src/test/ui/in-band-lifetimes/single_use_lifetimes-4.rs +++ b/src/test/ui/single-use-lifetime/two-uses-in-fn-arguments.rs @@ -1,4 +1,4 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -7,14 +7,20 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. + +// Test that we DO NOT warn when lifetime name is used multiple +// argments, or more than once in a single argument. +// +// compile-pass + #![deny(single_use_lifetime)] - // Neither should issue a warning, as explicit lifetimes are mandatory in this case -struct Foo<'x> { //~ ERROR lifetime name `'x` only used once - x: &'x u32 +#![allow(dead_code)] +#![allow(unused_variables)] + +fn c<'a>(x: &'a u32, y: &'a u32) { // OK: used twice } -enum Bar<'x> { //~ ERROR lifetime name `'x` only used once - Variant(&'x u32) +fn d<'a>(x: (&'a u32, &'a u32)) { // OK: used twice } fn main() { } diff --git a/src/test/ui/in-band-lifetimes/single_use_lifetimes-3.rs b/src/test/ui/single-use-lifetime/two-uses-in-inherent-impl-header.rs similarity index 59% rename from src/test/ui/in-band-lifetimes/single_use_lifetimes-3.rs rename to src/test/ui/single-use-lifetime/two-uses-in-inherent-impl-header.rs index 263548ca7f4dd..d224d9fefa106 100644 --- a/src/test/ui/in-band-lifetimes/single_use_lifetimes-3.rs +++ b/src/test/ui/single-use-lifetime/two-uses-in-inherent-impl-header.rs @@ -1,4 +1,4 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -7,14 +7,22 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. + +// Test that we DO NOT warn for a lifetime used twice in an impl. +// +// compile-pass + #![deny(single_use_lifetime)] -struct Foo<'x> { //~ ERROR lifetime name `'x` only used once - x: &'x u32 // no warning! +#![allow(dead_code)] +#![allow(unused_variables)] + +struct Foo<'f> { + data: &'f u32 } -// Once #44524 is fixed, this should issue a warning. -impl<'y> Foo<'y> { //~ ERROR lifetime name `'y` only used once - fn method() { } +impl<'f> Foo<'f> { + fn inherent_a(&self, data: &'f u32) { + } } fn main() { } diff --git a/src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.rs b/src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.rs new file mode 100644 index 0000000000000..7b69a6f0d5250 --- /dev/null +++ b/src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.rs @@ -0,0 +1,28 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we DO NOT warn for a lifetime used twice in an impl method and +// header. + +#![deny(single_use_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +struct Foo<'f> { + data: &'f u32 +} + +impl<'f> Foo<'f> { //~ ERROR `'f` only used once + fn inherent_a<'a>(&self, data: &'a u32) -> &'a u32{ + data + } +} + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.stderr b/src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.stderr new file mode 100644 index 0000000000000..6fc66611b1ddf --- /dev/null +++ b/src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.stderr @@ -0,0 +1,14 @@ +error: lifetime parameter `'f` only used once + --> $DIR/two-uses-in-inherent-method-argument-and-return.rs:22:6 + | +LL | impl<'f> Foo<'f> { //~ ERROR `'f` only used once + | ^^ + | +note: lint level defined here + --> $DIR/two-uses-in-inherent-method-argument-and-return.rs:14:9 + | +LL | #![deny(single_use_lifetime)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/single-use-lifetime/two-uses-in-trait-impl.rs b/src/test/ui/single-use-lifetime/two-uses-in-trait-impl.rs new file mode 100644 index 0000000000000..ffba3d8527d0e --- /dev/null +++ b/src/test/ui/single-use-lifetime/two-uses-in-trait-impl.rs @@ -0,0 +1,32 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we DO NOT warn for a lifetime on an impl used in both +// header and in an associated type. +// +// compile-pass + +#![deny(single_use_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +struct Foo<'f> { + data: &'f u32 +} + +impl<'f> Iterator for Foo<'f> { + type Item = &'f u32; + + fn next(&mut self) -> Option { + None + } +} + +fn main() { } From 0a9dcaf04f09882e4be5c4487a08bb1db7e61437 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 3 May 2018 18:58:06 -0400 Subject: [PATCH 5/6] add warnings for unused lifetime parameters --- src/librustc/lint/builtin.rs | 9 +++++++- src/librustc/middle/resolve_lifetime.rs | 20 +++++++++++++++++- .../ui/single-use-lifetime/zero-uses-in-fn.rs | 19 +++++++++++++++++ .../zero-uses-in-fn.stderr | 14 +++++++++++++ .../single-use-lifetime/zero-uses-in-impl.rs | 21 +++++++++++++++++++ .../zero-uses-in-impl.stderr | 14 +++++++++++++ 6 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/single-use-lifetime/zero-uses-in-fn.rs create mode 100644 src/test/ui/single-use-lifetime/zero-uses-in-fn.stderr create mode 100644 src/test/ui/single-use-lifetime/zero-uses-in-impl.rs create mode 100644 src/test/ui/single-use-lifetime/zero-uses-in-impl.stderr diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 109edffcde38a..0606ecb9bbccd 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -233,7 +233,13 @@ declare_lint! { declare_lint! { pub SINGLE_USE_LIFETIME, Allow, - "detects single use lifetimes" + "detects lifetime parameters that are only used once" +} + +declare_lint! { + pub UNUSED_LIFETIME, + Allow, + "detects lifetime parameters that are never used" } declare_lint! { @@ -318,6 +324,7 @@ impl LintPass for HardwiredLints { UNUSED_UNSAFE, UNUSED_MUT, SINGLE_USE_LIFETIME, + UNUSED_LIFETIME, TYVAR_BEHIND_RAW_POINTER, ELIDED_LIFETIME_IN_PATH, BARE_TRAIT_OBJECT, diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index eaabc333e5d9b..49a8832365312 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -1358,7 +1358,25 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Some(LifetimeUseSet::Many) => { debug!("Not one use lifetime"); } - None => {} + None => { + let node_id = self.tcx.hir.as_local_node_id(*def_id).unwrap(); + if let hir::map::NodeLifetime(hir_lifetime) = self.tcx.hir.get(node_id) { + let span = hir_lifetime.span; + let id = hir_lifetime.id; + + self.tcx + .struct_span_lint_node( + lint::builtin::UNUSED_LIFETIME, + id, + span, + &format!( + "lifetime parameter `{}` never used", + hir_lifetime.name.name() + ), + ) + .emit(); + } + } } } } diff --git a/src/test/ui/single-use-lifetime/zero-uses-in-fn.rs b/src/test/ui/single-use-lifetime/zero-uses-in-fn.rs new file mode 100644 index 0000000000000..b71b189833a72 --- /dev/null +++ b/src/test/ui/single-use-lifetime/zero-uses-in-fn.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we DO warn when lifetime name is not used at all. + +#![deny(unused_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +fn d<'a>() { } //~ ERROR `'a` never used + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/zero-uses-in-fn.stderr b/src/test/ui/single-use-lifetime/zero-uses-in-fn.stderr new file mode 100644 index 0000000000000..f1cdc6e495aa7 --- /dev/null +++ b/src/test/ui/single-use-lifetime/zero-uses-in-fn.stderr @@ -0,0 +1,14 @@ +error: lifetime parameter `'a` never used + --> $DIR/zero-uses-in-fn.rs:17:6 + | +LL | fn d<'a>() { } //~ ERROR `'a` never used + | ^^ + | +note: lint level defined here + --> $DIR/zero-uses-in-fn.rs:13:9 + | +LL | #![deny(unused_lifetime)] + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/single-use-lifetime/zero-uses-in-impl.rs b/src/test/ui/single-use-lifetime/zero-uses-in-impl.rs new file mode 100644 index 0000000000000..6a09727015fa2 --- /dev/null +++ b/src/test/ui/single-use-lifetime/zero-uses-in-impl.rs @@ -0,0 +1,21 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we DO warn when lifetime name is not used at all. + +#![deny(unused_lifetime)] +#![allow(dead_code)] +#![allow(unused_variables)] + +struct Foo { } + +impl<'a> Foo { } //~ ERROR `'a` never used + +fn main() { } diff --git a/src/test/ui/single-use-lifetime/zero-uses-in-impl.stderr b/src/test/ui/single-use-lifetime/zero-uses-in-impl.stderr new file mode 100644 index 0000000000000..d2dd26e68a289 --- /dev/null +++ b/src/test/ui/single-use-lifetime/zero-uses-in-impl.stderr @@ -0,0 +1,14 @@ +error: lifetime parameter `'a` never used + --> $DIR/zero-uses-in-impl.rs:19:6 + | +LL | impl<'a> Foo { } //~ ERROR `'a` never used + | ^^ + | +note: lint level defined here + --> $DIR/zero-uses-in-impl.rs:13:9 + | +LL | #![deny(unused_lifetime)] + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + From 6f98ee95d8ac9fb310cb6b8fcf2ce48d68d08a24 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 10 May 2018 19:04:02 -0400 Subject: [PATCH 6/6] ensure lint are issued in a stable order --- src/librustc/middle/resolve_lifetime.rs | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 49a8832365312..8c19a19523275 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -1306,33 +1306,33 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } }; - for region in defined_by.values() { - debug!( - "check_uses_for_lifetimes_defined_by_scope: region = {:?}", - region - ); - - let def_id = match region { + let mut def_ids: Vec<_> = defined_by.values() + .flat_map(|region| match region { Region::EarlyBound(_, def_id, _) | Region::LateBound(_, def_id, _) - | Region::Free(_, def_id) => def_id, + | Region::Free(_, def_id) => Some(*def_id), - Region::LateBoundAnon(..) | Region::Static => continue, - }; + Region::LateBoundAnon(..) | Region::Static => None, + }) + .collect(); + + // ensure that we issue lints in a repeatable order + def_ids.sort_by_key(|&def_id| self.tcx.def_path_hash(def_id)); + for def_id in def_ids { debug!( "check_uses_for_lifetimes_defined_by_scope: def_id = {:?}", - def_id + def_id, ); - let lifetimeuseset = self.lifetime_uses.remove(def_id); + let lifetimeuseset = self.lifetime_uses.remove(&def_id); debug!( "check_uses_for_lifetimes_defined_by_scope: lifetimeuseset = {:?}", lifetimeuseset ); match lifetimeuseset { Some(LifetimeUseSet::One(_)) => { - let node_id = self.tcx.hir.as_local_node_id(*def_id).unwrap(); + let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap(); debug!("node id first={:?}", node_id); if let hir::map::NodeLifetime(hir_lifetime) = self.tcx.hir.get(node_id) { let span = hir_lifetime.span; @@ -1359,7 +1359,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { debug!("Not one use lifetime"); } None => { - let node_id = self.tcx.hir.as_local_node_id(*def_id).unwrap(); + let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap(); if let hir::map::NodeLifetime(hir_lifetime) = self.tcx.hir.get(node_id) { let span = hir_lifetime.span; let id = hir_lifetime.id;