summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_infer/src/infer/error_reporting
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:39 +0000
commit1376c5a617be5c25655d0d7cb63e3beaa5a6e026 (patch)
tree3bb8d61aee02bc7a15eab3f36e3b921afc2075d0 /compiler/rustc_infer/src/infer/error_reporting
parentReleasing progress-linux version 1.69.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.tar.xz
rustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.zip
Merging upstream version 1.70.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_infer/src/infer/error_reporting')
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs620
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs23
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs20
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs9
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs8
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/suggest.rs217
8 files changed, 460 insertions, 441 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 8a2b800af..9e5f6d107 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -49,11 +49,10 @@ use super::lexical_region_resolve::RegionResolutionError;
use super::region_constraints::GenericKind;
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
-use crate::errors;
+use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags};
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,
PredicateObligation,
@@ -75,6 +74,7 @@ use rustc_middle::ty::{
self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt,
};
+use rustc_span::DUMMY_SP;
use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
use std::ops::{ControlFlow, Deref};
@@ -90,9 +90,35 @@ pub use need_type_info::TypeAnnotationNeeded;
pub mod nice_region_error;
+/// Makes a valid string literal from a string by escaping special characters (" and \),
+/// unless they are already escaped.
+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
+}
+
/// 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`.
+/// Get an instance by calling `InferCtxt::err_ctxt` or `FnCtxt::err_ctxt`.
+///
+/// You must only create this if you intend to actually emit an error.
+/// This provides a lot of utility methods which should not be used
+/// during the happy path.
pub struct TypeErrCtxt<'a, 'tcx> {
pub infcx: &'a InferCtxt<'tcx>,
pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
@@ -104,6 +130,19 @@ pub struct TypeErrCtxt<'a, 'tcx> {
Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>,
}
+impl Drop for TypeErrCtxt<'_, '_> {
+ fn drop(&mut self) {
+ if let Some(_) = self.infcx.tcx.sess.has_errors_or_delayed_span_bugs() {
+ // ok, emitted an error.
+ } else {
+ self.infcx
+ .tcx
+ .sess
+ .delay_span_bug(DUMMY_SP, "used a `TypeErrCtxt` without failing compilation");
+ }
+ }
+}
+
impl TypeErrCtxt<'_, '_> {
/// This is just to avoid a potential footgun of accidentally
/// dropping `typeck_results` by calling `InferCtxt::err_ctxt`
@@ -164,83 +203,73 @@ fn msg_span_from_named_region<'tcx>(
alt_span: Option<Span>,
) -> (String, Option<Span>) {
match *region {
- ty::ReEarlyBound(_) | ty::ReFree(_) => {
- let (msg, span) = msg_span_from_early_bound_and_free_regions(tcx, region);
- (msg, Some(span))
- }
- ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
- ty::RePlaceholder(ty::PlaceholderRegion {
- name: ty::BoundRegionKind::BrNamed(def_id, name),
- ..
- }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))),
- ty::RePlaceholder(ty::PlaceholderRegion {
- name: ty::BoundRegionKind::BrAnon(_, Some(span)),
- ..
- }) => (format!("the anonymous lifetime defined here"), Some(span)),
- ty::RePlaceholder(ty::PlaceholderRegion {
- name: ty::BoundRegionKind::BrAnon(_, None),
- ..
- }) => (format!("an anonymous lifetime"), None),
- _ => bug!("{:?}", region),
- }
-}
-
-fn msg_span_from_early_bound_and_free_regions<'tcx>(
- tcx: TyCtxt<'tcx>,
- region: ty::Region<'tcx>,
-) -> (String, Span) {
- let scope = region.free_region_binding_scope(tcx).expect_local();
- match *region {
ty::ReEarlyBound(ref br) => {
- let mut sp = tcx.def_span(scope);
- if let Some(param) =
+ let scope = region.free_region_binding_scope(tcx).expect_local();
+ let span = if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
{
- sp = param.span;
- }
+ param.span
+ } else {
+ tcx.def_span(scope)
+ };
let text = if br.has_name() {
format!("the lifetime `{}` as defined here", br.name)
} else {
"the anonymous lifetime as defined here".to_string()
};
- (text, sp)
+ (text, Some(span))
}
ty::ReFree(ref fr) => {
if !fr.bound_region.is_named()
&& let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region)
{
- ("the anonymous lifetime defined here".to_string(), ty.span)
+ ("the anonymous lifetime defined here".to_string(), Some(ty.span))
} else {
+ let scope = region.free_region_binding_scope(tcx).expect_local();
match fr.bound_region {
ty::BoundRegionKind::BrNamed(_, name) => {
- let mut sp = tcx.def_span(scope);
- if let Some(param) =
+ let span = if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
{
- sp = param.span;
- }
+ param.span
+ } else {
+ tcx.def_span(scope)
+ };
let text = if name == kw::UnderscoreLifetime {
"the anonymous lifetime as defined here".to_string()
} else {
format!("the lifetime `{}` as defined here", name)
};
- (text, sp)
+ (text, Some(span))
}
- ty::BrAnon(idx, span) => (
- format!("the anonymous lifetime #{} defined here", idx + 1),
- match span {
+ ty::BrAnon(span) => (
+ "the anonymous lifetime as defined here".to_string(),
+ Some(match span {
Some(span) => span,
None => tcx.def_span(scope)
- }
+ })
),
_ => (
format!("the lifetime `{}` as defined here", region),
- tcx.def_span(scope),
+ Some(tcx.def_span(scope)),
),
}
}
}
- _ => bug!(),
+ ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
+ ty::RePlaceholder(ty::PlaceholderRegion {
+ bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. },
+ ..
+ }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))),
+ ty::RePlaceholder(ty::PlaceholderRegion {
+ bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(Some(span)), .. },
+ ..
+ }) => (format!("the anonymous lifetime defined here"), Some(span)),
+ ty::RePlaceholder(ty::PlaceholderRegion {
+ bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(None), .. },
+ ..
+ }) => (format!("an anonymous lifetime"), None),
+ _ => bug!("{:?}", region),
}
}
@@ -359,10 +388,12 @@ impl<'tcx> InferCtxt<'tcx> {
pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
let (def_id, substs) = match *ty.kind() {
ty::Alias(_, ty::AliasTy { def_id, substs, .. })
- if matches!(
- self.tcx.def_kind(def_id),
- DefKind::OpaqueTy | DefKind::ImplTraitPlaceholder
- ) =>
+ if matches!(self.tcx.def_kind(def_id), DefKind::OpaqueTy) =>
+ {
+ (def_id, substs)
+ }
+ ty::Alias(_, ty::AliasTy { def_id, substs, .. })
+ if self.tcx.is_impl_trait_in_trait(def_id) =>
{
(def_id, substs)
}
@@ -396,7 +427,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
&self,
generic_param_scope: LocalDefId,
errors: &[RegionResolutionError<'tcx>],
- ) {
+ ) -> ErrorGuaranteed {
+ if let Some(guaranteed) = self.infcx.tainted_by_errors() {
+ return guaranteed;
+ }
+
debug!("report_region_errors(): {} errors to start", errors.len());
// try to pre-process the errors, which will group some of them
@@ -476,6 +511,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}
}
+
+ self.tcx
+ .sess
+ .delay_span_bug(self.tcx.def_span(generic_param_scope), "expected region errors")
}
// This method goes through all the errors and try to group certain types
@@ -613,9 +652,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
let report_path_match = |err: &mut Diagnostic, did1: DefId, did2: DefId| {
- // Only external crates, if either is from a local
- // module we could have false positives
- if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate {
+ // Only report definitions from different crates. If both definitions
+ // are from a local module we could have false positives, e.g.
+ // let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
+ if did1.krate != did2.krate {
let abs_path =
|def_id| AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]);
@@ -627,10 +667,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
};
if same_path().unwrap_or(false) {
let crate_name = self.tcx.crate_name(did1.krate);
- err.note(&format!(
- "perhaps two different versions of crate `{}` are being used?",
- crate_name
- ));
+ let msg = if did1.is_local() || did2.is_local() {
+ format!(
+ "the crate `{crate_name}` is compiled multiple times, possibly with different configurations"
+ )
+ } else {
+ format!(
+ "perhaps two different versions of crate `{crate_name}` are being used?"
+ )
+ };
+ err.note(msg);
}
}
};
@@ -969,7 +1015,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let (_, sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS)
.name_all_regions(sig)
.unwrap();
- let lts: Vec<String> = reg.into_iter().map(|(_, kind)| kind.to_string()).collect();
+ let lts: Vec<String> = reg.into_values().map(|kind| kind.to_string()).collect();
(if lts.is_empty() { String::new() } else { format!("for<{}> ", lts.join(", ")) }, sig)
};
@@ -1568,6 +1614,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
ValuePairs::TraitRefs(_) | ValuePairs::PolyTraitRefs(_) => {
(false, Mismatch::Fixed("trait"))
}
+ ValuePairs::Aliases(infer::ExpectedFound { expected, .. }) => {
+ (false, Mismatch::Fixed(self.tcx.def_descr(expected.def_id)))
+ }
ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")),
};
let Some(vals) = self.values_str(values) else {
@@ -1754,8 +1803,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
)
}
(true, ty::Alias(ty::Projection, proj))
- if self.tcx.def_kind(proj.def_id)
- == DefKind::ImplTraitPlaceholder =>
+ if self.tcx.is_impl_trait_in_trait(proj.def_id) =>
{
let sm = self.tcx.sess.source_map();
let pos = sm.lookup_char_pos(self.tcx.def_span(proj.def_id).lo());
@@ -1797,7 +1845,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
// will try to hide in some case such as `async fn`, so
// to make an error more use friendly we will
// avoid to suggest a mismatch type with a
- // type that the user usually are not usign
+ // type that the user usually are not using
// directly such as `impl Future<Output = u8>`.
if !self.tcx.ty_is_opaque_future(found_ty) {
diag.note_expected_found_extra(
@@ -1888,232 +1936,182 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
debug!(?diag);
}
- pub fn report_and_explain_type_error(
+ pub fn type_error_additional_suggestions(
&self,
- trace: TypeTrace<'tcx>,
+ trace: &TypeTrace<'tcx>,
terr: TypeError<'tcx>,
- ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ ) -> Vec<TypeErrorAdditionalDiags> {
use crate::traits::ObligationCauseCode::MatchExpressionArm;
-
- debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
-
+ let mut suggestions = Vec::new();
let span = trace.cause.span();
- let failure_code = trace.cause.as_failure_code(terr);
- let mut diag = match failure_code {
- FailureCode::Error0038(did) => {
- let violations = self.tcx.object_safety_violations(did);
- report_object_safety_error(self.tcx, span, did, violations)
- }
- FailureCode::Error0317(failure_str) => {
- struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str)
- }
- FailureCode::Error0580(failure_str) => {
- 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),
- };
+ let values = self.resolve_vars_if_possible(trace.values);
+ if let Some((expected, found)) = values.ty() {
+ match (expected.kind(), found.kind()) {
+ (ty::Tuple(_), ty::Tuple(_)) => {}
+ // If a tuple of length one was expected and the found expression has
+ // parentheses around it, perhaps the user meant to write `(expr,)` to
+ // build a tuple (issue #86100)
+ (ty::Tuple(fields), _) => {
+ suggestions.extend(self.suggest_wrap_to_build_a_tuple( span, found, fields))
+ }
+ // If a byte was expected and the found expression is a char literal
+ // containing a single ASCII character, perhaps the user meant to write `b'c'` to
+ // specify a byte literal
+ (ty::Uint(ty::UintTy::U8), ty::Char) => {
+ if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
+ && let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
+ && !code.starts_with("\\u") // forbid all Unicode escapes
+ && code.chars().next().map_or(false, |c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII
+ {
+ suggestions.push(TypeErrorAdditionalDiags::MeantByteLiteral { span, code: escape_literal(code) })
}
- 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()) {
- (ty::Tuple(_), ty::Tuple(_)) => {}
- // If a tuple of length one was expected and the found expression has
- // parentheses around it, perhaps the user meant to write `(expr,)` to
- // build a tuple (issue #86100)
- (ty::Tuple(fields), _) => {
- self.emit_tuple_wrap_err(&mut err, span, found, fields)
- }
- // If a byte was expected and the found expression is a char literal
- // containing a single ASCII character, perhaps the user meant to write `b'c'` to
- // specify a byte literal
- (ty::Uint(ty::UintTy::U8), ty::Char) => {
- if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
- && let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
- && !code.starts_with("\\u") // forbid all Unicode escapes
- && code.chars().next().map_or(false, |c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII
- {
- err.span_suggestion(
- span,
- "if you meant to write a byte literal, prefix with `b`",
- format!("b'{}'", escape_literal(code)),
- Applicability::MachineApplicable,
- );
- }
- }
- // If a character was expected and the found expression is a string literal
- // containing a single character, perhaps the user meant to write `'c'` to
- // specify a character literal (issue #92479)
- (ty::Char, ty::Ref(_, r, _)) if r.is_str() => {
- if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
- && let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
- && code.chars().count() == 1
- {
- err.span_suggestion(
- span,
- "if you meant to write a `char` literal, use single quotes",
- format!("'{}'", escape_literal(code)),
- Applicability::MachineApplicable,
- );
- }
- }
- // If a string was expected and the found expression is a character literal,
- // perhaps the user meant to write `"s"` to specify a string literal.
- (ty::Ref(_, r, _), ty::Char) if r.is_str() => {
- if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) {
- if let Some(code) =
- code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
- {
- err.span_suggestion(
- span,
- "if you meant to write a `str` literal, use double quotes",
- format!("\"{}\"", escape_literal(code)),
- Applicability::MachineApplicable,
- );
- }
- }
- }
- // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`,
- // we try to suggest to add the missing `let` for `if let Some(..) = expr`
- (ty::Bool, ty::Tuple(list)) => if list.len() == 0 {
- self.suggest_let_for_letchains(&mut err, &trace.cause, span);
- }
- (ty::Array(_, _), ty::Array(_, _)) => 'block: {
- let hir = self.tcx.hir();
- let TypeError::FixedArraySize(sz) = terr else {
- break 'block;
- };
- let tykind = match hir.find_by_def_id(trace.cause.body_id) {
- Some(hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Fn(_, _, body_id),
- ..
- })) => {
- let body = hir.body(*body_id);
- struct LetVisitor<'v> {
- span: Span,
- result: Option<&'v hir::Ty<'v>>,
- }
- impl<'v> Visitor<'v> for LetVisitor<'v> {
- fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
- if self.result.is_some() {
- return;
- }
- // Find a local statement where the initializer has
- // the same span as the error and the type is specified.
- if let hir::Stmt {
- kind: hir::StmtKind::Local(hir::Local {
- init: Some(hir::Expr {
- span: init_span,
- ..
- }),
- ty: Some(array_ty),
- ..
- }),
- ..
- } = s
- && init_span == &self.span {
- self.result = Some(*array_ty);
- }
- }
- }
- let mut visitor = LetVisitor {span, result: None};
- visitor.visit_body(body);
- visitor.result.map(|r| &r.peel_refs().kind)
- }
- Some(hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Const(ty, _),
- ..
- })) => {
- Some(&ty.peel_refs().kind)
- }
- _ => None
- };
-
- if let Some(tykind) = tykind
- && let hir::TyKind::Array(_, length) = tykind
- && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length
- && let Some(span) = self.tcx.hir().opt_span(*hir_id)
- {
- err.span_suggestion(
- span,
- "consider specifying the actual array length",
- sz.found,
- Applicability::MaybeIncorrect,
- );
- }
+ // If a character was expected and the found expression is a string literal
+ // containing a single character, perhaps the user meant to write `'c'` to
+ // specify a character literal (issue #92479)
+ (ty::Char, ty::Ref(_, r, _)) if r.is_str() => {
+ if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
+ && let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
+ && code.chars().count() == 1
+ {
+ suggestions.push(TypeErrorAdditionalDiags::MeantCharLiteral { span, code: escape_literal(code) })
+ }
+ }
+ // If a string was expected and the found expression is a character literal,
+ // perhaps the user meant to write `"s"` to specify a string literal.
+ (ty::Ref(_, r, _), ty::Char) if r.is_str() => {
+ if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) {
+ if let Some(code) =
+ code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
+ {
+ suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { span, code: escape_literal(code) })
}
- _ => {}
}
}
- let code = trace.cause.code();
- if let &MatchExpressionArm(box MatchExpressionArmCause { source, .. }) = code
+ // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`,
+ // we try to suggest to add the missing `let` for `if let Some(..) = expr`
+ (ty::Bool, ty::Tuple(list)) => if list.len() == 0 {
+ suggestions.extend(self.suggest_let_for_letchains(&trace.cause, span));
+ }
+ (ty::Array(_, _), ty::Array(_, _)) => suggestions.extend(self.suggest_specify_actual_length(terr, trace, span)),
+ _ => {}
+ }
+ }
+ let code = trace.cause.code();
+ if let &MatchExpressionArm(box MatchExpressionArmCause { source, .. }) = code
&& let hir::MatchSource::TryDesugar = source
&& let Some((expected_ty, found_ty, _, _)) = self.values_str(trace.values)
{
- err.note(&format!(
- "`?` operator cannot convert from `{}` to `{}`",
- found_ty.content(),
- expected_ty.content(),
- ));
+ suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert { found: found_ty.content(), expected: expected_ty.content() });
}
- err
+ suggestions
+ }
+
+ fn suggest_specify_actual_length(
+ &self,
+ terr: TypeError<'_>,
+ trace: &TypeTrace<'_>,
+ span: Span,
+ ) -> Option<TypeErrorAdditionalDiags> {
+ let hir = self.tcx.hir();
+ let TypeError::FixedArraySize(sz) = terr else {
+ return None;
+ };
+ let tykind = match hir.find_by_def_id(trace.cause.body_id) {
+ Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => {
+ let body = hir.body(*body_id);
+ struct LetVisitor<'v> {
+ span: Span,
+ result: Option<&'v hir::Ty<'v>>,
+ }
+ impl<'v> Visitor<'v> for LetVisitor<'v> {
+ fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
+ if self.result.is_some() {
+ return;
+ }
+ // Find a local statement where the initializer has
+ // the same span as the error and the type is specified.
+ if let hir::Stmt {
+ kind: hir::StmtKind::Local(hir::Local {
+ init: Some(hir::Expr {
+ span: init_span,
+ ..
+ }),
+ ty: Some(array_ty),
+ ..
+ }),
+ ..
+ } = s
+ && init_span == &self.span {
+ self.result = Some(*array_ty);
+ }
+ }
+ }
+ let mut visitor = LetVisitor { span, result: None };
+ visitor.visit_body(body);
+ visitor.result.map(|r| &r.peel_refs().kind)
}
- FailureCode::Error0644(failure_str) => {
- struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str)
+ Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. })) => {
+ Some(&ty.peel_refs().kind)
}
+ _ => None,
};
+ if let Some(tykind) = tykind
+ && let hir::TyKind::Array(_, length) = tykind
+ && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length
+ && let Some(span) = self.tcx.hir().opt_span(*hir_id)
+ {
+ Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found })
+ } else {
+ None
+ }
+ }
+
+ pub fn report_and_explain_type_error(
+ &self,
+ trace: TypeTrace<'tcx>,
+ terr: TypeError<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
+
+ let span = trace.cause.span();
+ let failure_code = trace.cause.as_failure_code_diag(
+ terr,
+ span,
+ self.type_error_additional_suggestions(&trace, terr),
+ );
+ let mut diag = self.tcx.sess.create_err(failure_code);
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false);
diag
}
- fn emit_tuple_wrap_err(
+ fn suggest_wrap_to_build_a_tuple(
&self,
- err: &mut Diagnostic,
span: Span,
found: Ty<'tcx>,
expected_fields: &List<Ty<'tcx>>,
- ) {
- let [expected_tup_elem] = expected_fields[..] else { return };
+ ) -> Option<TypeErrorAdditionalDiags> {
+ let [expected_tup_elem] = expected_fields[..] else { return None};
if !self.same_type_modulo_infer(expected_tup_elem, found) {
- return;
+ return None;
}
let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
- else { return };
+ else { return None };
- let msg = "use a trailing comma to create a tuple with one element";
- if code.starts_with('(') && code.ends_with(')') {
+ let sugg = if code.starts_with('(') && code.ends_with(')') {
let before_close = span.hi() - BytePos::from_u32(1);
- err.span_suggestion(
- span.with_hi(before_close).shrink_to_hi(),
- msg,
- ",",
- Applicability::MachineApplicable,
- );
+ TypeErrorAdditionalDiags::TupleOnlyComma {
+ span: span.with_hi(before_close).shrink_to_hi(),
+ }
} else {
- err.multipart_suggestion(
- msg,
- vec![(span.shrink_to_lo(), "(".into()), (span.shrink_to_hi(), ",)".into())],
- Applicability::MachineApplicable,
- );
- }
+ TypeErrorAdditionalDiags::TupleAlsoParentheses {
+ span_low: span.shrink_to_lo(),
+ span_high: span.shrink_to_hi(),
+ }
+ };
+ Some(sugg)
}
fn values_str(
@@ -2124,6 +2122,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
match values {
infer::Regions(exp_found) => self.expected_found_str(exp_found),
infer::Terms(exp_found) => self.expected_found_str_term(exp_found),
+ infer::Aliases(exp_found) => self.expected_found_str(exp_found),
infer::TraitRefs(exp_found) => {
let pretty_exp_found = ty::error::ExpectedFound {
expected: exp_found.expected.print_only_trait_path(),
@@ -2386,10 +2385,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let suggestion =
if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) };
let mut suggestions = vec![(sp, suggestion)];
- for add_lt_sugg in add_lt_suggs {
- if let Some(add_lt_sugg) = add_lt_sugg {
- suggestions.push(add_lt_sugg);
- }
+ for add_lt_sugg in add_lt_suggs.into_iter().flatten() {
+ suggestions.push(add_lt_sugg);
}
err.multipart_suggestion_verbose(
format!("{msg}..."),
@@ -2413,11 +2410,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
};
let mut sugg =
vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {}", new_lt))];
- for add_lt_sugg in add_lt_suggs.clone() {
- if let Some(lt) = add_lt_sugg {
- sugg.push(lt);
- sugg.rotate_right(1);
- }
+ for lt in add_lt_suggs.clone().into_iter().flatten() {
+ sugg.push(lt);
+ sugg.rotate_right(1);
}
// `MaybeIncorrect` due to issue #41966.
err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
@@ -2688,11 +2683,6 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
self.0.tcx
}
- fn intercrate(&self) -> bool {
- assert!(!self.0.intercrate);
- false
- }
-
fn param_env(&self) -> ty::ParamEnv<'tcx> {
// Unused, only for consts which we treat as always equal
ty::ParamEnv::empty()
@@ -2706,10 +2696,6 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
true
}
- fn mark_ambiguous(&mut self) {
- bug!()
- }
-
fn relate_with_variance<T: relate::Relate<'tcx>>(
&mut self,
_variance: ty::Variance,
@@ -2828,15 +2814,21 @@ impl<'tcx> InferCtxt<'tcx> {
}
pub enum FailureCode {
- Error0038(DefId),
- Error0317(&'static str),
- Error0580(&'static str),
- Error0308(&'static str),
- Error0644(&'static str),
+ Error0317,
+ Error0580,
+ Error0308,
+ Error0644,
}
pub trait ObligationCauseExt<'tcx> {
fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode;
+
+ fn as_failure_code_diag(
+ &self,
+ terr: TypeError<'tcx>,
+ span: Span,
+ subdiags: Vec<TypeErrorAdditionalDiags>,
+ ) -> ObligationCauseFailureCode;
fn as_requirement_str(&self) -> &'static str;
}
@@ -2845,40 +2837,68 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
use self::FailureCode::*;
use crate::traits::ObligationCauseCode::*;
match self.code() {
+ IfExpressionWithNoElse => Error0317,
+ MainFunctionType => Error0580,
+ CompareImplItemObligation { .. }
+ | MatchExpressionArm(_)
+ | IfExpression { .. }
+ | LetElse
+ | StartFunctionType
+ | IntrinsicType
+ | MethodReceiver => Error0308,
+
+ // In the case where we have no more specific thing to
+ // say, also take a look at the error code, maybe we can
+ // tailor to that.
+ _ => match terr {
+ TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => Error0644,
+ TypeError::IntrinsicCast => Error0308,
+ _ => Error0308,
+ },
+ }
+ }
+ fn as_failure_code_diag(
+ &self,
+ terr: TypeError<'tcx>,
+ span: Span,
+ subdiags: Vec<TypeErrorAdditionalDiags>,
+ ) -> ObligationCauseFailureCode {
+ use crate::traits::ObligationCauseCode::*;
+ match self.code() {
CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => {
- Error0308("method not compatible with trait")
+ ObligationCauseFailureCode::MethodCompat { span, subdiags }
}
CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => {
- Error0308("type not compatible with trait")
+ ObligationCauseFailureCode::TypeCompat { span, subdiags }
}
CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => {
- Error0308("const not compatible with trait")
- }
- MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => {
- Error0308(match source {
- hir::MatchSource::TryDesugar => "`?` operator has incompatible types",
- _ => "`match` arms have incompatible types",
- })
- }
- IfExpression { .. } => Error0308("`if` and `else` have incompatible types"),
- IfExpressionWithNoElse => Error0317("`if` may be missing an `else` clause"),
- LetElse => Error0308("`else` clause of `let...else` does not diverge"),
- MainFunctionType => Error0580("`main` function has wrong type"),
- StartFunctionType => Error0308("`#[start]` function has wrong type"),
- IntrinsicType => Error0308("intrinsic has wrong type"),
- MethodReceiver => Error0308("mismatched `self` parameter type"),
+ ObligationCauseFailureCode::ConstCompat { span, subdiags }
+ }
+ MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => match source {
+ hir::MatchSource::TryDesugar => {
+ ObligationCauseFailureCode::TryCompat { span, subdiags }
+ }
+ _ => ObligationCauseFailureCode::MatchCompat { span, subdiags },
+ },
+ IfExpression { .. } => ObligationCauseFailureCode::IfElseDifferent { span, subdiags },
+ IfExpressionWithNoElse => ObligationCauseFailureCode::NoElse { span },
+ LetElse => ObligationCauseFailureCode::NoDiverge { span, subdiags },
+ MainFunctionType => ObligationCauseFailureCode::FnMainCorrectType { span },
+ StartFunctionType => ObligationCauseFailureCode::FnStartCorrectType { span, subdiags },
+ IntrinsicType => ObligationCauseFailureCode::IntristicCorrectType { span, subdiags },
+ MethodReceiver => ObligationCauseFailureCode::MethodCorrectType { span, subdiags },
// In the case where we have no more specific thing to
// say, also take a look at the error code, maybe we can
// tailor to that.
_ => match terr {
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => {
- Error0644("closure/generator type that references itself")
+ ObligationCauseFailureCode::ClosureSelfref { span }
}
TypeError::IntrinsicCast => {
- Error0308("cannot coerce intrinsics to function pointers")
+ ObligationCauseFailureCode::CantCoerce { span, subdiags }
}
- _ => Error0308("mismatched types"),
+ _ => ObligationCauseFailureCode::Generic { span, subdiags },
},
}
}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index e242900fd..75cc4e257 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -1,5 +1,5 @@
use crate::errors::{
- AmbigousImpl, AmbigousReturn, AnnotationRequired, InferenceBadError, NeedTypeInfoInGenerator,
+ AmbigousReturn, AmbiguousImpl, AnnotationRequired, InferenceBadError, NeedTypeInfoInGenerator,
SourceKindMultiSuggestion, SourceKindSubdiag,
};
use crate::infer::error_reporting::TypeErrCtxt;
@@ -10,14 +10,14 @@ use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def::{CtorOf, DefKind, Namespace};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource};
use rustc_middle::hir::nested_filter;
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
-use rustc_middle::ty::{self, DefIdTree, InferConst};
+use rustc_middle::ty::{self, InferConst};
use rustc_middle::ty::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults};
use rustc_span::symbol::{kw, sym, Ident};
@@ -358,7 +358,7 @@ impl<'tcx> InferCtxt<'tcx> {
bad_label,
}
.into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic),
- TypeAnnotationNeeded::E0283 => AmbigousImpl {
+ TypeAnnotationNeeded::E0283 => AmbiguousImpl {
span,
source_kind,
source_name,
@@ -386,7 +386,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
#[instrument(level = "debug", skip(self, error_code))]
pub fn emit_inference_failure_err(
&self,
- body_id: Option<hir::BodyId>,
+ body_def_id: LocalDefId,
failure_span: Span,
arg: GenericArg<'tcx>,
error_code: TypeAnnotationNeeded,
@@ -403,8 +403,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
};
let mut local_visitor = FindInferSourceVisitor::new(&self, typeck_results, arg);
- if let Some(body_id) = body_id {
- let expr = self.tcx.hir().expect_expr(body_id.hir_id);
+ if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(
+ self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(),
+ ) {
+ let expr = self.tcx.hir().body(body_id).value;
local_visitor.visit_expr(expr);
}
@@ -561,7 +563,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
bad_label: None,
}
.into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic),
- TypeAnnotationNeeded::E0283 => AmbigousImpl {
+ TypeAnnotationNeeded::E0283 => AmbiguousImpl {
span,
source_kind,
source_name: &name,
@@ -1189,11 +1191,14 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
have_turbofish,
} = args;
let generics = tcx.generics_of(generics_def_id);
- if let Some(argument_index) = generics
+ if let Some(mut argument_index) = generics
.own_substs(substs)
.iter()
.position(|&arg| self.generic_arg_contains_target(arg))
{
+ if generics.parent.is_none() && generics.has_self {
+ argument_index += 1;
+ }
let substs = self.infcx.resolve_vars_if_possible(substs);
let generic_args = &generics.own_substs_no_defaults(tcx, substs)
[generics.own_counts().lifetimes..];
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs
index e8d94f0c0..8a78a1956 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs
@@ -16,22 +16,34 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
match &self.error {
Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::RelateRegionParamBound(span),
- Region(Interned(RePlaceholder(ty::Placeholder { name: sub_name, .. }), _)),
- Region(Interned(RePlaceholder(ty::Placeholder { name: sup_name, .. }), _)),
+ Region(Interned(
+ RePlaceholder(ty::Placeholder {
+ bound: ty::BoundRegion { kind: sub_name, .. },
+ ..
+ }),
+ _,
+ )),
+ Region(Interned(
+ RePlaceholder(ty::Placeholder {
+ bound: ty::BoundRegion { kind: sup_name, .. },
+ ..
+ }),
+ _,
+ )),
)) => {
let span = *span;
let (sub_span, sub_symbol) = match sub_name {
ty::BrNamed(def_id, symbol) => {
(Some(self.tcx().def_span(def_id)), Some(symbol))
}
- ty::BrAnon(_, span) => (*span, None),
+ ty::BrAnon(span) => (*span, None),
ty::BrEnv => (None, None),
};
let (sup_span, sup_symbol) = match sup_name {
ty::BrNamed(def_id, symbol) => {
(Some(self.tcx().def_span(def_id)), Some(symbol))
}
- ty::BrAnon(_, span) => (*span, None),
+ ty::BrAnon(span) => (*span, None),
ty::BrEnv => (None, None),
};
let diag = match (sub_span, sup_span, sub_symbol, sup_symbol) {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index b06ff10d8..22c1e3871 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -104,7 +104,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
let (mention_influencer, influencer_point) =
if sup_origin.span().overlaps(param.param_ty_span) {
// Account for `async fn` like in `async-await/issues/issue-62097.rs`.
- // The desugaring of `async `fn`s causes `sup_origin` and `param` to point at the same
+ // The desugaring of `async fn`s causes `sup_origin` and `param` to point at the same
// place (but with different `ctxt`, hence `overlaps` instead of `==` above).
//
// This avoids the following:
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
index db4b8af46..c5ef48fe3 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
@@ -5,7 +5,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::TyCtxt;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
-use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeVisitable};
+use rustc_middle::ty::{self, Binder, Region, Ty, TypeVisitable};
use rustc_span::Span;
/// Information about the anonymous region we are searching for.
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 7ffe1fd20..07a9eff2d 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -1,5 +1,5 @@
use crate::errors::{
- note_and_explain, FullfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent,
+ note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent,
RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
};
use crate::fluent_generated as fluent;
@@ -176,7 +176,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let note = note_and_explain::RegionExplanation::new(
self.tcx, sub, opt_span, prefix, suffix,
);
- FullfillReqLifetime { span, ty: self.resolve_vars_if_possible(ty), note }
+ FulfillReqLifetime { span, ty: self.resolve_vars_if_possible(ty), note }
.into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic)
}
infer::RelateRegionParamBound(span) => {
@@ -306,9 +306,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
// Replace the explicit self type with `Self` for better suggestion rendering
.with_self_ty(self.tcx, self.tcx.mk_ty_param(0, kw::SelfUpper))
.substs;
- let trait_item_substs =
- ty::InternalSubsts::identity_for_item(self.tcx, impl_item_def_id.to_def_id())
- .rebase_onto(self.tcx, impl_def_id, trait_substs);
+ let trait_item_substs = ty::InternalSubsts::identity_for_item(self.tcx, impl_item_def_id)
+ .rebase_onto(self.tcx, impl_def_id, trait_substs);
let Ok(trait_predicates) = self
.tcx
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index b33729d0b..b38bbdfe7 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -1,7 +1,7 @@
use super::TypeErrCtxt;
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
use rustc_errors::{pluralize, Diagnostic, MultiSpan};
-use rustc_hir::{self as hir, def::DefKind};
+use rustc_hir as hir;
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::print::Printer;
@@ -75,7 +75,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
diag.note("an associated type was expected, but a different one was found");
}
(ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
- if tcx.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder =>
+ if !tcx.is_impl_trait_in_trait(proj.def_id) =>
{
let p_def_id = tcx
.generics_of(body_owner_def_id)
@@ -222,7 +222,7 @@ impl<T> Trait<T> for X {
diag.span_label(p_span, "this type parameter");
}
}
- (ty::Alias(ty::Projection, proj_ty), _) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
+ (ty::Alias(ty::Projection, proj_ty), _) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => {
self.expected_projection(
diag,
proj_ty,
@@ -231,7 +231,7 @@ impl<T> Trait<T> for X {
cause.code(),
);
}
- (_, ty::Alias(ty::Projection, proj_ty)) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
+ (_, ty::Alias(ty::Projection, proj_ty)) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => {
let msg = format!(
"consider constraining the associated type `{}` to `{}`",
values.found, values.expected,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
index 55dcfd05e..b5aeca12a 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
@@ -1,7 +1,7 @@
use hir::def::CtorKind;
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::Diagnostic;
use rustc_hir as hir;
use rustc_middle::traits::{
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@@ -10,13 +10,23 @@ use rustc_middle::traits::{
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitableExt};
use rustc_span::{sym, BytePos, Span};
+use rustc_target::abi::FieldIdx;
use crate::errors::{
- ConsiderAddingAwait, SuggAddLetForLetChains, SuggestRemoveSemiOrReturnBinding,
+ ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
+ FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate,
+ SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany,
+ SuggestTuplePatternOne, TypeErrorAdditionalDiags,
};
use super::TypeErrCtxt;
+#[derive(Clone, Copy)]
+pub enum SuggestAsRefKind {
+ Option,
+ Result,
+}
+
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
pub(super) fn suggest_remove_semi_or_return_binding(
&self,
@@ -71,25 +81,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
return_sp: Span,
arm_spans: impl Iterator<Item = Span>,
) {
- err.multipart_suggestion(
- "you could change the return type to be a boxed trait object",
- vec![
- (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
- (return_sp.shrink_to_hi(), ">".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- let sugg = arm_spans
- .flat_map(|sp| {
- [(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())]
- .into_iter()
- })
- .collect::<Vec<_>>();
- err.multipart_suggestion(
- "if you change the return type to expect trait objects, box the returned expressions",
- sugg,
- Applicability::MaybeIncorrect,
- );
+ let sugg = SuggestBoxingForReturnImplTrait::ChangeReturnType {
+ start_sp: return_sp.with_hi(return_sp.lo() + BytePos(4)),
+ end_sp: return_sp.shrink_to_hi(),
+ };
+ err.subdiagnostic(sugg);
+
+ let mut starts = Vec::new();
+ let mut ends = Vec::new();
+ for span in arm_spans {
+ starts.push(span.shrink_to_lo());
+ ends.push(span.shrink_to_hi());
+ }
+ let sugg = SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends };
+ err.subdiagnostic(sugg);
}
pub(super) fn suggest_tuple_pattern(
@@ -109,7 +114,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
})
.filter_map(|variant| {
- let sole_field = &variant.fields[0];
+ let sole_field = &variant.fields[FieldIdx::from_u32(0)];
let sole_field_ty = sole_field.ty(self.tcx, substs);
if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
let variant_path =
@@ -129,30 +134,21 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
match &compatible_variants[..] {
[] => {}
[variant] => {
- diag.multipart_suggestion_verbose(
- &format!("try wrapping the pattern in `{}`", variant),
- vec![
- (cause.span.shrink_to_lo(), format!("{}(", variant)),
- (cause.span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
+ let sugg = SuggestTuplePatternOne {
+ variant: variant.to_owned(),
+ span_low: cause.span.shrink_to_lo(),
+ span_high: cause.span.shrink_to_hi(),
+ };
+ diag.subdiagnostic(sugg);
}
_ => {
// More than one matching variant.
- diag.multipart_suggestions(
- &format!(
- "try wrapping the pattern in a variant of `{}`",
- self.tcx.def_path_str(expected_adt.did())
- ),
- compatible_variants.into_iter().map(|variant| {
- vec![
- (cause.span.shrink_to_lo(), format!("{}(", variant)),
- (cause.span.shrink_to_hi(), ")".to_string()),
- ]
- }),
- Applicability::MaybeIncorrect,
- );
+ let sugg = SuggestTuplePatternMany {
+ path: self.tcx.def_path_str(expected_adt.did()),
+ cause_span: cause.span,
+ compatible_variants,
+ };
+ diag.subdiagnostic(sugg);
}
}
}
@@ -255,15 +251,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}
- pub fn suggest_await_on_future(&self, diag: &mut Diagnostic, sp: Span) {
- diag.span_suggestion_verbose(
- sp.shrink_to_hi(),
- "consider `await`ing on the `Future`",
- ".await",
- Applicability::MaybeIncorrect,
- );
- }
-
pub(super) fn suggest_accessing_field_where_appropriate(
&self,
cause: &ObligationCause<'tcx>,
@@ -290,21 +277,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
let suggestion = if expected_def.is_struct() {
- format!("{}.{}", snippet, name)
+ SuggestAccessingField::Safe { span, snippet, name, ty }
} else if expected_def.is_union() {
- format!("unsafe {{ {}.{} }}", snippet, name)
+ SuggestAccessingField::Unsafe { span, snippet, name, ty }
} else {
return;
};
- diag.span_suggestion(
- span,
- &format!(
- "you might have meant to use field `{}` whose type is `{}`",
- name, ty
- ),
- suggestion,
- Applicability::MaybeIncorrect,
- );
+ diag.subdiagnostic(suggestion);
}
}
}
@@ -320,15 +299,15 @@ impl<'tcx> TypeErrCtxt<'_, '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)
+ && let Some(msg) = self.should_suggest_as_ref_kind(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,
- );
+ // HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
+ let snippet = snippet.trim_start_matches('&');
+ let subdiag = match msg {
+ SuggestAsRefKind::Option => SuggestAsRefWhereAppropriate::Option { span, snippet },
+ SuggestAsRefKind::Result => SuggestAsRefWhereAppropriate::Result { span, snippet },
+ };
+ diag.subdiagnostic(subdiag);
}
}
@@ -356,36 +335,24 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|| !sig.is_suggestable(self.tcx, true)
- || ty::util::is_intrinsic(self.tcx, *did)
+ || self.tcx.is_intrinsic(*did)
{
return;
}
- let (msg, sug) = match (expected.is_ref(), found.is_ref()) {
- (true, false) => {
- let msg = "consider using a reference";
- let sug = format!("&{fn_name}");
- (msg, sug)
- }
- (false, true) => {
- let msg = "consider removing the reference";
- let sug = format!("{fn_name}");
- (msg, sug)
- }
+ let sugg = match (expected.is_ref(), found.is_ref()) {
+ (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
+ (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
(true, true) => {
- diag.note("fn items are distinct from fn pointers");
- let msg = "consider casting to a fn pointer";
- let sug = format!("&({fn_name} as {sig})");
- (msg, sug)
+ diag.subdiagnostic(FnItemsAreDistinct);
+ FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig }
}
(false, false) => {
- diag.note("fn items are distinct from fn pointers");
- let msg = "consider casting to a fn pointer";
- let sug = format!("{fn_name} as {sig}");
- (msg, sug)
+ diag.subdiagnostic(FnItemsAreDistinct);
+ FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig }
}
};
- diag.span_suggestion_verbose(span, msg, sug, Applicability::MaybeIncorrect);
+ diag.subdiagnostic(sugg);
}
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
let expected_sig =
@@ -394,30 +361,36 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).subst(self.tcx, substs2));
if self.same_type_modulo_infer(*expected_sig, *found_sig) {
- diag.note("different fn items have unique types, even if their signatures are the same");
+ diag.subdiagnostic(FnUniqTypes);
}
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|| !found_sig.is_suggestable(self.tcx, true)
|| !expected_sig.is_suggestable(self.tcx, true)
- || ty::util::is_intrinsic(self.tcx, *did1)
- || ty::util::is_intrinsic(self.tcx, *did2)
+ || self.tcx.is_intrinsic(*did1)
+ || self.tcx.is_intrinsic(*did2)
{
return;
}
let fn_name = self.tcx.def_path_str_with_substs(*did2, substs2);
let sug = if found.is_ref() {
- format!("&({fn_name} as {found_sig})")
+ FunctionPointerSuggestion::CastBothRef {
+ span,
+ fn_name,
+ found_sig: *found_sig,
+ expected_sig: *expected_sig,
+ }
} else {
- format!("{fn_name} as {found_sig}")
+ FunctionPointerSuggestion::CastBoth {
+ span,
+ fn_name,
+ found_sig: *found_sig,
+ expected_sig: *expected_sig,
+ }
};
- let msg = format!(
- "consider casting both fn items to fn pointers using `as {expected_sig}`"
- );
-
- diag.span_suggestion_hidden(span, msg, sug, Applicability::MaybeIncorrect);
+ diag.subdiagnostic(sug);
}
(ty::FnDef(did, substs), ty::FnPtr(sig)) => {
let expected_sig =
@@ -436,7 +409,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
format!("{fn_name} as {found_sig}")
};
- diag.help(&format!("consider casting the fn item to a fn pointer: `{}`", casting));
+ diag.subdiagnostic(FnConsiderCasting { casting });
}
_ => {
return;
@@ -444,23 +417,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
};
}
- pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
+ pub fn should_suggest_as_ref_kind(
+ &self,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) -> Option<SuggestAsRefKind> {
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
(expected.kind(), found.kind())
{
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
if exp_def == &found_def {
let have_as_ref = &[
- (
- sym::Option,
- "you can convert from `&Option<T>` to `Option<&T>` using \
- `.as_ref()`",
- ),
- (
- sym::Result,
- "you can convert from `&Result<T, E>` to \
- `Result<&T, &E>` using `.as_ref()`",
- ),
+ (sym::Option, SuggestAsRefKind::Option),
+ (sym::Result, SuggestAsRefKind::Result),
];
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
@@ -494,15 +463,28 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
None
}
+ // FIXME: Remove once `rustc_hir_typeck` is migrated to diagnostic structs
+ pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
+ match self.should_suggest_as_ref_kind(expected, found) {
+ Some(SuggestAsRefKind::Option) => Some(
+ "you can convert from `&Option<T>` to `Option<&T>` using \
+ `.as_ref()`",
+ ),
+ Some(SuggestAsRefKind::Result) => Some(
+ "you can convert from `&Result<T, E>` to \
+ `Result<&T, &E>` using `.as_ref()`",
+ ),
+ None => None,
+ }
+ }
/// Try to find code with pattern `if Some(..) = expr`
/// use a `visitor` to mark the `if` which its span contains given error span,
/// and then try to find a assignment in the `cond` part, which span is equal with error span
pub(super) fn suggest_let_for_letchains(
&self,
- err: &mut Diagnostic,
cause: &ObligationCause<'_>,
span: Span,
- ) {
+ ) -> Option<TypeErrorAdditionalDiags> {
let hir = self.tcx.hir();
if let Some(node) = self.tcx.hir().find_by_def_id(cause.body_id) &&
let hir::Node::Item(hir::Item {
@@ -549,9 +531,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
visitor.visit_body(&body);
if visitor.result {
- err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()});
+ return Some(TypeErrorAdditionalDiags::AddLetForLetChains{span: span.shrink_to_lo()});
}
}
+ None
}
}