From 032d97fa018de3d50136b338e02dabe84ab9c695 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Sun, 26 Aug 2018 12:22:04 -0700 Subject: [PATCH] in which inferable outlives-requirements are linted MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit RFC 2093 (tracking issue #44493) lets us leave off commonsensically inferable `T: 'a` outlives requirements. (A separate feature-gate was split off for the case of 'static lifetimes, for which questions still remain.) Detecting these was requested as an idioms-2018 lint. It turns out that issuing a correct, autofixable suggestion here is somewhat subtle in the presence of other bounds and generic parameters. Basically, we want to handle these three cases: • One outlives-bound. We want to drop the bound altogether, including the colon— MyStruct<'a, T: 'a> ^^^^ help: remove this bound • An outlives bound first, followed by a trait bound. We want to delete the outlives bound and the following plus sign (and hopefully get the whitespace right, too)— MyStruct<'a, T: 'a + MyTrait> ^^^^^ help: remove this bound • An outlives bound after a trait bound. We want to delete the outlives lifetime and the preceding plus sign— MyStruct<'a, T: MyTrait + 'a> ^^^^^ help: remove this bound This gets (slightly) even more complicated in the case of where clauses, where we want to drop the where clause altogether if there's just the one bound. Hopefully the comments are enough to explain what's going on! A script (in Python, sorry) was used to generate the hopefully-sufficiently-exhaustive UI test input. Some of these are split off into a different file because rust-lang-nursery/rustfix#141 (and, causally upstream of that, #53934) prevents them from being `run-rustfix`-tested. We also make sure to include a UI test of a case (copied from RFC 2093) where the outlives-bound can't be inferred. Special thanks to Niko Matsakis for pointing out the `inferred_outlives_of` query, rather than blindly stripping outlives requirements as if we weren't a production compiler and didn't care. This concerns #52042. --- src/librustc/lint/builtin.rs | 6 + src/librustc_lint/builtin.rs | 230 ++++++++++++++++++ src/librustc_lint/lib.rs | 5 +- .../edition-lint-infer-outlives-multispan.rs | 85 +++++++ ...ition-lint-infer-outlives-multispan.stderr | 118 +++++++++ .../edition-lint-infer-outlives.fixed | 212 ++++++++++++++++ .../rust-2018/edition-lint-infer-outlives.rs | 212 ++++++++++++++++ .../edition-lint-infer-outlives.stderr | 188 ++++++++++++++ .../edition-lint-uninferable-outlives.rs | 40 +++ 9 files changed, 1095 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/rust-2018/edition-lint-infer-outlives-multispan.rs create mode 100644 src/test/ui/rust-2018/edition-lint-infer-outlives-multispan.stderr create mode 100644 src/test/ui/rust-2018/edition-lint-infer-outlives.fixed create mode 100644 src/test/ui/rust-2018/edition-lint-infer-outlives.rs create mode 100644 src/test/ui/rust-2018/edition-lint-infer-outlives.stderr create mode 100644 src/test/ui/rust-2018/edition-lint-uninferable-outlives.rs diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index e6452ad0927..38ec414fda9 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -338,6 +338,12 @@ 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 adf23a5cf2c..1b4a783be0b 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1999,3 +1999,233 @@ fn check_ident(&mut self, cx: &EarlyContext, ident: ast::Ident) { 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 9e2e3435bd9..9e0471f59fb 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -48,6 +48,7 @@ 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 @@ macro_rules! add_lint_group { 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 @@ macro_rules! add_lint_group { 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 00000000000..441e1446e5a --- /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 00000000000..8b05a809461 --- /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 00000000000..d70c847e9fe --- /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 00000000000..0e4436fe163 --- /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 00000000000..910de1dd06c --- /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 00000000000..00059294a97 --- /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() {} -- 2.44.0