Warn against redundant use<...>
This commit is contained in:
parent
f3fb727b08
commit
1529c661e4
@ -1407,7 +1407,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
bounds,
|
bounds,
|
||||||
fn_kind,
|
fn_kind,
|
||||||
itctx,
|
itctx,
|
||||||
precise_capturing.as_deref().map(|(args, _)| args.as_slice()),
|
precise_capturing.as_deref().map(|(args, span)| (args.as_slice(), *span)),
|
||||||
),
|
),
|
||||||
ImplTraitContext::Universal => {
|
ImplTraitContext::Universal => {
|
||||||
if let Some(&(_, span)) = precise_capturing.as_deref() {
|
if let Some(&(_, span)) = precise_capturing.as_deref() {
|
||||||
@ -1523,7 +1523,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
bounds: &GenericBounds,
|
bounds: &GenericBounds,
|
||||||
fn_kind: Option<FnDeclKind>,
|
fn_kind: Option<FnDeclKind>,
|
||||||
itctx: ImplTraitContext,
|
itctx: ImplTraitContext,
|
||||||
precise_capturing_args: Option<&[PreciseCapturingArg]>,
|
precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
|
||||||
) -> hir::TyKind<'hir> {
|
) -> hir::TyKind<'hir> {
|
||||||
// Make sure we know that some funky desugaring has been going on here.
|
// Make sure we know that some funky desugaring has been going on here.
|
||||||
// This is a first: there is code in other places like for loop
|
// This is a first: there is code in other places like for loop
|
||||||
@ -1533,7 +1533,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
|
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
|
||||||
|
|
||||||
let captured_lifetimes_to_duplicate =
|
let captured_lifetimes_to_duplicate =
|
||||||
if let Some(precise_capturing) = precise_capturing_args {
|
if let Some((precise_capturing, _)) = precise_capturing_args {
|
||||||
// We'll actually validate these later on; all we need is the list of
|
// We'll actually validate these later on; all we need is the list of
|
||||||
// lifetimes to duplicate during this portion of lowering.
|
// lifetimes to duplicate during this portion of lowering.
|
||||||
precise_capturing
|
precise_capturing
|
||||||
@ -1607,7 +1607,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
|
captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
|
||||||
span: Span,
|
span: Span,
|
||||||
opaque_ty_span: Span,
|
opaque_ty_span: Span,
|
||||||
precise_capturing_args: Option<&[PreciseCapturingArg]>,
|
precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
|
||||||
lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
|
lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
|
||||||
) -> hir::TyKind<'hir> {
|
) -> hir::TyKind<'hir> {
|
||||||
let opaque_ty_def_id = self.create_def(
|
let opaque_ty_def_id = self.create_def(
|
||||||
@ -1698,8 +1698,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
this.with_remapping(captured_to_synthesized_mapping, |this| {
|
this.with_remapping(captured_to_synthesized_mapping, |this| {
|
||||||
(
|
(
|
||||||
lower_item_bounds(this),
|
lower_item_bounds(this),
|
||||||
precise_capturing_args.map(|precise_capturing| {
|
precise_capturing_args.map(|(precise_capturing, span)| {
|
||||||
this.lower_precise_capturing_args(precise_capturing)
|
(
|
||||||
|
this.lower_precise_capturing_args(precise_capturing),
|
||||||
|
this.lower_span(span),
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -2631,7 +2631,7 @@ pub struct OpaqueTy<'hir> {
|
|||||||
/// lowered as an associated type.
|
/// lowered as an associated type.
|
||||||
pub in_trait: bool,
|
pub in_trait: bool,
|
||||||
/// List of arguments captured via `impl use<'a, P, ...> Trait` syntax.
|
/// List of arguments captured via `impl use<'a, P, ...> Trait` syntax.
|
||||||
pub precise_capturing_args: Option<&'hir [PreciseCapturingArg<'hir>]>,
|
pub precise_capturing_args: Option<(&'hir [PreciseCapturingArg<'hir>], Span)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, HashStable_Generic)]
|
#[derive(Debug, Clone, Copy, HashStable_Generic)]
|
||||||
@ -2641,6 +2641,15 @@ pub enum PreciseCapturingArg<'hir> {
|
|||||||
Param(PreciseCapturingNonLifetimeArg),
|
Param(PreciseCapturingNonLifetimeArg),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PreciseCapturingArg<'_> {
|
||||||
|
pub fn hir_id(self) -> HirId {
|
||||||
|
match self {
|
||||||
|
PreciseCapturingArg::Lifetime(lt) => lt.hir_id,
|
||||||
|
PreciseCapturingArg::Param(param) => param.hir_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// We need to have a [`Node`] for the [`HirId`] that we attach the type/const param
|
/// We need to have a [`Node`] for the [`HirId`] that we attach the type/const param
|
||||||
/// resolution to. Lifetimes don't have this problem, and for them, it's actually
|
/// resolution to. Lifetimes don't have this problem, and for them, it's actually
|
||||||
/// kind of detrimental to use a custom node type versus just using [`Lifetime`],
|
/// kind of detrimental to use a custom node type versus just using [`Lifetime`],
|
||||||
|
@ -533,7 +533,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
|
|||||||
try_visit!(visitor.visit_id(item.hir_id()));
|
try_visit!(visitor.visit_id(item.hir_id()));
|
||||||
try_visit!(walk_generics(visitor, generics));
|
try_visit!(walk_generics(visitor, generics));
|
||||||
walk_list!(visitor, visit_param_bound, bounds);
|
walk_list!(visitor, visit_param_bound, bounds);
|
||||||
if let Some(precise_capturing_args) = precise_capturing_args {
|
if let Some((precise_capturing_args, _)) = precise_capturing_args {
|
||||||
for arg in precise_capturing_args {
|
for arg in precise_capturing_args {
|
||||||
try_visit!(visitor.visit_precise_capturing_arg(arg));
|
try_visit!(visitor.visit_precise_capturing_arg(arg));
|
||||||
}
|
}
|
||||||
|
@ -486,7 +486,7 @@ fn sanity_check_found_hidden_type<'tcx>(
|
|||||||
fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) {
|
fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) {
|
||||||
let hir::OpaqueTy { precise_capturing_args, .. } =
|
let hir::OpaqueTy { precise_capturing_args, .. } =
|
||||||
*tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
|
*tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
|
||||||
let Some(precise_capturing_args) = precise_capturing_args else {
|
let Some((precise_capturing_args, _)) = precise_capturing_args else {
|
||||||
// No precise capturing args; nothing to validate
|
// No precise capturing args; nothing to validate
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -9,14 +9,6 @@ lint_array_into_iter =
|
|||||||
.use_explicit_into_iter_suggestion =
|
.use_explicit_into_iter_suggestion =
|
||||||
or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
|
||||||
|
|
||||||
lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than possibly intended in edition 2024
|
|
||||||
.note = specifically, {$num_captured ->
|
|
||||||
[one] this lifetime is
|
|
||||||
*[other] these lifetimes are
|
|
||||||
} in scope but not mentioned in the type's bounds
|
|
||||||
.note2 = all lifetimes in scope will be captured by `impl Trait`s in edition 2024
|
|
||||||
.suggestion = use the precise capturing `use<...>` syntax to make the captures explicit
|
|
||||||
|
|
||||||
lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
|
lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
|
||||||
.note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`
|
.note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`
|
||||||
.suggestion = you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change
|
.suggestion = you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change
|
||||||
@ -277,6 +269,17 @@ lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len ->
|
|||||||
|
|
||||||
lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level
|
lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level
|
||||||
|
|
||||||
|
lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than possibly intended in edition 2024
|
||||||
|
.note = specifically, {$num_captured ->
|
||||||
|
[one] this lifetime is
|
||||||
|
*[other] these lifetimes are
|
||||||
|
} in scope but not mentioned in the type's bounds
|
||||||
|
.note2 = all lifetimes in scope will be captured by `impl Trait`s in edition 2024
|
||||||
|
.suggestion = use the precise capturing `use<...>` syntax to make the captures explicit
|
||||||
|
|
||||||
|
lint_impl_trait_redundant_captures = all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
|
||||||
|
.suggestion = remove the `use<...>` syntax
|
||||||
|
|
||||||
lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe
|
lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe
|
||||||
.label = not FFI-safe
|
.label = not FFI-safe
|
||||||
.note = the type is defined here
|
.note = the type is defined here
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
use rustc_data_structures::fx::FxIndexSet;
|
use rustc_data_structures::fx::FxIndexSet;
|
||||||
|
use rustc_data_structures::unord::UnordSet;
|
||||||
use rustc_errors::{Applicability, LintDiagnostic};
|
use rustc_errors::{Applicability, LintDiagnostic};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::intravisit;
|
use rustc_macros::LintDiagnostic;
|
||||||
|
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||||
};
|
};
|
||||||
@ -14,10 +16,12 @@ use rustc_span::{BytePos, Span};
|
|||||||
use crate::fluent_generated as fluent;
|
use crate::fluent_generated as fluent;
|
||||||
use crate::{LateContext, LateLintPass};
|
use crate::{LateContext, LateLintPass};
|
||||||
|
|
||||||
|
// TODO: feature gate these too
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
/// UwU
|
/// UwU
|
||||||
pub IMPL_TRAIT_OVERCAPTURES,
|
pub IMPL_TRAIT_OVERCAPTURES,
|
||||||
Warn,
|
Allow,
|
||||||
"will capture more lifetimes than possibly intended in edition 2024",
|
"will capture more lifetimes than possibly intended in edition 2024",
|
||||||
@future_incompatible = FutureIncompatibleInfo {
|
@future_incompatible = FutureIncompatibleInfo {
|
||||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||||
@ -25,52 +29,51 @@ declare_lint! {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// UwU
|
||||||
|
pub IMPL_TRAIT_REDUNDANT_CAPTURES,
|
||||||
|
Warn,
|
||||||
|
"uwu 2"
|
||||||
|
}
|
||||||
|
|
||||||
declare_lint_pass!(
|
declare_lint_pass!(
|
||||||
/// Lint for opaque types that will begin capturing in-scope but unmentioned lifetimes
|
/// Lint for opaque types that will begin capturing in-scope but unmentioned lifetimes
|
||||||
/// in edition 2024.
|
/// in edition 2024.
|
||||||
ImplTraitOvercaptures => [IMPL_TRAIT_OVERCAPTURES]
|
ImplTraitOvercaptures => [IMPL_TRAIT_OVERCAPTURES, IMPL_TRAIT_REDUNDANT_CAPTURES]
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures {
|
impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures {
|
||||||
fn check_fn(
|
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'tcx>) {
|
||||||
&mut self,
|
match &it.kind {
|
||||||
cx: &LateContext<'tcx>,
|
hir::ItemKind::Fn(..) => check_fn(cx.tcx, it.owner_id.def_id),
|
||||||
_: intravisit::FnKind<'tcx>,
|
_ => {}
|
||||||
_: &'tcx hir::FnDecl<'tcx>,
|
|
||||||
_: &'tcx hir::Body<'tcx>,
|
|
||||||
_: Span,
|
|
||||||
parent_def_id: LocalDefId,
|
|
||||||
) {
|
|
||||||
match cx.tcx.def_kind(parent_def_id) {
|
|
||||||
DefKind::AssocFn => {
|
|
||||||
// RPITITs already capture all lifetimes in scope, so skip them.
|
|
||||||
if matches!(
|
|
||||||
cx.tcx.def_kind(cx.tcx.local_parent(parent_def_id)),
|
|
||||||
DefKind::Trait | DefKind::Impl { of_trait: true }
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DefKind::Fn => {
|
|
||||||
// All free functions need to check for overcaptures.
|
|
||||||
}
|
|
||||||
DefKind::Closure => return,
|
|
||||||
kind => {
|
|
||||||
unreachable!(
|
|
||||||
"expected function item, found {}",
|
|
||||||
kind.descr(parent_def_id.to_def_id())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let sig = cx.tcx.fn_sig(parent_def_id).instantiate_identity();
|
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::ImplItem<'tcx>) {
|
||||||
|
match &it.kind {
|
||||||
|
hir::ImplItemKind::Fn(_, _) => check_fn(cx.tcx, it.owner_id.def_id),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::TraitItem<'tcx>) {
|
||||||
|
match &it.kind {
|
||||||
|
hir::TraitItemKind::Fn(_, _) => check_fn(cx.tcx, it.owner_id.def_id),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) {
|
||||||
|
let sig = tcx.fn_sig(parent_def_id).instantiate_identity();
|
||||||
|
|
||||||
let mut in_scope_parameters = FxIndexSet::default();
|
let mut in_scope_parameters = FxIndexSet::default();
|
||||||
// Populate the in_scope_parameters list first with all of the generics in scope
|
// Populate the in_scope_parameters list first with all of the generics in scope
|
||||||
let mut current_def_id = Some(parent_def_id.to_def_id());
|
let mut current_def_id = Some(parent_def_id.to_def_id());
|
||||||
while let Some(def_id) = current_def_id {
|
while let Some(def_id) = current_def_id {
|
||||||
let generics = cx.tcx.generics_of(def_id);
|
let generics = tcx.generics_of(def_id);
|
||||||
for param in &generics.params {
|
for param in &generics.own_params {
|
||||||
in_scope_parameters.insert(param.def_id);
|
in_scope_parameters.insert(param.def_id);
|
||||||
}
|
}
|
||||||
current_def_id = generics.parent;
|
current_def_id = generics.parent;
|
||||||
@ -79,13 +82,12 @@ impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures {
|
|||||||
// Then visit the signature to walk through all the binders (incl. the late-bound
|
// Then visit the signature to walk through all the binders (incl. the late-bound
|
||||||
// vars on the function itself, which we need to count too).
|
// vars on the function itself, which we need to count too).
|
||||||
sig.visit_with(&mut VisitOpaqueTypes {
|
sig.visit_with(&mut VisitOpaqueTypes {
|
||||||
tcx: cx.tcx,
|
tcx,
|
||||||
parent_def_id,
|
parent_def_id,
|
||||||
in_scope_parameters,
|
in_scope_parameters,
|
||||||
seen: Default::default(),
|
seen: Default::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
struct VisitOpaqueTypes<'tcx> {
|
struct VisitOpaqueTypes<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
@ -109,14 +111,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
|
|||||||
let unique = self.in_scope_parameters.insert(def_id);
|
let unique = self.in_scope_parameters.insert(def_id);
|
||||||
assert!(unique);
|
assert!(unique);
|
||||||
}
|
}
|
||||||
ty::BoundVariableKind::Ty(_) => {
|
_ => {
|
||||||
todo!("we don't support late-bound type params in `impl Trait`")
|
self.tcx.dcx().span_delayed_bug(
|
||||||
}
|
self.tcx.def_span(self.parent_def_id),
|
||||||
ty::BoundVariableKind::Region(..) => {
|
format!("unsupported bound variable kind: {arg:?}"),
|
||||||
unreachable!("all AST-derived bound regions should have a name")
|
);
|
||||||
}
|
|
||||||
ty::BoundVariableKind::Const => {
|
|
||||||
unreachable!("non-lifetime binder consts are not allowed")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,11 +130,19 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
|
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
|
||||||
if !t.has_opaque_types() {
|
if !t.has_aliases() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ty::Alias(ty::Opaque, opaque_ty) = *t.kind()
|
if let ty::Alias(ty::Projection, opaque_ty) = *t.kind()
|
||||||
|
&& self.tcx.is_impl_trait_in_trait(opaque_ty.def_id)
|
||||||
|
{
|
||||||
|
// visit the opaque of the RPITIT
|
||||||
|
self.tcx
|
||||||
|
.type_of(opaque_ty.def_id)
|
||||||
|
.instantiate(self.tcx, opaque_ty.args)
|
||||||
|
.visit_with(self)
|
||||||
|
} else if let ty::Alias(ty::Opaque, opaque_ty) = *t.kind()
|
||||||
&& let Some(opaque_def_id) = opaque_ty.def_id.as_local()
|
&& let Some(opaque_def_id) = opaque_ty.def_id.as_local()
|
||||||
// Don't recurse infinitely on an opaque
|
// Don't recurse infinitely on an opaque
|
||||||
&& self.seen.insert(opaque_def_id)
|
&& self.seen.insert(opaque_def_id)
|
||||||
@ -144,8 +151,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
|
|||||||
self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty()
|
self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty()
|
||||||
&& let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin
|
&& let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin
|
||||||
&& parent_def_id == self.parent_def_id
|
&& parent_def_id == self.parent_def_id
|
||||||
// And if the opaque doesn't already have `use<>` syntax on it...
|
|
||||||
&& opaque.precise_capturing_args.is_none()
|
|
||||||
{
|
{
|
||||||
// Compute the set of args that are captured by the opaque...
|
// Compute the set of args that are captured by the opaque...
|
||||||
let mut captured = FxIndexSet::default();
|
let mut captured = FxIndexSet::default();
|
||||||
@ -178,9 +183,16 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
|
|||||||
.map(|def_id| self.tcx.def_span(def_id))
|
.map(|def_id| self.tcx.def_span(def_id))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if !uncaptured_spans.is_empty() {
|
|
||||||
let opaque_span = self.tcx.def_span(opaque_def_id);
|
let opaque_span = self.tcx.def_span(opaque_def_id);
|
||||||
|
let new_capture_rules =
|
||||||
|
opaque_span.at_least_rust_2024() || self.tcx.features().lifetime_capture_rules_2024;
|
||||||
|
|
||||||
|
// If we have uncaptured args, and if the opaque doesn't already have
|
||||||
|
// `use<>` syntax on it, and we're < edition 2024, then warn the user.
|
||||||
|
if !new_capture_rules
|
||||||
|
&& opaque.precise_capturing_args.is_none()
|
||||||
|
&& !uncaptured_spans.is_empty()
|
||||||
|
{
|
||||||
let suggestion = if let Ok(snippet) =
|
let suggestion = if let Ok(snippet) =
|
||||||
self.tcx.sess.source_map().span_to_snippet(opaque_span)
|
self.tcx.sess.source_map().span_to_snippet(opaque_span)
|
||||||
&& snippet.starts_with("impl ")
|
&& snippet.starts_with("impl ")
|
||||||
@ -207,11 +219,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
self.tcx.emit_node_lint(
|
self.tcx.emit_node_span_lint(
|
||||||
IMPL_TRAIT_OVERCAPTURES,
|
IMPL_TRAIT_OVERCAPTURES,
|
||||||
self.tcx.local_def_id_to_hir_id(opaque_def_id),
|
self.tcx.local_def_id_to_hir_id(opaque_def_id),
|
||||||
ImplTraitOvercapturesLint {
|
|
||||||
opaque_span,
|
opaque_span,
|
||||||
|
ImplTraitOvercapturesLint {
|
||||||
self_ty: t,
|
self_ty: t,
|
||||||
num_captured: uncaptured_spans.len(),
|
num_captured: uncaptured_spans.len(),
|
||||||
uncaptured_spans,
|
uncaptured_spans,
|
||||||
@ -219,6 +231,60 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// Otherwise, if we are edition 2024, have `use<>` syntax, and
|
||||||
|
// have no uncaptured args, then we should warn to the user that
|
||||||
|
// it's redundant to capture all args explicitly.
|
||||||
|
else if new_capture_rules
|
||||||
|
&& let Some((captured_args, capturing_span)) = opaque.precise_capturing_args
|
||||||
|
{
|
||||||
|
let mut explicitly_captured = UnordSet::default();
|
||||||
|
for arg in captured_args {
|
||||||
|
match self.tcx.named_bound_var(arg.hir_id()) {
|
||||||
|
Some(
|
||||||
|
ResolvedArg::EarlyBound(def_id) | ResolvedArg::LateBound(_, _, def_id),
|
||||||
|
) => {
|
||||||
|
if self.tcx.def_kind(self.tcx.parent(def_id)) == DefKind::OpaqueTy {
|
||||||
|
let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
|
||||||
|
| ty::ReLateParam(ty::LateParamRegion {
|
||||||
|
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
|
||||||
|
..
|
||||||
|
})) = self
|
||||||
|
.tcx
|
||||||
|
.map_opaque_lifetime_to_parent_lifetime(def_id.expect_local())
|
||||||
|
.kind()
|
||||||
|
else {
|
||||||
|
span_bug!(
|
||||||
|
self.tcx.def_span(def_id),
|
||||||
|
"variable should have been duplicated from a parent"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
explicitly_captured.insert(def_id);
|
||||||
|
} else {
|
||||||
|
explicitly_captured.insert(def_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.tcx.dcx().span_delayed_bug(
|
||||||
|
self.tcx().hir().span(arg.hir_id()),
|
||||||
|
"no valid for captured arg",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.in_scope_parameters
|
||||||
|
.iter()
|
||||||
|
.all(|def_id| explicitly_captured.contains(def_id))
|
||||||
|
{
|
||||||
|
self.tcx.emit_node_span_lint(
|
||||||
|
IMPL_TRAIT_REDUNDANT_CAPTURES,
|
||||||
|
self.tcx.local_def_id_to_hir_id(opaque_def_id),
|
||||||
|
opaque_span,
|
||||||
|
ImplTraitRedundantCapturesLint { capturing_span },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Walk into the bounds of the opaque, too, since we want to get nested opaques
|
// Walk into the bounds of the opaque, too, since we want to get nested opaques
|
||||||
// in this lint as well. Interestingly, one place that I expect this lint to fire
|
// in this lint as well. Interestingly, one place that I expect this lint to fire
|
||||||
@ -236,7 +302,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ImplTraitOvercapturesLint<'tcx> {
|
struct ImplTraitOvercapturesLint<'tcx> {
|
||||||
opaque_span: Span,
|
|
||||||
uncaptured_spans: Vec<Span>,
|
uncaptured_spans: Vec<Span>,
|
||||||
self_ty: Ty<'tcx>,
|
self_ty: Ty<'tcx>,
|
||||||
num_captured: usize,
|
num_captured: usize,
|
||||||
@ -247,7 +312,6 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
|
|||||||
fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
|
fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
|
||||||
diag.arg("self_ty", self.self_ty.to_string())
|
diag.arg("self_ty", self.self_ty.to_string())
|
||||||
.arg("num_captured", self.num_captured)
|
.arg("num_captured", self.num_captured)
|
||||||
.span(self.opaque_span)
|
|
||||||
.span_note(self.uncaptured_spans, fluent::lint_note)
|
.span_note(self.uncaptured_spans, fluent::lint_note)
|
||||||
.note(fluent::lint_note2);
|
.note(fluent::lint_note2);
|
||||||
if let Some((suggestion, span)) = self.suggestion {
|
if let Some((suggestion, span)) = self.suggestion {
|
||||||
@ -265,6 +329,13 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(lint_impl_trait_redundant_captures)]
|
||||||
|
struct ImplTraitRedundantCapturesLint {
|
||||||
|
#[suggestion(lint_suggestion, code = "", applicability = "machine-applicable")]
|
||||||
|
capturing_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_def_id_from_arg<'tcx>(
|
fn extract_def_id_from_arg<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
generics: &'tcx ty::Generics,
|
generics: &'tcx ty::Generics,
|
||||||
|
@ -675,8 +675,8 @@ impl<'a> Parser<'a> {
|
|||||||
let precise_capturing = if self.eat_keyword(kw::Use) {
|
let precise_capturing = if self.eat_keyword(kw::Use) {
|
||||||
let use_span = self.prev_token.span;
|
let use_span = self.prev_token.span;
|
||||||
self.psess.gated_spans.gate(sym::precise_capturing, use_span);
|
self.psess.gated_spans.gate(sym::precise_capturing, use_span);
|
||||||
let args = self.parse_precise_capturing_args()?;
|
let (args, args_span) = self.parse_precise_capturing_args()?;
|
||||||
Some(P((args, use_span)))
|
Some(P((args, use_span.to(args_span))))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -689,9 +689,11 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, precise_capturing))
|
Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, precise_capturing))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_precise_capturing_args(&mut self) -> PResult<'a, ThinVec<PreciseCapturingArg>> {
|
fn parse_precise_capturing_args(
|
||||||
Ok(self
|
&mut self,
|
||||||
.parse_unspanned_seq(
|
) -> PResult<'a, (ThinVec<PreciseCapturingArg>, Span)> {
|
||||||
|
let lo = self.token.span;
|
||||||
|
let (args, _) = self.parse_unspanned_seq(
|
||||||
&TokenKind::Lt,
|
&TokenKind::Lt,
|
||||||
&TokenKind::Gt,
|
&TokenKind::Gt,
|
||||||
SeqSep::trailing_allowed(token::Comma),
|
SeqSep::trailing_allowed(token::Comma),
|
||||||
@ -713,8 +715,8 @@ impl<'a> Parser<'a> {
|
|||||||
self_.unexpected_any()
|
self_.unexpected_any()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)?
|
)?;
|
||||||
.0)
|
Ok((args, lo.to(self.prev_token.span)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is a `dyn B0 + ... + Bn` type allowed here?
|
/// Is a `dyn B0 + ... + Bn` type allowed here?
|
||||||
|
@ -11,7 +11,7 @@ error: `use<...>` precise capturing syntax not allowed on argument-position `imp
|
|||||||
--> $DIR/apit.rs:4:18
|
--> $DIR/apit.rs:4:18
|
||||||
|
|
|
|
||||||
LL | fn hello(_: impl use<> Sized) {}
|
LL | fn hello(_: impl use<> Sized) {}
|
||||||
| ^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: aborting due to 1 previous error; 1 warning emitted
|
error: aborting due to 1 previous error; 1 warning emitted
|
||||||
|
|
||||||
|
25
tests/ui/impl-trait/precise-capturing/redundant.rs
Normal file
25
tests/ui/impl-trait/precise-capturing/redundant.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//@ compile-flags: -Zunstable-options --edition=2024
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
|
#![feature(precise_capturing)]
|
||||||
|
//~^ WARN the feature `precise_capturing` is incomplete
|
||||||
|
|
||||||
|
fn hello<'a>() -> impl use<'a> Sized {}
|
||||||
|
//~^ WARN all possible in-scope parameters are already captured
|
||||||
|
|
||||||
|
struct Inherent;
|
||||||
|
impl Inherent {
|
||||||
|
fn inherent(&self) -> impl use<'_> Sized {}
|
||||||
|
//~^ WARN all possible in-scope parameters are already captured
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Test<'a> {
|
||||||
|
fn in_trait() -> impl use<'a, Self> Sized;
|
||||||
|
//~^ WARN all possible in-scope parameters are already captured
|
||||||
|
}
|
||||||
|
impl<'a> Test<'a> for () {
|
||||||
|
fn in_trait() -> impl use<'a> Sized {}
|
||||||
|
//~^ WARN all possible in-scope parameters are already captured
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
45
tests/ui/impl-trait/precise-capturing/redundant.stderr
Normal file
45
tests/ui/impl-trait/precise-capturing/redundant.stderr
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/redundant.rs:4:12
|
||||||
|
|
|
||||||
|
LL | #![feature(precise_capturing)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
|
||||||
|
--> $DIR/redundant.rs:7:19
|
||||||
|
|
|
||||||
|
LL | fn hello<'a>() -> impl use<'a> Sized {}
|
||||||
|
| ^^^^^-------^^^^^^
|
||||||
|
| |
|
||||||
|
| help: remove the `use<...>` syntax
|
||||||
|
|
|
||||||
|
= note: `#[warn(impl_trait_redundant_captures)]` on by default
|
||||||
|
|
||||||
|
warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
|
||||||
|
--> $DIR/redundant.rs:12:27
|
||||||
|
|
|
||||||
|
LL | fn inherent(&self) -> impl use<'_> Sized {}
|
||||||
|
| ^^^^^-------^^^^^^
|
||||||
|
| |
|
||||||
|
| help: remove the `use<...>` syntax
|
||||||
|
|
||||||
|
warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
|
||||||
|
--> $DIR/redundant.rs:17:22
|
||||||
|
|
|
||||||
|
LL | fn in_trait() -> impl use<'a, Self> Sized;
|
||||||
|
| ^^^^^-------------^^^^^^
|
||||||
|
| |
|
||||||
|
| help: remove the `use<...>` syntax
|
||||||
|
|
||||||
|
warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
|
||||||
|
--> $DIR/redundant.rs:21:22
|
||||||
|
|
|
||||||
|
LL | fn in_trait() -> impl use<'a> Sized {}
|
||||||
|
| ^^^^^-------^^^^^^
|
||||||
|
| |
|
||||||
|
| help: remove the `use<...>` syntax
|
||||||
|
|
||||||
|
warning: 5 warnings emitted
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user