1
Fork 0

Auto merge of #92441 - cjgillot:resolve-trait-impl-item, r=matthewjasper

Link impl items to corresponding trait items in late resolver.

Hygienically linking trait impl items to declarations in the trait can be done directly by the late resolver. In fact, it is already done to diagnose unknown items.

This PR uses this resolution work and stores the `DefId` of the trait item in the HIR. This avoids having to do this resolution manually later.

r? `@matthewjasper`
Related to #90639. The added `trait_item_id` field can be moved to `ImplItemRef` to be used directly by your PR.
This commit is contained in:
bors 2022-01-15 14:43:45 +00:00
commit ec4bcaac45
26 changed files with 478 additions and 515 deletions

View file

@ -602,6 +602,25 @@ impl<'a> Resolver<'a> {
err
}
ResolutionError::TraitImplMismatch {
name,
kind,
code,
trait_item_span,
trait_path,
} => {
let mut err = self.session.struct_span_err_with_code(
span,
&format!(
"item `{}` is an associated {}, which doesn't match its trait `{}`",
name, kind, trait_path,
),
code,
);
err.span_label(span, "does not match trait");
err.span_label(trait_item_span, "item in trait");
err
}
}
}

View file

@ -1247,15 +1247,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
);
let res = res.base_res();
if res != Res::Err {
new_id = Some(res.def_id());
let span = trait_ref.path.span;
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(
&path,
Some(TypeNS),
false,
span,
true,
trait_ref.path.span,
CrateLint::SimplePath(trait_ref.ref_id),
) {
new_id = Some(res.def_id());
new_val = Some((module, trait_ref.clone()));
}
}
@ -1324,6 +1323,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
// If this is a trait impl, ensure the const
// exists in trait
this.check_trait_item(
item.id,
item.ident,
&item.kind,
ValueNS,
@ -1359,6 +1359,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
// If this is a trait impl, ensure the method
// exists in trait
this.check_trait_item(
item.id,
item.ident,
&item.kind,
ValueNS,
@ -1386,6 +1387,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
// If this is a trait impl, ensure the type
// exists in trait
this.check_trait_item(
item.id,
item.ident,
&item.kind,
TypeNS,
@ -1416,7 +1418,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
fn check_trait_item<F>(
&mut self,
ident: Ident,
id: NodeId,
mut ident: Ident,
kind: &AssocItemKind,
ns: Namespace,
span: Span,
@ -1424,26 +1427,62 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
) where
F: FnOnce(Ident, &str, Option<Symbol>) -> ResolutionError<'_>,
{
// If there is a TraitRef in scope for an impl, then the method must be in the
// trait.
if let Some((module, _)) = self.current_trait_ref {
if self
.r
.resolve_ident_in_module(
ModuleOrUniformRoot::Module(module),
ident,
ns,
&self.parent_scope,
false,
span,
)
.is_err()
{
let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
self.report_error(span, err(ident, &path_names_to_string(path), candidate));
}
// If there is a TraitRef in scope for an impl, then the method must be in the trait.
let Some((module, _)) = &self.current_trait_ref else { return; };
ident.span.normalize_to_macros_2_0_and_adjust(module.expansion);
let key = self.r.new_key(ident, ns);
let mut binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding);
debug!(?binding);
if binding.is_none() {
// We could not find the trait item in the correct namespace.
// Check the other namespace to report an error.
let ns = match ns {
ValueNS => TypeNS,
TypeNS => ValueNS,
_ => ns,
};
let key = self.r.new_key(ident, ns);
binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding);
debug!(?binding);
}
let Some(binding) = binding else {
// We could not find the method: report an error.
let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
self.report_error(span, err(ident, &path_names_to_string(path), candidate));
return;
};
let res = binding.res();
let Res::Def(def_kind, _) = res else { bug!() };
match (def_kind, kind) {
(DefKind::AssocTy, AssocItemKind::TyAlias(..))
| (DefKind::AssocFn, AssocItemKind::Fn(..))
| (DefKind::AssocConst, AssocItemKind::Const(..)) => {
self.r.record_partial_res(id, PartialRes::new(res));
return;
}
_ => {}
}
// The method kind does not correspond to what appeared in the trait, report.
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
let (code, kind) = match kind {
AssocItemKind::Const(..) => (rustc_errors::error_code!(E0323), "const"),
AssocItemKind::Fn(..) => (rustc_errors::error_code!(E0324), "method"),
AssocItemKind::TyAlias(..) => (rustc_errors::error_code!(E0325), "type"),
AssocItemKind::MacCall(..) => span_bug!(span, "unexpanded macro"),
};
self.report_error(
span,
ResolutionError::TraitImplMismatch {
name: ident.name,
kind,
code,
trait_path: path_names_to_string(path),
trait_item_span: binding.span,
},
);
}
fn resolve_params(&mut self, params: &'ast [Param]) {

View file

@ -262,6 +262,14 @@ enum ResolutionError<'a> {
SelfInGenericParamDefault,
/// Error E0767: use of unreachable label
UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option<LabelSuggestion> },
/// Error E0323, E0324, E0325: mismatch between trait item and impl item.
TraitImplMismatch {
name: Symbol,
kind: &'static str,
trait_path: String,
trait_item_span: Span,
code: rustc_errors::DiagnosticId,
},
}
enum VisResolutionError<'a> {