]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs
New upstream version 1.57.0+dfsg1
[rustc.git] / compiler / rustc_mir / src / borrow_check / diagnostics / outlives_suggestion.rs
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs
deleted file mode 100644 (file)
index 7dc3434..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-//! Contains utilities for generating suggestions for borrowck errors related to unsatisfied
-//! outlives constraints.
-
-use std::collections::BTreeMap;
-
-use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::DiagnosticBuilder;
-use rustc_middle::ty::RegionVid;
-use tracing::debug;
-
-use smallvec::SmallVec;
-
-use crate::borrow_check::MirBorrowckCtxt;
-
-use super::{ErrorConstraintInfo, RegionName, RegionNameSource};
-
-/// The different things we could suggest.
-enum SuggestedConstraint {
-    /// Outlives(a, [b, c, d, ...]) => 'a: 'b + 'c + 'd + ...
-    Outlives(RegionName, SmallVec<[RegionName; 2]>),
-
-    /// 'a = 'b
-    Equal(RegionName, RegionName),
-
-    /// 'a: 'static i.e. 'a = 'static and the user should just use 'static
-    Static(RegionName),
-}
-
-/// Collects information about outlives constraints that needed to be added for a given MIR node
-/// corresponding to a function definition.
-///
-/// Adds a help note suggesting adding a where clause with the needed constraints.
-#[derive(Default)]
-pub struct OutlivesSuggestionBuilder {
-    /// The list of outlives constraints that need to be added. Specifically, we map each free
-    /// region to all other regions that it must outlive. I will use the shorthand `fr:
-    /// outlived_frs`. Not all of these regions will already have names necessarily. Some could be
-    /// implicit free regions that we inferred. These will need to be given names in the final
-    /// suggestion message.
-    constraints_to_add: BTreeMap<RegionVid, Vec<RegionVid>>,
-}
-
-impl OutlivesSuggestionBuilder {
-    /// Returns `true` iff the `RegionNameSource` is a valid source for an outlives
-    /// suggestion.
-    //
-    // FIXME: Currently, we only report suggestions if the `RegionNameSource` is an early-bound
-    // region or a named region, avoiding using regions with synthetic names altogether. This
-    // allows us to avoid giving impossible suggestions (e.g. adding bounds to closure args).
-    // We can probably be less conservative, since some inferred free regions are namable (e.g.
-    // the user can explicitly name them. To do this, we would allow some regions whose names
-    // come from `MatchedAdtAndSegment`, being careful to filter out bad suggestions, such as
-    // naming the `'self` lifetime in methods, etc.
-    fn region_name_is_suggestable(name: &RegionName) -> bool {
-        match name.source {
-            RegionNameSource::NamedEarlyBoundRegion(..)
-            | RegionNameSource::NamedFreeRegion(..)
-            | RegionNameSource::Static => true,
-
-            // Don't give suggestions for upvars, closure return types, or other unnamable
-            // regions.
-            RegionNameSource::SynthesizedFreeEnvRegion(..)
-            | RegionNameSource::AnonRegionFromArgument(..)
-            | RegionNameSource::AnonRegionFromUpvar(..)
-            | RegionNameSource::AnonRegionFromOutput(..)
-            | RegionNameSource::AnonRegionFromYieldTy(..)
-            | RegionNameSource::AnonRegionFromAsyncFn(..) => {
-                debug!("Region {:?} is NOT suggestable", name);
-                false
-            }
-        }
-    }
-
-    /// Returns a name for the region if it is suggestable. See `region_name_is_suggestable`.
-    fn region_vid_to_name(
-        &self,
-        mbcx: &MirBorrowckCtxt<'_, '_>,
-        region: RegionVid,
-    ) -> Option<RegionName> {
-        mbcx.give_region_a_name(region).filter(Self::region_name_is_suggestable)
-    }
-
-    /// Compiles a list of all suggestions to be printed in the final big suggestion.
-    fn compile_all_suggestions(
-        &self,
-        mbcx: &MirBorrowckCtxt<'_, '_>,
-    ) -> SmallVec<[SuggestedConstraint; 2]> {
-        let mut suggested = SmallVec::new();
-
-        // Keep track of variables that we have already suggested unifying so that we don't print
-        // out silly duplicate messages.
-        let mut unified_already = FxHashSet::default();
-
-        for (fr, outlived) in &self.constraints_to_add {
-            let fr_name = if let Some(fr_name) = self.region_vid_to_name(mbcx, *fr) {
-                fr_name
-            } else {
-                continue;
-            };
-
-            let outlived = outlived
-                .iter()
-                // if there is a `None`, we will just omit that constraint
-                .filter_map(|fr| self.region_vid_to_name(mbcx, *fr).map(|rname| (fr, rname)))
-                .collect::<Vec<_>>();
-
-            // No suggestable outlived lifetimes.
-            if outlived.is_empty() {
-                continue;
-            }
-
-            // There are three types of suggestions we can make:
-            // 1) Suggest a bound: 'a: 'b
-            // 2) Suggest replacing 'a with 'static. If any of `outlived` is `'static`, then we
-            //    should just replace 'a with 'static.
-            // 3) Suggest unifying 'a with 'b if we have both 'a: 'b and 'b: 'a
-
-            if outlived
-                .iter()
-                .any(|(_, outlived_name)| matches!(outlived_name.source, RegionNameSource::Static))
-            {
-                suggested.push(SuggestedConstraint::Static(fr_name));
-            } else {
-                // We want to isolate out all lifetimes that should be unified and print out
-                // separate messages for them.
-
-                let (unified, other): (Vec<_>, Vec<_>) = outlived.into_iter().partition(
-                    // Do we have both 'fr: 'r and 'r: 'fr?
-                    |(r, _)| {
-                        self.constraints_to_add
-                            .get(r)
-                            .map(|r_outlived| r_outlived.as_slice().contains(fr))
-                            .unwrap_or(false)
-                    },
-                );
-
-                for (r, bound) in unified.into_iter() {
-                    if !unified_already.contains(fr) {
-                        suggested.push(SuggestedConstraint::Equal(fr_name.clone(), bound));
-                        unified_already.insert(r);
-                    }
-                }
-
-                if !other.is_empty() {
-                    let other =
-                        other.iter().map(|(_, rname)| rname.clone()).collect::<SmallVec<_>>();
-                    suggested.push(SuggestedConstraint::Outlives(fr_name, other))
-                }
-            }
-        }
-
-        suggested
-    }
-
-    /// Add the outlives constraint `fr: outlived_fr` to the set of constraints we need to suggest.
-    crate fn collect_constraint(&mut self, fr: RegionVid, outlived_fr: RegionVid) {
-        debug!("Collected {:?}: {:?}", fr, outlived_fr);
-
-        // Add to set of constraints for final help note.
-        self.constraints_to_add.entry(fr).or_default().push(outlived_fr);
-    }
-
-    /// Emit an intermediate note on the given `Diagnostic` if the involved regions are
-    /// suggestable.
-    crate fn intermediate_suggestion(
-        &mut self,
-        mbcx: &MirBorrowckCtxt<'_, '_>,
-        errci: &ErrorConstraintInfo,
-        diag: &mut DiagnosticBuilder<'_>,
-    ) {
-        // Emit an intermediate note.
-        let fr_name = self.region_vid_to_name(mbcx, errci.fr);
-        let outlived_fr_name = self.region_vid_to_name(mbcx, errci.outlived_fr);
-
-        if let (Some(fr_name), Some(outlived_fr_name)) = (fr_name, outlived_fr_name) {
-            if let RegionNameSource::Static = outlived_fr_name.source {
-                diag.help(&format!("consider replacing `{}` with `'static`", fr_name));
-            } else {
-                diag.help(&format!(
-                    "consider adding the following bound: `{}: {}`",
-                    fr_name, outlived_fr_name
-                ));
-            }
-        }
-    }
-
-    /// If there is a suggestion to emit, add a diagnostic to the buffer. This is the final
-    /// suggestion including all collected constraints.
-    crate fn add_suggestion(&self, mbcx: &mut MirBorrowckCtxt<'_, '_>) {
-        // No constraints to add? Done.
-        if self.constraints_to_add.is_empty() {
-            debug!("No constraints to suggest.");
-            return;
-        }
-
-        // If there is only one constraint to suggest, then we already suggested it in the
-        // intermediate suggestion above.
-        if self.constraints_to_add.len() == 1
-            && self.constraints_to_add.values().next().unwrap().len() == 1
-        {
-            debug!("Only 1 suggestion. Skipping.");
-            return;
-        }
-
-        // Get all suggestable constraints.
-        let suggested = self.compile_all_suggestions(mbcx);
-
-        // If there are no suggestable constraints...
-        if suggested.is_empty() {
-            debug!("Only 1 suggestable constraint. Skipping.");
-            return;
-        }
-
-        // If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a
-        // list of diagnostics.
-        let mut diag = if suggested.len() == 1 {
-            mbcx.infcx.tcx.sess.diagnostic().struct_help(&match suggested.last().unwrap() {
-                SuggestedConstraint::Outlives(a, bs) => {
-                    let bs: SmallVec<[String; 2]> = bs.iter().map(|r| format!("{}", r)).collect();
-                    format!("add bound `{}: {}`", a, bs.join(" + "))
-                }
-
-                SuggestedConstraint::Equal(a, b) => {
-                    format!("`{}` and `{}` must be the same: replace one with the other", a, b)
-                }
-                SuggestedConstraint::Static(a) => format!("replace `{}` with `'static`", a),
-            })
-        } else {
-            // Create a new diagnostic.
-            let mut diag = mbcx
-                .infcx
-                .tcx
-                .sess
-                .diagnostic()
-                .struct_help("the following changes may resolve your lifetime errors");
-
-            // Add suggestions.
-            for constraint in suggested {
-                match constraint {
-                    SuggestedConstraint::Outlives(a, bs) => {
-                        let bs: SmallVec<[String; 2]> =
-                            bs.iter().map(|r| format!("{}", r)).collect();
-                        diag.help(&format!("add bound `{}: {}`", a, bs.join(" + ")));
-                    }
-                    SuggestedConstraint::Equal(a, b) => {
-                        diag.help(&format!(
-                            "`{}` and `{}` must be the same: replace one with the other",
-                            a, b
-                        ));
-                    }
-                    SuggestedConstraint::Static(a) => {
-                        diag.help(&format!("replace `{}` with `'static`", a));
-                    }
-                }
-            }
-
-            diag
-        };
-
-        // We want this message to appear after other messages on the mir def.
-        let mir_span = mbcx.body.span;
-        diag.sort_span = mir_span.shrink_to_hi();
-
-        // Buffer the diagnostic
-        diag.buffer(&mut mbcx.errors_buffer);
-    }
-}