diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_borrowck/src/diagnostics/mod.rs | 155 | ||||
| -rw-r--r-- | compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs | 102 | ||||
| -rw-r--r-- | compiler/rustc_feature/src/removed.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_feature/src/unstable.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/astconv/generics.rs | 25 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 40 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/check_match.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 43 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/generics.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/diagnostics.rs | 87 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/infer.rs | 72 |
12 files changed, 471 insertions, 85 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 4deed98d002..1844e766a82 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -11,6 +11,7 @@ use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::CoroutineKind; use rustc_index::IndexSlice; use rustc_infer::infer::BoundRegionConversionTime; +use rustc_infer::traits::{FulfillmentErrorCode, SelectionError}; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::{ AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location, @@ -24,10 +25,9 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; use rustc_span::def_id::LocalDefId; use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{FieldIdx, VariantIdx}; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::{ - type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause, -}; +use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; +use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; use super::borrow_set::BorrowData; use super::MirBorrowckCtxt; @@ -1043,7 +1043,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } CallKind::Normal { self_arg, desugaring, method_did, method_args } => { let self_arg = self_arg.unwrap(); + let mut has_sugg = false; let tcx = self.infcx.tcx; + // Avoid pointing to the same function in multiple different + // error messages. + if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) { + self.explain_iterator_advancement_in_for_loop_if_applicable( + err, + span, + &move_spans, + ); + + let func = tcx.def_path_str(method_did); + err.subdiagnostic(CaptureReasonNote::FuncTakeSelf { + func, + place_name: place_name.clone(), + span: self_arg.span, + }); + } + let parent_did = tcx.parent(method_did); + let parent_self_ty = + matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. }) + .then_some(parent_did) + .and_then(|did| match tcx.type_of(did).instantiate_identity().kind() { + ty::Adt(def, ..) => Some(def.did()), + _ => None, + }); + let is_option_or_result = parent_self_ty.is_some_and(|def_id| { + matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) + }); + if is_option_or_result && maybe_reinitialized_locations_is_empty { + err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span }); + } if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring { let ty = moved_place.ty(self.body, tcx).ty; let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) { @@ -1108,7 +1139,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Erase and shadow everything that could be passed to the new infcx. let ty = moved_place.ty(self.body, tcx).ty; - if let ty::Adt(def, args) = ty.kind() + if let ty::Adt(def, args) = ty.peel_refs().kind() && Some(def.did()) == tcx.lang_items().pin_type() && let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind() && let self_ty = self.infcx.instantiate_binder_with_fresh_vars( @@ -1124,56 +1155,76 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { span: move_span.shrink_to_hi(), }, ); + has_sugg = true; } - if let Some(clone_trait) = tcx.lang_items().clone_trait() - && let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty]) - && let o = Obligation::new( - tcx, - ObligationCause::dummy(), - self.param_env, - ty::Binder::dummy(trait_ref), - ) - && self.infcx.predicate_must_hold_modulo_regions(&o) - { - err.span_suggestion_verbose( - move_span.shrink_to_hi(), - "you can `clone` the value and consume it, but this might not be \ - your desired behavior", - ".clone()".to_string(), - Applicability::MaybeIncorrect, - ); + if let Some(clone_trait) = tcx.lang_items().clone_trait() { + let sugg = if moved_place + .iter_projections() + .any(|(_, elem)| matches!(elem, ProjectionElem::Deref)) + { + vec![ + // We use the fully-qualified path because `.clone()` can + // sometimes choose `<&T as Clone>` instead of `<T as Clone>` + // when going through auto-deref, so this ensures that doesn't + // happen, causing suggestions for `.clone().clone()`. + (move_span.shrink_to_lo(), format!("<{ty} as Clone>::clone(&")), + (move_span.shrink_to_hi(), ")".to_string()), + ] + } else { + vec![(move_span.shrink_to_hi(), ".clone()".to_string())] + }; + if let Some(errors) = + self.infcx.could_impl_trait(clone_trait, ty, self.param_env) + && !has_sugg + { + let msg = match &errors[..] { + [] => "you can `clone` the value and consume it, but this \ + might not be your desired behavior" + .to_string(), + [error] => { + format!( + "you could `clone` the value and consume it, if \ + the `{}` trait bound could be satisfied", + error.obligation.predicate, + ) + } + [errors @ .., last] => { + format!( + "you could `clone` the value and consume it, if \ + the following trait bounds could be satisfied: {} \ + and `{}`", + errors + .iter() + .map(|e| format!("`{}`", e.obligation.predicate)) + .collect::<Vec<_>>() + .join(", "), + last.obligation.predicate, + ) + } + }; + err.multipart_suggestion_verbose( + msg, + sugg.clone(), + Applicability::MaybeIncorrect, + ); + for error in errors { + if let FulfillmentErrorCode::CodeSelectionError( + SelectionError::Unimplemented, + ) = error.code + && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( + pred, + )) = error.obligation.predicate.kind().skip_binder() + { + self.infcx.err_ctxt().suggest_derive( + &error.obligation, + err, + error.obligation.predicate.kind().rebind(pred), + ); + } + } + } } } - // Avoid pointing to the same function in multiple different - // error messages. - if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) { - self.explain_iterator_advancement_in_for_loop_if_applicable( - err, - span, - &move_spans, - ); - - let func = tcx.def_path_str(method_did); - err.subdiagnostic(CaptureReasonNote::FuncTakeSelf { - func, - place_name, - span: self_arg.span, - }); - } - let parent_did = tcx.parent(method_did); - let parent_self_ty = - matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. }) - .then_some(parent_did) - .and_then(|did| match tcx.type_of(did).instantiate_identity().kind() { - ty::Adt(def, ..) => Some(def.did()), - _ => None, - }); - let is_option_or_result = parent_self_ty.is_some_and(|def_id| { - matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) - }); - if is_option_or_result && maybe_reinitialized_locations_is_empty { - err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span }); - } } // Other desugarings takes &self, which cannot cause a move _ => {} diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index d9ec2860962..8fe552708ed 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -3,8 +3,9 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_hir::Node; +use rustc_infer::traits; use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; -use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt}; +use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt}; use rustc_middle::{ hir::place::PlaceBase, mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location}, @@ -12,6 +13,8 @@ use rustc_middle::{ use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, BytePos, DesugaringKind, Span}; use rustc_target::abi::FieldIdx; +use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use crate::diagnostics::BorrowedContentSource; use crate::util::FindAssignments; @@ -1212,6 +1215,103 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(hir_id) = hir_id && let Some(hir::Node::Local(local)) = hir_map.find(hir_id) { + let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap()); + if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait() + && let Some(expr) = local.init + && let ty = tables.node_type_opt(expr.hir_id) + && let Some(ty) = ty + && let ty::Ref(..) = ty.kind() + { + match self + .infcx + .could_impl_trait(clone_trait, ty.peel_refs(), self.param_env) + .as_deref() + { + Some([]) => { + // The type implements Clone. + err.span_help( + expr.span, + format!( + "you can `clone` the `{}` value and consume it, but this \ + might not be your desired behavior", + ty.peel_refs(), + ), + ); + } + None => { + if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = + expr.kind + && segment.ident.name == sym::clone + { + err.span_help( + span, + format!( + "`{}` doesn't implement `Clone`, so this call clones \ + the reference `{ty}`", + ty.peel_refs(), + ), + ); + } + // The type doesn't implement Clone. + let trait_ref = ty::Binder::dummy(ty::TraitRef::new( + self.infcx.tcx, + clone_trait, + [ty.peel_refs()], + )); + let obligation = traits::Obligation::new( + self.infcx.tcx, + traits::ObligationCause::dummy(), + self.param_env, + trait_ref, + ); + self.infcx.err_ctxt().suggest_derive( + &obligation, + err, + trait_ref.to_predicate(self.infcx.tcx), + ); + } + Some(errors) => { + if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) = + expr.kind + && segment.ident.name == sym::clone + { + err.span_help( + span, + format!( + "`{}` doesn't implement `Clone` because its \ + implementations trait bounds could not be met, so \ + this call clones the reference `{ty}`", + ty.peel_refs(), + ), + ); + err.note(format!( + "the following trait bounds weren't met: {}", + errors + .iter() + .map(|e| e.obligation.predicate.to_string()) + .collect::<Vec<_>>() + .join("\n"), + )); + } + // The type doesn't implement Clone because of unmet obligations. + for error in errors { + if let traits::FulfillmentErrorCode::CodeSelectionError( + traits::SelectionError::Unimplemented, + ) = error.code + && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( + pred, + )) = error.obligation.predicate.kind().skip_binder() + { + self.infcx.err_ctxt().suggest_derive( + &error.obligation, + err, + error.obligation.predicate.kind().rebind(pred), + ); + } + } + } + } + } let (changing, span, sugg) = match local.ty { Some(ty) => ("changing", ty.span, message), None => { diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 4385e745bac..c0d3fc3fae0 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -158,6 +158,9 @@ declare_features! ( /// Allows using `#[plugin_registrar]` on functions. (removed, plugin_registrar, "1.54.0", Some(29597), None, Some("plugins are no longer supported")), + /// Allows exhaustive integer pattern matching with `usize::MAX`/`isize::MIN`/`isize::MAX`. + (removed, precise_pointer_size_matching, "1.32.0", Some(56354), None, + Some("removed in favor of half-open ranges")), (removed, proc_macro_expr, "1.27.0", Some(54727), None, Some("subsumed by `#![feature(proc_macro_hygiene)]`")), (removed, proc_macro_gen, "1.27.0", Some(54727), None, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index dee68bff21d..1d4fa9c75d9 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -543,8 +543,6 @@ declare_features! ( (unstable, offset_of_enum, "1.75.0", Some(106655), None), /// Allows using `#[optimize(X)]`. (unstable, optimize_attribute, "1.34.0", Some(54882), None), - /// Allows exhaustive integer pattern matching on `usize` and `isize`. - (unstable, precise_pointer_size_matching, "1.32.0", Some(56354), None), /// Allows macro attributes on expressions, statements and non-inline modules. (unstable, proc_macro_hygiene, "1.30.0", Some(54727), None), /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index d29a27eced0..47fbed45b91 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -243,6 +243,31 @@ pub fn create_args_for_parent_generic_args<'tcx, 'a>( match (args_iter.peek(), params.peek()) { (Some(&arg), Some(¶m)) => { match (arg, ¶m.kind, arg_count.explicit_late_bound) { + ( + GenericArg::Const(hir::ConstArg { + is_desugared_from_effects: true, + .. + }), + GenericParamDefKind::Const { is_host_effect: false, .. } + | GenericParamDefKind::Type { .. } + | GenericParamDefKind::Lifetime, + _, + ) => { + // SPECIAL CASE FOR DESUGARED EFFECT PARAMS + // This comes from the following example: + // + // ``` + // #[const_trait] + // pub trait PartialEq<Rhs: ?Sized = Self> {} + // impl const PartialEq for () {} + // ``` + // + // Since this is a const impl, we need to insert `<false>` at the end of + // `PartialEq`'s generics, but this errors since `Rhs` isn't specified. + // To work around this, we infer all arguments until we reach the host param. + args.push(ctx.inferred_kind(Some(&args), param, infer_args)); + params.next(); + } (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) | ( GenericArg::Type(_) | GenericArg::Infer(_), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index a904b419a94..2c9942caab2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1619,6 +1619,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, ); } else { + if let Some(errors) = + self.could_impl_trait(clone_trait_did, expected_ty, self.param_env) + { + match &errors[..] { + [] => {} + [error] => { + diag.help(format!( + "`Clone` is not implemented because the trait bound `{}` is \ + not satisfied", + error.obligation.predicate, + )); + } + [errors @ .., last] => { + diag.help(format!( + "`Clone` is not implemented because the following trait bounds \ + could not be satisfied: {} and `{}`", + errors + .iter() + .map(|e| format!("`{}`", e.obligation.predicate)) + .collect::<Vec<_>>() + .join(", "), + last.obligation.predicate, + )); + } + } + for error in errors { + if let traits::FulfillmentErrorCode::CodeSelectionError( + traits::SelectionError::Unimplemented, + ) = error.code + && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = + error.obligation.predicate.kind().skip_binder() + { + self.infcx.err_ctxt().suggest_derive( + &error.obligation, + diag, + error.obligation.predicate.kind().rebind(pred), + ); + } + } + } self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]); } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 80602713905..b72b9da21b7 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -867,12 +867,6 @@ fn report_non_exhaustive_match<'p, 'tcx>( exhaustively", )); } - if cx.tcx.sess.is_nightly_build() { - err.help(format!( - "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \ - enable precise `{ty}` matching", - )); - } } else if ty == cx.tcx.types.str_ { err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary"); } else if cx.is_foreign_non_exhaustive_enum(ty) { diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 31114190f07..ddcceeb7ef6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -326,8 +326,7 @@ impl IntRange { /// `NegInfinity..PosInfinity`. In other words, as far as `IntRange` is concerned, there are /// values before `isize::MIN` and after `usize::MAX`/`isize::MAX`. /// This is to avoid e.g. `0..(u32::MAX as usize)` from being exhaustive on one architecture and - /// not others. See discussions around the `precise_pointer_size_matching` feature for more - /// details. + /// not others. This was decided in <https://github.com/rust-lang/rfcs/pull/2591>. /// /// These infinities affect splitting subtly: it is possible to get `NegInfinity..0` and /// `usize::MAX+1..PosInfinity` in the output. Diagnostics must be careful to handle these @@ -380,7 +379,7 @@ impl IntRange { /// Whether the range denotes the fictitious values before `isize::MIN` or after /// `usize::MAX`/`isize::MAX` (see doc of [`IntRange::split`] for why these exist). pub(crate) fn is_beyond_boundaries<'tcx>(&self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> bool { - ty.is_ptr_sized_integral() && !tcx.features().precise_pointer_size_matching && { + ty.is_ptr_sized_integral() && { // The two invalid ranges are `NegInfinity..isize::MIN` (represented as // `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `to_diagnostic_pat_range_bdy` // converts `MAX+1` to `PosInfinity`, and we couldn't have `PosInfinity` in `self.lo` @@ -941,11 +940,8 @@ impl ConstructorSet { } } &ty::Int(ity) => { - let range = if ty.is_ptr_sized_integral() - && !cx.tcx.features().precise_pointer_size_matching - { - // The min/max values of `isize` are not allowed to be observed unless the - // `precise_pointer_size_matching` feature is enabled. + let range = if ty.is_ptr_sized_integral() { + // The min/max values of `isize` are not allowed to be observed. IntRange { lo: NegInfinity, hi: PosInfinity } } else { let bits = Integer::from_int_ty(&cx.tcx, ity).size().bits() as u128; @@ -956,11 +952,8 @@ impl ConstructorSet { Self::Integers { range_1: range, range_2: None } } &ty::Uint(uty) => { - let range = if ty.is_ptr_sized_integral() - && !cx.tcx.features().precise_pointer_size_matching - { - // The max value of `usize` is not allowed to be observed unless the - // `precise_pointer_size_matching` feature is enabled. + let range = if ty.is_ptr_sized_integral() { + // The max value of `usize` is not allowed to be observed. let lo = MaybeInfiniteInt::new_finite(cx.tcx, ty, 0); IntRange { lo, hi: PosInfinity } } else { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 98e68e682ab..7ab0d3f35ea 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -673,15 +673,6 @@ impl<'a> Parser<'a> { ); } - // Add suggestion for a missing closing angle bracket if '>' is included in expected_tokens - // there are unclosed angle brackets - if self.unmatched_angle_bracket_count > 0 - && self.token.kind == TokenKind::Eq - && expected.iter().any(|tok| matches!(tok, TokenType::Token(TokenKind::Gt))) - { - err.span_label(self.prev_token.span, "maybe try to close unmatched angle bracket"); - } - let sp = if self.token == token::Eof { // This is EOF; don't want to point at the following char, but rather the last token. self.prev_token.span @@ -811,6 +802,7 @@ impl<'a> Parser<'a> { } err.emit(); } + fn check_too_many_raw_str_terminators(&mut self, err: &mut Diagnostic) -> bool { let sm = self.sess.source_map(); match (&self.prev_token.kind, &self.token.kind) { @@ -1986,6 +1978,39 @@ impl<'a> Parser<'a> { } } + /// When trying to close a generics list and encountering code like + /// ```text + /// impl<S: Into<std::borrow::Cow<'static, str>> From<S> for Canonical {} + /// // ^ missing > here + /// ``` + /// we provide a structured suggestion on the error from `expect_gt`. + pub(super) fn expect_gt_or_maybe_suggest_closing_generics( + &mut self, + params: &[ast::GenericParam], + ) -> PResult<'a, ()> { + let Err(mut err) = self.expect_gt() else { + return Ok(()); + }; + // Attempt to find places where a missing `>` might belong. + if let [.., ast::GenericParam { bounds, .. }] = params + && let Some(poly) = bounds + .iter() + .filter_map(|bound| match bound { + ast::GenericBound::Trait(poly, _) => Some(poly), + _ => None, + }) + .last() + { + err.span_suggestion_verbose( + poly.span.shrink_to_hi(), + "you might have meant to end the type parameters here", + ">", + Applicability::MaybeIncorrect, + ); + } + Err(err) + } + pub(super) fn recover_seq_parse_error( &mut self, delim: Delimiter, diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 242c9d332bb..20f67b284b2 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -279,7 +279,7 @@ impl<'a> Parser<'a> { let span_lo = self.token.span; let (params, span) = if self.eat_lt() { let params = self.parse_generic_params()?; - self.expect_gt()?; + self.expect_gt_or_maybe_suggest_closing_generics(¶ms)?; (params, span_lo.to(self.prev_token.span)) } else { (ThinVec::new(), self.prev_token.span.shrink_to_hi()) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 6c387a385e6..444110c7e7e 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1697,6 +1697,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { struct_span_err!(self.tcx.sess, ident.span, E0603, "{} `{}` is private", descr, ident); err.span_label(ident.span, format!("private {descr}")); + let mut not_publicly_reexported = false; if let Some((this_res, outer_ident)) = outermost_res { let import_suggestions = self.lookup_import_candidates( outer_ident, @@ -1717,6 +1718,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ); // If we suggest importing a public re-export, don't point at the definition. if point_to_def && ident.span != outer_ident.span { + not_publicly_reexported = true; err.span_label( outer_ident.span, format!("{} `{outer_ident}` is not publicly re-exported", this_res.descr()), @@ -1749,10 +1751,51 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } + let mut sugg_paths = vec![]; + if let Some(mut def_id) = res.opt_def_id() { + // We can't use `def_path_str` in resolve. + let mut path = vec![def_id]; + while let Some(parent) = self.tcx.opt_parent(def_id) { + def_id = parent; + if !def_id.is_top_level_module() { + path.push(def_id); + } else { + break; + } + } + // We will only suggest importing directly if it is accessible through that path. + let path_names: Option<Vec<String>> = path + .iter() + .rev() + .map(|def_id| { + self.tcx.opt_item_name(*def_id).map(|n| { + if def_id.is_top_level_module() { + "crate".to_string() + } else { + n.to_string() + } + }) + }) + .collect(); + if let Some(def_id) = path.get(0) + && let Some(path) = path_names + { + if let Some(def_id) = def_id.as_local() { + if self.effective_visibilities.is_directly_public(def_id) { + sugg_paths.push((path, false)); + } + } else if self.is_accessible_from(self.tcx.visibility(def_id), parent_scope.module) + { + sugg_paths.push((path, false)); + } + } + } + // Print the whole import chain to make it easier to see what happens. let first_binding = binding; let mut next_binding = Some(binding); let mut next_ident = ident; + let mut path = vec![]; while let Some(binding) = next_binding { let name = next_ident; next_binding = match binding.kind { @@ -1771,6 +1814,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { _ => None, }; + match binding.kind { + NameBindingKind::Import { import, .. } => { + for segment in import.module_path.iter().skip(1) { + path.push(segment.ident.to_string()); + } + sugg_paths.push(( + path.iter() + .cloned() + .chain(vec![ident.to_string()].into_iter()) + .collect::<Vec<_>>(), + true, // re-export + )); + } + NameBindingKind::Res(_) | NameBindingKind::Module(_) => {} + } let first = binding == first_binding; let msg = format!( "{and_refers_to}the {item} `{name}`{which} is defined here{dots}", @@ -1782,7 +1840,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); let mut note_span = MultiSpan::from_span(def_span); if !first && binding.vis.is_public() { - note_span.push_span_label(def_span, "consider importing it directly"); + let desc = match binding.kind { + NameBindingKind::Import { .. } => "re-export", + _ => "directly", + }; + note_span.push_span_label(def_span, format!("you could import this {desc}")); } // Final step in the import chain, point out if the ADT is `non_exhaustive` // which is probably why this privacy violation occurred. @@ -1796,6 +1858,29 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } err.span_note(note_span, msg); } + // We prioritize shorter paths, non-core imports and direct imports over the alternatives. + sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0] == "core", *reexport)); + for (sugg, reexport) in sugg_paths { + if not_publicly_reexported { + break; + } + if sugg.len() <= 1 { + // A single path segment suggestion is wrong. This happens on circular imports. + // `tests/ui/imports/issue-55884-2.rs` + continue; + } + let path = sugg.join("::"); + err.span_suggestion_verbose( + dedup_span, + format!( + "import `{ident}` {}", + if reexport { "through the re-export" } else { "directly" } + ), + path, + Applicability::MachineApplicable, + ); + break; + } err.emit(); } diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 38153cccfdd..992bfd97e0e 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,8 +1,10 @@ +use crate::solve::FulfillmentCtxt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::{self, DefiningAnchor, ObligationCtxt}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_infer::traits::{TraitEngine, TraitEngineExt}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; use rustc_middle::traits::query::NoSolution; @@ -35,6 +37,13 @@ pub trait InferCtxtExt<'tcx> { params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>, param_env: ty::ParamEnv<'tcx>, ) -> traits::EvaluationResult; + + fn could_impl_trait( + &self, + trait_def_id: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Option<Vec<traits::FulfillmentError<'tcx>>>; } impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { @@ -76,6 +85,69 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { }; self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr) } + + fn could_impl_trait( + &self, + trait_def_id: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Option<Vec<traits::FulfillmentError<'tcx>>> { + self.probe(|_snapshot| { + if let ty::Adt(def, args) = ty.kind() + && let Some((impl_def_id, _)) = self + .tcx + .all_impls(trait_def_id) + .filter_map(|impl_def_id| { + self.tcx.impl_trait_ref(impl_def_id).map(|r| (impl_def_id, r)) + }) + .map(|(impl_def_id, imp)| (impl_def_id, imp.skip_binder())) + .filter(|(_, imp)| match imp.self_ty().peel_refs().kind() { + ty::Adt(i_def, _) if i_def.did() == def.did() => true, + _ => false, + }) + .next() + { + let mut fulfill_cx = FulfillmentCtxt::new(self); + // We get all obligations from the impl to talk about specific + // trait bounds. + let obligations = self + .tcx + .predicates_of(impl_def_id) + .instantiate(self.tcx, args) + .into_iter() + .map(|(clause, span)| { + traits::Obligation::new( + self.tcx, + traits::ObligationCause::dummy_with_span(span), + param_env, + clause, + ) + }) + .collect::<Vec<_>>(); + fulfill_cx.register_predicate_obligations(self, obligations); + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty]); + let obligation = traits::Obligation::new( + self.tcx, + traits::ObligationCause::dummy(), + param_env, + trait_ref, + ); + fulfill_cx.register_predicate_obligation(self, obligation); + let mut errors = fulfill_cx.select_all_or_error(self); + // We remove the last predicate failure, which corresponds to + // the top-level obligation, because most of the type we only + // care about the other ones, *except* when it is the only one. + // This seems to only be relevant for arbitrary self-types. + // Look at `tests/ui/moves/move-fn-self-receiver.rs`. + if errors.len() > 1 { + errors.truncate(errors.len() - 1); + } + Some(errors) + } else { + None + } + }) + } } pub trait InferCtxtBuilderExt<'tcx> { |
