diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/confirm.rs (renamed from compiler/rustc_typeck/src/check/method/confirm.rs) | 6 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/mod.rs (renamed from compiler/rustc_typeck/src/check/method/mod.rs) | 5 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/prelude2021.rs (renamed from compiler/rustc_typeck/src/check/method/prelude2021.rs) | 121 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/probe.rs (renamed from compiler/rustc_typeck/src/check/method/probe.rs) | 163 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/suggest.rs (renamed from compiler/rustc_typeck/src/check/method/suggest.rs) | 388 |
5 files changed, 459 insertions, 224 deletions
diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 59fd5c315..be4ea9986 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -1,16 +1,16 @@ use super::{probe, MethodCallee}; -use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; -use crate::check::{callee, FnCtxt}; +use crate::{callee, FnCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; +use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; use rustc_infer::infer::{self, InferOk}; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{self, Subst, SubstsRef}; +use rustc_middle::ty::subst::{self, SubstsRef}; use rustc_middle::ty::{self, GenericParamDefKind, Ty}; use rustc_span::Span; use rustc_trait_selection::traits; diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 249e9c66b..a1278edef 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -10,15 +10,14 @@ mod suggest; pub use self::suggest::SelfSource; pub use self::MethodError::*; -use crate::check::{Expectation, FnCtxt}; -use crate::ObligationCause; +use crate::{Expectation, FnCtxt}; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; -use rustc_middle::ty::subst::Subst; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TypeVisitable}; use rustc_span::symbol::Ident; diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs index 392695cca..3c98a2aa3 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs @@ -1,3 +1,7 @@ +use crate::{ + method::probe::{self, Pick}, + FnCtxt, +}; use hir::def_id::DefId; use hir::HirId; use hir::ItemKind; @@ -12,11 +16,6 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; -use crate::check::{ - method::probe::{self, Pick}, - FnCtxt, -}; - impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(super) fn lint_dot_call_from_2018( &self, @@ -82,14 +81,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, self_expr.hir_id, self_expr.span, + format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), |lint| { let sp = self_expr.span; - let mut lint = lint.build(&format!( - "trait method `{}` will become ambiguous in Rust 2021", - segment.ident.name - )); - let derefs = "*".repeat(pick.autoderefs); let autoref = match pick.autoref_or_ptr_adjustment { @@ -133,7 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - lint.emit(); + lint }, ); } else { @@ -143,6 +138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, call_expr.hir_id, call_expr.span, + format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), |lint| { let sp = call_expr.span; let trait_name = self.trait_path_or_bare_name( @@ -151,11 +147,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pick.item.container_id(self.tcx), ); - let mut lint = lint.build(&format!( - "trait method `{}` will become ambiguous in Rust 2021", - segment.ident.name - )); - let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp); if precise { let args = args @@ -202,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - lint.emit(); + lint }, ); } @@ -257,15 +248,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| { - // "type" refers to either a type or, more likely, a trait from which - // the associated function or method is from. - let container_id = pick.item.container_id(self.tcx); - let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); - let trait_generics = self.tcx.generics_of(container_id); - - let trait_name = - if trait_generics.params.len() <= trait_generics.has_self as usize { + self.tcx.struct_span_lint_hir( + RUST_2021_PRELUDE_COLLISIONS, + expr_id, + span, + format!( + "trait-associated function `{}` will become ambiguous in Rust 2021", + method_name.name + ), + |lint| { + // "type" refers to either a type or, more likely, a trait from which + // the associated function or method is from. + let container_id = pick.item.container_id(self.tcx); + let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); + let trait_generics = self.tcx.generics_of(container_id); + + let trait_name = if trait_generics.params.len() <= trait_generics.has_self as usize + { trait_path } else { let counts = trait_generics.own_counts(); @@ -282,44 +281,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }; - let mut lint = lint.build(&format!( - "trait-associated function `{}` will become ambiguous in Rust 2021", - method_name.name - )); - - let mut self_ty_name = self_ty_span - .find_ancestor_inside(span) - .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) - .unwrap_or_else(|| self_ty.to_string()); - - // Get the number of generics the self type has (if an Adt) unless we can determine that - // the user has written the self type with generics already which we (naively) do by looking - // for a "<" in `self_ty_name`. - if !self_ty_name.contains('<') { - if let Adt(def, _) = self_ty.kind() { - let generics = self.tcx.generics_of(def.did()); - if !generics.params.is_empty() { - let counts = generics.own_counts(); - self_ty_name += &format!( - "<{}>", - std::iter::repeat("'_") - .take(counts.lifetimes) - .chain(std::iter::repeat("_").take(counts.types + counts.consts)) - .collect::<Vec<_>>() - .join(", ") - ); + let mut self_ty_name = self_ty_span + .find_ancestor_inside(span) + .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) + .unwrap_or_else(|| self_ty.to_string()); + + // Get the number of generics the self type has (if an Adt) unless we can determine that + // the user has written the self type with generics already which we (naively) do by looking + // for a "<" in `self_ty_name`. + if !self_ty_name.contains('<') { + if let Adt(def, _) = self_ty.kind() { + let generics = self.tcx.generics_of(def.did()); + if !generics.params.is_empty() { + let counts = generics.own_counts(); + self_ty_name += &format!( + "<{}>", + std::iter::repeat("'_") + .take(counts.lifetimes) + .chain( + std::iter::repeat("_").take(counts.types + counts.consts) + ) + .collect::<Vec<_>>() + .join(", ") + ); + } } } - } - lint.span_suggestion( - span, - "disambiguate the associated function", - format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), - Applicability::MachineApplicable, - ); - - lint.emit(); - }); + lint.span_suggestion( + span, + "disambiguate the associated function", + format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), + Applicability::MachineApplicable, + ); + + lint + }, + ); } fn trait_path_or_bare_name( diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index e9f55ab34..28aa2302f 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,14 +3,12 @@ use super::CandidateSource; use super::MethodError; use super::NoMatchData; -use crate::check::FnCtxt; use crate::errors::MethodCallOnUnknownType; -use crate::hir::def::DefKind; -use crate::hir::def_id::DefId; - +use crate::FnCtxt; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def::Namespace; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; @@ -19,10 +17,11 @@ use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable}; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint; +use rustc_span::def_id::DefId; use rustc_span::def_id::LocalDefId; use rustc_span::lev_distance::{ find_best_match_for_name_with_substrings, lev_distance_with_substrings, @@ -253,7 +252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// would result in an error (basically, the same criteria we /// would use to decide if a method is a plausible fit for /// ambiguity purposes). - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self, candidate_filter))] pub fn probe_for_return_type( &self, span: Span, @@ -261,6 +260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_type: Ty<'tcx>, self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, + candidate_filter: impl Fn(&ty::AssocItem) -> bool, ) -> Vec<ty::AssocItem> { let method_names = self .probe_op( @@ -272,7 +272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, scope_expr_id, ProbeScope::AllTraits, - |probe_cx| Ok(probe_cx.candidate_method_names()), + |probe_cx| Ok(probe_cx.candidate_method_names(candidate_filter)), ) .unwrap_or_default(); method_names @@ -409,9 +409,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint::builtin::TYVAR_BEHIND_RAW_POINTER, scope_expr_id, span, - |lint| { - lint.build("type annotations needed").emit(); - }, + "type annotations needed", + |lint| lint, ); } } else { @@ -473,69 +472,65 @@ fn method_autoderef_steps<'tcx>( ) -> MethodAutoderefStepsResult<'tcx> { debug!("method_autoderef_steps({:?})", goal); - tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| { - let ParamEnvAnd { param_env, value: self_ty } = goal; - - let mut autoderef = - Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) - .include_raw_pointers() - .silence_errors(); - let mut reached_raw_pointer = false; - let mut steps: Vec<_> = autoderef - .by_ref() - .map(|(ty, d)| { - let step = CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations( - inference_vars.clone(), - ty, - ), - autoderefs: d, - from_unsafe_deref: reached_raw_pointer, - unsize: false, - }; - if let ty::RawPtr(_) = ty.kind() { - // all the subsequent steps will be from_unsafe_deref - reached_raw_pointer = true; - } - step - }) - .collect(); - - let final_ty = autoderef.final_ty(true); - let opt_bad_ty = match final_ty.kind() { - ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { - reached_raw_pointer, - ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, final_ty), - }), - ty::Array(elem_ty, _) => { - let dereferences = steps.len() - 1; - - steps.push(CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations( - inference_vars, - infcx.tcx.mk_slice(*elem_ty), - ), - autoderefs: dereferences, - // this could be from an unsafe deref if we had - // a *mut/const [T; N] - from_unsafe_deref: reached_raw_pointer, - unsize: true, - }); - - None + let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); + let ParamEnvAnd { param_env, value: self_ty } = goal; + + let mut autoderef = + Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) + .include_raw_pointers() + .silence_errors(); + let mut reached_raw_pointer = false; + let mut steps: Vec<_> = autoderef + .by_ref() + .map(|(ty, d)| { + let step = CandidateStep { + self_ty: infcx + .make_query_response_ignoring_pending_obligations(inference_vars.clone(), ty), + autoderefs: d, + from_unsafe_deref: reached_raw_pointer, + unsize: false, + }; + if let ty::RawPtr(_) = ty.kind() { + // all the subsequent steps will be from_unsafe_deref + reached_raw_pointer = true; } - _ => None, - }; - - debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); + step + }) + .collect(); + + let final_ty = autoderef.final_ty(true); + let opt_bad_ty = match final_ty.kind() { + ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { + reached_raw_pointer, + ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + }), + ty::Array(elem_ty, _) => { + let dereferences = steps.len() - 1; + + steps.push(CandidateStep { + self_ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + infcx.tcx.mk_slice(*elem_ty), + ), + autoderefs: dereferences, + // this could be from an unsafe deref if we had + // a *mut/const [T; N] + from_unsafe_deref: reached_raw_pointer, + unsize: true, + }); - MethodAutoderefStepsResult { - steps: tcx.arena.alloc_from_iter(steps), - opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), - reached_recursion_limit: autoderef.reached_recursion_limit(), + None } - }) + _ => None, + }; + + debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); + + MethodAutoderefStepsResult { + steps: tcx.arena.alloc_from_iter(steps), + opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), + reached_recursion_limit: autoderef.reached_recursion_limit(), + } } impl<'a, 'tcx> ProbeContext<'a, 'tcx> { @@ -972,12 +967,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - fn candidate_method_names(&self) -> Vec<Ident> { + fn candidate_method_names( + &self, + candidate_filter: impl Fn(&ty::AssocItem) -> bool, + ) -> Vec<Ident> { let mut set = FxHashSet::default(); let mut names: Vec<_> = self .inherent_candidates .iter() .chain(&self.extension_candidates) + .filter(|candidate| candidate_filter(&candidate.item)) .filter(|candidate| { if let Some(return_ty) = self.return_type { self.matches_return_type(&candidate.item, None, return_ty) @@ -1358,24 +1357,24 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { stable_pick: &Pick<'_>, unstable_candidates: &[(Candidate<'tcx>, Symbol)], ) { + let def_kind = stable_pick.item.kind.as_def_kind(); self.tcx.struct_span_lint_hir( lint::builtin::UNSTABLE_NAME_COLLISIONS, self.scope_expr_id, self.span, + format!( + "{} {} with this name may be added to the standard library in the future", + def_kind.article(), + def_kind.descr(stable_pick.item.def_id), + ), |lint| { - let def_kind = stable_pick.item.kind.as_def_kind(); - let mut diag = lint.build(&format!( - "{} {} with this name may be added to the standard library in the future", - def_kind.article(), - def_kind.descr(stable_pick.item.def_id), - )); match (stable_pick.item.kind, stable_pick.item.container) { (ty::AssocKind::Fn, _) => { // FIXME: This should be a `span_suggestion` instead of `help` // However `self.span` only // highlights the method name, so we can't use it. Also consider reusing // the code from `report_method_error()`. - diag.help(&format!( + lint.help(&format!( "call with fully qualified syntax `{}(...)` to keep using the current \ method", self.tcx.def_path_str(stable_pick.item.def_id), @@ -1383,7 +1382,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => { let def_id = stable_pick.item.container_id(self.tcx); - diag.span_suggestion( + lint.span_suggestion( self.span, "use the fully qualified path to the associated const", format!( @@ -1399,7 +1398,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } if self.tcx.sess.is_nightly_build() { for (candidate, feature) in unstable_candidates { - diag.help(&format!( + lint.help(&format!( "add `#![feature({})]` to the crate attributes to enable `{}`", feature, self.tcx.def_path_str(candidate.item.def_id), @@ -1407,7 +1406,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - diag.emit(); + lint }, ); } @@ -1695,7 +1694,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pcx.allow_similar_names = true; pcx.assemble_inherent_candidates(); - let method_names = pcx.candidate_method_names(); + let method_names = pcx.candidate_method_names(|_| true); pcx.allow_similar_names = false; let applicable_close_candidates: Vec<ty::AssocItem> = method_names .iter() diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 2d459b2cc..6c21ed902 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1,7 +1,9 @@ //! Give useful errors and suggestions to users when an item can't be //! found or is otherwise invalid. -use crate::check::FnCtxt; +use crate::errors; +use crate::FnCtxt; +use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, @@ -21,7 +23,7 @@ use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; -use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote, @@ -30,7 +32,7 @@ use rustc_trait_selection::traits::{ use std::cmp::Ordering; use std::iter; -use super::probe::{IsSuggestion, Mode, ProbeScope}; +use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -104,7 +106,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let report_candidates = |span: Span, err: &mut Diagnostic, - mut sources: Vec<CandidateSource>, + sources: &mut Vec<CandidateSource>, sugg_span: Span| { sources.sort(); sources.dedup(); @@ -246,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match error { MethodError::NoMatch(NoMatchData { - static_candidates: static_sources, + static_candidates: mut static_sources, unsatisfied_predicates, out_of_scope_traits, lev_candidate, @@ -270,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; @@ -418,9 +422,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help(&format!("try with `{}::{}`", ty_str, item_name,)); } - report_candidates(span, &mut err, static_sources, sugg_span); + report_candidates(span, &mut err, &mut static_sources, sugg_span); } else if static_sources.len() > 1 { - report_candidates(span, &mut err, static_sources, sugg_span); + report_candidates(span, &mut err, &mut static_sources, sugg_span); } let mut bound_spans = vec![]; @@ -854,8 +858,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Avoid crashing. return (None, None); } - let OnUnimplementedNote { message, label, .. } = - self.on_unimplemented_note(trait_ref, &obligation); + let OnUnimplementedNote { message, label, .. } = self + .err_ctxt() + .on_unimplemented_note(trait_ref, &obligation); (message, label) }) .unwrap_or((None, None)) @@ -983,7 +988,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_for_field_method(&mut err, source, span, actual, item_name); } - self.check_for_unwrap_self(&mut err, source, span, actual, item_name); + self.check_for_inner_self(&mut err, source, span, actual, item_name); bound_spans.sort(); bound_spans.dedup(); @@ -1002,6 +1007,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source, out_of_scope_traits, &unsatisfied_predicates, + &static_sources, unsatisfied_bounds, ); } @@ -1074,7 +1080,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Some(err); } - MethodError::Ambiguity(sources) => { + MethodError::Ambiguity(mut sources) => { let mut err = struct_span_err!( self.sess(), item_name.span, @@ -1083,7 +1089,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(item_name.span, format!("multiple `{}` found", item_name)); - report_candidates(span, &mut err, sources, sugg_span); + report_candidates(span, &mut err, &mut sources, sugg_span); err.emit(); } @@ -1200,6 +1206,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>, @@ -1262,7 +1351,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!( @@ -1395,7 +1483,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_for_unwrap_self( + fn check_for_inner_self( &self, err: &mut Diagnostic, source: SelfSource<'tcx>, @@ -1408,81 +1496,168 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); let ty::Adt(kind, substs) = actual.kind() else { return; }; - if !kind.is_enum() { - return; - } + match kind.adt_kind() { + ty::AdtKind::Enum => { + let matching_variants: Vec<_> = kind + .variants() + .iter() + .flat_map(|variant| { + let [field] = &variant.fields[..] else { return None; }; + let field_ty = field.ty(tcx, substs); + + // Skip `_`, since that'll just lead to ambiguity. + if self.resolve_vars_if_possible(field_ty).is_ty_var() { + return None; + } - let matching_variants: Vec<_> = kind - .variants() - .iter() - .flat_map(|variant| { - let [field] = &variant.fields[..] else { return None; }; - let field_ty = field.ty(tcx, substs); + self.lookup_probe( + span, + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + ) + .ok() + .map(|pick| (variant, field, pick)) + }) + .collect(); + + let ret_ty_matches = |diagnostic_item| { + if let Some(ret_ty) = self + .ret_coercion + .as_ref() + .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) + && let ty::Adt(kind, _) = ret_ty.kind() + && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) + { + true + } else { + false + } + }; - // Skip `_`, since that'll just lead to ambiguity. - if self.resolve_vars_if_possible(field_ty).is_ty_var() { - return None; + match &matching_variants[..] { + [(_, field, pick)] => { + let self_ty = field.ty(tcx, substs); + err.span_note( + tcx.def_span(pick.item.def_id), + &format!("the method `{item_name}` exists on the type `{self_ty}`"), + ); + let (article, kind, variant, question) = + if tcx.is_diagnostic_item(sym::Result, kind.did()) { + ("a", "Result", "Err", ret_ty_matches(sym::Result)) + } else if tcx.is_diagnostic_item(sym::Option, kind.did()) { + ("an", "Option", "None", ret_ty_matches(sym::Option)) + } else { + return; + }; + if question { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use the `?` operator to extract the `{self_ty}` value, propagating \ + {article} `{kind}::{variant}` value to the caller" + ), + "?", + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ + panicking if the value is {article} `{kind}::{variant}`" + ), + ".expect(\"REASON\")", + Applicability::HasPlaceholders, + ); + } + } + // FIXME(compiler-errors): Support suggestions for other matching enum variants + _ => {} } - - self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits) - .ok() - .map(|pick| (variant, field, pick)) - }) - .collect(); - - let ret_ty_matches = |diagnostic_item| { - if let Some(ret_ty) = self - .ret_coercion - .as_ref() - .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) - && let ty::Adt(kind, _) = ret_ty.kind() - && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) - { - true - } else { - false } - }; + // Target wrapper types - types that wrap or pretend to wrap another type, + // perhaps this inner type is meant to be called? + ty::AdtKind::Struct | ty::AdtKind::Union => { + let [first] = ***substs else { return; }; + let ty::GenericArgKind::Type(ty) = first.unpack() else { return; }; + let Ok(pick) = self.lookup_probe( + span, + item_name, + ty, + call_expr, + ProbeScope::TraitsInScope, + ) else { return; }; - match &matching_variants[..] { - [(_, field, pick)] => { - let self_ty = field.ty(tcx, substs); - err.span_note( - tcx.def_span(pick.item.def_id), - &format!("the method `{item_name}` exists on the type `{self_ty}`"), - ); - let (article, kind, variant, question) = - if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) { - ("a", "Result", "Err", ret_ty_matches(sym::Result)) - } else if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) { - ("an", "Option", "None", ret_ty_matches(sym::Option)) - } else { - return; + let name = self.ty_to_value_string(actual); + let inner_id = kind.did(); + let mutable = if let Some(AutorefOrPtrAdjustment::Autoref { mutbl, .. }) = + pick.autoref_or_ptr_adjustment + { + Some(mutbl) + } else { + None + }; + + if tcx.is_diagnostic_item(sym::LocalKey, inner_id) { + err.help("use `with` or `try_with` to access thread local storage"); + } else if Some(kind.did()) == tcx.lang_items().maybe_uninit() { + err.help(format!( + "if this `{name}` has been initialized, \ + use one of the `assume_init` methods to access the inner value" + )); + } else if tcx.is_diagnostic_item(sym::RefCell, inner_id) { + let (suggestion, borrow_kind, panic_if) = match mutable { + Some(Mutability::Not) => (".borrow()", "borrow", "a mutable borrow exists"), + Some(Mutability::Mut) => { + (".borrow_mut()", "mutably borrow", "any borrows exist") + } + None => return, }; - if question { err.span_suggestion_verbose( expr.span.shrink_to_hi(), format!( - "use the `?` operator to extract the `{self_ty}` value, propagating \ - {article} `{kind}::{variant}` value to the caller" + "use `{suggestion}` to {borrow_kind} the `{ty}`, \ + panicking if {panic_if}" ), - "?", - Applicability::MachineApplicable, + suggestion, + Applicability::MaybeIncorrect, ); - } else { + } else if tcx.is_diagnostic_item(sym::Mutex, inner_id) { err.span_suggestion_verbose( expr.span.shrink_to_hi(), format!( - "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ - panicking if the value is {article} `{kind}::{variant}`" + "use `.lock().unwrap()` to borrow the `{ty}`, \ + blocking the current thread until it can be acquired" ), - ".expect(\"REASON\")", - Applicability::HasPlaceholders, + ".lock().unwrap()", + Applicability::MaybeIncorrect, ); - } + } else if tcx.is_diagnostic_item(sym::RwLock, inner_id) { + let (suggestion, borrow_kind) = match mutable { + Some(Mutability::Not) => (".read().unwrap()", "borrow"), + Some(Mutability::Mut) => (".write().unwrap()", "mutably borrow"), + None => return, + }; + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use `{suggestion}` to {borrow_kind} the `{ty}`, \ + blocking the current thread until it can be acquired" + ), + suggestion, + Applicability::MaybeIncorrect, + ); + } else { + return; + }; + + err.span_note( + tcx.def_span(pick.item.def_id), + &format!("the method `{item_name}` exists on the type `{ty}`"), + ); } - // FIXME(compiler-errors): Support suggestions for other matching enum variants - _ => {} } } @@ -1841,6 +2016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>, )], + static_candidates: &[CandidateSource], unsatisfied_bounds: bool, ) { let mut alt_rcvr_sugg = false; @@ -1955,6 +2131,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => true, }) .filter(|info| { + // Static candidates are already implemented, and known not to work + // Do not suggest them again + static_candidates.iter().all(|sc| match *sc { + CandidateSource::Trait(def_id) => def_id != info.def_id, + CandidateSource::Impl(def_id) => { + self.tcx.trait_id_of_impl(def_id) != Some(info.def_id) + } + }) + }) + .filter(|info| { // We approximate the coherence rules to only suggest // traits that are legal to implement by requiring that // either the type or trait is local. Multi-dispatch means @@ -2077,7 +2263,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Colon, Nothing, } - let ast_generics = hir.get_generics(id.owner).unwrap(); + let ast_generics = hir.get_generics(id.owner.def_id).unwrap(); let (sp, mut introducer) = if let Some(span) = ast_generics.bounds_span_for_suggestions(def_id) { @@ -2236,6 +2422,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else` + /// FIXME: currently not working for suggesting `map_or_else`, see #102408 + pub(crate) fn suggest_else_fn_with_closure( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + found: Ty<'tcx>, + expected: Ty<'tcx>, + ) -> bool { + let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found) + else { return false; }; + + if !self.can_coerce(output, expected) { + return false; + } + + let parent = self.tcx.hir().get_parent_node(expr.hir_id); + if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) && + let hir::ExprKind::MethodCall( + hir::PathSegment { ident: method_name, .. }, + self_expr, + args, + .., + ) = call_expr.kind && + let Some(self_ty) = self.typeck_results.borrow().expr_ty_opt(self_expr) { + let new_name = Ident { + name: Symbol::intern(&format!("{}_else", method_name.as_str())), + span: method_name.span, + }; + let probe = self.lookup_probe( + expr.span, + new_name, + self_ty, + self_expr, + ProbeScope::TraitsInScope, + ); + + // check the method arguments number + if let Ok(pick) = probe && + let fn_sig = self.tcx.fn_sig(pick.item.def_id) && + let fn_args = fn_sig.skip_binder().inputs() && + fn_args.len() == args.len() + 1 { + err.span_suggestion_verbose( + method_name.span.shrink_to_hi(), + &format!("try calling `{}` instead", new_name.name.as_str()), + "_else", + Applicability::MaybeIncorrect, + ); + return true; + } + } + false + } + /// Checks whether there is a local type somewhere in the chain of /// autoderefs of `rcvr_ty`. fn type_derefs_to_local( |