From 052de1da4f579f63d2403f8045f2d6e342a90200 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 21 Apr 2024 11:27:27 -0400 Subject: [PATCH] And finally add tests --- .../rustc_lint/src/impl_trait_overcaptures.rs | 89 +++++++++++++++---- .../precise-capturing/overcaptures-2024.fixed | 29 ++++++ .../precise-capturing/overcaptures-2024.rs | 29 ++++++ .../overcaptures-2024.stderr | 75 ++++++++++++++++ 4 files changed, 207 insertions(+), 15 deletions(-) create mode 100644 tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed create mode 100644 tests/ui/impl-trait/precise-capturing/overcaptures-2024.rs create mode 100644 tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index a239f38006bdc..30bf80b915b87 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -9,31 +9,89 @@ use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; -use rustc_session::lint::FutureIncompatibilityReason; -use rustc_span::edition::Edition; -use rustc_span::{BytePos, Span}; +use rustc_middle::{bug, span_bug}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::{sym, BytePos, Span}; use crate::fluent_generated as fluent; use crate::{LateContext, LateLintPass}; -// TODO: feature gate these too - declare_lint! { - /// UwU + /// The `impl_trait_overcaptures` lint warns against cases where lifetime + /// capture behavior will differ in edition 2024. + /// + /// In the 2024 edition, `impl Trait`s will capture all lifetimes in scope, + /// rather than just the lifetimes that are mentioned in the bounds of the type. + /// Often these sets are equal, but if not, it means that the `impl Trait` may + /// cause erroneous borrow-checker errors. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// # #![feature(precise_capturing)] + /// # #![allow(incomplete_features)] + /// # #![deny(impl_trait_overcaptures)] + /// # use std::fmt::Display; + /// let mut x = vec![]; + /// x.push(1); + /// + /// fn test(x: &Vec) -> impl Display { + /// x[0] + /// } + /// + /// let element = test(&x); + /// x.push(2); + /// println!("{element}"); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In edition < 2024, the returned `impl Display` doesn't capture the + /// lifetime from the `&Vec`, so the vector can be mutably borrowed + /// while the `impl Display` is live. + /// + /// To fix this, we can explicitly state that the `impl Display` doesn't + /// capture any lifetimes, using `impl use<> Display`. pub IMPL_TRAIT_OVERCAPTURES, Allow, - "will capture more lifetimes than possibly intended in edition 2024", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), - reference: "", - }; + "`impl Trait` will capture more lifetimes than possibly intended in edition 2024", + @feature_gate = sym::precise_capturing; + //@future_incompatible = FutureIncompatibleInfo { + // reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), + // reference: "", + //}; } declare_lint! { - /// UwU + /// The `impl_trait_redundant_captures` lint warns against cases where use of the + /// precise capturing `use<...>` syntax is not needed. + /// + /// In the 2024 edition, `impl Trait`s will capture all lifetimes in scope. + /// If precise-capturing `use<...>` syntax is used, and the set of parameters + /// that are captures are *equal* to the set of parameters in scope, then + /// the syntax is redundant, and can be removed. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// # #![feature(precise_capturing, lifetime_capture_rules_2024)] + /// # #![allow(incomplete_features)] + /// # #![deny(impl_trait_redundant_captures)] + /// fn test<'a>(x: &'a i32) -> impl use<'a> Sized { x } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// To fix this, remove the `use<'a>`, since the lifetime is already captured + /// since it is in scope. pub IMPL_TRAIT_REDUNDANT_CAPTURES, Warn, - "uwu 2" + "redundant precise-capturing `use<...>` syntax on an `impl Trait`", + @feature_gate = sym::precise_capturing; } declare_lint_pass!( @@ -106,7 +164,8 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { for arg in t.bound_vars() { let arg: ty::BoundVariableKind = arg; match arg { - ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(def_id, ..)) => { + ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(def_id, ..)) + | ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, _)) => { added.push(def_id); let unique = self.in_scope_parameters.insert(def_id); assert!(unique); @@ -265,7 +324,7 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { } _ => { self.tcx.dcx().span_delayed_bug( - self.tcx().hir().span(arg.hir_id()), + self.tcx.hir().span(arg.hir_id()), "no valid for captured arg", ); } diff --git a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed new file mode 100644 index 0000000000000..014ab23e4ebc2 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed @@ -0,0 +1,29 @@ +//@ run-rustfix + +#![feature(precise_capturing)] +#![allow(unused, incomplete_features)] +#![deny(impl_trait_overcaptures)] + +fn named<'a>(x: &'a i32) -> impl use<> Sized { *x } +//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024 + +fn implicit(x: &i32) -> impl use<> Sized { *x } +//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024 + +struct W; +impl W { + fn hello(&self, x: &i32) -> impl use<'_> Sized + '_ { self } + //~^ ERROR `impl Sized + '_` will capture more lifetimes than possibly intended in edition 2024 +} + +trait Higher<'a> { + type Output; +} +impl Higher<'_> for () { + type Output = (); +} + +fn hrtb() -> impl for<'a> Higher<'a, Output = impl use<> Sized> {} +//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024 + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.rs b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.rs new file mode 100644 index 0000000000000..e4b7828d60ffd --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.rs @@ -0,0 +1,29 @@ +//@ run-rustfix + +#![feature(precise_capturing)] +#![allow(unused, incomplete_features)] +#![deny(impl_trait_overcaptures)] + +fn named<'a>(x: &'a i32) -> impl Sized { *x } +//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024 + +fn implicit(x: &i32) -> impl Sized { *x } +//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024 + +struct W; +impl W { + fn hello(&self, x: &i32) -> impl Sized + '_ { self } + //~^ ERROR `impl Sized + '_` will capture more lifetimes than possibly intended in edition 2024 +} + +trait Higher<'a> { + type Output; +} +impl Higher<'_> for () { + type Output = (); +} + +fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized> {} +//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024 + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr new file mode 100644 index 0000000000000..16cb8b7e94b11 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr @@ -0,0 +1,75 @@ +error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024 + --> $DIR/overcaptures-2024.rs:7:29 + | +LL | fn named<'a>(x: &'a i32) -> impl Sized { *x } + | ^^^^^^^^^^ + | +note: specifically, this lifetime is in scope but not mentioned in the type's bounds + --> $DIR/overcaptures-2024.rs:7:10 + | +LL | fn named<'a>(x: &'a i32) -> impl Sized { *x } + | ^^ + = note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024 +note: the lint level is defined here + --> $DIR/overcaptures-2024.rs:5:9 + | +LL | #![deny(impl_trait_overcaptures)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: use the precise capturing `use<...>` syntax to make the captures explicit + | +LL | fn named<'a>(x: &'a i32) -> impl use<> Sized { *x } + | +++++ + +error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024 + --> $DIR/overcaptures-2024.rs:10:25 + | +LL | fn implicit(x: &i32) -> impl Sized { *x } + | ^^^^^^^^^^ + | +note: specifically, this lifetime is in scope but not mentioned in the type's bounds + --> $DIR/overcaptures-2024.rs:10:16 + | +LL | fn implicit(x: &i32) -> impl Sized { *x } + | ^ + = note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024 +help: use the precise capturing `use<...>` syntax to make the captures explicit + | +LL | fn implicit(x: &i32) -> impl use<> Sized { *x } + | +++++ + +error: `impl Sized + '_` will capture more lifetimes than possibly intended in edition 2024 + --> $DIR/overcaptures-2024.rs:15:33 + | +LL | fn hello(&self, x: &i32) -> impl Sized + '_ { self } + | ^^^^^^^^^^^^^^^ + | +note: specifically, this lifetime is in scope but not mentioned in the type's bounds + --> $DIR/overcaptures-2024.rs:15:24 + | +LL | fn hello(&self, x: &i32) -> impl Sized + '_ { self } + | ^ + = note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024 +help: use the precise capturing `use<...>` syntax to make the captures explicit + | +LL | fn hello(&self, x: &i32) -> impl use<'_> Sized + '_ { self } + | +++++++ + +error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024 + --> $DIR/overcaptures-2024.rs:26:47 + | +LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized> {} + | ^^^^^^^^^^ + | +note: specifically, this lifetime is in scope but not mentioned in the type's bounds + --> $DIR/overcaptures-2024.rs:26:23 + | +LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized> {} + | ^^ + = note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024 +help: use the precise capturing `use<...>` syntax to make the captures explicit + | +LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl use<> Sized> {} + | +++++ + +error: aborting due to 4 previous errors +