]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_infer/src/infer/error_reporting/mod.rs
New upstream version 1.66.0+dfsg1
[rustc.git] / compiler / rustc_infer / src / infer / error_reporting / mod.rs
index eb5afe828119cbcd5b15bef7768ac19f7d505cf0..9ff703e521ff666aa5b2a44d8bb0effe9b776725 100644 (file)
@@ -51,6 +51,7 @@ use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePa
 
 use crate::infer;
 use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
+use crate::infer::ExpectedFound;
 use crate::traits::error_reporting::report_object_safety_error;
 use crate::traits::{
     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@@ -69,12 +70,12 @@ use rustc_middle::dep_graph::DepContext;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
 use rustc_middle::ty::{
-    self, error::TypeError, Binder, List, Region, Subst, Ty, TyCtxt, TypeFoldable,
-    TypeSuperVisitable, TypeVisitable,
+    self, error::TypeError, Binder, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
+    TypeVisitable,
 };
 use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
 use rustc_target::spec::abi;
-use std::ops::ControlFlow;
+use std::ops::{ControlFlow, Deref};
 use std::{cmp, fmt, iter};
 
 mod note;
@@ -84,6 +85,31 @@ pub use need_type_info::TypeAnnotationNeeded;
 
 pub mod nice_region_error;
 
+/// A helper for building type related errors. The `typeck_results`
+/// field is only populated during an in-progress typeck.
+/// Get an instance by calling `InferCtxt::err` or `FnCtxt::infer_err`.
+pub struct TypeErrCtxt<'a, 'tcx> {
+    pub infcx: &'a InferCtxt<'tcx>,
+    pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
+}
+
+impl TypeErrCtxt<'_, '_> {
+    /// This is just to avoid a potential footgun of accidentally
+    /// dropping `typeck_results` by calling `InferCtxt::err_ctxt`
+    #[deprecated(note = "you already have a `TypeErrCtxt`")]
+    #[allow(unused)]
+    pub fn err_ctxt(&self) -> ! {
+        bug!("called `err_ctxt` on `TypeErrCtxt`. Try removing the call");
+    }
+}
+
+impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> {
+    type Target = InferCtxt<'tcx>;
+    fn deref(&self) -> &InferCtxt<'tcx> {
+        &self.infcx
+    }
+}
+
 pub(super) fn note_and_explain_region<'tcx>(
     tcx: TyCtxt<'tcx>,
     err: &mut Diagnostic,
@@ -303,7 +329,38 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
     err
 }
 
-impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+impl<'tcx> InferCtxt<'tcx> {
+    pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Binder<'tcx, Ty<'tcx>>> {
+        if let ty::Opaque(def_id, substs) = ty.kind() {
+            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+            // Future::Output
+            let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
+
+            let bounds = self.tcx.bound_explicit_item_bounds(*def_id);
+
+            for (predicate, _) in bounds.subst_iter_copied(self.tcx, substs) {
+                let output = predicate
+                    .kind()
+                    .map_bound(|kind| match kind {
+                        ty::PredicateKind::Projection(projection_predicate)
+                            if projection_predicate.projection_ty.item_def_id == item_def_id =>
+                        {
+                            projection_predicate.term.ty()
+                        }
+                        _ => None,
+                    })
+                    .transpose();
+                if output.is_some() {
+                    // We don't account for multiple `Future::Output = Ty` constraints.
+                    return output;
+                }
+            }
+        }
+        None
+    }
+}
+
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     pub fn report_region_errors(
         &self,
         generic_param_scope: LocalDefId,
@@ -577,13 +634,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 {
                     // don't show type `_`
                     if span.desugaring_kind() == Some(DesugaringKind::ForLoop)
-                    && let ty::Adt(def, substs) = ty.kind()
-                    && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option)
+                        && let ty::Adt(def, substs) = ty.kind()
+                        && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option)
                     {
                         err.span_label(span, format!("this is an iterator with items of type `{}`", substs.type_at(0)));
                     } else {
-                        err.span_label(span, format!("this expression has type `{}`", ty));
-                    }
+                    err.span_label(span, format!("this expression has type `{}`", ty));
+                }
                 }
                 if let Some(ty::error::ExpectedFound { found, .. }) = exp_found
                     && ty.is_box() && ty.boxed_ty() == found
@@ -619,8 +676,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id);
                         let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {
                             let arg_expr = args.first().expect("try desugaring call w/out arg");
-                            self.in_progress_typeck_results.and_then(|typeck_results| {
-                                typeck_results.borrow().expr_ty_opt(arg_expr)
+                            self.typeck_results.as_ref().and_then(|typeck_results| {
+                                typeck_results.expr_ty_opt(arg_expr)
                             })
                         } else {
                             bug!("try desugaring w/out call expr as scrutinee");
@@ -726,10 +783,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             _ => {
                 if let ObligationCauseCode::BindingObligation(_, span)
                 | ObligationCauseCode::ExprBindingObligation(_, span, ..)
-                    = cause.code().peel_derives()
+                = cause.code().peel_derives()
                     && let TypeError::RegionsPlaceholderMismatch = terr
                 {
-                    err.span_note(*span, "the lifetime requirement is introduced here");
+                    err.span_note( * span,
+                    "the lifetime requirement is introduced here");
                 }
             }
         }
@@ -1653,8 +1711,114 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 ),
                 Mismatch::Fixed(s) => (s.into(), s.into(), None),
             };
-            match (&terr, expected == found) {
-                (TypeError::Sorts(values), extra) => {
+
+            enum Similar<'tcx> {
+                Adts { expected: ty::AdtDef<'tcx>, found: ty::AdtDef<'tcx> },
+                PrimitiveFound { expected: ty::AdtDef<'tcx>, found: Ty<'tcx> },
+                PrimitiveExpected { expected: Ty<'tcx>, found: ty::AdtDef<'tcx> },
+            }
+
+            let similarity = |ExpectedFound { expected, found }: ExpectedFound<Ty<'tcx>>| {
+                if let ty::Adt(expected, _) = expected.kind() && let Some(primitive) = found.primitive_symbol() {
+                    let path = self.tcx.def_path(expected.did()).data;
+                    let name = path.last().unwrap().data.get_opt_name();
+                    if name == Some(primitive) {
+                        return Some(Similar::PrimitiveFound { expected: *expected, found });
+                    }
+                } else if let Some(primitive) = expected.primitive_symbol() && let ty::Adt(found, _) = found.kind() {
+                    let path = self.tcx.def_path(found.did()).data;
+                    let name = path.last().unwrap().data.get_opt_name();
+                    if name == Some(primitive) {
+                        return Some(Similar::PrimitiveExpected { expected, found: *found });
+                    }
+                } else if let ty::Adt(expected, _) = expected.kind() && let ty::Adt(found, _) = found.kind() {
+                    if !expected.did().is_local() && expected.did().krate == found.did().krate {
+                        // Most likely types from different versions of the same crate
+                        // are in play, in which case this message isn't so helpful.
+                        // A "perhaps two different versions..." error is already emitted for that.
+                        return None;
+                    }
+                    let f_path = self.tcx.def_path(found.did()).data;
+                    let e_path = self.tcx.def_path(expected.did()).data;
+
+                    if let (Some(e_last), Some(f_last)) = (e_path.last(), f_path.last()) && e_last ==  f_last {
+                        return Some(Similar::Adts{expected: *expected, found: *found});
+                    }
+                }
+                None
+            };
+
+            match terr {
+                // If two types mismatch but have similar names, mention that specifically.
+                TypeError::Sorts(values) if let Some(s) = similarity(values) => {
+                    let diagnose_primitive =
+                        |prim: Ty<'tcx>,
+                         shadow: Ty<'tcx>,
+                         defid: DefId,
+                         diagnostic: &mut Diagnostic| {
+                            let name = shadow.sort_string(self.tcx);
+                            diagnostic.note(format!(
+                            "{prim} and {name} have similar names, but are actually distinct types"
+                        ));
+                            diagnostic
+                                .note(format!("{prim} is a primitive defined by the language"));
+                            let def_span = self.tcx.def_span(defid);
+                            let msg = if defid.is_local() {
+                                format!("{name} is defined in the current crate")
+                            } else {
+                                let crate_name = self.tcx.crate_name(defid.krate);
+                                format!("{name} is defined in crate `{crate_name}")
+                            };
+                            diagnostic.span_note(def_span, msg);
+                        };
+
+                    let diagnose_adts =
+                        |expected_adt : ty::AdtDef<'tcx>,
+                         found_adt: ty::AdtDef<'tcx>,
+                         diagnostic: &mut Diagnostic| {
+                            let found_name = values.found.sort_string(self.tcx);
+                            let expected_name = values.expected.sort_string(self.tcx);
+
+                            let found_defid = found_adt.did();
+                            let expected_defid = expected_adt.did();
+
+                            diagnostic.note(format!("{found_name} and {expected_name} have similar names, but are actually distinct types"));
+                            for (defid, name) in
+                                [(found_defid, found_name), (expected_defid, expected_name)]
+                            {
+                                let def_span = self.tcx.def_span(defid);
+
+                                let msg = if found_defid.is_local() && expected_defid.is_local() {
+                                    let module = self
+                                        .tcx
+                                        .parent_module_from_def_id(defid.expect_local())
+                                        .to_def_id();
+                                    let module_name = self.tcx.def_path(module).to_string_no_crate_verbose();
+                                    format!("{name} is defined in module `crate{module_name}` of the current crate")
+                                } else if defid.is_local() {
+                                    format!("{name} is defined in the current crate")
+                                } else {
+                                    let crate_name = self.tcx.crate_name(defid.krate);
+                                    format!("{name} is defined in crate `{crate_name}`")
+                                };
+                                diagnostic.span_note(def_span, msg);
+                            }
+                        };
+
+                    match s {
+                        Similar::Adts{expected, found} => {
+                            diagnose_adts(expected, found, diag)
+                        }
+                        Similar::PrimitiveFound{expected, found: prim} => {
+                            diagnose_primitive(prim, values.expected, expected.did(), diag)
+                        }
+                        Similar::PrimitiveExpected{expected: prim, found} => {
+                            diagnose_primitive(prim, values.found, found.did(), diag)
+                        }
+                    }
+                }
+                TypeError::Sorts(values) => {
+                    let extra = expected == found;
                     let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) {
                         (true, ty::Opaque(def_id, _)) => {
                             let sm = self.tcx.sess.source_map();
@@ -1707,10 +1871,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         );
                     }
                 }
-                (TypeError::ObjectUnsafeCoercion(_), _) => {
+                TypeError::ObjectUnsafeCoercion(_) => {
                     diag.note_unsuccessful_coercion(found, expected);
                 }
-                (_, _) => {
+                _ => {
                     debug!(
                         "note_type_err: exp_found={:?}, expected={:?} found={:?}",
                         exp_found, expected, found
@@ -1755,7 +1919,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
         // In some (most?) cases cause.body_id points to actual body, but in some cases
         // it's an actual definition. According to the comments (e.g. in
-        // librustc_typeck/check/compare_method.rs:compare_predicate_entailment) the latter
+        // rustc_hir_analysis/check/compare_method.rs:compare_predicate_entailment) the latter
         // is relied upon by some other code. This might (or might not) need cleanup.
         let body_owner_def_id =
             self.tcx.hir().opt_local_def_id(cause.body_id).unwrap_or_else(|| {
@@ -1847,36 +2011,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         }
     }
 
-    pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Binder<'tcx, Ty<'tcx>>> {
-        if let ty::Opaque(def_id, substs) = ty.kind() {
-            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
-            // Future::Output
-            let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
-
-            let bounds = self.tcx.bound_explicit_item_bounds(*def_id);
-
-            for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) {
-                let predicate = predicate.subst(self.tcx, substs);
-                let output = predicate
-                    .kind()
-                    .map_bound(|kind| match kind {
-                        ty::PredicateKind::Projection(projection_predicate)
-                            if projection_predicate.projection_ty.item_def_id == item_def_id =>
-                        {
-                            projection_predicate.term.ty()
-                        }
-                        _ => None,
-                    })
-                    .transpose();
-                if output.is_some() {
-                    // We don't account for multiple `Future::Output = Ty` constraints.
-                    return output;
-                }
-            }
-        }
-        None
-    }
-
     /// A possible error is to forget to add `.await` when using futures:
     ///
     /// ```compile_fail,E0308
@@ -2051,8 +2185,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
         diag: &mut Diagnostic,
     ) {
+        if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+            && let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
+        {
+            diag.span_suggestion(
+                span,
+                msg,
+                // HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
+                format!("{}.as_ref()", snippet.trim_start_matches('&')),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+
+    pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
         if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
-            (exp_found.expected.kind(), exp_found.found.kind())
+            (expected.kind(), found.kind())
         {
             if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
                 if exp_def == &found_def {
@@ -2090,21 +2238,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                                 _ => show_suggestion = false,
                             }
                         }
-                        if let (Ok(snippet), true) =
-                            (self.tcx.sess.source_map().span_to_snippet(span), show_suggestion)
-                        {
-                            diag.span_suggestion(
-                                span,
-                                *msg,
-                                // HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
-                                format!("{}.as_ref()", snippet.trim_start_matches('&')),
-                                Applicability::MachineApplicable,
-                            );
+                        if show_suggestion {
+                            return Some(*msg);
                         }
                     }
                 }
             }
         }
+        None
     }
 
     pub fn report_and_explain_type_error(
@@ -2130,6 +2271,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str)
             }
             FailureCode::Error0308(failure_str) => {
+                fn escape_literal(s: &str) -> String {
+                    let mut escaped = String::with_capacity(s.len());
+                    let mut chrs = s.chars().peekable();
+                    while let Some(first) = chrs.next() {
+                        match (first, chrs.peek()) {
+                            ('\\', Some(&delim @ '"') | Some(&delim @ '\'')) => {
+                                escaped.push('\\');
+                                escaped.push(delim);
+                                chrs.next();
+                            }
+                            ('"' | '\'', _) => {
+                                escaped.push('\\');
+                                escaped.push(first)
+                            }
+                            (c, _) => escaped.push(c),
+                        };
+                    }
+                    escaped
+                }
                 let mut err = struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str);
                 if let Some((expected, found)) = trace.values.ty() {
                     match (expected.kind(), found.kind()) {
@@ -2151,7 +2311,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                                 err.span_suggestion(
                                     span,
                                     "if you meant to write a `char` literal, use single quotes",
-                                    format!("'{}'", code),
+                                    format!("'{}'", escape_literal(code)),
                                     Applicability::MachineApplicable,
                                 );
                             }
@@ -2166,7 +2326,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                                     err.span_suggestion(
                                         span,
                                         "if you meant to write a `str` literal, use double quotes",
-                                        format!("\"{}\"", code),
+                                        format!("\"{}\"", escape_literal(code)),
                                         Applicability::MachineApplicable,
                                     );
                                 }
@@ -2317,7 +2477,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         origin: Option<SubregionOrigin<'tcx>>,
         bound_kind: GenericKind<'tcx>,
         sub: Region<'tcx>,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
         // Attempt to obtain the span of the parameter so we can
         // suggest adding an explicit lifetime bound to it.
         let generics = self.tcx.generics_of(generic_param_scope);
@@ -2333,7 +2493,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         // We do this to avoid suggesting code that ends up as `T: 'a'b`,
                         // instead we suggest `T: 'a + 'b` in that case.
                         let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
-                        let ast_generics = self.tcx.hir().get_generics(hir_id.owner);
+                        let ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id);
                         let bounds =
                             ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id));
                         // `sp` only covers `T`, change it so that it covers
@@ -2374,6 +2534,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         let labeled_user_string = match bound_kind {
             GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
             GenericKind::Projection(ref p) => format!("the associated type `{}`", p),
+            GenericKind::Opaque(def_id, substs) => {
+                format!("the opaque type `{}`", self.tcx.def_path_str_with_substs(def_id, substs))
+            }
         };
 
         if let Some(SubregionOrigin::CompareImplItemObligation {
@@ -2453,7 +2616,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                             for h in self.tcx.hir().parent_iter(param.hir_id) {
                                 break 'origin match h.1 {
                                     Node::ImplItem(hir::ImplItem {
-                                        kind: hir::ImplItemKind::TyAlias(..),
+                                        kind: hir::ImplItemKind::Type(..),
                                         generics,
                                         ..
                                     })
@@ -2692,7 +2855,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     }
 }
 
-struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'a, 'tcx>);
+struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'tcx>);
 
 impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
@@ -2779,7 +2942,7 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
+impl<'tcx> InferCtxt<'tcx> {
     fn report_inference_failure(
         &self,
         var_origin: RegionVariableOrigin,
@@ -2967,7 +3130,7 @@ impl TyCategory {
     }
 }
 
-impl<'tcx> InferCtxt<'_, 'tcx> {
+impl<'tcx> InferCtxt<'tcx> {
     /// Given a [`hir::Block`], get the span of its last expression or
     /// statement, peeling off any inner blocks.
     pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span {
@@ -2994,7 +3157,9 @@ impl<'tcx> InferCtxt<'_, 'tcx> {
             _ => rustc_span::DUMMY_SP,
         }
     }
+}
 
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// Be helpful when the user wrote `{... expr; }` and taking the `;` off
     /// is enough to fix the error.
     pub fn could_remove_semicolon(
@@ -3011,7 +3176,7 @@ impl<'tcx> InferCtxt<'_, 'tcx> {
         let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
             return None;
         };
-        let last_expr_ty = self.in_progress_typeck_results?.borrow().expr_ty_opt(*last_expr)?;
+        let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(*last_expr)?;
         let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
             _ if last_expr_ty.references_error() => return None,
             _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
@@ -3094,8 +3259,9 @@ impl<'tcx> InferCtxt<'_, 'tcx> {
         let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
             if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
                 && let Some(pat_ty) = self
-                    .in_progress_typeck_results
-                    .and_then(|typeck_results| typeck_results.borrow().node_type_opt(*hir_id))
+                    .typeck_results
+                    .as_ref()
+                    .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
             {
                 let pat_ty = self.resolve_vars_if_possible(pat_ty);
                 if self.same_type_modulo_infer(pat_ty, expected_ty)