about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-04-18 21:57:07 -0400
committerMichael Goulet <michael@errs.io>2024-05-13 23:57:56 -0400
commit6afe1352d934a15d1cfacf7eaef774898e76011f (patch)
treed981b7e932aa31d276ee81ae3b9cf6e1aa52036b
parent554becc180b42c41c35a25811cdc7059c7acc4fb (diff)
downloadrust-6afe1352d934a15d1cfacf7eaef774898e76011f.tar.gz
rust-6afe1352d934a15d1cfacf7eaef774898e76011f.zip
Suggest adding use<> syntax
-rw-r--r--compiler/rustc_lint/messages.ftl1
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs47
2 files changed, 42 insertions, 6 deletions
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 67b2121e5c0..d54e9a3c6eb 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 b5fa322d89f..7272618c5f2 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<TyCtxt<'tcx>> 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<TyCtxt<'tcx>> 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<Span>,
     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 {