diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index e6452ad09278e..38ec414fda9e3 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -338,6 +338,12 @@ declare_lint! { cannot be referred to by absolute paths" } +declare_lint! { + pub EXPLICIT_OUTLIVES_REQUIREMENTS, + Allow, + "outlives requirements can be inferred" +} + /// Some lints that are buffered from `libsyntax`. See `syntax::early_buffered_lints`. pub mod parser { declare_lint! { diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index adf23a5cf2c85..1b4a783be0b14 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1999,3 +1999,233 @@ impl EarlyLintPass for KeywordIdents { lint.emit() } } + + +pub struct ExplicitOutlivesRequirements; + +impl LintPass for ExplicitOutlivesRequirements { + fn get_lints(&self) -> LintArray { + lint_array![EXPLICIT_OUTLIVES_REQUIREMENTS] + } +} + +impl ExplicitOutlivesRequirements { + fn collect_outlives_bound_spans( + &self, + cx: &LateContext, + item_def_id: DefId, + param_name: &str, + bounds: &hir::GenericBounds, + infer_static: bool + ) -> Vec<(usize, Span)> { + // For lack of a more elegant strategy for comparing the `ty::Predicate`s + // returned by this query with the params/bounds grabbed from the HIR—and + // with some regrets—we're going to covert the param/lifetime names to + // strings + let inferred_outlives = cx.tcx.inferred_outlives_of(item_def_id); + + let ty_lt_names = inferred_outlives.iter().filter_map(|pred| { + let binder = match pred { + ty::Predicate::TypeOutlives(binder) => binder, + _ => { return None; } + }; + let ty_outlives_pred = binder.skip_binder(); + let ty_name = match ty_outlives_pred.0.sty { + ty::Param(param) => param.name.to_string(), + _ => { return None; } + }; + let lt_name = match ty_outlives_pred.1 { + ty::RegionKind::ReEarlyBound(region) => { + region.name.to_string() + }, + _ => { return None; } + }; + Some((ty_name, lt_name)) + }).collect::>(); + + let mut bound_spans = Vec::new(); + for (i, bound) in bounds.iter().enumerate() { + if let hir::GenericBound::Outlives(lifetime) = bound { + let is_static = match lifetime.name { + hir::LifetimeName::Static => true, + _ => false + }; + if is_static && !infer_static { + // infer-outlives for 'static is still feature-gated (tracking issue #44493) + continue; + } + + let lt_name = &lifetime.name.ident().to_string(); + if ty_lt_names.contains(&(param_name.to_owned(), lt_name.to_owned())) { + bound_spans.push((i, bound.span())); + } + } + } + bound_spans + } + + fn consolidate_outlives_bound_spans( + &self, + lo: Span, + bounds: &hir::GenericBounds, + bound_spans: Vec<(usize, Span)> + ) -> Vec { + if bounds.is_empty() { + return Vec::new(); + } + if bound_spans.len() == bounds.len() { + let (_, last_bound_span) = bound_spans[bound_spans.len()-1]; + // If all bounds are inferable, we want to delete the colon, so + // start from just after the parameter (span passed as argument) + vec![lo.to(last_bound_span)] + } else { + let mut merged = Vec::new(); + let mut last_merged_i = None; + + let mut from_start = true; + for (i, bound_span) in bound_spans { + match last_merged_i { + // If the first bound is inferable, our span should also eat the trailing `+` + None if i == 0 => { + merged.push(bound_span.to(bounds[1].span().shrink_to_lo())); + last_merged_i = Some(0); + }, + // If consecutive bounds are inferable, merge their spans + Some(h) if i == h+1 => { + if let Some(tail) = merged.last_mut() { + // Also eat the trailing `+` if the first + // more-than-one bound is inferable + let to_span = if from_start && i < bounds.len() { + bounds[i+1].span().shrink_to_lo() + } else { + bound_span + }; + *tail = tail.to(to_span); + last_merged_i = Some(i); + } else { + bug!("another bound-span visited earlier"); + } + }, + _ => { + // When we find a non-inferable bound, subsequent inferable bounds + // won't be consecutive from the start (and we'll eat the leading + // `+` rather than the trailing one) + from_start = false; + merged.push(bounds[i-1].span().shrink_to_hi().to(bound_span)); + last_merged_i = Some(i); + } + } + } + merged + } + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements { + fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) { + let infer_static = cx.tcx.features().infer_static_outlives_requirements; + let def_id = cx.tcx.hir.local_def_id(item.id); + if let hir::ItemKind::Struct(_, ref generics) = item.node { + let mut bound_count = 0; + let mut lint_spans = Vec::new(); + + for param in &generics.params { + let param_name = match param.kind { + hir::GenericParamKind::Lifetime { .. } => { continue; }, + hir::GenericParamKind::Type { .. } => { + match param.name { + hir::ParamName::Fresh(_) => { continue; }, + hir::ParamName::Plain(name) => name.to_string() + } + } + }; + let bound_spans = self.collect_outlives_bound_spans( + cx, def_id, ¶m_name, ¶m.bounds, infer_static + ); + bound_count += bound_spans.len(); + lint_spans.extend( + self.consolidate_outlives_bound_spans( + param.span.shrink_to_hi(), ¶m.bounds, bound_spans + ) + ); + } + + let mut where_lint_spans = Vec::new(); + let mut dropped_predicate_count = 0; + let num_predicates = generics.where_clause.predicates.len(); + for (i, where_predicate) in generics.where_clause.predicates.iter().enumerate() { + if let hir::WherePredicate::BoundPredicate(predicate) = where_predicate { + let param_name = match predicate.bounded_ty.node { + hir::TyKind::Path(ref qpath) => { + if let hir::QPath::Resolved(None, ty_param_path) = qpath { + ty_param_path.segments[0].ident.to_string() + } else { + continue; + } + }, + _ => { continue; } + }; + let bound_spans = self.collect_outlives_bound_spans( + cx, def_id, ¶m_name, &predicate.bounds, infer_static + ); + bound_count += bound_spans.len(); + + let drop_predicate = bound_spans.len() == predicate.bounds.len(); + if drop_predicate { + dropped_predicate_count += 1; + } + + // If all the bounds on a predicate were inferable and there are + // further predicates, we want to eat the trailing comma + if drop_predicate && i + 1 < num_predicates { + let next_predicate_span = generics.where_clause.predicates[i+1].span(); + where_lint_spans.push( + predicate.span.to(next_predicate_span.shrink_to_lo()) + ); + } else { + where_lint_spans.extend( + self.consolidate_outlives_bound_spans( + predicate.span.shrink_to_lo(), + &predicate.bounds, + bound_spans + ) + ); + } + } + } + + // If all predicates are inferable, drop the entire clause + // (including the `where`) + if num_predicates > 0 && dropped_predicate_count == num_predicates { + let full_where_span = generics.span.shrink_to_hi() + .to(generics.where_clause.span() + .expect("span of (nonempty) where clause should exist")); + lint_spans.push( + full_where_span + ); + } else { + lint_spans.extend(where_lint_spans); + } + + if !lint_spans.is_empty() { + let mut err = cx.struct_span_lint( + EXPLICIT_OUTLIVES_REQUIREMENTS, + lint_spans.clone(), + "outlives requirements can be inferred" + ); + err.multipart_suggestion_with_applicability( + if bound_count == 1 { + "remove this bound" + } else { + "remove these bounds" + }, + lint_spans.into_iter().map(|span| (span, "".to_owned())).collect::>(), + Applicability::MachineApplicable + ); + err.emit(); + } + + } + } + +} diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 9e2e3435bd9b5..9e0471f59fbab 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -48,6 +48,7 @@ use rustc::lint::builtin::{ BARE_TRAIT_OBJECTS, ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, ELIDED_LIFETIMES_IN_PATHS, + EXPLICIT_OUTLIVES_REQUIREMENTS, parser::QUESTION_MARK_MACRO_SEP }; use rustc::session; @@ -157,6 +158,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { TypeLimits: TypeLimits::new(), MissingDoc: MissingDoc::new(), MissingDebugImplementations: MissingDebugImplementations::new(), + ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, ]], ['tcx]); store.register_late_pass(sess, false, box BuiltinCombinedLateLintPass::new()); @@ -199,7 +201,8 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { BARE_TRAIT_OBJECTS, UNUSED_EXTERN_CRATES, ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, - ELIDED_LIFETIMES_IN_PATHS + ELIDED_LIFETIMES_IN_PATHS, + EXPLICIT_OUTLIVES_REQUIREMENTS // FIXME(#52665, #47816) not always applicable and not all // macros are ready for this yet. diff --git a/src/test/ui/rust-2018/edition-lint-infer-outlives-multispan.rs b/src/test/ui/rust-2018/edition-lint-infer-outlives-multispan.rs new file mode 100644 index 0000000000000..441e1446e5ad0 --- /dev/null +++ b/src/test/ui/rust-2018/edition-lint-infer-outlives-multispan.rs @@ -0,0 +1,85 @@ +// Copyright 2018 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. + +#![allow(unused)] +#![deny(explicit_outlives_requirements)] + +use std::fmt::{Debug, Display}; + +// These examples should live in edition-lint-infer-outlives.rs, but are split +// into this separate file because they can't be `rustfix`'d (and thus, can't +// be part of a `run-rustfix` test file) until rust-lang-nursery/rustfix#141 +// is solved + +struct TeeOutlivesAyIsDebugBee<'a, 'b, T: 'a + Debug + 'b> { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeWhereOutlivesAyIsDebugBee<'a, 'b, T> where T: 'a + Debug + 'b { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeYooOutlivesAyIsDebugBee<'a, 'b, T, U: 'a + Debug + 'b> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeOutlivesAyYooBeeIsDebug<'a, 'b, T: 'a, U: 'b + Debug> { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: &'b U +} + +struct TeeOutlivesAyYooIsDebugBee<'a, 'b, T: 'a, U: Debug + 'b> { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: &'b U +} + +struct TeeOutlivesAyYooWhereBee<'a, 'b, T: 'a, U> where U: 'b { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: &'b U +} + +struct TeeYooWhereOutlivesAyIsDebugBee<'a, 'b, T, U> where U: 'a + Debug + 'b { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeOutlivesAyYooWhereBeeIsDebug<'a, 'b, T: 'a, U> where U: 'b + Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: &'b U +} + +struct TeeOutlivesAyYooWhereIsDebugBee<'a, 'b, T: 'a, U> where U: Debug + 'b { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: &'b U +} + +struct TeeWhereOutlivesAyYooWhereBeeIsDebug<'a, 'b, T, U> where T: 'a, U: 'b + Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: &'b U +} + +struct TeeWhereOutlivesAyYooWhereIsDebugBee<'a, 'b, T, U> where T: 'a, U: Debug + 'b { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: &'b U +} + +fn main() {} diff --git a/src/test/ui/rust-2018/edition-lint-infer-outlives-multispan.stderr b/src/test/ui/rust-2018/edition-lint-infer-outlives-multispan.stderr new file mode 100644 index 0000000000000..8b05a8094619b --- /dev/null +++ b/src/test/ui/rust-2018/edition-lint-infer-outlives-multispan.stderr @@ -0,0 +1,118 @@ +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-multispan.rs:21:43 + | +LL | struct TeeOutlivesAyIsDebugBee<'a, 'b, T: 'a + Debug + 'b> { + | ^^^^^ ^^^^^ + | +note: lint level defined here + --> $DIR/edition-lint-infer-outlives-multispan.rs:12:9 + | +LL | #![deny(explicit_outlives_requirements)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: remove these bounds + | +LL | struct TeeOutlivesAyIsDebugBee<'a, 'b, T: Debug> { + | -- -- + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-multispan.rs:26:57 + | +LL | struct TeeWhereOutlivesAyIsDebugBee<'a, 'b, T> where T: 'a + Debug + 'b { + | ^^^^^ ^^^^^ +help: remove these bounds + | +LL | struct TeeWhereOutlivesAyIsDebugBee<'a, 'b, T> where T: Debug { + | -- -- + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-multispan.rs:31:49 + | +LL | struct TeeYooOutlivesAyIsDebugBee<'a, 'b, T, U: 'a + Debug + 'b> { + | ^^^^^ ^^^^^ +help: remove these bounds + | +LL | struct TeeYooOutlivesAyIsDebugBee<'a, 'b, T, U: Debug> { + | -- -- + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-multispan.rs:37:44 + | +LL | struct TeeOutlivesAyYooBeeIsDebug<'a, 'b, T: 'a, U: 'b + Debug> { + | ^^^^ ^^^^^ +help: remove these bounds + | +LL | struct TeeOutlivesAyYooBeeIsDebug<'a, 'b, T, U: Debug> { + | -- -- + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-multispan.rs:43:44 + | +LL | struct TeeOutlivesAyYooIsDebugBee<'a, 'b, T: 'a, U: Debug + 'b> { + | ^^^^ ^^^^^ +help: remove these bounds + | +LL | struct TeeOutlivesAyYooIsDebugBee<'a, 'b, T, U: Debug> { + | -- -- + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-multispan.rs:49:42 + | +LL | struct TeeOutlivesAyYooWhereBee<'a, 'b, T: 'a, U> where U: 'b { + | ^^^^ ^^^^^^^^^^^^ +help: remove these bounds + | +LL | struct TeeOutlivesAyYooWhereBee<'a, 'b, T, U> { + | -- -- + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-multispan.rs:55:63 + | +LL | struct TeeYooWhereOutlivesAyIsDebugBee<'a, 'b, T, U> where U: 'a + Debug + 'b { + | ^^^^^ ^^^^^ +help: remove these bounds + | +LL | struct TeeYooWhereOutlivesAyIsDebugBee<'a, 'b, T, U> where U: Debug { + | -- -- + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-multispan.rs:61:49 + | +LL | struct TeeOutlivesAyYooWhereBeeIsDebug<'a, 'b, T: 'a, U> where U: 'b + Debug { + | ^^^^ ^^^^^ +help: remove these bounds + | +LL | struct TeeOutlivesAyYooWhereBeeIsDebug<'a, 'b, T, U> where U: Debug { + | -- -- + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-multispan.rs:67:49 + | +LL | struct TeeOutlivesAyYooWhereIsDebugBee<'a, 'b, T: 'a, U> where U: Debug + 'b { + | ^^^^ ^^^^^ +help: remove these bounds + | +LL | struct TeeOutlivesAyYooWhereIsDebugBee<'a, 'b, T, U> where U: Debug { + | -- -- + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-multispan.rs:73:65 + | +LL | struct TeeWhereOutlivesAyYooWhereBeeIsDebug<'a, 'b, T, U> where T: 'a, U: 'b + Debug { + | ^^^^^^^ ^^^^^ +help: remove these bounds + | +LL | struct TeeWhereOutlivesAyYooWhereBeeIsDebug<'a, 'b, T, U> where U: Debug { + | -- -- + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives-multispan.rs:79:65 + | +LL | struct TeeWhereOutlivesAyYooWhereIsDebugBee<'a, 'b, T, U> where T: 'a, U: Debug + 'b { + | ^^^^^^^ ^^^^^ +help: remove these bounds + | +LL | struct TeeWhereOutlivesAyYooWhereIsDebugBee<'a, 'b, T, U> where U: Debug { + | -- -- + +error: aborting due to 11 previous errors + diff --git a/src/test/ui/rust-2018/edition-lint-infer-outlives.fixed b/src/test/ui/rust-2018/edition-lint-infer-outlives.fixed new file mode 100644 index 0000000000000..d70c847e9fe68 --- /dev/null +++ b/src/test/ui/rust-2018/edition-lint-infer-outlives.fixed @@ -0,0 +1,212 @@ +// Copyright 2018 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. + +// run-rustfix + +#![allow(unused)] +#![deny(explicit_outlives_requirements)] + +use std::fmt::{Debug, Display}; + + +// Programmatically generated examples! +// +// Exercise outlives bounds for each of the following parameter/position +// combinations— +// +// • one generic parameter (T) bound inline +// • one parameter (T) with a where clause +// • two parameters (T and U), both bound inline +// • two paramters (T and U), one bound inline, one with a where clause +// • two parameters (T and U), both with where clauses +// +// —and for every permutation of 0, 1, or 2 lifetimes to outlive and 0 or 1 +// trait bounds distributed among said parameters (subject to no where clause +// being empty and the struct having at least one lifetime). + + +struct TeeOutlivesAy<'a, T> { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeOutlivesAyIsDebug<'a, T: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeIsDebugOutlivesAy<'a, T: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeOutlivesAyBee<'a, 'b, T> { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeOutlivesAyBeeIsDebug<'a, 'b, T: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeIsDebugOutlivesAyBee<'a, 'b, T: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeWhereOutlivesAy<'a, T> { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeWhereOutlivesAyIsDebug<'a, T> where T: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeWhereIsDebugOutlivesAy<'a, T> where T: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeWhereOutlivesAyBee<'a, 'b, T> { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeWhereOutlivesAyBeeIsDebug<'a, 'b, T> where T: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeWhereIsDebugOutlivesAyBee<'a, 'b, T> where T: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeYooOutlivesAy<'a, T, U> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeYooOutlivesAyIsDebug<'a, T, U: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeYooIsDebugOutlivesAy<'a, T, U: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeOutlivesAyYooIsDebug<'a, T, U: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: U +} + +struct TeeYooOutlivesAyBee<'a, 'b, T, U> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeYooOutlivesAyBeeIsDebug<'a, 'b, T, U: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeYooIsDebugOutlivesAyBee<'a, 'b, T, U: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeOutlivesAyBeeYooIsDebug<'a, 'b, T, U: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T, + yoo: U +} + +struct TeeYooWhereOutlivesAy<'a, T, U> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeYooWhereOutlivesAyIsDebug<'a, T, U> where U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeYooWhereIsDebugOutlivesAy<'a, T, U> where U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeOutlivesAyYooWhereIsDebug<'a, T, U> where U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: U +} + +struct TeeYooWhereOutlivesAyBee<'a, 'b, T, U> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeYooWhereOutlivesAyBeeIsDebug<'a, 'b, T, U> where U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeYooWhereIsDebugOutlivesAyBee<'a, 'b, T, U> where U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeOutlivesAyBeeYooWhereIsDebug<'a, 'b, T, U> where U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T, + yoo: U +} + +struct TeeWhereOutlivesAyYooWhereIsDebug<'a, T, U> where U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: U +} + +struct TeeWhereOutlivesAyBeeYooWhereIsDebug<'a, 'b, T, U> where U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T, + yoo: U +} + + +// But outlives inference for 'static lifetimes is under a separate +// feature-gate for now +// (https://github.com/rust-lang/rust/issues/44493#issuecomment-407846046). +struct StaticRef { + field: &'static T +} + + +fn main() {} diff --git a/src/test/ui/rust-2018/edition-lint-infer-outlives.rs b/src/test/ui/rust-2018/edition-lint-infer-outlives.rs new file mode 100644 index 0000000000000..0e4436fe1632f --- /dev/null +++ b/src/test/ui/rust-2018/edition-lint-infer-outlives.rs @@ -0,0 +1,212 @@ +// Copyright 2018 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. + +// run-rustfix + +#![allow(unused)] +#![deny(explicit_outlives_requirements)] + +use std::fmt::{Debug, Display}; + + +// Programmatically generated examples! +// +// Exercise outlives bounds for each of the following parameter/position +// combinations— +// +// • one generic parameter (T) bound inline +// • one parameter (T) with a where clause +// • two parameters (T and U), both bound inline +// • two paramters (T and U), one bound inline, one with a where clause +// • two parameters (T and U), both with where clauses +// +// —and for every permutation of 0, 1, or 2 lifetimes to outlive and 0 or 1 +// trait bounds distributed among said parameters (subject to no where clause +// being empty and the struct having at least one lifetime). + + +struct TeeOutlivesAy<'a, T: 'a> { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeOutlivesAyIsDebug<'a, T: 'a + Debug> { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeIsDebugOutlivesAy<'a, T: Debug + 'a> { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeOutlivesAyBee<'a, 'b, T: 'a + 'b> { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeOutlivesAyBeeIsDebug<'a, 'b, T: 'a + 'b + Debug> { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeIsDebugOutlivesAyBee<'a, 'b, T: Debug + 'a + 'b> { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeWhereOutlivesAy<'a, T> where T: 'a { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeWhereOutlivesAyIsDebug<'a, T> where T: 'a + Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeWhereIsDebugOutlivesAy<'a, T> where T: Debug + 'a { + //~^ ERROR outlives requirements can be inferred + tee: &'a T +} + +struct TeeWhereOutlivesAyBee<'a, 'b, T> where T: 'a + 'b { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeWhereOutlivesAyBeeIsDebug<'a, 'b, T> where T: 'a + 'b + Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeWhereIsDebugOutlivesAyBee<'a, 'b, T> where T: Debug + 'a + 'b { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T +} + +struct TeeYooOutlivesAy<'a, T, U: 'a> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeYooOutlivesAyIsDebug<'a, T, U: 'a + Debug> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeYooIsDebugOutlivesAy<'a, T, U: Debug + 'a> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeOutlivesAyYooIsDebug<'a, T: 'a, U: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: U +} + +struct TeeYooOutlivesAyBee<'a, 'b, T, U: 'a + 'b> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeYooOutlivesAyBeeIsDebug<'a, 'b, T, U: 'a + 'b + Debug> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeYooIsDebugOutlivesAyBee<'a, 'b, T, U: Debug + 'a + 'b> { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeOutlivesAyBeeYooIsDebug<'a, 'b, T: 'a + 'b, U: Debug> { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T, + yoo: U +} + +struct TeeYooWhereOutlivesAy<'a, T, U> where U: 'a { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeYooWhereOutlivesAyIsDebug<'a, T, U> where U: 'a + Debug { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeYooWhereIsDebugOutlivesAy<'a, T, U> where U: Debug + 'a { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a U +} + +struct TeeOutlivesAyYooWhereIsDebug<'a, T: 'a, U> where U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: U +} + +struct TeeYooWhereOutlivesAyBee<'a, 'b, T, U> where U: 'a + 'b { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeYooWhereOutlivesAyBeeIsDebug<'a, 'b, T, U> where U: 'a + 'b + Debug { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeYooWhereIsDebugOutlivesAyBee<'a, 'b, T, U> where U: Debug + 'a + 'b { + //~^ ERROR outlives requirements can be inferred + tee: T, + yoo: &'a &'b U +} + +struct TeeOutlivesAyBeeYooWhereIsDebug<'a, 'b, T: 'a + 'b, U> where U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T, + yoo: U +} + +struct TeeWhereOutlivesAyYooWhereIsDebug<'a, T, U> where T: 'a, U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a T, + yoo: U +} + +struct TeeWhereOutlivesAyBeeYooWhereIsDebug<'a, 'b, T, U> where T: 'a + 'b, U: Debug { + //~^ ERROR outlives requirements can be inferred + tee: &'a &'b T, + yoo: U +} + + +// But outlives inference for 'static lifetimes is under a separate +// feature-gate for now +// (https://github.com/rust-lang/rust/issues/44493#issuecomment-407846046). +struct StaticRef { + field: &'static T +} + + +fn main() {} diff --git a/src/test/ui/rust-2018/edition-lint-infer-outlives.stderr b/src/test/ui/rust-2018/edition-lint-infer-outlives.stderr new file mode 100644 index 0000000000000..910de1dd06c0e --- /dev/null +++ b/src/test/ui/rust-2018/edition-lint-infer-outlives.stderr @@ -0,0 +1,188 @@ +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:35:27 + | +LL | struct TeeOutlivesAy<'a, T: 'a> { + | ^^^^ help: remove this bound + | +note: lint level defined here + --> $DIR/edition-lint-infer-outlives.rs:14:9 + | +LL | #![deny(explicit_outlives_requirements)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:40:36 + | +LL | struct TeeOutlivesAyIsDebug<'a, T: 'a + Debug> { + | ^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:45:41 + | +LL | struct TeeIsDebugOutlivesAy<'a, T: Debug + 'a> { + | ^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:50:34 + | +LL | struct TeeOutlivesAyBee<'a, 'b, T: 'a + 'b> { + | ^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:55:43 + | +LL | struct TeeOutlivesAyBeeIsDebug<'a, 'b, T: 'a + 'b + Debug> { + | ^^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:60:48 + | +LL | struct TeeIsDebugOutlivesAyBee<'a, 'b, T: Debug + 'a + 'b> { + | ^^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:65:33 + | +LL | struct TeeWhereOutlivesAy<'a, T> where T: 'a { + | ^^^^^^^^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:70:50 + | +LL | struct TeeWhereOutlivesAyIsDebug<'a, T> where T: 'a + Debug { + | ^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:75:55 + | +LL | struct TeeWhereIsDebugOutlivesAy<'a, T> where T: Debug + 'a { + | ^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:80:40 + | +LL | struct TeeWhereOutlivesAyBee<'a, 'b, T> where T: 'a + 'b { + | ^^^^^^^^^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:85:57 + | +LL | struct TeeWhereOutlivesAyBeeIsDebug<'a, 'b, T> where T: 'a + 'b + Debug { + | ^^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:90:62 + | +LL | struct TeeWhereIsDebugOutlivesAyBee<'a, 'b, T> where T: Debug + 'a + 'b { + | ^^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:95:33 + | +LL | struct TeeYooOutlivesAy<'a, T, U: 'a> { + | ^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:101:42 + | +LL | struct TeeYooOutlivesAyIsDebug<'a, T, U: 'a + Debug> { + | ^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:107:47 + | +LL | struct TeeYooIsDebugOutlivesAy<'a, T, U: Debug + 'a> { + | ^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:113:37 + | +LL | struct TeeOutlivesAyYooIsDebug<'a, T: 'a, U: Debug> { + | ^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:119:40 + | +LL | struct TeeYooOutlivesAyBee<'a, 'b, T, U: 'a + 'b> { + | ^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:125:49 + | +LL | struct TeeYooOutlivesAyBeeIsDebug<'a, 'b, T, U: 'a + 'b + Debug> { + | ^^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:131:54 + | +LL | struct TeeYooIsDebugOutlivesAyBee<'a, 'b, T, U: Debug + 'a + 'b> { + | ^^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:137:44 + | +LL | struct TeeOutlivesAyBeeYooIsDebug<'a, 'b, T: 'a + 'b, U: Debug> { + | ^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:143:39 + | +LL | struct TeeYooWhereOutlivesAy<'a, T, U> where U: 'a { + | ^^^^^^^^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:149:56 + | +LL | struct TeeYooWhereOutlivesAyIsDebug<'a, T, U> where U: 'a + Debug { + | ^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:155:61 + | +LL | struct TeeYooWhereIsDebugOutlivesAy<'a, T, U> where U: Debug + 'a { + | ^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:161:42 + | +LL | struct TeeOutlivesAyYooWhereIsDebug<'a, T: 'a, U> where U: Debug { + | ^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:167:46 + | +LL | struct TeeYooWhereOutlivesAyBee<'a, 'b, T, U> where U: 'a + 'b { + | ^^^^^^^^^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:173:63 + | +LL | struct TeeYooWhereOutlivesAyBeeIsDebug<'a, 'b, T, U> where U: 'a + 'b + Debug { + | ^^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:179:68 + | +LL | struct TeeYooWhereIsDebugOutlivesAyBee<'a, 'b, T, U> where U: Debug + 'a + 'b { + | ^^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:185:49 + | +LL | struct TeeOutlivesAyBeeYooWhereIsDebug<'a, 'b, T: 'a + 'b, U> where U: Debug { + | ^^^^^^^^^ help: remove these bounds + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:191:58 + | +LL | struct TeeWhereOutlivesAyYooWhereIsDebug<'a, T, U> where T: 'a, U: Debug { + | ^^^^^^^ help: remove this bound + +error: outlives requirements can be inferred + --> $DIR/edition-lint-infer-outlives.rs:197:65 + | +LL | struct TeeWhereOutlivesAyBeeYooWhereIsDebug<'a, 'b, T, U> where T: 'a + 'b, U: Debug { + | ^^^^^^^^^^^^ help: remove these bounds + +error: aborting due to 30 previous errors + diff --git a/src/test/ui/rust-2018/edition-lint-uninferable-outlives.rs b/src/test/ui/rust-2018/edition-lint-uninferable-outlives.rs new file mode 100644 index 0000000000000..00059294a9764 --- /dev/null +++ b/src/test/ui/rust-2018/edition-lint-uninferable-outlives.rs @@ -0,0 +1,40 @@ +// Copyright 2018 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 + +#![allow(unused)] +#![deny(explicit_outlives_requirements)] + +// A case where we can't infer the outlives requirement. Example copied from +// RFC 2093. +// (https://rust-lang.github.io/rfcs/2093-infer-outlives.html +// #where-explicit-annotations-would-still-be-required) + + +trait MakeRef<'a> { + type Type; +} + +impl<'a, T> MakeRef<'a> for Vec + where T: 'a // still required +{ + type Type = &'a T; +} + + +struct Foo<'a, T> + where T: 'a // still required, not inferred from `field` +{ + field: as MakeRef<'a>>::Type +} + + +fn main() {}