HIR ty lowering: Refactor the way the projectee ("QSelf") gets passed to diagnostics

This commit is contained in:
León Orell Valerian Liehr 2024-06-14 12:28:23 +02:00
parent ef121f28d8
commit 898448ca58
No known key found for this signature in database
GPG Key ID: D17A07215F68E713
5 changed files with 86 additions and 69 deletions

View File

@ -1,4 +1,4 @@
hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$ty_param_name}` hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$qself}`
.label = ambiguous associated {$assoc_kind} `{$assoc_name}` .label = ambiguous associated {$assoc_kind} `{$assoc_name}`
hir_analysis_ambiguous_lifetime_bound = hir_analysis_ambiguous_lifetime_bound =
@ -12,14 +12,14 @@ hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private
.label = private {$kind} .label = private {$kind}
.defined_here_label = the {$kind} is defined here .defined_here_label = the {$kind} is defined here
hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}` hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$qself}`
hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named -> hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named ->
[true] an [true] an
*[false] a similarly named *[false] a similarly named
} associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}`
hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found
hir_analysis_assoc_item_not_found_other_sugg = `{$ty_param_name}` has the following associated {$assoc_kind} hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind}
hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}` hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}`
hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name
hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name

View File

@ -22,7 +22,7 @@ pub struct AmbiguousAssocItem<'a> {
pub span: Span, pub span: Span,
pub assoc_kind: &'static str, pub assoc_kind: &'static str,
pub assoc_name: Ident, pub assoc_name: Ident,
pub ty_param_name: &'a str, pub qself: &'a str,
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]
@ -75,7 +75,7 @@ pub struct AssocItemNotFound<'a> {
pub span: Span, pub span: Span,
pub assoc_name: Ident, pub assoc_name: Ident,
pub assoc_kind: &'static str, pub assoc_kind: &'static str,
pub ty_param_name: &'a str, pub qself: &'a str,
#[subdiagnostic] #[subdiagnostic]
pub label: Option<AssocItemNotFoundLabel<'a>>, pub label: Option<AssocItemNotFoundLabel<'a>>,
#[subdiagnostic] #[subdiagnostic]
@ -134,7 +134,7 @@ pub enum AssocItemNotFoundSugg<'a> {
Other { Other {
#[primary_span] #[primary_span]
span: Span, span: Span,
ty_param_name: &'a str, qself: &'a str,
assoc_kind: &'static str, assoc_kind: &'static str,
suggested_name: Symbol, suggested_name: Symbol,
}, },

View File

@ -6,7 +6,6 @@
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{ErrorGuaranteed, Span, Symbol}; use rustc_span::{ErrorGuaranteed, Span, Symbol};
@ -16,9 +15,8 @@
use crate::bounds::Bounds; use crate::bounds::Bounds;
use crate::errors; use crate::errors;
use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter}; use crate::hir_ty_lowering::HirTyLowerer;
use crate::hir_ty_lowering::{AssocItemQSelf, OnlySelfBounds, PredicateFilter, RegionInferReason};
use super::RegionInferReason;
impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// Add a `Sized` bound to the `bounds` if appropriate. /// Add a `Sized` bound to the `bounds` if appropriate.
@ -288,8 +286,7 @@ pub(super) fn lower_assoc_item_constraint(
// one that does define it. // one that does define it.
self.probe_single_bound_for_assoc_item( self.probe_single_bound_for_assoc_item(
|| traits::supertraits(tcx, trait_ref), || traits::supertraits(tcx, trait_ref),
trait_ref.skip_binder().print_only_trait_name(), AssocItemQSelf::Trait(trait_ref.def_id()),
None,
assoc_kind, assoc_kind,
constraint.ident, constraint.ident,
path_span, path_span,

View File

@ -3,7 +3,7 @@
ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits, ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits,
}; };
use crate::fluent_generated as fluent; use crate::fluent_generated as fluent;
use crate::hir_ty_lowering::HirTyLowerer; use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::unord::UnordMap; use rustc_data_structures::unord::UnordMap;
@ -11,9 +11,9 @@
use rustc_errors::{ use rustc_errors::{
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed,
}; };
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, Node};
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::query::Key; use rustc_middle::query::Key;
use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
@ -116,8 +116,7 @@ pub(crate) fn complain_about_internal_fn_trait(
pub(super) fn complain_about_assoc_item_not_found<I>( pub(super) fn complain_about_assoc_item_not_found<I>(
&self, &self,
all_candidates: impl Fn() -> I, all_candidates: impl Fn() -> I,
ty_param_name: &str, qself: AssocItemQSelf,
ty_param_def_id: Option<LocalDefId>,
assoc_kind: ty::AssocKind, assoc_kind: ty::AssocKind,
assoc_name: Ident, assoc_name: Ident,
span: Span, span: Span,
@ -139,7 +138,8 @@ pub(super) fn complain_about_assoc_item_not_found<I>(
); );
} }
let assoc_kind_str = super::assoc_kind_str(assoc_kind); let assoc_kind_str = assoc_kind_str(assoc_kind);
let qself_str = qself.to_string(tcx);
// The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
// valid span, so we point at the whole path segment instead. // valid span, so we point at the whole path segment instead.
@ -149,7 +149,7 @@ pub(super) fn complain_about_assoc_item_not_found<I>(
span: if is_dummy { span } else { assoc_name.span }, span: if is_dummy { span } else { assoc_name.span },
assoc_name, assoc_name,
assoc_kind: assoc_kind_str, assoc_kind: assoc_kind_str,
ty_param_name, qself: &qself_str,
label: None, label: None,
sugg: None, sugg: None,
}; };
@ -219,19 +219,28 @@ pub(super) fn complain_about_assoc_item_not_found<I>(
suggested_name, suggested_name,
identically_named: suggested_name == assoc_name.name, identically_named: suggested_name == assoc_name.name,
}); });
let hir = tcx.hir(); if let AssocItemQSelf::TyParam(ty_param_def_id) = qself
if let Some(def_id) = ty_param_def_id // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're
&& let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id)) // inside an opaque type while we're interested in the overarching type alias (TAIT).
&& let Some(generics) = hir.get_generics(parent.def_id) // FIXME: However, for trait aliases, this incorrectly returns the enclosing module...
&& let item_def_id =
tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(ty_param_def_id))
// FIXME: ...which obviously won't have any generics.
&& let Some(generics) = tcx.hir().get_generics(item_def_id.def_id)
{ {
if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any( // FIXME: Suggest adding supertrait bounds if we have a `Self` type param.
|b| match b { // FIXME(trait_alias): Suggest adding `Self: Trait` to
// `trait Alias = where Self::Proj:;` with `trait Trait { type Proj; }`.
if generics
.bounds_for_param(ty_param_def_id)
.flat_map(|pred| pred.bounds.iter())
.any(|b| match b {
hir::GenericBound::Trait(t, ..) => { hir::GenericBound::Trait(t, ..) => {
t.trait_ref.trait_def_id() == Some(best_trait) t.trait_ref.trait_def_id() == Some(best_trait)
} }
_ => false, _ => false,
}, })
) { {
// The type param already has a bound for `trait_name`, we just need to // The type param already has a bound for `trait_name`, we just need to
// change the associated item. // change the associated item.
err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait { err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
@ -247,7 +256,7 @@ pub(super) fn complain_about_assoc_item_not_found<I>(
tcx, tcx,
generics, generics,
&mut err, &mut err,
&ty_param_name, &qself_str,
&trait_name, &trait_name,
None, None,
None, None,
@ -273,7 +282,7 @@ pub(super) fn complain_about_assoc_item_not_found<I>(
if let [candidate_name] = all_candidate_names.as_slice() { if let [candidate_name] = all_candidate_names.as_slice() {
err.sugg = Some(errors::AssocItemNotFoundSugg::Other { err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
span: assoc_name.span, span: assoc_name.span,
ty_param_name, qself: &qself_str,
assoc_kind: assoc_kind_str, assoc_kind: assoc_kind_str,
suggested_name: *candidate_name, suggested_name: *candidate_name,
}); });
@ -339,10 +348,10 @@ fn complain_about_assoc_kind_mismatch(
self.dcx().emit_err(errors::AssocKindMismatch { self.dcx().emit_err(errors::AssocKindMismatch {
span, span,
expected: super::assoc_kind_str(expected), expected: assoc_kind_str(expected),
got: super::assoc_kind_str(got), got: assoc_kind_str(got),
expected_because_label, expected_because_label,
assoc_kind: super::assoc_kind_str(assoc_item.kind), assoc_kind: assoc_kind_str(assoc_item.kind),
def_span: tcx.def_span(assoc_item.def_id), def_span: tcx.def_span(assoc_item.def_id),
bound_on_assoc_const_label, bound_on_assoc_const_label,
wrap_in_braces_sugg, wrap_in_braces_sugg,
@ -736,7 +745,7 @@ pub(crate) fn complain_about_missing_assoc_tys(
if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id)); let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id));
in_expr_or_pat = match grandparent { in_expr_or_pat = match grandparent {
Node::Expr(_) | Node::Pat(_) => true, hir::Node::Expr(_) | hir::Node::Pat(_) => true,
_ => false, _ => false,
}; };
match bound.trait_ref.path.segments { match bound.trait_ref.path.segments {
@ -1602,3 +1611,11 @@ fn generics_args_err_extend<'a>(
_ => {} _ => {}
} }
} }
pub(super) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
match kind {
ty::AssocKind::Fn => "function",
ty::AssocKind::Const => "constant",
ty::AssocKind::Type => "type",
}
}

View File

@ -55,7 +55,6 @@
use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::wf::object_region_bounds;
use rustc_trait_selection::traits::{self, ObligationCtxt}; use rustc_trait_selection::traits::{self, ObligationCtxt};
use std::fmt::Display;
use std::slice; use std::slice;
/// A path segment that is semantically allowed to have generic arguments. /// A path segment that is semantically allowed to have generic arguments.
@ -193,6 +192,25 @@ fn lowerer(&self) -> &dyn HirTyLowerer<'tcx>
} }
} }
/// The "qualified self" of an associated item path.
///
/// For diagnostic purposes only.
enum AssocItemQSelf {
Trait(DefId),
TyParam(LocalDefId),
SelfTyAlias,
}
impl AssocItemQSelf {
fn to_string(&self, tcx: TyCtxt<'_>) -> String {
match *self {
Self::Trait(def_id) => tcx.def_path_str(def_id),
Self::TyParam(def_id) => tcx.hir().ty_param_name(def_id).to_string(),
Self::SelfTyAlias => kw::SelfUpper.to_string(),
}
}
}
/// New-typed boolean indicating whether explicit late-bound lifetimes /// New-typed boolean indicating whether explicit late-bound lifetimes
/// are present in a set of generic arguments. /// are present in a set of generic arguments.
/// ///
@ -811,19 +829,14 @@ fn probe_single_ty_param_bound_for_assoc_ty(
let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates; let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates;
debug!("predicates={:#?}", predicates); debug!("predicates={:#?}", predicates);
let param_name = tcx.hir().ty_param_name(ty_param_def_id);
self.probe_single_bound_for_assoc_item( self.probe_single_bound_for_assoc_item(
|| { || {
traits::transitive_bounds_that_define_assoc_item( let trait_refs = predicates
tcx,
predicates
.iter() .iter()
.filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))), .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref)));
assoc_name, traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name)
)
}, },
param_name, AssocItemQSelf::TyParam(ty_param_def_id),
Some(ty_param_def_id),
ty::AssocKind::Type, ty::AssocKind::Type,
assoc_name, assoc_name,
span, span,
@ -835,12 +848,11 @@ fn probe_single_ty_param_bound_for_assoc_ty(
/// ///
/// This fails if there is no such bound in the list of candidates or if there are multiple /// This fails if there is no such bound in the list of candidates or if there are multiple
/// candidates in which case it reports ambiguity. /// candidates in which case it reports ambiguity.
#[instrument(level = "debug", skip(self, all_candidates, ty_param_name, constraint), ret)] #[instrument(level = "debug", skip(self, all_candidates, qself, constraint), ret)]
fn probe_single_bound_for_assoc_item<I>( fn probe_single_bound_for_assoc_item<I>(
&self, &self,
all_candidates: impl Fn() -> I, all_candidates: impl Fn() -> I,
ty_param_name: impl Display, qself: AssocItemQSelf,
ty_param_def_id: Option<LocalDefId>,
assoc_kind: ty::AssocKind, assoc_kind: ty::AssocKind,
assoc_name: Ident, assoc_name: Ident,
span: Span, span: Span,
@ -858,8 +870,7 @@ fn probe_single_bound_for_assoc_item<I>(
let Some(bound) = matching_candidates.next() else { let Some(bound) = matching_candidates.next() else {
let reported = self.complain_about_assoc_item_not_found( let reported = self.complain_about_assoc_item_not_found(
all_candidates, all_candidates,
&ty_param_name.to_string(), qself,
ty_param_def_id,
assoc_kind, assoc_kind,
assoc_name, assoc_name,
span, span,
@ -872,13 +883,13 @@ fn probe_single_bound_for_assoc_item<I>(
if let Some(bound2) = matching_candidates.next() { if let Some(bound2) = matching_candidates.next() {
debug!(?bound2); debug!(?bound2);
let assoc_kind_str = assoc_kind_str(assoc_kind); let assoc_kind_str = errors::assoc_kind_str(assoc_kind);
let ty_param_name = &ty_param_name.to_string(); let qself_str = qself.to_string(tcx);
let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem {
span, span,
assoc_kind: assoc_kind_str, assoc_kind: assoc_kind_str,
assoc_name, assoc_name,
ty_param_name, qself: &qself_str,
}); });
// Provide a more specific error code index entry for equality bindings. // Provide a more specific error code index entry for equality bindings.
err.code( err.code(
@ -929,7 +940,7 @@ trait = bound.print_only_trait_path(),
err.span_suggestion_verbose( err.span_suggestion_verbose(
span.with_hi(assoc_name.span.lo()), span.with_hi(assoc_name.span.lo()),
"use fully-qualified syntax to disambiguate", "use fully-qualified syntax to disambiguate",
format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()), format!("<{qself_str} as {}>::", bound.print_only_trait_path()),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} }
@ -943,7 +954,7 @@ trait = bound.print_only_trait_path(),
if !where_bounds.is_empty() { if !where_bounds.is_empty() {
err.help(format!( err.help(format!(
"consider introducing a new type parameter `T` and adding `where` constraints:\ "consider introducing a new type parameter `T` and adding `where` constraints:\
\n where\n T: {ty_param_name},\n{}", \n where\n T: {qself_str},\n{}",
where_bounds.join(",\n"), where_bounds.join(",\n"),
)); ));
} }
@ -997,11 +1008,6 @@ pub fn lower_assoc_path(
let tcx = self.tcx(); let tcx = self.tcx();
let assoc_ident = assoc_segment.ident; let assoc_ident = assoc_segment.ident;
let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
path.res
} else {
Res::Err
};
// Check if we have an enum variant or an inherent associated type. // Check if we have an enum variant or an inherent associated type.
let mut variant_resolution = None; let mut variant_resolution = None;
@ -1038,6 +1044,12 @@ pub fn lower_assoc_path(
} }
} }
let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
path.res
} else {
Res::Err
};
// Find the type of the associated item, and the trait where the associated // Find the type of the associated item, and the trait where the associated
// item is declared. // item is declared.
let bound = match (&qself_ty.kind(), qself_res) { let bound = match (&qself_ty.kind(), qself_res) {
@ -1056,8 +1068,7 @@ pub fn lower_assoc_path(
ty::Binder::dummy(trait_ref.instantiate_identity()), ty::Binder::dummy(trait_ref.instantiate_identity()),
) )
}, },
kw::SelfUpper, AssocItemQSelf::SelfTyAlias,
None,
ty::AssocKind::Type, ty::AssocKind::Type,
assoc_ident, assoc_ident,
span, span,
@ -2522,11 +2533,3 @@ fn compute_object_lifetime_bound(
Some(r) Some(r)
} }
} }
fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
match kind {
ty::AssocKind::Fn => "function",
ty::AssocKind::Const => "constant",
ty::AssocKind::Type => "type",
}
}