From 6afe1352d934a15d1cfacf7eaef774898e76011f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 18 Apr 2024 21:57:07 -0400 Subject: [PATCH] Suggest adding use<> syntax --- compiler/rustc_lint/messages.ftl | 1 + .../rustc_lint/src/impl_trait_overcaptures.rs | 47 ++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 67b2121e5c008..d54e9a3c6eb32 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -15,6 +15,7 @@ lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than pos *[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 .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` diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index b5fa322d89fbd..7272618c5f20d 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -1,5 +1,5 @@ -use rustc_data_structures::{fx::FxIndexSet, unord::UnordSet}; -use rustc_errors::LintDiagnostic; +use rustc_data_structures::fx::FxIndexSet; +use rustc_errors::{Applicability, LintDiagnostic}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -7,7 +7,9 @@ use rustc_hir::intravisit; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; -use rustc_span::Span; +use rustc_session::lint::FutureIncompatibilityReason; +use rustc_span::edition::Edition; +use rustc_span::{BytePos, Span}; use crate::fluent_generated as fluent; use crate::{LateContext, LateLintPass}; @@ -146,7 +148,7 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { && opaque.precise_capturing_args.is_none() { // Compute the set of args that are captured by the opaque... - let mut captured = UnordSet::default(); + let mut captured = FxIndexSet::default(); let variances = self.tcx.variances_of(opaque_def_id); let mut current_def_id = Some(opaque_def_id.to_def_id()); while let Some(def_id) = current_def_id { @@ -172,19 +174,43 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { let uncaptured_spans: Vec<_> = self .in_scope_parameters .iter() - .filter(|def_id| !captured.contains(def_id)) + .filter(|def_id| !captured.contains(*def_id)) .map(|def_id| self.tcx.def_span(def_id)) .collect(); if !uncaptured_spans.is_empty() { + let opaque_span = self.tcx.def_span(opaque_def_id); + + let suggestion = if let Ok(snippet) = + self.tcx.sess.source_map().span_to_snippet(opaque_span) + && snippet.starts_with("impl ") + { + let (lifetimes, others): (Vec<_>, Vec<_>) = captured + .into_iter() + .partition(|def_id| self.tcx.def_kind(*def_id) == DefKind::LifetimeParam); + // Take all lifetime params first, then all others (ty/ct). + let generics: Vec<_> = lifetimes + .into_iter() + .chain(others) + .map(|def_id| self.tcx.item_name(def_id).to_string()) + .collect(); + Some(( + format!(" use<{}>", generics.join(", ")), + opaque_span.with_lo(opaque_span.lo() + BytePos(4)).shrink_to_lo(), + )) + } else { + None + }; + self.tcx.emit_node_lint( IMPL_TRAIT_OVERCAPTURES, self.tcx.local_def_id_to_hir_id(opaque_def_id), ImplTraitOvercapturesLint { - opaque_span: self.tcx.def_span(opaque_def_id), + opaque_span, self_ty: t, num_captured: uncaptured_spans.len(), uncaptured_spans, + suggestion, }, ); } @@ -209,6 +235,7 @@ struct ImplTraitOvercapturesLint<'tcx> { uncaptured_spans: Vec, self_ty: Ty<'tcx>, num_captured: usize, + suggestion: Option<(String, Span)>, } impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { @@ -218,6 +245,14 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { .span(self.opaque_span) .span_note(self.uncaptured_spans, fluent::lint_note) .note(fluent::lint_note2); + if let Some((suggestion, span)) = self.suggestion { + diag.span_suggestion( + span, + fluent::lint_suggestion, + suggestion, + Applicability::MachineApplicable, + ); + } } fn msg(&self) -> rustc_errors::DiagMessage {