Skip to content

Commit

Permalink
And finally add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed May 14, 2024
1 parent 1529c66 commit 052de1d
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 15 deletions.
89 changes: 74 additions & 15 deletions compiler/rustc_lint/src/impl_trait_overcaptures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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",
);
}
Expand Down
29 changes: 29 additions & 0 deletions tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed
Original file line number Diff line number Diff line change
@@ -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() {}
29 changes: 29 additions & 0 deletions tests/ui/impl-trait/precise-capturing/overcaptures-2024.rs
Original file line number Diff line number Diff line change
@@ -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() {}
75 changes: 75 additions & 0 deletions tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 052de1d

Please sign in to comment.