]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
Rollup merge of #106779 - RReverser:patch-2, r=Mark-Simulacrum
[rust.git] / compiler / rustc_infer / src / infer / error_reporting / nice_region_error / static_impl_trait.rs
index d9cdfa9dd4fc9973151c55ecbf98837c65cfbe1e..6a463583dfb0ff42573d3cbf0c308ef67fe434a1 100644 (file)
@@ -1,20 +1,28 @@
 //! Error Reporting for static impl Traits.
 
+use crate::errors::{
+    ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted,
+    ReqIntroducedLocations,
+};
 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
 use crate::infer::lexical_region_resolve::RegionResolutionError;
 use crate::infer::{SubregionOrigin, TypeTrace};
 use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_ty, Visitor};
-use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind};
+use rustc_hir::{
+    self as hir, GenericBound, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, Node,
+    TyKind,
+};
 use rustc_middle::ty::{
     self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
 };
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 
+use rustc_span::def_id::LocalDefId;
 use std::ops::ControlFlow;
 
 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
@@ -49,46 +57,32 @@ pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
                     }
 
                     let param = self.find_param_with_region(*sup_r, *sub_r)?;
-                    let lifetime = if sup_r.has_name() {
-                        format!("lifetime `{}`", sup_r)
-                    } else {
-                        "an anonymous lifetime `'_`".to_string()
+                    let simple_ident = param.param.pat.simple_ident();
+
+                    let (has_impl_path, impl_path) = match ctxt.assoc_item.container {
+                        AssocItemContainer::TraitContainer => {
+                            let id = ctxt.assoc_item.container_id(tcx);
+                            (true, tcx.def_path_str(id))
+                        }
+                        AssocItemContainer::ImplContainer => (false, String::new()),
                     };
-                    let mut err = struct_span_err!(
-                        tcx.sess,
-                        cause.span,
-                        E0772,
-                        "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
-                         requirement",
-                        param
-                            .param
-                            .pat
-                            .simple_ident()
-                            .map(|s| format!("`{}`", s))
-                            .unwrap_or_else(|| "`fn` parameter".to_string()),
-                        lifetime,
-                        ctxt.assoc_item.name,
-                    );
-                    err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
-                    err.span_label(
-                        cause.span,
-                        &format!(
-                            "...is used and required to live as long as `'static` here \
-                             because of an implicit lifetime bound on the {}",
-                            match ctxt.assoc_item.container {
-                                AssocItemContainer::TraitContainer => {
-                                    let id = ctxt.assoc_item.container_id(tcx);
-                                    format!("`impl` of `{}`", tcx.def_path_str(id))
-                                }
-                                AssocItemContainer::ImplContainer => "inherent `impl`".to_string(),
-                            },
-                        ),
-                    );
+
+                    let mut err = self.tcx().sess.create_err(ButCallingIntroduces {
+                        param_ty_span: param.param_ty_span,
+                        cause_span: cause.span,
+                        has_param_name: simple_ident.is_some(),
+                        param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
+                        has_lifetime: sup_r.has_name(),
+                        lifetime: sup_r.to_string(),
+                        assoc_item: ctxt.assoc_item.name,
+                        has_impl_path,
+                        impl_path,
+                    });
                     if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) {
                         let reported = err.emit();
                         return Some(reported);
                     } else {
-                        err.cancel();
+                        err.cancel()
                     }
                 }
                 return None;
@@ -104,25 +98,8 @@ pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
         let sp = var_origin.span();
         let return_sp = sub_origin.span();
         let param = self.find_param_with_region(*sup_r, *sub_r)?;
-        let (lifetime_name, lifetime) = if sup_r.has_name() {
-            (sup_r.to_string(), format!("lifetime `{}`", sup_r))
-        } else {
-            ("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
-        };
-        let param_name = param
-            .param
-            .pat
-            .simple_ident()
-            .map(|s| format!("`{}`", s))
-            .unwrap_or_else(|| "`fn` parameter".to_string());
-        let mut err = struct_span_err!(
-            tcx.sess,
-            sp,
-            E0759,
-            "{} has {} but it needs to satisfy a `'static` lifetime requirement",
-            param_name,
-            lifetime,
-        );
+        let simple_ident = param.param.pat.simple_ident();
+        let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() };
 
         let (mention_influencer, influencer_point) =
             if sup_origin.span().overlaps(param.param_ty_span) {
@@ -141,7 +118,6 @@ pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
             } else {
                 (!sup_origin.span().overlaps(return_sp), param.param_ty_span)
             };
-        err.span_label(influencer_point, &format!("this data with {}...", lifetime));
 
         debug!("try_report_static_impl_trait: param_info={:?}", param);
 
@@ -155,31 +131,19 @@ pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
         spans.dedup_by_key(|span| (span.lo(), span.hi()));
 
         // We try to make the output have fewer overlapping spans if possible.
-        let require_msg = if spans.is_empty() {
-            "...is used and required to live as long as `'static` here"
-        } else {
-            "...and is required to live as long as `'static` here"
-        };
         let require_span =
             if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp };
 
-        for span in &spans {
-            err.span_label(*span, "...is used here...");
-        }
-
-        if spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp) {
-            // If any of the "captured here" labels appears on the same line or after
-            // `require_span`, we put it on a note to ensure the text flows by appearing
-            // always at the end.
-            err.span_note(require_span, require_msg);
+        let spans_empty = spans.is_empty();
+        let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp);
+        let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin {
+            Some(*bound)
         } else {
-            // We don't need a note, it's already at the end, it can be shown as a `span_label`.
-            err.span_label(require_span, require_msg);
-        }
+            None
+        };
+
+        let mut subdiag = None;
 
-        if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin {
-            err.span_note(*bound, "`'static` lifetime requirement introduced by this bound");
-        }
         if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin {
             if let ObligationCauseCode::ReturnValue(hir_id)
             | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code()
@@ -187,33 +151,52 @@ pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
                 let parent_id = tcx.hir().get_parent_item(*hir_id);
                 if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) {
                     let mut span: MultiSpan = fn_decl.output.span().into();
+                    let mut spans = Vec::new();
                     let mut add_label = true;
                     if let hir::FnRetTy::Return(ty) = fn_decl.output {
                         let mut v = StaticLifetimeVisitor(vec![], tcx.hir());
                         v.visit_ty(ty);
                         if !v.0.is_empty() {
                             span = v.0.clone().into();
-                            for sp in v.0 {
-                                span.push_span_label(sp, "`'static` requirement introduced here");
-                            }
+                            spans = v.0;
                             add_label = false;
                         }
                     }
-                    if add_label {
-                        span.push_span_label(
-                            fn_decl.output.span(),
-                            "requirement introduced by this return type",
-                        );
-                    }
-                    span.push_span_label(cause.span, "because of this returned expression");
-                    err.span_note(
+                    let fn_decl_span = fn_decl.output.span();
+
+                    subdiag = Some(ReqIntroducedLocations {
                         span,
-                        "`'static` lifetime requirement introduced by the return type",
-                    );
+                        spans,
+                        fn_decl_span,
+                        cause_span: cause.span,
+                        add_label,
+                    });
                 }
             }
         }
 
+        let diag = ButNeedsToSatisfy {
+            sp,
+            influencer_point,
+            spans: spans.clone(),
+            // If any of the "captured here" labels appears on the same line or after
+            // `require_span`, we put it on a note to ensure the text flows by appearing
+            // always at the end.
+            require_span_as_note: require_as_note.then_some(require_span),
+            // We don't need a note, it's already at the end, it can be shown as a `span_label`.
+            require_span_as_label: (!require_as_note).then_some(require_span),
+            req_introduces_loc: subdiag,
+
+            has_lifetime: sup_r.has_name(),
+            lifetime: lifetime_name.clone(),
+            has_param_name: simple_ident.is_some(),
+            param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
+            spans_empty,
+            bound,
+        };
+
+        let mut err = self.tcx().sess.create_err(diag);
+
         let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
 
         let mut override_error_code = None;
@@ -247,12 +230,8 @@ pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
         }
         if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) {
             // Provide a more targeted error code and description.
-            err.code(rustc_errors::error_code!(E0772));
-            err.set_primary_message(&format!(
-                "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
-                requirement",
-                param_name, lifetime, ident,
-            ));
+            let retarget_subdiag = MoreTargeted { ident };
+            retarget_subdiag.add_to_diagnostic(&mut err);
         }
 
         let arg = match param.param.pat.simple_ident() {
@@ -268,6 +247,7 @@ pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
             Some(arg),
             captures,
             Some((param.param_ty_span, param.param_ty.to_string())),
+            Some(anon_reg_sup.def_id),
         );
 
         let reported = err.emit();
@@ -283,6 +263,7 @@ pub fn suggest_new_region_bound(
     arg: Option<String>,
     captures: String,
     param: Option<(Span, String)>,
+    scope_def_id: Option<LocalDefId>,
 ) {
     debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
     // FIXME: account for the need of parens in `&(dyn Trait + '_)`
@@ -340,12 +321,69 @@ pub fn suggest_new_region_bound(
                     _ => false,
                 }) {
                 } else {
-                    err.span_suggestion_verbose(
-                        fn_return.span.shrink_to_hi(),
-                        &format!("{declare} `{ty}` {captures}, {explicit}",),
-                        &plus_lt,
-                        Applicability::MaybeIncorrect,
-                    );
+                    // get a lifetime name of existing named lifetimes if any
+                    let existing_lt_name = if let Some(id) = scope_def_id
+                        && let Some(generics) = tcx.hir().get_generics(id)
+                        && let named_lifetimes = generics
+                        .params
+                        .iter()
+                        .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }))
+                        .map(|p| { if let hir::ParamName::Plain(name) = p.name {Some(name.to_string())} else {None}})
+                        .filter(|n| ! matches!(n, None))
+                        .collect::<Vec<_>>()
+                        && named_lifetimes.len() > 0 {
+                        named_lifetimes[0].clone()
+                    } else {
+                        None
+                    };
+                    let name = if let Some(name) = &existing_lt_name {
+                        format!("{}", name)
+                    } else {
+                        format!("'a")
+                    };
+                    // if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used.
+                    // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any
+                    if let Some(id) = scope_def_id
+                        && let Some(generics) = tcx.hir().get_generics(id)
+                        && let mut spans_suggs = generics
+                            .params
+                            .iter()
+                            .filter(|p| p.is_elided_lifetime())
+                            .map(|p|
+                                  if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { // Ampersand (elided without '_)
+                                      (p.span.shrink_to_hi(),format!("{name} "))
+                                  } else { // Underscore (elided with '_)
+                                      (p.span, format!("{name}"))
+                                  }
+                            )
+                            .collect::<Vec<_>>()
+                        && spans_suggs.len() > 1
+                    {
+                        let use_lt =
+                        if existing_lt_name == None {
+                            spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>")));
+                            format!("you can introduce a named lifetime parameter `{name}`")
+                        } else {
+                            // make use the existing named lifetime
+                            format!("you can use the named lifetime parameter `{name}`")
+                        };
+                        spans_suggs
+                            .push((fn_return.span.shrink_to_hi(), format!(" + {name} ")));
+                        err.multipart_suggestion_verbose(
+                            &format!(
+                                "{declare} `{ty}` {captures}, {use_lt}",
+                            ),
+                            spans_suggs,
+                            Applicability::MaybeIncorrect,
+                        );
+                    } else {
+                        err.span_suggestion_verbose(
+                            fn_return.span.shrink_to_hi(),
+                            &format!("{declare} `{ty}` {captures}, {explicit}",),
+                            &plus_lt,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
                 }
             }
             TyKind::TraitObject(_, lt, _) => {
@@ -488,21 +526,9 @@ fn suggest_constrain_dyn_trait_in_impl(
             let mut traits = vec![];
             let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
             hir_v.visit_ty(&self_ty);
-            for span in &traits {
-                let mut multi_span: MultiSpan = vec![*span].into();
-                multi_span
-                    .push_span_label(*span, "this has an implicit `'static` lifetime requirement");
-                multi_span.push_span_label(
-                    ident.span,
-                    "calling this method introduces the `impl`'s 'static` requirement",
-                );
-                err.span_note(multi_span, "the used `impl` has a `'static` requirement");
-                err.span_suggestion_verbose(
-                    span.shrink_to_hi(),
-                    "consider relaxing the implicit `'static` requirement",
-                    " + '_",
-                    Applicability::MaybeIncorrect,
-                );
+            for &span in &traits {
+                let subdiag = DynTraitConstraintSuggestion { span, ident };
+                subdiag.add_to_diagnostic(err);
                 suggested = true;
             }
         }
@@ -520,7 +546,7 @@ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
                 if let Some(def_id) = preds.principal_def_id() {
                     self.0.insert(def_id);
                 }
-                ControlFlow::CONTINUE
+                ControlFlow::Continue(())
             }
             _ => t.super_visit_with(self),
         }