1//! Error Reporting Code for the inference engine2//!3//! Because of the way inference, and in particular region inference,4//! works, it often happens that errors are not detected until far after5//! the relevant line of code has been type-checked. Therefore, there is6//! an elaborate system to track why a particular constraint in the7//! inference graph arose so that we can explain to the user what gave8//! rise to a particular error.9//!10//! The system is based around a set of "origin" types. An "origin" is the11//! reason that a constraint or inference variable arose. There are12//! different "origin" enums for different kinds of constraints/variables13//! (e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has14//! a span, but also more information so that we can generate a meaningful15//! error message.16//!17//! Having a catalog of all the different reasons an error can arise is18//! also useful for other reasons, like cross-referencing FAQs etc, though19//! we are not really taking advantage of this yet.20//!21//! # Region Inference22//!23//! Region inference is particularly tricky because it always succeeds "in24//! the moment" and simply registers a constraint. Then, at the end, we25//! can compute the full graph and report errors, so we need to be able to26//! store and later report what gave rise to the conflicting constraints.27//!28//! # Subtype Trace29//!30//! Determining whether `T1 <: T2` often involves a number of subtypes and31//! subconstraints along the way. A "TypeTrace" is an extended version32//! of an origin that traces the types and other values that were being33//! compared. It is not necessarily comprehensive (in fact, at the time of34//! this writing it only tracks the root values being compared) but I'd35//! like to extend it to include significant "waypoints". For example, if36//! you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T237//! <: T4` fails, I'd like the trace to include enough information to say38//! "in the 2nd element of the tuple". Similarly, failures when comparing39//! arguments or return types in fn types should be able to cite the40//! specific position, etc.41//!42//! # Reality vs plan43//!44//! Of course, there is still a LOT of code in typeck that has yet to be45//! ported to this system, and which relies on string concatenation at the46//! time of error detection.4748use std::borrow::Cow;49use std::ops::ControlFlow;50use std::path::PathBuf;51use std::{cmp, fmt, iter};5253use rustc_abi::ExternAbi;54use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};55use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize};56use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs};57use rustc_hir::def_id::{CRATE_DEF_ID, DefId};58use rustc_hir::intravisit::Visitor;59use rustc_hir::{self as hir, find_attr};60use rustc_infer::infer::DefineOpaqueTypes;61use rustc_macros::extension;62use rustc_middle::bug;63use rustc_middle::traits::PatternOriginExpr;64use rustc_middle::ty::error::{ExpectedFound, TypeError, TypeErrorToStringExt};65use rustc_middle::ty::print::{PrintTraitRefExt as _, WrapBinderMode, with_forced_trimmed_paths};66use rustc_middle::ty::{67 self, List, ParamEnv, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,68 TypeVisitableExt, Unnormalized,69};70use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, sym};71use thin_vec::ThinVec;72use tracing::{debug, instrument};7374use crate::diagnostics::{ObligationCauseFailureCode, TypeErrorAdditionalDiags};75use crate::error_reporting::TypeErrCtxt;76use crate::error_reporting::traits::ambiguity::{77 CandidateSource, compute_applicable_impls_for_diagnostics,78};79use crate::infer;80use crate::infer::relate::{self, RelateResult, TypeRelation};81use crate::infer::{InferCtxt, InferCtxtExt as _, TypeTrace, ValuePairs};82use crate::solve::deeply_normalize_for_diagnostics;83use crate::traits::{84 MatchExpressionArmCause, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,85 specialization_graph,86};8788mod note_and_explain;89mod suggest;9091pub mod need_type_info;92pub mod nice_region_error;93pub mod region;9495/// Makes a valid string literal from a string by escaping special characters (" and \),96/// unless they are already escaped.97fn escape_literal(s: &str) -> String {98 let mut escaped = String::with_capacity(s.len());99 let mut chrs = s.chars().peekable();100 while let Some(first) = chrs.next() {101 match (first, chrs.peek()) {102 ('\\', Some(&delim @ '"') | Some(&delim @ '\'')) => {103 escaped.push('\\');104 escaped.push(delim);105 chrs.next();106 }107 ('"' | '\'', _) => {108 escaped.push('\\');109 escaped.push(first)110 }111 (c, _) => escaped.push(c),112 };113 }114 escaped115}116117impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {118 fn normalize_fn_sig(119 &self,120 fn_sig: Unnormalized<'tcx, ty::PolyFnSig<'tcx>>,121 ) -> ty::PolyFnSig<'tcx> {122 let Some(param_env) = self.param_env else {123 return fn_sig.skip_normalization();124 };125126 if fn_sig.skip_normalization().has_escaping_bound_vars() {127 return fn_sig.skip_normalization();128 }129130 self.probe(|_| {131 let ocx = ObligationCtxt::new(self);132 let normalized_fn_sig = ocx.normalize(&ObligationCause::dummy(), param_env, fn_sig);133 if ocx.evaluate_obligations_error_on_ambiguity().is_empty() {134 let normalized_fn_sig = self.resolve_vars_if_possible(normalized_fn_sig);135 if !normalized_fn_sig.has_infer() {136 return normalized_fn_sig;137 }138 }139 fn_sig.skip_normalization()140 })141 }142143 // [Note-Type-error-reporting]144 // An invariant is that anytime the expected or actual type is Error (the special145 // error type, meaning that an error occurred when typechecking this expression),146 // this is a derived error. The error cascaded from another error (that was already147 // reported), so it's not useful to display it to the user.148 // The following methods implement this logic.149 // They check if either the actual or expected type is Error, and don't print the error150 // in this case. The typechecker should only ever report type errors involving mismatched151 // types using one of these methods, and should not call span_err directly for such152 // errors.153 pub fn type_error_struct_with_diag<M>(154 &self,155 sp: Span,156 mk_diag: M,157 actual_ty: Ty<'tcx>,158 ) -> Diag<'a>159 where160 M: FnOnce(String) -> Diag<'a>,161 {162 let actual_ty = self.resolve_vars_if_possible(actual_ty);163 debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty);164165 let mut err = mk_diag(self.ty_to_string(actual_ty));166167 // Don't report an error if actual type is `Error`.168 if actual_ty.references_error() {169 err.downgrade_to_delayed_bug();170 }171172 err173 }174175 pub fn report_mismatched_types(176 &self,177 cause: &ObligationCause<'tcx>,178 param_env: ty::ParamEnv<'tcx>,179 expected: Ty<'tcx>,180 actual: Ty<'tcx>,181 err: TypeError<'tcx>,182 ) -> Diag<'a> {183 let mut diag = self.report_and_explain_type_error(184 TypeTrace::types(cause, expected, actual),185 param_env,186 err,187 );188189 self.suggest_param_env_shadowing(&mut diag, expected, actual, param_env);190191 diag192 }193194 pub fn report_mismatched_consts(195 &self,196 cause: &ObligationCause<'tcx>,197 param_env: ty::ParamEnv<'tcx>,198 expected: ty::Const<'tcx>,199 actual: ty::Const<'tcx>,200 err: TypeError<'tcx>,201 ) -> Diag<'a> {202 self.report_and_explain_type_error(203 TypeTrace::consts(cause, expected, actual),204 param_env,205 err,206 )207 }208209 /// Adds a note if the types come from similarly named crates210 fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) -> bool {211 match terr {212 TypeError::Sorts(ref exp_found) => {213 // if they are both "path types", there's a chance of ambiguity214 // due to different versions of the same crate215 if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) =216 (exp_found.expected.kind(), exp_found.found.kind())217 {218 return self.check_same_definition_different_crate(219 err,220 exp_adt.did(),221 [found_adt.did()].into_iter(),222 |did| vec![self.tcx.def_span(did)],223 "type",224 );225 }226 }227 TypeError::Traits(ref exp_found) => {228 return self.check_same_definition_different_crate(229 err,230 exp_found.expected,231 [exp_found.found].into_iter(),232 |did| vec![self.tcx.def_span(did)],233 "trait",234 );235 }236 _ => (), // FIXME(#22750) handle traits and stuff237 }238 false239 }240241 fn suggest_param_env_shadowing(242 &self,243 diag: &mut Diag<'_>,244 expected: Ty<'tcx>,245 found: Ty<'tcx>,246 param_env: ty::ParamEnv<'tcx>,247 ) {248 let (alias, &def_id, concrete) = match (expected.kind(), found.kind()) {249 (ty::Alias(_, proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. }), _) => {250 (proj, def_id, found)251 }252 (_, ty::Alias(_, proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. })) => {253 (proj, def_id, expected)254 }255 _ => return,256 };257258 let tcx = self.tcx;259260 let trait_ref = alias.trait_ref(tcx);261 let obligation =262 Obligation::new(tcx, ObligationCause::dummy(), param_env, ty::Binder::dummy(trait_ref));263264 let applicable_impls =265 compute_applicable_impls_for_diagnostics(self.infcx, &obligation, false);266267 for candidate in applicable_impls {268 let impl_def_id = match candidate {269 CandidateSource::DefId(did) => did,270 CandidateSource::ParamEnv(_) => continue,271 };272273 let is_shadowed = self.infcx.probe(|_| {274 let impl_substs = self.infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);275 let impl_trait_ref =276 tcx.impl_trait_ref(impl_def_id).instantiate(tcx, impl_substs).skip_norm_wip();277278 let expected_trait_ref = alias.trait_ref(tcx);279280 if let Err(_) = self.infcx.at(&ObligationCause::dummy(), param_env).eq(281 DefineOpaqueTypes::No,282 expected_trait_ref,283 impl_trait_ref,284 ) {285 return false;286 }287288 let leaf_def = match specialization_graph::assoc_def(tcx, impl_def_id, def_id) {289 Ok(leaf) => leaf,290 Err(_) => return false,291 };292293 let trait_def_id = alias.trait_def_id(tcx);294 let rebased_args = alias.args.rebase_onto(tcx, trait_def_id, impl_substs);295296 // The impl is erroneous missing a definition for the associated type.297 // Skipping it since calling `TyCtxt::type_of` on its assoc ty will trigger an ICE.298 if !leaf_def.item.defaultness(tcx).has_value() {299 return false;300 }301302 let impl_item_def_id = leaf_def.item.def_id;303 if !tcx.check_args_compatible(impl_item_def_id, rebased_args) {304 return false;305 }306 let impl_assoc_ty =307 tcx.type_of(impl_item_def_id).instantiate(tcx, rebased_args).skip_norm_wip();308309 self.infcx.can_eq(param_env, impl_assoc_ty, concrete)310 });311312 if is_shadowed {313 diag.note(format!(314 "the associated type `{}` is defined as `{}` in the implementation, \315 but the where-bound `{}` shadows this definition\n\316 see issue #152409 <https://github.com/rust-lang/rust/issues/152409> for more information",317 self.ty_to_string(alias.to_ty(tcx, ty::IsRigid::No)),318 self.ty_to_string(concrete),319 self.ty_to_string(alias.self_ty())320 ));321 return;322 }323 }324 }325326 fn note_error_origin(327 &self,328 err: &mut Diag<'_>,329 cause: &ObligationCause<'tcx>,330 exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,331 terr: TypeError<'tcx>,332 param_env: Option<ParamEnv<'tcx>>,333 ) {334 match *cause.code() {335 ObligationCauseCode::Pattern {336 origin_expr: Some(origin_expr),337 span: Some(span),338 root_ty,339 } => {340 let expected_ty = self.resolve_vars_if_possible(root_ty);341 if !matches!(342 expected_ty.kind(),343 ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_))344 ) {345 // don't show type `_`346 if span.desugaring_kind() == Some(DesugaringKind::ForLoop)347 && let ty::Adt(def, args) = expected_ty.kind()348 && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option)349 {350 err.span_label(351 span,352 format!("this is an iterator with items of type `{}`", args.type_at(0)),353 );354 } else if !span.overlaps(cause.span) {355 let expected_ty = self.tcx.short_string(expected_ty, err.long_ty_path());356 err.span_label(span, format!("this expression has type `{expected_ty}`"));357 }358 }359 if let Some(ty::error::ExpectedFound { found, .. }) = exp_found360 && let Ok(mut peeled_snippet) =361 self.tcx.sess.source_map().span_to_snippet(origin_expr.peeled_span)362 {363 // Parentheses are needed for cases like as casts.364 // We use the peeled_span for deref suggestions.365 // It's also safe to use for box, since box only triggers if there366 // wasn't a reference to begin with.367 if origin_expr.peeled_prefix_suggestion_parentheses {368 peeled_snippet = format!("({peeled_snippet})");369 }370371 // Try giving a box suggestion first, as it is a special case of the372 // deref suggestion.373 if expected_ty.boxed_ty() == Some(found) {374 err.span_suggestion_verbose(375 span,376 "consider dereferencing the boxed value",377 format!("*{peeled_snippet}"),378 Applicability::MachineApplicable,379 );380 } else if let Some(param_env) = param_env381 && let Some(prefix) = self.should_deref_suggestion_on_mismatch(382 param_env,383 found,384 expected_ty,385 origin_expr,386 )387 {388 err.span_suggestion_verbose(389 span,390 "consider dereferencing to access the inner value using the `Deref` trait",391 format!("{prefix}{peeled_snippet}"),392 Applicability::MaybeIncorrect,393 );394 }395 }396 }397 ObligationCauseCode::Pattern { origin_expr: None, span: Some(span), .. } => {398 err.span_label(span, "expected due to this");399 }400 ObligationCauseCode::BlockTailExpression(401 _,402 hir::MatchSource::TryDesugar(scrut_hir_id),403 ) => {404 if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {405 let scrut_expr = self.tcx.hir_expect_expr(scrut_hir_id);406 let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {407 let arg_expr = args.first().expect("try desugaring call w/out arg");408 self.typeck_results409 .as_ref()410 .and_then(|typeck_results| typeck_results.expr_ty_opt(arg_expr))411 } else {412 bug!("try desugaring w/out call expr as scrutinee");413 };414415 match scrut_ty {416 Some(ty) if expected == ty => {417 let source_map = self.tcx.sess.source_map();418 err.span_suggestion(419 source_map.end_point(cause.span),420 "try removing this `?`",421 "",422 Applicability::MachineApplicable,423 );424 }425 _ => {}426 }427 }428 }429 ObligationCauseCode::MatchExpressionArm(MatchExpressionArmCause {430 arm_block_id,431 arm_span,432 arm_ty,433 prior_arm_block_id,434 prior_arm_span,435 prior_arm_ty,436 source,437 ref prior_non_diverging_arms,438 scrut_span,439 expr_span,440 ..441 }) => match source {442 hir::MatchSource::TryDesugar(scrut_hir_id) => {443 if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {444 let scrut_expr = self.tcx.hir_expect_expr(scrut_hir_id);445 let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {446 let arg_expr = args.first().expect("try desugaring call w/out arg");447 self.typeck_results448 .as_ref()449 .and_then(|typeck_results| typeck_results.expr_ty_opt(arg_expr))450 } else {451 bug!("try desugaring w/out call expr as scrutinee");452 };453454 match scrut_ty {455 Some(ty) if expected == ty => {456 let source_map = self.tcx.sess.source_map();457 err.span_suggestion(458 source_map.end_point(cause.span),459 "try removing this `?`",460 "",461 Applicability::MachineApplicable,462 );463 }464 _ => {}465 }466 }467 }468 _ => {469 // `prior_arm_ty` can be `!`, `expected` will have better info when present.470 let t = self.resolve_vars_if_possible(match exp_found {471 Some(ty::error::ExpectedFound { expected, .. }) => expected,472 _ => prior_arm_ty,473 });474 let source_map = self.tcx.sess.source_map();475 let mut any_multiline_arm = source_map.is_multiline(arm_span);476 if prior_non_diverging_arms.len() <= 4 {477 for sp in prior_non_diverging_arms {478 any_multiline_arm |= source_map.is_multiline(*sp);479 err.span_label(*sp, format!("this is found to be of type `{t}`"));480 }481 } else if let Some(sp) = prior_non_diverging_arms.last() {482 any_multiline_arm |= source_map.is_multiline(*sp);483 err.span_label(484 *sp,485 format!("this and all prior arms are found to be of type `{t}`"),486 );487 }488 let outer = if any_multiline_arm || !source_map.is_multiline(expr_span) {489 // Cover just `match` and the scrutinee expression, not490 // the entire match body, to reduce diagram noise.491 expr_span.shrink_to_lo().to(scrut_span)492 } else {493 expr_span494 };495 let msg = "`match` arms have incompatible types";496 err.span_label(outer, msg);497 if let Some(subdiag) = self.suggest_remove_semi_or_return_binding(498 prior_arm_block_id,499 prior_arm_ty,500 prior_arm_span,501 arm_block_id,502 arm_ty,503 arm_span,504 ) {505 err.subdiagnostic(subdiag);506 }507 }508 },509 ObligationCauseCode::IfExpression { expr_id, .. } => {510 let hir::Node::Expr(&hir::Expr {511 kind: hir::ExprKind::If(cond_expr, then_expr, Some(else_expr)),512 span: expr_span,513 ..514 }) = self.tcx.hir_node(expr_id)515 else {516 return;517 };518 let then_span = self.find_block_span_from_hir_id(then_expr.hir_id);519 let then_ty = self520 .typeck_results521 .as_ref()522 .expect("if expression only expected inside FnCtxt")523 .expr_ty(then_expr);524 let else_span = self.find_block_span_from_hir_id(else_expr.hir_id);525 let else_ty = self526 .typeck_results527 .as_ref()528 .expect("if expression only expected inside FnCtxt")529 .expr_ty(else_expr);530 if let hir::ExprKind::If(_cond, _then, None) = else_expr.kind531 && else_ty.is_unit()532 {533 // Account for `let x = if a { 1 } else if b { 2 };`534 err.note("`if` expressions without `else` evaluate to `()`");535 err.note("consider adding an `else` block that evaluates to the expected type");536 }537 err.span_label(then_span, "expected because of this");538539 let outer_span = if self.tcx.sess.source_map().is_multiline(expr_span) {540 if then_span.hi() == expr_span.hi() || else_span.hi() == expr_span.hi() {541 // Point at condition only if either block has the same end point as542 // the whole expression, since that'll cause awkward overlapping spans.543 Some(expr_span.shrink_to_lo().to(cond_expr.peel_drop_temps().span))544 } else {545 Some(expr_span)546 }547 } else {548 None549 };550 if let Some(sp) = outer_span {551 err.span_label(sp, "`if` and `else` have incompatible types");552 }553554 let then_id = if let hir::ExprKind::Block(then_blk, _) = then_expr.kind {555 then_blk.hir_id556 } else {557 then_expr.hir_id558 };559 let else_id = if let hir::ExprKind::Block(else_blk, _) = else_expr.kind {560 else_blk.hir_id561 } else {562 else_expr.hir_id563 };564 if let Some(subdiag) = self.suggest_remove_semi_or_return_binding(565 Some(then_id),566 then_ty,567 then_span,568 Some(else_id),569 else_ty,570 else_span,571 ) {572 err.subdiagnostic(subdiag);573 }574 }575 ObligationCauseCode::LetElse => {576 err.help("try adding a diverging expression, such as `return` or `panic!(..)`");577 err.help("...or use `match` instead of `let...else`");578 }579 _ => {580 if let ObligationCauseCode::WhereClause(_, span)581 | ObligationCauseCode::WhereClauseInExpr(_, span, ..) =582 cause.code().peel_derives()583 && !span.is_dummy()584 && let TypeError::RegionsPlaceholderMismatch = terr585 {586 err.span_note(*span, "the lifetime requirement is introduced here");587 }588 }589 }590 }591592 /// Determines whether deref_to == <deref_from as Deref>::Target, and if so,593 /// returns a prefix that should be added to deref_from as a suggestion.594 fn should_deref_suggestion_on_mismatch(595 &self,596 param_env: ParamEnv<'tcx>,597 deref_to: Ty<'tcx>,598 deref_from: Ty<'tcx>,599 origin_expr: PatternOriginExpr,600 ) -> Option<String> {601 // origin_expr contains stripped away versions of our expression.602 // We'll want to use that to avoid suggesting things like *&x.603 // However, the type that we have access to hasn't been stripped away,604 // so we need to ignore the first n dereferences, where n is the number605 // that's been stripped away in origin_expr.606607 // Find a way to autoderef from deref_from to deref_to.608 let Some((num_derefs, (after_deref_ty, _))) = (self.autoderef_steps)(deref_from)609 .into_iter()610 .enumerate()611 .find(|(_, (ty, _))| self.infcx.can_eq(param_env, *ty, deref_to))612 else {613 return None;614 };615616 if num_derefs <= origin_expr.peeled_count {617 return None;618 }619620 let deref_part = "*".repeat(num_derefs - origin_expr.peeled_count);621622 // If the user used a reference in the original expression, they probably623 // want the suggestion to still give a reference.624 if deref_from.is_ref() && !after_deref_ty.is_ref() {625 Some(format!("&{deref_part}"))626 } else {627 Some(deref_part)628 }629 }630631 /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`632 /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and633 /// populate `other_value` with `other_ty`.634 ///635 /// ```text636 /// Foo<Bar<Qux>>637 /// ^^^^--------^ this is highlighted638 /// | |639 /// | this type argument is exactly the same as the other type, not highlighted640 /// this is highlighted641 /// Bar<Qux>642 /// -------- this type is the same as a type argument in the other type, not highlighted643 /// ```644 fn highlight_outer(645 &self,646 value: &mut DiagStyledString,647 other_value: &mut DiagStyledString,648 name: String,649 args: &[ty::GenericArg<'tcx>],650 pos: usize,651 other_ty: Ty<'tcx>,652 ) {653 // `value` and `other_value` hold two incomplete type representation for display.654 // `name` is the path of both types being compared. `sub`655 value.push_highlighted(name);656657 if args.is_empty() {658 return;659 }660 value.push_highlighted("<");661662 for (i, arg) in args.iter().enumerate() {663 if i > 0 {664 value.push_normal(", ");665 }666667 match arg.kind() {668 ty::GenericArgKind::Lifetime(lt) => {669 let s = lt.to_string();670 value.push_normal(if s.is_empty() { "'_" } else { &s });671 }672 ty::GenericArgKind::Const(ct) => {673 value.push_normal(ct.to_string());674 }675 // Highlight all the type arguments that aren't at `pos` and compare676 // the type argument at `pos` and `other_ty`.677 ty::GenericArgKind::Type(type_arg) => {678 if i == pos {679 let values = self.cmp(type_arg, other_ty);680 value.0.extend((values.0).0);681 other_value.0.extend((values.1).0);682 } else {683 value.push_highlighted(type_arg.to_string());684 }685 }686 }687 }688689 value.push_highlighted(">");690 }691692 /// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`,693 /// as that is the difference to the other type.694 ///695 /// For the following code:696 ///697 /// ```ignore (illustrative)698 /// let x: Foo<Bar<Qux>> = foo::<Bar<Qux>>();699 /// ```700 ///701 /// The type error output will behave in the following way:702 ///703 /// ```text704 /// Foo<Bar<Qux>>705 /// ^^^^--------^ this is highlighted706 /// | |707 /// | this type argument is exactly the same as the other type, not highlighted708 /// this is highlighted709 /// Bar<Qux>710 /// -------- this type is the same as a type argument in the other type, not highlighted711 /// ```712 fn cmp_type_arg(713 &self,714 t1_out: &mut DiagStyledString,715 t2_out: &mut DiagStyledString,716 path: String,717 args: &'tcx [ty::GenericArg<'tcx>],718 other_path: String,719 other_ty: Ty<'tcx>,720 ) -> bool {721 for (i, arg) in args.iter().enumerate() {722 if let Some(ta) = arg.as_type() {723 if ta == other_ty {724 self.highlight_outer(t1_out, t2_out, path, args, i, other_ty);725 return true;726 }727 if let ty::Adt(def, _) = ta.kind() {728 let path_ = self.tcx.def_path_str(def.did());729 if path_ == other_path {730 self.highlight_outer(t1_out, t2_out, path, args, i, other_ty);731 return true;732 }733 }734 }735 }736 false737 }738739 /// Adds a `,` to the type representation only if it is appropriate.740 fn push_comma(741 &self,742 value: &mut DiagStyledString,743 other_value: &mut DiagStyledString,744 pos: usize,745 ) {746 if pos > 0 {747 value.push_normal(", ");748 other_value.push_normal(", ");749 }750 }751752 /// Given two `fn` signatures highlight only sub-parts that are different.753 fn cmp_fn_sig(754 &self,755 sig1: ty::PolyFnSig<'tcx>,756 fn_def1: Option<(DefId, Option<&'tcx [ty::GenericArg<'tcx>]>)>,757 sig2: ty::PolyFnSig<'tcx>,758 fn_def2: Option<(DefId, Option<&'tcx [ty::GenericArg<'tcx>]>)>,759 ) -> (DiagStyledString, DiagStyledString) {760 let sig1 = self.normalize_fn_sig(Unnormalized::new_wip(sig1));761 let sig2 = self.normalize_fn_sig(Unnormalized::new_wip(sig2));762763 let get_lifetimes = |sig| {764 use rustc_hir::def::Namespace;765 let (sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS)766 .name_all_regions(&sig, WrapBinderMode::ForAll)767 .unwrap();768 let lts: Vec<String> =769 reg.into_items().map(|(_, kind)| kind.to_string()).into_sorted_stable_ord();770 (if lts.is_empty() { String::new() } else { format!("for<{}> ", lts.join(", ")) }, sig)771 };772773 let (lt1, sig1) = get_lifetimes(sig1);774 let (lt2, sig2) = get_lifetimes(sig2);775776 // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T777 let mut values =778 (DiagStyledString::normal("".to_string()), DiagStyledString::normal("".to_string()));779780 // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T781 // ^^^^^^^^^^^^^^^^^^782 let fn_item_prefix_and_safety = |fn_def, sig: ty::FnSig<'_>| match fn_def {783 None => ("", sig.safety().prefix_str()),784 Some((did, _)) => {785 if self.tcx.codegen_fn_attrs(did).safe_target_features {786 ("#[target_features] ", "")787 } else {788 ("", sig.safety().prefix_str())789 }790 }791 };792 let (prefix1, safety1) = fn_item_prefix_and_safety(fn_def1, sig1);793 let (prefix2, safety2) = fn_item_prefix_and_safety(fn_def2, sig2);794 values.0.push(prefix1, prefix1 != prefix2);795 values.1.push(prefix2, prefix1 != prefix2);796797 // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T798 // ^^^^^^^^799 let lifetime_diff = lt1 != lt2;800 values.0.push(lt1, lifetime_diff);801 values.1.push(lt2, lifetime_diff);802803 // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T804 // ^^^^^^805 values.0.push(safety1, safety1 != safety2);806 values.1.push(safety2, safety1 != safety2);807808 // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T809 // ^^^^^^^^^^810 if sig1.abi() != ExternAbi::Rust {811 values.0.push(format!("extern {} ", sig1.abi()), sig1.abi() != sig2.abi());812 }813 if sig2.abi() != ExternAbi::Rust {814 values.1.push(format!("extern {} ", sig2.abi()), sig1.abi() != sig2.abi());815 }816817 // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T818 // ^^^819 values.0.push_normal("fn(");820 values.1.push_normal("fn(");821822 // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T823 // ^^^^^824 let len1 = sig1.inputs().len();825 let len2 = sig2.inputs().len();826 let splatted_arg_index1 = sig1.splatted().map(usize::from);827 let splatted_arg_index2 = sig2.splatted().map(usize::from);828 if len1 == len2 {829 for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() {830 self.push_comma(&mut values.0, &mut values.1, i);831 if Some(i) == splatted_arg_index1 {832 values.0.push("#[splat]", splatted_arg_index1 != splatted_arg_index2);833 values.0.push_normal(" ");834 }835 if Some(i) == splatted_arg_index2 {836 values.1.push("#[splat]", splatted_arg_index1 != splatted_arg_index2);837 values.1.push_normal(" ");838 }839 let (x1, x2) = self.cmp(*l, *r);840 (values.0).0.extend(x1.0);841 (values.1).0.extend(x2.0);842 }843 } else {844 for (i, l) in sig1.inputs().iter().enumerate() {845 values.0.push_highlighted(l.to_string());846 if i != len1 - 1 {847 values.0.push_highlighted(", ");848 }849 }850 for (i, r) in sig2.inputs().iter().enumerate() {851 values.1.push_highlighted(r.to_string());852 if i != len2 - 1 {853 values.1.push_highlighted(", ");854 }855 }856 }857858 if sig1.c_variadic() {859 if len1 > 0 {860 values.0.push_normal(", ");861 }862 values.0.push("...", !sig2.c_variadic());863 }864 if sig2.c_variadic() {865 if len2 > 0 {866 values.1.push_normal(", ");867 }868 values.1.push("...", !sig1.c_variadic());869 }870871 // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T872 // ^873 values.0.push_normal(")");874 values.1.push_normal(")");875876 // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T877 // ^^^^^^^^878 let output1 = sig1.output();879 let output2 = sig2.output();880 let (x1, x2) = self.cmp(output1, output2);881 let output_diff = x1 != x2;882 if !output1.is_unit() || output_diff {883 values.0.push_normal(" -> ");884 (values.0).0.extend(x1.0);885 }886 if !output2.is_unit() || output_diff {887 values.1.push_normal(" -> ");888 (values.1).0.extend(x2.0);889 }890891 let fmt = |did, args| format!(" {{{}}}", self.tcx.def_path_str_with_args(did, args));892893 match (fn_def1, fn_def2) {894 (Some((fn_def1, Some(fn_args1))), Some((fn_def2, Some(fn_args2)))) => {895 let path1 = fmt(fn_def1, fn_args1);896 let path2 = fmt(fn_def2, fn_args2);897 let same_path = path1 == path2;898 values.0.push(path1, !same_path);899 values.1.push(path2, !same_path);900 }901 (Some((fn_def1, Some(fn_args1))), None) => {902 values.0.push_highlighted(fmt(fn_def1, fn_args1));903 }904 (None, Some((fn_def2, Some(fn_args2)))) => {905 values.1.push_highlighted(fmt(fn_def2, fn_args2));906 }907 _ => {}908 }909910 values911 }912913 pub fn cmp_traits(914 &self,915 def_id1: DefId,916 args1: &[ty::GenericArg<'tcx>],917 def_id2: DefId,918 args2: &[ty::GenericArg<'tcx>],919 ) -> (DiagStyledString, DiagStyledString) {920 let mut values = (DiagStyledString::new(), DiagStyledString::new());921922 if def_id1 != def_id2 {923 values.0.push_highlighted(self.tcx.def_path_str(def_id1).as_str());924 values.1.push_highlighted(self.tcx.def_path_str(def_id2).as_str());925 } else {926 values.0.push_normal(self.tcx.item_name(def_id1).as_str());927 values.1.push_normal(self.tcx.item_name(def_id2).as_str());928 }929930 if args1.len() != args2.len() {931 let (pre, post) = if args1.len() > 0 { ("<", ">") } else { ("", "") };932 values.0.push_normal(format!(933 "{pre}{}{post}",934 args1.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(", ")935 ));936 let (pre, post) = if args2.len() > 0 { ("<", ">") } else { ("", "") };937 values.1.push_normal(format!(938 "{pre}{}{post}",939 args2.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(", ")940 ));941 return values;942 }943944 if args1.len() > 0 {945 values.0.push_normal("<");946 values.1.push_normal("<");947 }948 for (i, (a, b)) in std::iter::zip(args1, args2).enumerate() {949 let a_str = a.to_string();950 let b_str = b.to_string();951 if let (Some(a), Some(b)) = (a.as_type(), b.as_type()) {952 let (a, b) = self.cmp(a, b);953 values.0.0.extend(a.0);954 values.1.0.extend(b.0);955 } else if a_str != b_str {956 values.0.push_highlighted(a_str);957 values.1.push_highlighted(b_str);958 } else {959 values.0.push_normal(a_str);960 values.1.push_normal(b_str);961 }962 if i + 1 < args1.len() {963 values.0.push_normal(", ");964 values.1.push_normal(", ");965 }966 }967 if args1.len() > 0 {968 values.0.push_normal(">");969 values.1.push_normal(">");970 }971 values972 }973974 /// Compares two given types, eliding parts that are the same between them and highlighting975 /// relevant differences, and return two representation of those types for highlighted printing.976 pub fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagStyledString, DiagStyledString) {977 debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind(), t2, t2.kind());978979 // helper functions980 let recurse = |t1, t2, values: &mut (DiagStyledString, DiagStyledString)| {981 let (x1, x2) = self.cmp(t1, t2);982 (values.0).0.extend(x1.0);983 (values.1).0.extend(x2.0);984 };985986 fn fmt_region<'tcx>(region: ty::Region<'tcx>) -> String {987 let mut r = region.to_string();988 if r == "'_" {989 r.clear();990 } else {991 r.push(' ');992 }993 format!("&{r}")994 }995996 fn push_ref<'tcx>(997 region: ty::Region<'tcx>,998 mutbl: hir::Mutability,999 s: &mut DiagStyledString,1000 ) {1001 s.push_highlighted(fmt_region(region));1002 s.push_highlighted(mutbl.prefix_str());1003 }10041005 fn maybe_highlight<T: Eq + ToString>(1006 t1: T,1007 t2: T,1008 (buf1, buf2): &mut (DiagStyledString, DiagStyledString),1009 tcx: TyCtxt<'_>,1010 ) {1011 let highlight = t1 != t2;1012 let (t1, t2) = if highlight || tcx.sess.opts.verbose {1013 (t1.to_string(), t2.to_string())1014 } else {1015 // The two types are the same, elide and don't highlight.1016 ("_".into(), "_".into())1017 };1018 buf1.push(t1, highlight);1019 buf2.push(t2, highlight);1020 }10211022 fn cmp_ty_refs<'tcx>(1023 r1: ty::Region<'tcx>,1024 mut1: hir::Mutability,1025 r2: ty::Region<'tcx>,1026 mut2: hir::Mutability,1027 ss: &mut (DiagStyledString, DiagStyledString),1028 ) {1029 let (r1, r2) = (fmt_region(r1), fmt_region(r2));1030 if r1 != r2 {1031 ss.0.push_highlighted(r1);1032 ss.1.push_highlighted(r2);1033 } else {1034 ss.0.push_normal(r1);1035 ss.1.push_normal(r2);1036 }10371038 if mut1 != mut2 {1039 ss.0.push_highlighted(mut1.prefix_str());1040 ss.1.push_highlighted(mut2.prefix_str());1041 } else {1042 ss.0.push_normal(mut1.prefix_str());1043 ss.1.push_normal(mut2.prefix_str());1044 }1045 }10461047 // process starts here1048 match (t1.kind(), t2.kind()) {1049 (&ty::Adt(def1, sub1), &ty::Adt(def2, sub2)) => {1050 let did1 = def1.did();1051 let did2 = def2.did();10521053 let generics1 = self.tcx.generics_of(did1);1054 let generics2 = self.tcx.generics_of(did2);10551056 let non_default_after_default = generics11057 .check_concrete_type_after_default(self.tcx, sub1)1058 || generics2.check_concrete_type_after_default(self.tcx, sub2);1059 let sub_no_defaults_1 = if non_default_after_default {1060 generics1.own_args(sub1)1061 } else {1062 generics1.own_args_no_defaults(self.tcx, sub1)1063 };1064 let sub_no_defaults_2 = if non_default_after_default {1065 generics2.own_args(sub2)1066 } else {1067 generics2.own_args_no_defaults(self.tcx, sub2)1068 };1069 let mut values = (DiagStyledString::new(), DiagStyledString::new());1070 let path1 = self.tcx.def_path_str(did1);1071 let path2 = self.tcx.def_path_str(did2);1072 if did1 == did2 {1073 // Easy case. Replace same types with `_` to shorten the output and highlight1074 // the differing ones.1075 // let x: Foo<Bar, Qux> = y::<Foo<Quz, Qux>>();1076 // Foo<Bar, _>1077 // Foo<Quz, _>1078 // --- ^ type argument elided1079 // |1080 // highlighted in output1081 values.0.push_normal(path1);1082 values.1.push_normal(path2);10831084 // Avoid printing out default generic parameters that are common to both1085 // types.1086 let len1 = sub_no_defaults_1.len();1087 let len2 = sub_no_defaults_2.len();1088 let common_len = cmp::min(len1, len2);1089 let remainder1 = &sub1[common_len..];1090 let remainder2 = &sub2[common_len..];1091 let common_default_params =1092 iter::zip(remainder1.iter().rev(), remainder2.iter().rev())1093 .filter(|(a, b)| a == b)1094 .count();1095 let len = sub1.len() - common_default_params;10961097 // Only draw `<...>` if there are lifetime/type arguments.1098 if len > 0 {1099 values.0.push_normal("<");1100 values.1.push_normal("<");1101 }11021103 fn lifetime_display(lifetime: Region<'_>) -> String {1104 let s = lifetime.to_string();1105 if s.is_empty() { "'_".to_string() } else { s }1106 }11071108 for (i, (arg1, arg2)) in sub1.iter().zip(sub2).enumerate().take(len) {1109 self.push_comma(&mut values.0, &mut values.1, i);1110 match arg1.kind() {1111 // At one point we'd like to elide all lifetimes here, they are1112 // irrelevant for all diagnostics that use this output.1113 //1114 // Foo<'x, '_, Bar>1115 // Foo<'y, '_, Qux>1116 // ^^ ^^ --- type arguments are not elided1117 // | |1118 // | elided as they were the same1119 // not elided, they were different, but irrelevant1120 //1121 // For bound lifetimes, keep the names of the lifetimes,1122 // even if they are the same so that it's clear what's happening1123 // if we have something like1124 //1125 // for<'r, 's> fn(Inv<'r>, Inv<'s>)1126 // for<'r> fn(Inv<'r>, Inv<'r>)1127 ty::GenericArgKind::Lifetime(l1) => {1128 let l1_str = lifetime_display(l1);1129 let l2 = arg2.expect_region();1130 let l2_str = lifetime_display(l2);1131 if l1 != l2 {1132 values.0.push_highlighted(l1_str);1133 values.1.push_highlighted(l2_str);1134 } else if l1.is_bound() || self.tcx.sess.opts.verbose {1135 values.0.push_normal(l1_str);1136 values.1.push_normal(l2_str);1137 } else {1138 values.0.push_normal("'_");1139 values.1.push_normal("'_");1140 }1141 }1142 ty::GenericArgKind::Type(ta1) => {1143 let ta2 = arg2.expect_ty();1144 if ta1 == ta2 && !self.tcx.sess.opts.verbose {1145 values.0.push_normal("_");1146 values.1.push_normal("_");1147 } else {1148 recurse(ta1, ta2, &mut values);1149 }1150 }1151 // We're comparing two types with the same path, so we compare the type1152 // arguments for both. If they are the same, do not highlight and elide1153 // from the output.1154 // Foo<_, Bar>1155 // Foo<_, Qux>1156 // ^ elided type as this type argument was the same in both sides11571158 // Do the same for const arguments, if they are equal, do not highlight and1159 // elide them from the output.1160 ty::GenericArgKind::Const(ca1) => {1161 let ca2 = arg2.expect_const();1162 maybe_highlight(ca1, ca2, &mut values, self.tcx);1163 }1164 }1165 }11661167 // Close the type argument bracket.1168 // Only draw `<...>` if there are arguments.1169 if len > 0 {1170 values.0.push_normal(">");1171 values.1.push_normal(">");1172 }1173 values1174 } else {1175 // Check for case:1176 // let x: Foo<Bar<Qux> = foo::<Bar<Qux>>();1177 // Foo<Bar<Qux>1178 // ------- this type argument is exactly the same as the other type1179 // Bar<Qux>1180 if self.cmp_type_arg(1181 &mut values.0,1182 &mut values.1,1183 path1.clone(),1184 sub_no_defaults_1,1185 path2.clone(),1186 t2,1187 ) {1188 return values;1189 }1190 // Check for case:1191 // let x: Bar<Qux> = y:<Foo<Bar<Qux>>>();1192 // Bar<Qux>1193 // Foo<Bar<Qux>>1194 // ------- this type argument is exactly the same as the other type1195 if self.cmp_type_arg(1196 &mut values.1,1197 &mut values.0,1198 path2,1199 sub_no_defaults_2,1200 path1,1201 t1,1202 ) {1203 return values;1204 }12051206 // We can't find anything in common, highlight relevant part of type path.1207 // let x: foo::bar::Baz<Qux> = y:<foo::bar::Bar<Zar>>();1208 // foo::bar::Baz<Qux>1209 // foo::bar::Bar<Zar>1210 // -------- this part of the path is different12111212 let t1_str = t1.to_string();1213 let t2_str = t2.to_string();1214 let min_len = t1_str.len().min(t2_str.len());12151216 const SEPARATOR: &str = "::";1217 let separator_len = SEPARATOR.len();1218 let split_idx: usize =1219 iter::zip(t1_str.split(SEPARATOR), t2_str.split(SEPARATOR))1220 .take_while(|(mod1_str, mod2_str)| mod1_str == mod2_str)1221 .map(|(mod_str, _)| mod_str.len() + separator_len)1222 .sum();12231224 debug!(?separator_len, ?split_idx, ?min_len, "cmp");12251226 if split_idx >= min_len {1227 // paths are identical, highlight everything1228 (1229 DiagStyledString::highlighted(t1_str),1230 DiagStyledString::highlighted(t2_str),1231 )1232 } else {1233 let (common, uniq1) = t1_str.split_at(split_idx);1234 let (_, uniq2) = t2_str.split_at(split_idx);1235 debug!(?common, ?uniq1, ?uniq2, "cmp");12361237 values.0.push_normal(common);1238 values.0.push_highlighted(uniq1);1239 values.1.push_normal(common);1240 values.1.push_highlighted(uniq2);12411242 values1243 }1244 }1245 }12461247 // When finding `&T != &T`, compare the references, then recurse into pointee type1248 (&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2)) => {1249 let mut values = (DiagStyledString::new(), DiagStyledString::new());1250 cmp_ty_refs(r1, mutbl1, r2, mutbl2, &mut values);1251 recurse(ref_ty1, ref_ty2, &mut values);1252 values1253 }1254 // When finding T != &T, highlight the borrow1255 (&ty::Ref(r1, ref_ty1, mutbl1), _) => {1256 let mut values = (DiagStyledString::new(), DiagStyledString::new());1257 push_ref(r1, mutbl1, &mut values.0);1258 recurse(ref_ty1, t2, &mut values);1259 values1260 }1261 (_, &ty::Ref(r2, ref_ty2, mutbl2)) => {1262 let mut values = (DiagStyledString::new(), DiagStyledString::new());1263 push_ref(r2, mutbl2, &mut values.1);1264 recurse(t1, ref_ty2, &mut values);1265 values1266 }12671268 // When encountering tuples of the same size, highlight only the differing types1269 (&ty::Tuple(args1), &ty::Tuple(args2)) if args1.len() == args2.len() => {1270 let mut values = (DiagStyledString::normal("("), DiagStyledString::normal("("));1271 let len = args1.len();1272 for (i, (left, right)) in args1.iter().zip(args2).enumerate() {1273 self.push_comma(&mut values.0, &mut values.1, i);1274 recurse(left, right, &mut values);1275 }1276 if len == 1 {1277 // Keep the output for single element tuples as `(ty,)`.1278 values.0.push_normal(",");1279 values.1.push_normal(",");1280 }1281 values.0.push_normal(")");1282 values.1.push_normal(")");1283 values1284 }12851286 (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {1287 let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1).skip_norm_wip();1288 let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2).skip_norm_wip();1289 self.cmp_fn_sig(sig1, Some((*did1, Some(args1))), sig2, Some((*did2, Some(args2))))1290 }12911292 (ty::FnDef(did1, args1), ty::FnPtr(sig_tys2, hdr2)) => {1293 let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1).skip_norm_wip();1294 self.cmp_fn_sig(sig1, Some((*did1, Some(args1))), sig_tys2.with(*hdr2), None)1295 }12961297 (ty::FnPtr(sig_tys1, hdr1), ty::FnDef(did2, args2)) => {1298 let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2).skip_norm_wip();1299 self.cmp_fn_sig(sig_tys1.with(*hdr1), None, sig2, Some((*did2, Some(args2))))1300 }13011302 (ty::FnPtr(sig_tys1, hdr1), ty::FnPtr(sig_tys2, hdr2)) => {1303 self.cmp_fn_sig(sig_tys1.with(*hdr1), None, sig_tys2.with(*hdr2), None)1304 }13051306 _ => {1307 let mut strs = (DiagStyledString::new(), DiagStyledString::new());1308 maybe_highlight(t1, t2, &mut strs, self.tcx);1309 strs1310 }1311 }1312 }13131314 /// Extend a type error with extra labels pointing at "non-trivial" types, like closures and1315 /// the return type of `async fn`s.1316 ///1317 /// `secondary_span` gives the caller the opportunity to expand `diag` with a `span_label`.1318 ///1319 /// `swap_secondary_and_primary` is used to make projection errors in particular nicer by using1320 /// the message in `secondary_span` as the primary label, and apply the message that would1321 /// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on1322 /// E0271, like `tests/ui/issues/issue-39970.stderr`.1323 #[instrument(level = "debug", skip(self, diag, secondary_span, prefer_label))]1324 pub fn note_type_err(1325 &self,1326 diag: &mut Diag<'_>,1327 cause: &ObligationCause<'tcx>,1328 secondary_span: Option<(Span, Cow<'static, str>, bool)>,1329 mut values: Option<ty::ParamEnvAnd<'tcx, ValuePairs<'tcx>>>,1330 terr: TypeError<'tcx>,1331 prefer_label: bool,1332 override_span: Option<Span>,1333 ) {1334 // We use `override_span` when we want the error to point at a `Span` other than1335 // `cause.span`. This is used in E0271, when a closure is passed in where the return type1336 // isn't what was expected. We want to point at the closure's return type (or expression),1337 // instead of the expression where the closure is passed as call argument.1338 let span = override_span.unwrap_or(cause.span);1339 // For some types of errors, expected-found does not make1340 // sense, so just ignore the values we were given.1341 if let TypeError::CyclicTy(_) = terr {1342 values = None;1343 }1344 struct OpaqueTypesVisitor<'tcx> {1345 types: FxIndexMap<TyCategory, FxIndexSet<Span>>,1346 expected: FxIndexMap<TyCategory, FxIndexSet<Span>>,1347 found: FxIndexMap<TyCategory, FxIndexSet<Span>>,1348 ignore_span: Span,1349 tcx: TyCtxt<'tcx>,1350 }13511352 impl<'tcx> OpaqueTypesVisitor<'tcx> {1353 fn visit_expected_found(1354 tcx: TyCtxt<'tcx>,1355 expected: impl TypeVisitable<TyCtxt<'tcx>>,1356 found: impl TypeVisitable<TyCtxt<'tcx>>,1357 ignore_span: Span,1358 ) -> Self {1359 let mut types_visitor = OpaqueTypesVisitor {1360 types: Default::default(),1361 expected: Default::default(),1362 found: Default::default(),1363 ignore_span,1364 tcx,1365 };1366 // The visitor puts all the relevant encountered types in `self.types`, but in1367 // here we want to visit two separate types with no relation to each other, so we1368 // move the results from `types` to `expected` or `found` as appropriate.1369 expected.visit_with(&mut types_visitor);1370 std::mem::swap(&mut types_visitor.expected, &mut types_visitor.types);1371 found.visit_with(&mut types_visitor);1372 std::mem::swap(&mut types_visitor.found, &mut types_visitor.types);1373 types_visitor1374 }13751376 fn report(&self, err: &mut Diag<'_>) {1377 self.add_labels_for_types(err, "expected", &self.expected);1378 self.add_labels_for_types(err, "found", &self.found);1379 }13801381 fn add_labels_for_types(1382 &self,1383 err: &mut Diag<'_>,1384 target: &str,1385 types: &FxIndexMap<TyCategory, FxIndexSet<Span>>,1386 ) {1387 for (kind, values) in types.iter() {1388 let count = values.len();1389 for &sp in values {1390 err.span_label(1391 sp,1392 format!(1393 "{}{} {:#}{}",1394 if count == 1 { "the " } else { "one of the " },1395 target,1396 kind,1397 pluralize!(count),1398 ),1399 );1400 }1401 }1402 }1403 }14041405 impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for OpaqueTypesVisitor<'tcx> {1406 fn visit_ty(&mut self, t: Ty<'tcx>) {1407 if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) {1408 let span = self.tcx.def_span(def_id);1409 // Avoid cluttering the output when the "found" and error span overlap:1410 //1411 // error[E0308]: mismatched types1412 // --> $DIR/issue-20862.rs:2:51413 // |1414 // LL | |y| x + y1415 // | ^^^^^^^^^1416 // | |1417 // | the found closure1418 // | expected `()`, found closure1419 // |1420 // = note: expected unit type `()`1421 // found closure `{closure@$DIR/issue-20862.rs:2:5: 2:14 x:_}`1422 //1423 // Also ignore opaque `Future`s that come from async fns.1424 if !self.ignore_span.overlaps(span)1425 && !span.is_desugaring(DesugaringKind::Async)1426 {1427 self.types.entry(kind).or_default().insert(span);1428 }1429 }1430 t.super_visit_with(self)1431 }1432 }14331434 debug!("note_type_err(diag={:?})", diag);1435 enum Mismatch<'a> {1436 Variable(ty::error::ExpectedFound<Ty<'a>>),1437 Fixed(&'static str),1438 }1439 let (expected_found, exp_found, is_simple_error, values, param_env) = match values {1440 None => (None, Mismatch::Fixed("type"), false, None, None),1441 Some(ty::ParamEnvAnd { param_env, value: values }) => {1442 let mut values = self.resolve_vars_if_possible(values);1443 if self.next_trait_solver() {1444 values = deeply_normalize_for_diagnostics(self, param_env, values);1445 }1446 let (is_simple_error, exp_found) = match values {1447 ValuePairs::Terms(ExpectedFound { expected, found }) => {1448 match (expected.kind(), found.kind()) {1449 (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => {1450 let is_simple_err =1451 expected.is_simple_text() && found.is_simple_text();1452 OpaqueTypesVisitor::visit_expected_found(1453 self.tcx, expected, found, span,1454 )1455 .report(diag);14561457 (1458 is_simple_err,1459 Mismatch::Variable(ExpectedFound { expected, found }),1460 )1461 }1462 (ty::TermKind::Const(_), ty::TermKind::Const(_)) => {1463 (false, Mismatch::Fixed("constant"))1464 }1465 _ => (false, Mismatch::Fixed("type")),1466 }1467 }1468 ValuePairs::PolySigs(ExpectedFound { expected, found }) => {1469 OpaqueTypesVisitor::visit_expected_found(self.tcx, expected, found, span)1470 .report(diag);1471 (false, Mismatch::Fixed("signature"))1472 }1473 ValuePairs::TraitRefs(_) => (false, Mismatch::Fixed("trait")),1474 ValuePairs::Aliases(ExpectedFound { expected, .. }) => {1475 let def_id = match expected.kind {1476 ty::AliasTermKind::ProjectionTy { def_id } => def_id.into(),1477 ty::AliasTermKind::InherentTy { def_id } => def_id.into(),1478 ty::AliasTermKind::OpaqueTy { def_id } => def_id.into(),1479 ty::AliasTermKind::FreeTy { def_id } => def_id.into(),1480 ty::AliasTermKind::AnonConst { def_id } => def_id.into(),1481 ty::AliasTermKind::ProjectionConst { def_id } => def_id.into(),1482 ty::AliasTermKind::FreeConst { def_id } => def_id.into(),1483 ty::AliasTermKind::InherentConst { def_id } => def_id.into(),1484 };1485 (false, Mismatch::Fixed(self.tcx.def_descr(def_id)))1486 }1487 ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")),1488 ValuePairs::ExistentialTraitRef(_) => {1489 (false, Mismatch::Fixed("existential trait ref"))1490 }1491 ValuePairs::ExistentialProjection(_) => {1492 (false, Mismatch::Fixed("existential projection"))1493 }1494 };1495 let Some(vals) = self.values_str(values, cause, diag.long_ty_path()) else {1496 // Derived error. Cancel the emitter.1497 // NOTE(eddyb) this was `.cancel()`, but `diag`1498 // is borrowed, so we can't fully defuse it.1499 diag.downgrade_to_delayed_bug();1500 return;1501 };1502 (Some(vals), exp_found, is_simple_error, Some(values), Some(param_env))1503 }1504 };15051506 let mut label_or_note = |span: Span, msg: Cow<'static, str>| {1507 if (prefer_label && is_simple_error) || &[span] == diag.span.primary_spans() {1508 diag.span_label(span, msg);1509 } else {1510 diag.span_note(span, msg);1511 }1512 };1513 if let Some((secondary_span, secondary_msg, swap_secondary_and_primary)) = secondary_span {1514 if swap_secondary_and_primary {1515 let terr = if let Some(infer::ValuePairs::Terms(ExpectedFound {1516 expected, ..1517 })) = values1518 {1519 Cow::from(format!("expected this to be `{expected}`"))1520 } else {1521 terr.to_string(self.tcx)1522 };1523 label_or_note(secondary_span, terr);1524 label_or_note(span, secondary_msg);1525 } else {1526 label_or_note(span, terr.to_string(self.tcx));1527 label_or_note(secondary_span, secondary_msg);1528 }1529 } else if let Some(values) = values1530 && let Some((e, f)) = values.ty()1531 && let TypeError::ArgumentSorts(..) | TypeError::Sorts(_) = terr1532 {1533 let e = self.tcx.erase_and_anonymize_regions(e);1534 let f = self.tcx.erase_and_anonymize_regions(f);1535 let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx));1536 let found = with_forced_trimmed_paths!(f.sort_string(self.tcx));1537 if expected == found {1538 label_or_note(span, terr.to_string(self.tcx));1539 } else {1540 label_or_note(span, Cow::from(format!("expected {expected}, found {found}")));1541 }1542 } else {1543 label_or_note(span, terr.to_string(self.tcx));1544 }15451546 if let Some(param_env) = param_env {1547 self.note_field_shadowed_by_private_candidate_in_cause(diag, cause, param_env);1548 }15491550 if self.check_and_note_conflicting_crates(diag, terr) {1551 return;1552 }15531554 if let Some((expected, found)) = expected_found {1555 let (expected_label, found_label, exp_found) = match exp_found {1556 Mismatch::Variable(ef) => (1557 ef.expected.prefix_string(self.tcx),1558 ef.found.prefix_string(self.tcx),1559 Some(ef),1560 ),1561 Mismatch::Fixed(s) => (s.into(), s.into(), None),1562 };15631564 enum Similar<'tcx> {1565 Adts { expected: ty::AdtDef<'tcx>, found: ty::AdtDef<'tcx> },1566 PrimitiveFound { expected: ty::AdtDef<'tcx>, found: Ty<'tcx> },1567 PrimitiveExpected { expected: Ty<'tcx>, found: ty::AdtDef<'tcx> },1568 }15691570 let similarity = |ExpectedFound { expected, found }: ExpectedFound<Ty<'tcx>>| {1571 if let ty::Adt(expected, _) = expected.kind()1572 && let Some(primitive) = found.primitive_symbol()1573 {1574 let path = self.tcx.def_path(expected.did()).data;1575 let name = path.last().unwrap().data.get_opt_name();1576 if name == Some(primitive) {1577 return Some(Similar::PrimitiveFound { expected: *expected, found });1578 }1579 } else if let Some(primitive) = expected.primitive_symbol()1580 && let ty::Adt(found, _) = found.kind()1581 {1582 let path = self.tcx.def_path(found.did()).data;1583 let name = path.last().unwrap().data.get_opt_name();1584 if name == Some(primitive) {1585 return Some(Similar::PrimitiveExpected { expected, found: *found });1586 }1587 } else if let ty::Adt(expected, _) = expected.kind()1588 && let ty::Adt(found, _) = found.kind()1589 {1590 if !expected.did().is_local() && expected.did().krate == found.did().krate {1591 // Most likely types from different versions of the same crate1592 // are in play, in which case this message isn't so helpful.1593 // A "perhaps two different versions..." error is already emitted for that.1594 return None;1595 }1596 let f_path = self.tcx.def_path(found.did()).data;1597 let e_path = self.tcx.def_path(expected.did()).data;15981599 if let (Some(e_last), Some(f_last)) = (e_path.last(), f_path.last())1600 && e_last == f_last1601 {1602 return Some(Similar::Adts { expected: *expected, found: *found });1603 }1604 }1605 None1606 };16071608 match terr {1609 // If two types mismatch but have similar names, mention that specifically.1610 TypeError::Sorts(values) if let Some(s) = similarity(values) => {1611 let diagnose_primitive =1612 |prim: Ty<'tcx>, shadow: Ty<'tcx>, defid: DefId, diag: &mut Diag<'_>| {1613 let name = shadow.sort_string(self.tcx);1614 diag.note(format!(1615 "`{prim}` and {name} have similar names, but are actually distinct types"1616 ));1617 diag.note(format!(1618 "one `{prim}` is a primitive defined by the language",1619 ));1620 let def_span = self.tcx.def_span(defid);1621 let msg = if defid.is_local() {1622 format!("the other {name} is defined in the current crate")1623 } else {1624 let crate_name = self.tcx.crate_name(defid.krate);1625 format!("the other {name} is defined in crate `{crate_name}`")1626 };1627 diag.span_note(def_span, msg);1628 };16291630 let diagnose_adts =1631 |expected_adt: ty::AdtDef<'tcx>,1632 found_adt: ty::AdtDef<'tcx>,1633 diag: &mut Diag<'_>| {1634 let found_name = values.found.sort_string(self.tcx);1635 let expected_name = values.expected.sort_string(self.tcx);16361637 let found_defid = found_adt.did();1638 let expected_defid = expected_adt.did();16391640 diag.note(format!("{found_name} and {expected_name} have similar names, but are actually distinct types"));1641 for (defid, name) in1642 [(found_defid, found_name), (expected_defid, expected_name)]1643 {1644 let def_span = self.tcx.def_span(defid);16451646 let msg = if found_defid.is_local() && expected_defid.is_local() {1647 let module = self1648 .tcx1649 .parent_module_from_def_id(defid.expect_local())1650 .to_def_id();1651 let module_name =1652 self.tcx.def_path(module).to_string_no_crate_verbose();1653 format!(1654 "{name} is defined in module `crate{module_name}` of the current crate"1655 )1656 } else if defid.is_local() {1657 format!("{name} is defined in the current crate")1658 } else {1659 let crate_name = self.tcx.crate_name(defid.krate);1660 format!("{name} is defined in crate `{crate_name}`")1661 };1662 diag.span_note(def_span, msg);1663 }1664 };16651666 match s {1667 Similar::Adts { expected, found } => diagnose_adts(expected, found, diag),1668 Similar::PrimitiveFound { expected, found: prim } => {1669 diagnose_primitive(prim, values.expected, expected.did(), diag)1670 }1671 Similar::PrimitiveExpected { expected: prim, found } => {1672 diagnose_primitive(prim, values.found, found.did(), diag)1673 }1674 }1675 }1676 TypeError::Sorts(values) => {1677 let extra = expected == found1678 // Ensure that we don't ever say something like1679 // expected `impl Trait` (opaque type `impl Trait`)1680 // found `impl Trait` (opaque type `impl Trait`)1681 && values.expected.sort_string(self.tcx)1682 != values.found.sort_string(self.tcx);1683 let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) {1684 (true, ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, .. })) => {1685 let sm = self.tcx.sess.source_map();1686 let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo());1687 DiagStyledString::normal(format!(1688 " (opaque type at <{}:{}:{}>)",1689 sm.filename_for_diagnostics(&pos.file.name),1690 pos.line,1691 pos.col.to_usize() + 1,1692 ))1693 }1694 (1695 true,1696 &ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, .. }),1697 ) if self.tcx.is_impl_trait_in_trait(def_id) => {1698 let sm = self.tcx.sess.source_map();1699 let pos = sm.lookup_char_pos(self.tcx.def_span(def_id).lo());1700 DiagStyledString::normal(format!(1701 " (trait associated opaque type at <{}:{}:{}>)",1702 sm.filename_for_diagnostics(&pos.file.name),1703 pos.line,1704 pos.col.to_usize() + 1,1705 ))1706 }1707 (true, _) => {1708 let mut s = DiagStyledString::normal(" (");1709 s.push_highlighted(ty.sort_string(self.tcx));1710 s.push_normal(")");1711 s1712 }1713 (false, _) => DiagStyledString::normal(""),1714 };1715 if !(values.expected.is_simple_text() && values.found.is_simple_text())1716 || (exp_found.is_some_and(|ef| {1717 // This happens when the type error is a subset of the expectation,1718 // like when you have two references but one is `usize` and the other1719 // is `f32`. In those cases we still want to show the `note`. If the1720 // value from `ef` is `Infer(_)`, then we ignore it.1721 if !ef.expected.is_ty_or_numeric_infer() {1722 ef.expected != values.expected1723 } else if !ef.found.is_ty_or_numeric_infer() {1724 ef.found != values.found1725 } else {1726 false1727 }1728 }))1729 {1730 if let Some(ExpectedFound { found: found_ty, .. }) = exp_found1731 && !self.tcx.ty_is_opaque_future(found_ty)1732 {1733 // `Future` is a special opaque type that the compiler1734 // will try to hide in some case such as `async fn`, so1735 // to make an error more use friendly we will1736 // avoid to suggest a mismatch type with a1737 // type that the user usually are not using1738 // directly such as `impl Future<Output = u8>`.1739 diag.note_expected_found_extra(1740 &expected_label,1741 expected,1742 &found_label,1743 found,1744 sort_string(values.expected),1745 sort_string(values.found),1746 );1747 }1748 }1749 }1750 _ => {1751 debug!(1752 "note_type_err: exp_found={:?}, expected={:?} found={:?}",1753 exp_found, expected, found1754 );1755 if !is_simple_error || terr.must_include_note() {1756 diag.note_expected_found(&expected_label, expected, &found_label, found);17571758 if let Some(ty::Closure(_, args)) =1759 exp_found.map(|expected_type_found| expected_type_found.found.kind())1760 {1761 diag.highlighted_note(vec![1762 StringPart::normal("closure has signature: `"),1763 StringPart::highlighted(1764 self.tcx1765 .signature_unclosure(1766 args.as_closure().sig(),1767 rustc_hir::Safety::Safe,1768 )1769 .to_string(),1770 ),1771 StringPart::normal("`"),1772 ]);1773 }1774 }1775 }1776 }1777 }1778 let exp_found = match exp_found {1779 Mismatch::Variable(exp_found) => Some(exp_found),1780 Mismatch::Fixed(_) => None,1781 };1782 let exp_found = match terr {1783 // `terr` has more accurate type information than `exp_found` in match expressions.1784 ty::error::TypeError::Sorts(terr)1785 if exp_found.is_some_and(|ef| terr.found == ef.found) =>1786 {1787 Some(terr)1788 }1789 _ => exp_found,1790 };1791 debug!("exp_found {:?} terr {:?} cause.code {:?}", exp_found, terr, cause.code());1792 if let Some(exp_found) = exp_found {1793 let should_suggest_fixes =1794 if let ObligationCauseCode::Pattern { root_ty, .. } = cause.code() {1795 // Skip if the root_ty of the pattern is not the same as the expected_ty.1796 // If these types aren't equal then we've probably peeled off a layer of arrays.1797 self.same_type_modulo_infer(*root_ty, exp_found.expected)1798 } else {1799 true1800 };18011802 // FIXME(#73154): For now, we do leak check when coercing function1803 // pointers in typeck, instead of only during borrowck. This can lead1804 // to these `RegionsInsufficientlyPolymorphic` errors that aren't helpful.1805 if should_suggest_fixes1806 && !matches!(terr, TypeError::RegionsInsufficientlyPolymorphic(..))1807 {1808 self.suggest_tuple_pattern(cause, &exp_found, diag);1809 self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);1810 self.suggest_await_on_expect_found(cause, span, &exp_found, diag);1811 self.suggest_function_pointers(cause, span, &exp_found, terr, diag);1812 self.suggest_turning_stmt_into_expr(cause, &exp_found, diag);1813 }1814 }18151816 let body_owner_def_id =1817 (cause.body_def_id != CRATE_DEF_ID).then(|| cause.body_def_id.to_def_id());1818 self.note_and_explain_type_err(diag, terr, cause, span, body_owner_def_id);1819 if let Some(exp_found) = exp_found1820 && let exp_found = TypeError::Sorts(exp_found)1821 && exp_found != terr1822 {1823 self.note_and_explain_type_err(diag, exp_found, cause, span, body_owner_def_id);1824 }18251826 if let Some(ValuePairs::TraitRefs(exp_found)) = values1827 && let ty::Closure(def_id, _) = exp_found.expected.self_ty().kind()1828 && let Some(def_id) = def_id.as_local()1829 && terr.involves_regions()1830 {1831 let span = self.tcx.def_span(def_id);1832 diag.span_note(span, "this closure does not fulfill the lifetime requirements");1833 self.suggest_for_all_lifetime_closure(1834 span,1835 self.tcx.hir_node_by_def_id(def_id),1836 &exp_found,1837 diag,1838 );1839 }18401841 // It reads better to have the error origin as the final1842 // thing.1843 self.note_error_origin(diag, cause, exp_found, terr, param_env);18441845 debug!(?diag);1846 }18471848 pub(crate) fn type_error_additional_suggestions(1849 &self,1850 trace: &TypeTrace<'tcx>,1851 terr: TypeError<'tcx>,1852 long_ty_path: &mut Option<PathBuf>,1853 ) -> Vec<TypeErrorAdditionalDiags> {1854 let mut suggestions = Vec::new();1855 let span = trace.cause.span;1856 let values = self.resolve_vars_if_possible(trace.values);1857 if let Some((expected, found)) = values.ty() {1858 match (expected.kind(), found.kind()) {1859 (ty::Tuple(_), ty::Tuple(_)) => {}1860 // If a tuple of length one was expected and the found expression has1861 // parentheses around it, perhaps the user meant to write `(expr,)` to1862 // build a tuple (issue #86100)1863 (ty::Tuple(fields), _) => {1864 suggestions.extend(self.suggest_wrap_to_build_a_tuple(span, found, fields))1865 }1866 // If a byte was expected and the found expression is a char literal1867 // containing a single ASCII character, perhaps the user meant to write `b'c'` to1868 // specify a byte literal1869 (ty::Uint(ty::UintTy::U8), ty::Char) => {1870 if let Ok(code) = self.tcx.sess.source_map().span_to_snippet(span)1871 && let Some(code) = code.strip_circumfix('\'', '\'')1872 // forbid all Unicode escapes1873 && !code.starts_with("\\u")1874 // forbids literal Unicode characters beyond ASCII1875 && code.chars().next().is_some_and(|c| c.is_ascii())1876 {1877 suggestions.push(TypeErrorAdditionalDiags::MeantByteLiteral {1878 span,1879 code: escape_literal(code),1880 })1881 }1882 }1883 // If a character was expected and the found expression is a string literal1884 // containing a single character, perhaps the user meant to write `'c'` to1885 // specify a character literal (issue #92479)1886 (ty::Char, ty::Ref(_, r, _)) if r.is_str() => {1887 if let Ok(code) = self.tcx.sess.source_map().span_to_snippet(span)1888 && let Some(code) = code.strip_circumfix('"', '"')1889 && code.chars().count() == 11890 {1891 suggestions.push(TypeErrorAdditionalDiags::MeantCharLiteral {1892 span,1893 code: escape_literal(code),1894 })1895 }1896 }1897 // If a string was expected and the found expression is a character literal,1898 // perhaps the user meant to write `"s"` to specify a string literal.1899 (ty::Ref(_, r, _), ty::Char) if r.is_str() => {1900 if let Ok(code) = self.tcx.sess.source_map().span_to_snippet(span)1901 && code.starts_with("'")1902 && code.ends_with("'")1903 {1904 suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral {1905 start: span.with_hi(span.lo() + BytePos(1)),1906 end: span.with_lo(span.hi() - BytePos(1)),1907 });1908 }1909 }1910 // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`,1911 // we try to suggest to add the missing `let` for `if let Some(..) = expr`1912 (ty::Bool, ty::Tuple(list)) => {1913 if list.len() == 0 {1914 suggestions.extend(self.suggest_let_for_letchains(&trace.cause, span));1915 }1916 }1917 (ty::Array(_, _), ty::Array(_, _)) => {1918 suggestions.extend(self.suggest_specify_actual_length(terr, trace, span))1919 }1920 _ => {}1921 }1922 }1923 let code = trace.cause.code();1924 if let &(ObligationCauseCode::MatchExpressionArm(MatchExpressionArmCause {1925 source, ..1926 })1927 | ObligationCauseCode::BlockTailExpression(.., source)) = code1928 && let hir::MatchSource::TryDesugar(_) = source1929 && let Some((expected_ty, found_ty)) =1930 self.values_str(trace.values, &trace.cause, long_ty_path)1931 {1932 suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert {1933 found: found_ty.content(),1934 expected: expected_ty.content(),1935 });1936 }1937 suggestions1938 }19391940 fn suggest_specify_actual_length(1941 &self,1942 terr: TypeError<'tcx>,1943 trace: &TypeTrace<'tcx>,1944 span: Span,1945 ) -> Option<TypeErrorAdditionalDiags> {1946 let TypeError::ArraySize(sz) = terr else {1947 return None;1948 };1949 let tykind = match self.tcx.hir_node_by_def_id(trace.cause.body_def_id) {1950 hir::Node::Item(hir::Item {1951 kind: hir::ItemKind::Fn { body: body_id, .. }, ..1952 }) => {1953 let body = self.tcx.hir_body(*body_id);1954 struct LetVisitor {1955 span: Span,1956 }1957 impl<'v> Visitor<'v> for LetVisitor {1958 type Result = ControlFlow<&'v hir::TyKind<'v>>;1959 fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result {1960 // Find a local statement where the initializer has1961 // the same span as the error and the type is specified.1962 if let hir::Stmt {1963 kind:1964 hir::StmtKind::Let(hir::LetStmt {1965 init: Some(hir::Expr { span: init_span, .. }),1966 ty: Some(array_ty),1967 ..1968 }),1969 ..1970 } = s1971 && init_span == &self.span1972 {1973 ControlFlow::Break(&array_ty.peel_refs().kind)1974 } else {1975 ControlFlow::Continue(())1976 }1977 }1978 }1979 LetVisitor { span }.visit_body(body).break_value()1980 }1981 hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(_, _, ty, _), .. }) => {1982 Some(&ty.peel_refs().kind)1983 }1984 _ => None,1985 };1986 if let Some(tykind) = tykind1987 && let hir::TyKind::Array(_, length_arg) = tykind1988 && let Some(length_val) = sz.found.try_to_target_usize(self.tcx)1989 {1990 Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength {1991 span: length_arg.span,1992 length: length_val,1993 })1994 } else {1995 None1996 }1997 }19981999 fn check_on_type_error_attribute(2000 &self,
Findings
✓ No findings reported for this file.