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}`
hir_analysis_ambiguous_lifetime_bound =
@ -12,14 +12,14 @@ hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private
.label = private {$kind}
.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 ->
[true] an
*[false] a similarly named
} 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_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_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

View File

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

View File

@ -6,7 +6,6 @@
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::bug;
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
use rustc_span::symbol::Ident;
use rustc_span::{ErrorGuaranteed, Span, Symbol};
@ -16,9 +15,8 @@
use crate::bounds::Bounds;
use crate::errors;
use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter};
use super::RegionInferReason;
use crate::hir_ty_lowering::HirTyLowerer;
use crate::hir_ty_lowering::{AssocItemQSelf, OnlySelfBounds, PredicateFilter, RegionInferReason};
impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// 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.
self.probe_single_bound_for_assoc_item(
|| traits::supertraits(tcx, trait_ref),
trait_ref.skip_binder().print_only_trait_name(),
None,
AssocItemQSelf::Trait(trait_ref.def_id()),
assoc_kind,
constraint.ident,
path_span,

View File

@ -3,7 +3,7 @@
ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits,
};
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::sorted_map::SortedMap;
use rustc_data_structures::unord::UnordMap;
@ -11,9 +11,9 @@
use rustc_errors::{
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed,
};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, Node};
use rustc_hir::def_id::DefId;
use rustc_middle::bug;
use rustc_middle::query::Key;
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>(
&self,
all_candidates: impl Fn() -> I,
ty_param_name: &str,
ty_param_def_id: Option<LocalDefId>,
qself: AssocItemQSelf,
assoc_kind: ty::AssocKind,
assoc_name: Ident,
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
// 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 },
assoc_name,
assoc_kind: assoc_kind_str,
ty_param_name,
qself: &qself_str,
label: None,
sugg: None,
};
@ -219,19 +219,28 @@ pub(super) fn complain_about_assoc_item_not_found<I>(
suggested_name,
identically_named: suggested_name == assoc_name.name,
});
let hir = tcx.hir();
if let Some(def_id) = ty_param_def_id
&& let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id))
&& let Some(generics) = hir.get_generics(parent.def_id)
if let AssocItemQSelf::TyParam(ty_param_def_id) = qself
// Not using `self.item_def_id()` here as that would yield the opaque type itself if we're
// inside an opaque type while we're interested in the overarching type alias (TAIT).
// 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(
|b| match b {
// FIXME: Suggest adding supertrait bounds if we have a `Self` type param.
// 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, ..) => {
t.trait_ref.trait_def_id() == Some(best_trait)
}
_ => false,
},
) {
})
{
// The type param already has a bound for `trait_name`, we just need to
// change the associated item.
err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
@ -247,7 +256,7 @@ pub(super) fn complain_about_assoc_item_not_found<I>(
tcx,
generics,
&mut err,
&ty_param_name,
&qself_str,
&trait_name,
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() {
err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
span: assoc_name.span,
ty_param_name,
qself: &qself_str,
assoc_kind: assoc_kind_str,
suggested_name: *candidate_name,
});
@ -339,10 +348,10 @@ fn complain_about_assoc_kind_mismatch(
self.dcx().emit_err(errors::AssocKindMismatch {
span,
expected: super::assoc_kind_str(expected),
got: super::assoc_kind_str(got),
expected: assoc_kind_str(expected),
got: assoc_kind_str(got),
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),
bound_on_assoc_const_label,
wrap_in_braces_sugg,
@ -736,7 +745,7 @@ pub(crate) fn complain_about_missing_assoc_tys(
if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id));
in_expr_or_pat = match grandparent {
Node::Expr(_) | Node::Pat(_) => true,
hir::Node::Expr(_) | hir::Node::Pat(_) => true,
_ => false,
};
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::{self, ObligationCtxt};
use std::fmt::Display;
use std::slice;
/// 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
/// 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;
debug!("predicates={:#?}", predicates);
let param_name = tcx.hir().ty_param_name(ty_param_def_id);
self.probe_single_bound_for_assoc_item(
|| {
traits::transitive_bounds_that_define_assoc_item(
tcx,
predicates
.iter()
.filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))),
assoc_name,
)
let trait_refs = predicates
.iter()
.filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref)));
traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name)
},
param_name,
Some(ty_param_def_id),
AssocItemQSelf::TyParam(ty_param_def_id),
ty::AssocKind::Type,
assoc_name,
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
/// 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>(
&self,
all_candidates: impl Fn() -> I,
ty_param_name: impl Display,
ty_param_def_id: Option<LocalDefId>,
qself: AssocItemQSelf,
assoc_kind: ty::AssocKind,
assoc_name: Ident,
span: Span,
@ -858,8 +870,7 @@ fn probe_single_bound_for_assoc_item<I>(
let Some(bound) = matching_candidates.next() else {
let reported = self.complain_about_assoc_item_not_found(
all_candidates,
&ty_param_name.to_string(),
ty_param_def_id,
qself,
assoc_kind,
assoc_name,
span,
@ -872,13 +883,13 @@ fn probe_single_bound_for_assoc_item<I>(
if let Some(bound2) = matching_candidates.next() {
debug!(?bound2);
let assoc_kind_str = assoc_kind_str(assoc_kind);
let ty_param_name = &ty_param_name.to_string();
let assoc_kind_str = errors::assoc_kind_str(assoc_kind);
let qself_str = qself.to_string(tcx);
let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem {
span,
assoc_kind: assoc_kind_str,
assoc_name,
ty_param_name,
qself: &qself_str,
});
// Provide a more specific error code index entry for equality bindings.
err.code(
@ -929,7 +940,7 @@ trait = bound.print_only_trait_path(),
err.span_suggestion_verbose(
span.with_hi(assoc_name.span.lo()),
"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,
);
}
@ -943,7 +954,7 @@ trait = bound.print_only_trait_path(),
if !where_bounds.is_empty() {
err.help(format!(
"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"),
));
}
@ -997,11 +1008,6 @@ pub fn lower_assoc_path(
let tcx = self.tcx();
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.
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
// item is declared.
let bound = match (&qself_ty.kind(), qself_res) {
@ -1056,8 +1068,7 @@ pub fn lower_assoc_path(
ty::Binder::dummy(trait_ref.instantiate_identity()),
)
},
kw::SelfUpper,
None,
AssocItemQSelf::SelfTyAlias,
ty::AssocKind::Type,
assoc_ident,
span,
@ -2522,11 +2533,3 @@ fn compute_object_lifetime_bound(
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",
}
}