diff options
Diffstat (limited to 'compiler/rustc_hir_analysis/src')
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/callee.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/compare_method.rs | 83 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/expr.rs | 11 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs | 15 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/method/suggest.rs | 89 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/region.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/writeback.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/errors.rs | 26 |
8 files changed, 196 insertions, 40 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/callee.rs b/compiler/rustc_hir_analysis/src/check/callee.rs index f0a7c910906..088de1979ba 100644 --- a/compiler/rustc_hir_analysis/src/check/callee.rs +++ b/compiler/rustc_hir_analysis/src/check/callee.rs @@ -1,6 +1,6 @@ use super::method::probe::{IsSuggestion, Mode, ProbeScope}; use super::method::MethodCallee; -use super::{DefIdOrName, Expectation, FnCtxt, TupleArgumentsFlag}; +use super::{Expectation, FnCtxt, TupleArgumentsFlag}; use crate::type_error_struct; use rustc_ast::util::parser::PREC_POSTFIX; @@ -27,6 +27,7 @@ use rustc_span::Span; use rustc_target::spec::abi; use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use std::iter; diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index 5e5dbedb4bd..60eaad9b498 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -465,30 +465,30 @@ pub fn collect_trait_impl_trait_tys<'tcx>( let ocx = ObligationCtxt::new(infcx); let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id); - let impl_return_ty = ocx.normalize( + let impl_sig = ocx.normalize( norm_cause.clone(), param_env, - infcx - .replace_bound_vars_with_fresh_vars( - return_span, - infer::HigherRankedType, - tcx.fn_sig(impl_m.def_id), - ) - .output(), + infcx.replace_bound_vars_with_fresh_vars( + return_span, + infer::HigherRankedType, + tcx.fn_sig(impl_m.def_id), + ), ); + let impl_return_ty = impl_sig.output(); let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id); - let unnormalized_trait_return_ty = tcx + let unnormalized_trait_sig = tcx .liberate_late_bound_regions( impl_m.def_id, tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs), ) - .output() .fold_with(&mut collector); - let trait_return_ty = - ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_return_ty); + let trait_sig = ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_sig); + let trait_return_ty = trait_sig.output(); - let wf_tys = FxHashSet::from_iter([unnormalized_trait_return_ty, trait_return_ty]); + let wf_tys = FxHashSet::from_iter( + unnormalized_trait_sig.inputs_and_output.iter().chain(trait_sig.inputs_and_output.iter()), + ); match infcx.at(&cause, param_env).eq(trait_return_ty, impl_return_ty) { Ok(infer::InferOk { value: (), obligations }) => { @@ -521,6 +521,26 @@ pub fn collect_trait_impl_trait_tys<'tcx>( } } + // Unify the whole function signature. We need to do this to fully infer + // the lifetimes of the return type, but do this after unifying just the + // return types, since we want to avoid duplicating errors from + // `compare_predicate_entailment`. + match infcx + .at(&cause, param_env) + .eq(tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)), tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig))) + { + Ok(infer::InferOk { value: (), obligations }) => { + ocx.register_obligations(obligations); + } + Err(terr) => { + let guar = tcx.sess.delay_span_bug( + return_span, + format!("could not unify `{trait_sig}` and `{impl_sig}`: {terr:?}"), + ); + return Err(guar); + } + } + // Check that all obligations are satisfied by the implementation's // RPITs. let errors = ocx.select_all_or_error(); @@ -551,15 +571,40 @@ pub fn collect_trait_impl_trait_tys<'tcx>( let id_substs = InternalSubsts::identity_for_item(tcx, def_id); debug!(?id_substs, ?substs); let map: FxHashMap<ty::GenericArg<'tcx>, ty::GenericArg<'tcx>> = - substs.iter().enumerate().map(|(index, arg)| (arg, id_substs[index])).collect(); + std::iter::zip(substs, id_substs).collect(); debug!(?map); + // NOTE(compiler-errors): RPITITs, like all other RPITs, have early-bound + // region substs that are synthesized during AST lowering. These are substs + // that are appended to the parent substs (trait and trait method). However, + // we're trying to infer the unsubstituted type value of the RPITIT inside + // the *impl*, so we can later use the impl's method substs to normalize + // an RPITIT to a concrete type (`confirm_impl_trait_in_trait_candidate`). + // + // Due to the design of RPITITs, during AST lowering, we have no idea that + // an impl method corresponds to a trait method with RPITITs in it. Therefore, + // we don't have a list of early-bound region substs for the RPITIT in the impl. + // Since early region parameters are index-based, we can't just rebase these + // (trait method) early-bound region substs onto the impl, and there's no + // guarantee that the indices from the trait substs and impl substs line up. + // So to fix this, we subtract the number of trait substs and add the number of + // impl substs to *renumber* these early-bound regions to their corresponding + // indices in the impl's substitutions list. + // + // Also, we only need to account for a difference in trait and impl substs, + // since we previously enforce that the trait method and impl method have the + // same generics. + let num_trait_substs = trait_to_impl_substs.len(); + let num_impl_substs = tcx.generics_of(impl_m.container_id(tcx)).params.len(); let ty = tcx.fold_regions(ty, |region, _| { - if let ty::ReFree(_) = region.kind() { - map[®ion.into()].expect_region() - } else { - region - } + let ty::ReFree(_) = region.kind() else { return region; }; + let ty::ReEarlyBound(e) = map[®ion.into()].expect_region().kind() + else { bug!("expected ReFree to map to ReEarlyBound"); }; + tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { + def_id: e.def_id, + name: e.name, + index: (e.index as usize - num_trait_substs + num_impl_substs) as u32, + })) }); debug!(%ty); collected_tys.insert(def_id, ty); diff --git a/compiler/rustc_hir_analysis/src/check/expr.rs b/compiler/rustc_hir_analysis/src/check/expr.rs index 71c6da862c9..ccdfd3a056b 100644 --- a/compiler/rustc_hir_analysis/src/check/expr.rs +++ b/compiler/rustc_hir_analysis/src/check/expr.rs @@ -1130,11 +1130,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - self.check_lhs_assignable(lhs, "E0070", span, |err| { - let rhs_ty = self.check_expr(&rhs); - suggest_deref_binop(err, rhs_ty); - }); - // This is (basically) inlined `check_expr_coercable_to_type`, but we want // to suggest an additional fixup here in `suggest_deref_binop`. let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty); @@ -1145,6 +1140,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { diag.emit(); } + self.check_lhs_assignable(lhs, "E0070", span, |err| { + if let Some(rhs_ty) = self.typeck_results.borrow().expr_ty_opt(rhs) { + suggest_deref_binop(err, rhs_ty); + } + }); + self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); if lhs_ty.references_error() || rhs_ty.references_error() { diff --git a/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs index 7a40def177a..08b21b82faf 100644 --- a/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs @@ -2,7 +2,6 @@ use super::FnCtxt; use crate::astconv::AstConv; use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; -use hir::def_id::DefId; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; @@ -19,6 +18,7 @@ use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -90,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if ty.is_suggestable(self.tcx, false) { format!("/* {ty} */") } else { - "".to_string() + "/* value */".to_string() } }) .collect::<Vec<_>>() @@ -102,10 +102,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let msg = match def_id_or_name { DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { - DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(), - DefKind::Ctor(CtorOf::Variant, _) => { - "instantiate this tuple variant".to_string() - } + DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(), + DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(), kind => format!("call this {}", kind.descr(def_id)), }, DefIdOrName::Name(name) => format!("call this {name}"), @@ -1209,8 +1207,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - -pub enum DefIdOrName { - DefId(DefId), - Name(&'static str), -} diff --git a/compiler/rustc_hir_analysis/src/check/method/suggest.rs b/compiler/rustc_hir_analysis/src/check/method/suggest.rs index e276c4f7d84..bfa5b68168f 100644 --- a/compiler/rustc_hir_analysis/src/check/method/suggest.rs +++ b/compiler/rustc_hir_analysis/src/check/method/suggest.rs @@ -2,6 +2,7 @@ //! found or is otherwise invalid. use crate::check::FnCtxt; +use crate::errors; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ @@ -271,7 +272,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - if self.suggest_constraining_numerical_ty( + if self.suggest_wrapping_range_with_parens( + tcx, actual, source, span, item_name, &ty_str, + ) || self.suggest_constraining_numerical_ty( tcx, actual, source, span, item_kind, item_name, &ty_str, ) { return None; @@ -1202,6 +1205,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + /// Suggest possible range with adding parentheses, for example: + /// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`. + fn suggest_wrapping_range_with_parens( + &self, + tcx: TyCtxt<'tcx>, + actual: Ty<'tcx>, + source: SelfSource<'tcx>, + span: Span, + item_name: Ident, + ty_str: &str, + ) -> bool { + if let SelfSource::MethodCall(expr) = source { + for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) { + if let Node::Expr(parent_expr) = parent { + let lang_item = match parent_expr.kind { + ExprKind::Struct(ref qpath, _, _) => match **qpath { + QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range), + QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo), + QPath::LangItem(LangItem::RangeToInclusive, ..) => { + Some(LangItem::RangeToInclusive) + } + _ => None, + }, + ExprKind::Call(ref func, _) => match func.kind { + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => { + Some(LangItem::RangeInclusiveStruct) + } + _ => None, + }, + _ => None, + }; + + if lang_item.is_none() { + continue; + } + + let span_included = match parent_expr.kind { + hir::ExprKind::Struct(_, eps, _) => { + eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)) + } + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + hir::ExprKind::Call(ref func, ..) => func.span.contains(span), + _ => false, + }; + + if !span_included { + continue; + } + + let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None); + let range_ty = + self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); + + let pick = self.probe_for_name( + span, + Mode::MethodCall, + item_name, + IsSuggestion(true), + range_ty, + expr.hir_id, + ProbeScope::AllTraits, + ); + if pick.is_ok() { + let range_span = parent_expr.span.with_hi(expr.span.hi()); + tcx.sess.emit_err(errors::MissingParentheseInRange { + span, + ty_str: ty_str.to_string(), + method_name: item_name.as_str().to_string(), + add_missing_parentheses: Some(errors::AddMissingParenthesesInRange { + func_name: item_name.name.as_str().to_string(), + left: range_span.shrink_to_lo(), + right: range_span.shrink_to_hi(), + }), + }); + return true; + } + } + } + } + false + } + fn suggest_constraining_numerical_ty( &self, tcx: TyCtxt<'tcx>, @@ -1264,7 +1350,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If this is a floating point literal that ends with '.', // get rid of it to stop this from becoming a member access. let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); - err.span_suggestion( lit.span, &format!( diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index b89db79bef8..ff32329e431 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -252,9 +252,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h ) => { // For shortcircuiting operators, mark the RHS as a terminating // scope since it only executes conditionally. - terminating(r.hir_id.local_id); - } + // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries + // should live beyond the immediate expression + if !matches!(r.kind, hir::ExprKind::Let(_)) { + terminating(r.hir_id.local_id); + } + } hir::ExprKind::If(_, ref then, Some(ref otherwise)) => { terminating(then.hir_id.local_id); terminating(otherwise.hir_id.local_id); diff --git a/compiler/rustc_hir_analysis/src/check/writeback.rs b/compiler/rustc_hir_analysis/src/check/writeback.rs index e3e4a934ab5..d2d596efb93 100644 --- a/compiler/rustc_hir_analysis/src/check/writeback.rs +++ b/compiler/rustc_hir_analysis/src/check/writeback.rs @@ -564,6 +564,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { opaque_type_key, self.fcx.infcx.tcx, true, + decl.origin, ); self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index d891171b824..41f73323d9a 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -346,3 +346,29 @@ pub struct ExpectedUsedSymbol { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(hir_analysis::missing_parentheses_in_range, code = "E0689")] +pub struct MissingParentheseInRange { + #[primary_span] + #[label(hir_analysis::missing_parentheses_in_range)] + pub span: Span, + pub ty_str: String, + pub method_name: String, + + #[subdiagnostic] + pub add_missing_parentheses: Option<AddMissingParenthesesInRange>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion_verbose( + hir_analysis::add_missing_parentheses_in_range, + applicability = "maybe-incorrect" +)] +pub struct AddMissingParenthesesInRange { + pub func_name: String, + #[suggestion_part(code = "(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} |
