diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 1fee0dd98634a..2a11b19f85561 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -719,7 +719,7 @@ pub struct Generics { pub type_param_to_index: BTreeMap, pub has_self: bool, - pub has_late_bound_regions: bool, + pub has_late_bound_regions: Option, } impl Generics { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index af11cacb247b6..b086c427ba59f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -121,7 +121,7 @@ use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax::ptr::P; use syntax::symbol::{Symbol, InternedString, keywords}; use syntax::util::lev_distance::find_best_match_for_name; -use syntax_pos::{self, BytePos, Span}; +use syntax_pos::{self, BytePos, Span, MultiSpan}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::itemlikevisit::ItemLikeVisitor; @@ -4689,20 +4689,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Prohibit explicit lifetime arguments if late bound lifetime parameters are present. let has_late_bound_lifetime_defs = - segment.map_or(false, |(_, generics)| generics.has_late_bound_regions); - if has_late_bound_lifetime_defs && !lifetimes.is_empty() { + segment.map_or(None, |(_, generics)| generics.has_late_bound_regions); + if let (Some(span_late), false) = (has_late_bound_lifetime_defs, lifetimes.is_empty()) { // Report this as a lint only if no error was reported previously. + let primary_msg = "cannot specify lifetime arguments explicitly \ + if late bound lifetime parameters are present"; + let note_msg = "the late bound lifetime parameter is introduced here"; if !is_method_call && (lifetimes.len() > lifetime_defs.len() || lifetimes.len() < required_len && !infer_lifetimes) { - self.tcx.sess.span_err(lifetimes[0].span, - "cannot specify lifetime arguments explicitly \ - if late bound lifetime parameters are present"); + let mut err = self.tcx.sess.struct_span_err(lifetimes[0].span, primary_msg); + err.span_note(span_late, note_msg); + err.emit(); *segment = None; } else { + let mut multispan = MultiSpan::from_span(lifetimes[0].span); + multispan.push_span_label(span_late, note_msg.to_string()); self.tcx.sess.add_lint(lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS, - lifetimes[0].id, lifetimes[0].span, - format!("cannot specify lifetime arguments explicitly \ - if late bound lifetime parameters are present")); + lifetimes[0].id, multispan, primary_msg.to_string()); } return; } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 72bd084330dd3..143079b0a0823 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -774,11 +774,11 @@ fn trait_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, node: hir_map::Node<'tcx>) - -> bool { + -> Option { struct LateBoundRegionsDetector<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, binder_depth: u32, - has_late_bound_regions: bool, + has_late_bound_regions: Option, } impl<'a, 'tcx> Visitor<'tcx> for LateBoundRegionsDetector<'a, 'tcx> { @@ -787,7 +787,7 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } fn visit_ty(&mut self, ty: &'tcx hir::Ty) { - if self.has_late_bound_regions { return } + if self.has_late_bound_regions.is_some() { return } match ty.node { hir::TyBareFn(..) => { self.binder_depth += 1; @@ -801,21 +801,21 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn visit_poly_trait_ref(&mut self, tr: &'tcx hir::PolyTraitRef, m: hir::TraitBoundModifier) { - if self.has_late_bound_regions { return } + if self.has_late_bound_regions.is_some() { return } self.binder_depth += 1; intravisit::walk_poly_trait_ref(self, tr, m); self.binder_depth -= 1; } fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { - if self.has_late_bound_regions { return } + if self.has_late_bound_regions.is_some() { return } match self.tcx.named_region_map.defs.get(<.id).cloned() { Some(rl::Region::Static) | Some(rl::Region::EarlyBound(..)) => {} Some(rl::Region::LateBound(debruijn, _)) | Some(rl::Region::LateBoundAnon(debruijn, _)) if debruijn.depth < self.binder_depth => {} - _ => self.has_late_bound_regions = true, + _ => self.has_late_bound_regions = Some(lt.span), } } } @@ -823,13 +823,13 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, generics: &'tcx hir::Generics, decl: &'tcx hir::FnDecl) - -> bool { + -> Option { let mut visitor = LateBoundRegionsDetector { - tcx, binder_depth: 1, has_late_bound_regions: false + tcx, binder_depth: 1, has_late_bound_regions: None }; for lifetime in &generics.lifetimes { if tcx.named_region_map.late_bound.contains(&lifetime.lifetime.id) { - return true; + return Some(lifetime.lifetime.span); } } visitor.visit_fn_decl(decl); @@ -840,24 +840,24 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, hir_map::NodeTraitItem(item) => match item.node { hir::TraitItemKind::Method(ref sig, _) => has_late_bound_regions(tcx, &sig.generics, &sig.decl), - _ => false, + _ => None, }, hir_map::NodeImplItem(item) => match item.node { hir::ImplItemKind::Method(ref sig, _) => has_late_bound_regions(tcx, &sig.generics, &sig.decl), - _ => false, + _ => None, }, hir_map::NodeForeignItem(item) => match item.node { hir::ForeignItemFn(ref fn_decl, _, ref generics) => has_late_bound_regions(tcx, generics, fn_decl), - _ => false, + _ => None, }, hir_map::NodeItem(item) => match item.node { hir::ItemFn(ref fn_decl, .., ref generics, _) => has_late_bound_regions(tcx, generics, fn_decl), - _ => false, + _ => None, }, - _ => false + _ => None } } diff --git a/src/test/ui/method-call-lifetime-args-lint.rs b/src/test/ui/method-call-lifetime-args-lint.rs new file mode 100644 index 0000000000000..f9a4f712e571c --- /dev/null +++ b/src/test/ui/method-call-lifetime-args-lint.rs @@ -0,0 +1,31 @@ +// 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(late_bound_lifetime_arguments)] +#![allow(unused)] + +struct S; + +impl S { + fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} + fn late_implicit(self, _: &u8, _: &u8) {} +} + +fn method_call() { + S.late::<'static>(&0, &0); + //~^ ERROR cannot specify lifetime arguments explicitly + //~| WARN this was previously accepted + + S.late_implicit::<'static>(&0, &0); + //~^ ERROR cannot specify lifetime arguments explicitly + //~| WARN this was previously accepted +} + +fn main() {} diff --git a/src/test/ui/method-call-lifetime-args-lint.stderr b/src/test/ui/method-call-lifetime-args-lint.stderr new file mode 100644 index 0000000000000..e319b54aa2cae --- /dev/null +++ b/src/test/ui/method-call-lifetime-args-lint.stderr @@ -0,0 +1,31 @@ +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/method-call-lifetime-args-lint.rs:22:14 + | +17 | fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} + | -- the late bound lifetime parameter is introduced here +... +22 | S.late::<'static>(&0, &0); + | ^^^^^^^ + | +note: lint level defined here + --> $DIR/method-call-lifetime-args-lint.rs:11:9 + | +11 | #![deny(late_bound_lifetime_arguments)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #42868 + +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/method-call-lifetime-args-lint.rs:26:23 + | +18 | fn late_implicit(self, _: &u8, _: &u8) {} + | - the late bound lifetime parameter is introduced here +... +26 | S.late_implicit::<'static>(&0, &0); + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #42868 + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/method-call-lifetime-args.rs b/src/test/ui/method-call-lifetime-args.rs new file mode 100644 index 0000000000000..7ce3ebb8beec4 --- /dev/null +++ b/src/test/ui/method-call-lifetime-args.rs @@ -0,0 +1,25 @@ +// 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. + +struct S; + +impl S { + fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} + fn late_implicit(self, _: &u8, _: &u8) {} +} + +fn ufcs() { + S::late::<'static>(S, &0, &0); + //~^ ERROR cannot specify lifetime arguments explicitly + S::late_implicit::<'static>(S, &0, &0); + //~^ ERROR cannot specify lifetime arguments explicitly +} + +fn main() {} diff --git a/src/test/ui/method-call-lifetime-args.stderr b/src/test/ui/method-call-lifetime-args.stderr new file mode 100644 index 0000000000000..a6c1b8efe2791 --- /dev/null +++ b/src/test/ui/method-call-lifetime-args.stderr @@ -0,0 +1,26 @@ +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/method-call-lifetime-args.rs:19:15 + | +19 | S::late::<'static>(S, &0, &0); + | ^^^^^^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/method-call-lifetime-args.rs:14:13 + | +14 | fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} + | ^^ + +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/method-call-lifetime-args.rs:21:24 + | +21 | S::late_implicit::<'static>(S, &0, &0); + | ^^^^^^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/method-call-lifetime-args.rs:15:31 + | +15 | fn late_implicit(self, _: &u8, _: &u8) {} + | ^ + +error: aborting due to 2 previous errors +