about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs89
-rw-r--r--tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed29
-rw-r--r--tests/ui/impl-trait/precise-capturing/overcaptures-2024.rs29
-rw-r--r--tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr75
4 files changed, 207 insertions, 15 deletions
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index a239f38006b..30bf80b915b 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<i32>) -> 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<i32>`, 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: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
-    };
+    "`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: "<FIXME>",
+    //};
 }
 
 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<TyCtxt<'tcx>> 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<TyCtxt<'tcx>> 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 00000000000..014ab23e4eb
--- /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 00000000000..e4b7828d60f
--- /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 00000000000..16cb8b7e94b
--- /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
+