Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ff76eab

Browse files
committedApr 21, 2024
Warn against redundant use<...>
1 parent bc67262 commit ff76eab

File tree

10 files changed

+185
-64
lines changed

10 files changed

+185
-64
lines changed
 

‎compiler/rustc_ast_lowering/src/lib.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -1407,7 +1407,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
14071407
bounds,
14081408
fn_kind,
14091409
itctx,
1410-
precise_capturing.as_deref().map(|(args, _)| args.as_slice()),
1410+
precise_capturing.as_deref().map(|(args, span)| (args.as_slice(), *span)),
14111411
),
14121412
ImplTraitContext::Universal => {
14131413
if let Some(&(_, span)) = precise_capturing.as_deref() {
@@ -1523,7 +1523,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
15231523
bounds: &GenericBounds,
15241524
fn_kind: Option<FnDeclKind>,
15251525
itctx: ImplTraitContext,
1526-
precise_capturing_args: Option<&[PreciseCapturingArg]>,
1526+
precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
15271527
) -> hir::TyKind<'hir> {
15281528
// Make sure we know that some funky desugaring has been going on here.
15291529
// This is a first: there is code in other places like for loop
@@ -1533,7 +1533,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
15331533
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
15341534

15351535
let captured_lifetimes_to_duplicate =
1536-
if let Some(precise_capturing) = precise_capturing_args {
1536+
if let Some((precise_capturing, _)) = precise_capturing_args {
15371537
// We'll actually validate these later on; all we need is the list of
15381538
// lifetimes to duplicate during this portion of lowering.
15391539
precise_capturing
@@ -1607,7 +1607,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
16071607
captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
16081608
span: Span,
16091609
opaque_ty_span: Span,
1610-
precise_capturing_args: Option<&[PreciseCapturingArg]>,
1610+
precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
16111611
lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
16121612
) -> hir::TyKind<'hir> {
16131613
let opaque_ty_def_id = self.create_def(
@@ -1698,8 +1698,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
16981698
this.with_remapping(captured_to_synthesized_mapping, |this| {
16991699
(
17001700
lower_item_bounds(this),
1701-
precise_capturing_args.map(|precise_capturing| {
1702-
this.lower_precise_capturing_args(precise_capturing)
1701+
precise_capturing_args.map(|(precise_capturing, span)| {
1702+
(
1703+
this.lower_precise_capturing_args(precise_capturing),
1704+
this.lower_span(span),
1705+
)
17031706
}),
17041707
)
17051708
});

‎compiler/rustc_hir/src/hir.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -2558,7 +2558,7 @@ pub struct OpaqueTy<'hir> {
25582558
/// lowered as an associated type.
25592559
pub in_trait: bool,
25602560
/// List of arguments captured via `impl use<'a, P, ...> Trait` syntax.
2561-
pub precise_capturing_args: Option<&'hir [PreciseCapturingArg<'hir>]>,
2561+
pub precise_capturing_args: Option<(&'hir [PreciseCapturingArg<'hir>], Span)>,
25622562
}
25632563

25642564
#[derive(Debug, Clone, Copy, HashStable_Generic)]
@@ -2568,6 +2568,15 @@ pub enum PreciseCapturingArg<'hir> {
25682568
Param(PreciseCapturingNonLifetimeArg),
25692569
}
25702570

2571+
impl PreciseCapturingArg<'_> {
2572+
pub fn hir_id(self) -> HirId {
2573+
match self {
2574+
PreciseCapturingArg::Lifetime(lt) => lt.hir_id,
2575+
PreciseCapturingArg::Param(param) => param.hir_id,
2576+
}
2577+
}
2578+
}
2579+
25712580
/// We need to have a [`Node`] for the [`HirId`] that we attach the type/const param
25722581
/// resolution to. Lifetimes don't have this problem, and for them, it's actually
25732582
/// kind of detrimental to use a custom node type versus just using [`Lifetime`],

‎compiler/rustc_hir/src/intravisit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
533533
try_visit!(visitor.visit_id(item.hir_id()));
534534
try_visit!(walk_generics(visitor, generics));
535535
walk_list!(visitor, visit_param_bound, bounds);
536-
if let Some(precise_capturing_args) = precise_capturing_args {
536+
if let Some((precise_capturing_args, _)) = precise_capturing_args {
537537
for arg in precise_capturing_args {
538538
try_visit!(visitor.visit_precise_capturing_arg(arg));
539539
}

‎compiler/rustc_hir_analysis/src/check/check.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ fn sanity_check_found_hidden_type<'tcx>(
486486
fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) {
487487
let hir::OpaqueTy { precise_capturing_args, .. } =
488488
*tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
489-
let Some(precise_capturing_args) = precise_capturing_args else {
489+
let Some((precise_capturing_args, _)) = precise_capturing_args else {
490490
// No precise capturing args; nothing to validate
491491
return;
492492
};

‎compiler/rustc_lint/messages.ftl

+11-8
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,6 @@ lint_array_into_iter =
99
.use_explicit_into_iter_suggestion =
1010
or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
1111
12-
lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than possibly intended in edition 2024
13-
.note = specifically, {$num_captured ->
14-
[one] this lifetime is
15-
*[other] these lifetimes are
16-
} in scope but not mentioned in the type's bounds
17-
.note2 = all lifetimes in scope will be captured by `impl Trait`s in edition 2024
18-
.suggestion = use the precise capturing `use<...>` syntax to make the captures explicit
19-
2012
lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
2113
.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`
2214
.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 ->
277269
278270
lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level
279271
272+
lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than possibly intended in edition 2024
273+
.note = specifically, {$num_captured ->
274+
[one] this lifetime is
275+
*[other] these lifetimes are
276+
} in scope but not mentioned in the type's bounds
277+
.note2 = all lifetimes in scope will be captured by `impl Trait`s in edition 2024
278+
.suggestion = use the precise capturing `use<...>` syntax to make the captures explicit
279+
280+
lint_impl_trait_redundant_captures = all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
281+
.suggestion = remove the `use<...>` syntax
282+
280283
lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe
281284
.label = not FFI-safe
282285
.note = the type is defined here

‎compiler/rustc_lint/src/impl_trait_overcaptures.rs

+91-18
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use rustc_data_structures::fx::FxIndexSet;
2+
use rustc_data_structures::unord::UnordSet;
23
use rustc_errors::{Applicability, LintDiagnostic};
34
use rustc_hir as hir;
45
use rustc_hir::def::DefKind;
56
use rustc_hir::def_id::{DefId, LocalDefId};
67
use rustc_hir::intravisit;
8+
use rustc_macros::LintDiagnostic;
9+
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
710
use rustc_middle::ty::{
811
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
912
};
@@ -14,21 +17,30 @@ use rustc_span::{BytePos, Span};
1417
use crate::fluent_generated as fluent;
1518
use crate::{LateContext, LateLintPass};
1619

20+
// TODO: feature gate these too
21+
1722
declare_lint! {
1823
/// UwU
1924
pub IMPL_TRAIT_OVERCAPTURES,
20-
Warn,
25+
Allow,
2126
"will capture more lifetimes than possibly intended in edition 2024",
2227
@future_incompatible = FutureIncompatibleInfo {
2328
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
2429
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
2530
};
2631
}
2732

33+
declare_lint! {
34+
/// UwU
35+
pub IMPL_TRAIT_REDUNDANT_CAPTURES,
36+
Warn,
37+
"uwu 2"
38+
}
39+
2840
declare_lint_pass!(
2941
/// Lint for opaque types that will begin capturing in-scope but unmentioned lifetimes
3042
/// in edition 2024.
31-
ImplTraitOvercaptures => [IMPL_TRAIT_OVERCAPTURES]
43+
ImplTraitOvercaptures => [IMPL_TRAIT_OVERCAPTURES, IMPL_TRAIT_REDUNDANT_CAPTURES]
3244
);
3345

3446
impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures {
@@ -109,14 +121,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
109121
let unique = self.in_scope_parameters.insert(def_id);
110122
assert!(unique);
111123
}
112-
ty::BoundVariableKind::Ty(_) => {
113-
todo!("we don't support late-bound type params in `impl Trait`")
114-
}
115-
ty::BoundVariableKind::Region(..) => {
116-
unreachable!("all AST-derived bound regions should have a name")
117-
}
118-
ty::BoundVariableKind::Const => {
119-
unreachable!("non-lifetime binder consts are not allowed")
124+
_ => {
125+
self.tcx.dcx().span_delayed_bug(
126+
self.tcx.def_span(self.parent_def_id),
127+
format!("unsupported bound variable kind: {arg:?}"),
128+
);
120129
}
121130
}
122131
}
@@ -144,8 +153,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
144153
self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty()
145154
&& let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin
146155
&& parent_def_id == self.parent_def_id
147-
// And if the opaque doesn't already have `use<>` syntax on it...
148-
&& opaque.precise_capturing_args.is_none()
149156
{
150157
// Compute the set of args that are captured by the opaque...
151158
let mut captured = FxIndexSet::default();
@@ -178,9 +185,16 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
178185
.map(|def_id| self.tcx.def_span(def_id))
179186
.collect();
180187

181-
if !uncaptured_spans.is_empty() {
182-
let opaque_span = self.tcx.def_span(opaque_def_id);
188+
let opaque_span = self.tcx.def_span(opaque_def_id);
189+
let new_capture_rules =
190+
opaque_span.at_least_rust_2024() || self.tcx.features().lifetime_capture_rules_2024;
183191

192+
// If we have uncaptured args, and if the opaque doesn't already have
193+
// `use<>` syntax on it, and we're < edition 2024, then warn the user.
194+
if !new_capture_rules
195+
&& opaque.precise_capturing_args.is_none()
196+
&& !uncaptured_spans.is_empty()
197+
{
184198
let suggestion = if let Ok(snippet) =
185199
self.tcx.sess.source_map().span_to_snippet(opaque_span)
186200
&& snippet.starts_with("impl ")
@@ -207,18 +221,72 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
207221
None
208222
};
209223

210-
self.tcx.emit_node_lint(
224+
self.tcx.emit_node_span_lint(
211225
IMPL_TRAIT_OVERCAPTURES,
212226
self.tcx.local_def_id_to_hir_id(opaque_def_id),
227+
opaque_span,
213228
ImplTraitOvercapturesLint {
214-
opaque_span,
215229
self_ty: t,
216230
num_captured: uncaptured_spans.len(),
217231
uncaptured_spans,
218232
suggestion,
219233
},
220234
);
221235
}
236+
// Otherwise, if we are edition 2024, have `use<>` syntax, and
237+
// have no uncaptured args, then we should warn to the user that
238+
// it's redundant to capture all args explicitly.
239+
else if new_capture_rules
240+
&& let Some((captured_args, capturing_span)) = opaque.precise_capturing_args
241+
{
242+
let mut explicitly_captured = UnordSet::default();
243+
for arg in captured_args {
244+
match self.tcx.named_bound_var(arg.hir_id()) {
245+
Some(
246+
ResolvedArg::EarlyBound(def_id) | ResolvedArg::LateBound(_, _, def_id),
247+
) => {
248+
if self.tcx.def_kind(self.tcx.parent(def_id)) == DefKind::OpaqueTy {
249+
let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
250+
| ty::ReLateParam(ty::LateParamRegion {
251+
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
252+
..
253+
})) = self
254+
.tcx
255+
.map_opaque_lifetime_to_parent_lifetime(def_id.expect_local())
256+
.kind()
257+
else {
258+
span_bug!(
259+
self.tcx.def_span(def_id),
260+
"variable should have been duplicated from a parent"
261+
);
262+
};
263+
explicitly_captured.insert(def_id);
264+
} else {
265+
explicitly_captured.insert(def_id);
266+
}
267+
}
268+
_ => {
269+
self.tcx.dcx().span_delayed_bug(
270+
self.tcx().hir().span(arg.hir_id()),
271+
"no valid for captured arg",
272+
);
273+
}
274+
}
275+
}
276+
277+
if self
278+
.in_scope_parameters
279+
.iter()
280+
.all(|def_id| explicitly_captured.contains(def_id))
281+
{
282+
self.tcx.emit_node_span_lint(
283+
IMPL_TRAIT_REDUNDANT_CAPTURES,
284+
self.tcx.local_def_id_to_hir_id(opaque_def_id),
285+
opaque_span,
286+
ImplTraitRedundantCapturesLint { capturing_span },
287+
);
288+
}
289+
}
222290

223291
// Walk into the bounds of the opaque, too, since we want to get nested opaques
224292
// in this lint as well. Interestingly, one place that I expect this lint to fire
@@ -236,7 +304,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
236304
}
237305

238306
struct ImplTraitOvercapturesLint<'tcx> {
239-
opaque_span: Span,
240307
uncaptured_spans: Vec<Span>,
241308
self_ty: Ty<'tcx>,
242309
num_captured: usize,
@@ -247,7 +314,6 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
247314
fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
248315
diag.arg("self_ty", self.self_ty.to_string())
249316
.arg("num_captured", self.num_captured)
250-
.span(self.opaque_span)
251317
.span_note(self.uncaptured_spans, fluent::lint_note)
252318
.note(fluent::lint_note2);
253319
if let Some((suggestion, span)) = self.suggestion {
@@ -265,6 +331,13 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
265331
}
266332
}
267333

334+
#[derive(LintDiagnostic)]
335+
#[diag(lint_impl_trait_redundant_captures)]
336+
struct ImplTraitRedundantCapturesLint {
337+
#[suggestion(lint_suggestion, code = "", applicability = "machine-applicable")]
338+
capturing_span: Span,
339+
}
340+
268341
fn extract_def_id_from_arg<'tcx>(
269342
tcx: TyCtxt<'tcx>,
270343
generics: &'tcx ty::Generics,

‎compiler/rustc_parse/src/parser/ty.rs

+30-28
Original file line numberDiff line numberDiff line change
@@ -675,8 +675,8 @@ impl<'a> Parser<'a> {
675675
let precise_capturing = if self.eat_keyword(kw::Use) {
676676
let use_span = self.prev_token.span;
677677
self.psess.gated_spans.gate(sym::precise_capturing, use_span);
678-
let args = self.parse_precise_capturing_args()?;
679-
Some(P((args, use_span)))
678+
let (args, args_span) = self.parse_precise_capturing_args()?;
679+
Some(P((args, use_span.to(args_span))))
680680
} else {
681681
None
682682
};
@@ -689,32 +689,34 @@ impl<'a> Parser<'a> {
689689
Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, precise_capturing))
690690
}
691691

692-
fn parse_precise_capturing_args(&mut self) -> PResult<'a, ThinVec<PreciseCapturingArg>> {
693-
Ok(self
694-
.parse_unspanned_seq(
695-
&TokenKind::Lt,
696-
&TokenKind::Gt,
697-
SeqSep::trailing_allowed(token::Comma),
698-
|self_| {
699-
if self_.check_keyword(kw::SelfUpper) {
700-
self_.bump();
701-
Ok(PreciseCapturingArg::Arg(
702-
ast::Path::from_ident(self_.prev_token.ident().unwrap().0),
703-
DUMMY_NODE_ID,
704-
))
705-
} else if self_.check_ident() {
706-
Ok(PreciseCapturingArg::Arg(
707-
ast::Path::from_ident(self_.parse_ident()?),
708-
DUMMY_NODE_ID,
709-
))
710-
} else if self_.check_lifetime() {
711-
Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime()))
712-
} else {
713-
self_.unexpected_any()
714-
}
715-
},
716-
)?
717-
.0)
692+
fn parse_precise_capturing_args(
693+
&mut self,
694+
) -> PResult<'a, (ThinVec<PreciseCapturingArg>, Span)> {
695+
let lo = self.token.span;
696+
let (args, _) = self.parse_unspanned_seq(
697+
&TokenKind::Lt,
698+
&TokenKind::Gt,
699+
SeqSep::trailing_allowed(token::Comma),
700+
|self_| {
701+
if self_.check_keyword(kw::SelfUpper) {
702+
self_.bump();
703+
Ok(PreciseCapturingArg::Arg(
704+
ast::Path::from_ident(self_.prev_token.ident().unwrap().0),
705+
DUMMY_NODE_ID,
706+
))
707+
} else if self_.check_ident() {
708+
Ok(PreciseCapturingArg::Arg(
709+
ast::Path::from_ident(self_.parse_ident()?),
710+
DUMMY_NODE_ID,
711+
))
712+
} else if self_.check_lifetime() {
713+
Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime()))
714+
} else {
715+
self_.unexpected_any()
716+
}
717+
},
718+
)?;
719+
Ok((args, lo.to(self.prev_token.span)))
718720
}
719721

720722
/// Is a `dyn B0 + ... + Bn` type allowed here?

‎tests/ui/impl-trait/precise-capturing/apit.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ error: `use<...>` precise capturing syntax not allowed on argument-position `imp
1111
--> $DIR/apit.rs:4:18
1212
|
1313
LL | fn hello(_: impl use<> Sized) {}
14-
| ^^^
14+
| ^^^^^
1515

1616
error: aborting due to 1 previous error; 1 warning emitted
1717

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//@ compile-flags: -Zunstable-options --edition=2024
2+
//@ check-pass
3+
4+
#![feature(precise_capturing)]
5+
//~^ WARN the feature `precise_capturing` is incomplete
6+
7+
fn hello<'a>() -> impl use<'a> Sized {}
8+
//~^ WARN all possible in-scope parameters are already captured
9+
10+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/redundant.rs:4:12
3+
|
4+
LL | #![feature(precise_capturing)]
5+
| ^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
8+
= note: `#[warn(incomplete_features)]` on by default
9+
10+
warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
11+
--> $DIR/redundant.rs:7:19
12+
|
13+
LL | fn hello<'a>() -> impl use<'a> Sized {}
14+
| ^^^^^-------^^^^^^
15+
| |
16+
| help: remove the `use<...>` syntax
17+
|
18+
= note: `#[warn(impl_trait_redundant_captures)]` on by default
19+
20+
warning: 2 warnings emitted
21+

0 commit comments

Comments
 (0)
Please sign in to comment.