Suggest assoc type on type not found in trait method definition

This commit is contained in:
Esteban Küber 2019-07-13 19:20:28 -07:00
parent 69656fa4cb
commit 6b9580b651
3 changed files with 85 additions and 34 deletions

View File

@ -52,7 +52,7 @@ use syntax::ast::{CRATE_NODE_ID, Arm, IsAsync, BindingMode, Block, Crate, Expr,
use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, GenericParamKind, Generics}; use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, GenericParamKind, Generics};
use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind}; use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind};
use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path}; use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path};
use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind}; use syntax::ast::{QSelf, TraitItem, TraitItemKind, TraitRef, Ty, TyKind};
use syntax::ptr::P; use syntax::ptr::P;
use syntax::{span_err, struct_span_err, unwrap_or, walk_list}; use syntax::{span_err, struct_span_err, unwrap_or, walk_list};
@ -1053,6 +1053,7 @@ impl<'a, R> Rib<'a, R> {
/// This refers to the thing referred by a name. The difference between `Res` and `Item` is that /// This refers to the thing referred by a name. The difference between `Res` and `Item` is that
/// items are visible in their whole block, while `Res`es only from the place they are defined /// items are visible in their whole block, while `Res`es only from the place they are defined
/// forward. /// forward.
#[derive(Debug)]
enum LexicalScopeBinding<'a> { enum LexicalScopeBinding<'a> {
Item(&'a NameBinding<'a>), Item(&'a NameBinding<'a>),
Res(Res), Res(Res),
@ -1601,6 +1602,9 @@ pub struct Resolver<'a> {
/// The trait that the current context can refer to. /// The trait that the current context can refer to.
current_trait_ref: Option<(Module<'a>, TraitRef)>, current_trait_ref: Option<(Module<'a>, TraitRef)>,
/// The current trait's associated types' ident, used for diagnostic suggestions.
current_trait_assoc_types: Vec<Ident>,
/// The current self type if inside an impl (used for better errors). /// The current self type if inside an impl (used for better errors).
current_self_type: Option<Ty>, current_self_type: Option<Ty>,
@ -1971,6 +1975,7 @@ impl<'a> Resolver<'a> {
label_ribs: Vec::new(), label_ribs: Vec::new(),
current_trait_ref: None, current_trait_ref: None,
current_trait_assoc_types: Vec::new(),
current_self_type: None, current_self_type: None,
current_self_item: None, current_self_item: None,
last_import_segment: false, last_import_segment: false,
@ -2579,32 +2584,36 @@ impl<'a> Resolver<'a> {
walk_list!(this, visit_param_bound, bounds); walk_list!(this, visit_param_bound, bounds);
for trait_item in trait_items { for trait_item in trait_items {
let generic_params = HasGenericParams(&trait_item.generics, this.with_trait_items(trait_items, |this| {
AssocItemRibKind); let generic_params = HasGenericParams(
this.with_generic_param_rib(generic_params, |this| { &trait_item.generics,
match trait_item.node { AssocItemRibKind,
TraitItemKind::Const(ref ty, ref default) => { );
this.visit_ty(ty); this.with_generic_param_rib(generic_params, |this| {
match trait_item.node {
TraitItemKind::Const(ref ty, ref default) => {
this.visit_ty(ty);
// Only impose the restrictions of // Only impose the restrictions of
// ConstRibKind for an actual constant // ConstRibKind for an actual constant
// expression in a provided default. // expression in a provided default.
if let Some(ref expr) = *default{ if let Some(ref expr) = *default{
this.with_constant_rib(|this| { this.with_constant_rib(|this| {
this.visit_expr(expr); this.visit_expr(expr);
}); });
}
} }
} TraitItemKind::Method(_, _) => {
TraitItemKind::Method(_, _) => { visit::walk_trait_item(this, trait_item)
visit::walk_trait_item(this, trait_item) }
} TraitItemKind::Type(..) => {
TraitItemKind::Type(..) => { visit::walk_trait_item(this, trait_item)
visit::walk_trait_item(this, trait_item) }
} TraitItemKind::Macro(_) => {
TraitItemKind::Macro(_) => { panic!("unexpanded macro in resolve!")
panic!("unexpanded macro in resolve!") }
} };
}; });
}); });
} }
}); });
@ -2774,6 +2783,22 @@ impl<'a> Resolver<'a> {
result result
} }
/// When evaluating a `trait` use its associated types' idents for suggestionsa in E0412.
fn with_trait_items<T, F>(&mut self, trait_items: &Vec<TraitItem>, f: F) -> T
where F: FnOnce(&mut Resolver<'_>) -> T
{
let trait_assoc_types = replace(
&mut self.current_trait_assoc_types,
trait_items.iter().filter_map(|item| match &item.node {
TraitItemKind::Type(bounds, _) if bounds.len() == 0 => Some(item.ident),
_ => None,
}).collect(),
);
let result = f(self);
self.current_trait_assoc_types = trait_assoc_types;
result
}
/// This is called to resolve a trait reference from an `impl` (i.e., `impl Trait for Foo`). /// This is called to resolve a trait reference from an `impl` (i.e., `impl Trait for Foo`).
fn with_optional_trait_ref<T, F>(&mut self, opt_trait_ref: Option<&TraitRef>, f: F) -> T fn with_optional_trait_ref<T, F>(&mut self, opt_trait_ref: Option<&TraitRef>, f: F) -> T
where F: FnOnce(&mut Resolver<'_>, Option<DefId>) -> T where F: FnOnce(&mut Resolver<'_>, Option<DefId>) -> T
@ -3464,8 +3489,12 @@ impl<'a> Resolver<'a> {
} }
fn self_type_is_available(&mut self, span: Span) -> bool { fn self_type_is_available(&mut self, span: Span) -> bool {
let binding = self.resolve_ident_in_lexical_scope(Ident::with_empty_ctxt(kw::SelfUpper), let binding = self.resolve_ident_in_lexical_scope(
TypeNS, None, span); Ident::with_empty_ctxt(kw::SelfUpper),
TypeNS,
None,
span,
);
if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false } if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false }
} }
@ -4095,13 +4124,12 @@ impl<'a> Resolver<'a> {
res res
} }
fn lookup_assoc_candidate<FilterFn>(&mut self, fn lookup_assoc_candidate<FilterFn: Fn(Res) -> bool>(
ident: Ident, &mut self,
ns: Namespace, ident: Ident,
filter_fn: FilterFn) ns: Namespace,
-> Option<AssocSuggestion> filter_fn: FilterFn,
where FilterFn: Fn(Res) -> bool ) -> Option<AssocSuggestion> {
{
fn extract_node_id(t: &Ty) -> Option<NodeId> { fn extract_node_id(t: &Ty) -> Option<NodeId> {
match t.node { match t.node {
TyKind::Path(None, _) => Some(t.id), TyKind::Path(None, _) => Some(t.id),
@ -4133,6 +4161,12 @@ impl<'a> Resolver<'a> {
} }
} }
for assoc_type_ident in &self.current_trait_assoc_types {
if *assoc_type_ident == ident {
return Some(AssocSuggestion::AssocItem);
}
}
// Look for associated items in the current trait. // Look for associated items in the current trait.
if let Some((module, _)) = self.current_trait_ref { if let Some((module, _)) = self.current_trait_ref {
if let Ok(binding) = self.resolve_ident_in_module( if let Ok(binding) = self.resolve_ident_in_module(
@ -4145,6 +4179,7 @@ impl<'a> Resolver<'a> {
) { ) {
let res = binding.res(); let res = binding.res();
if filter_fn(res) { if filter_fn(res) {
debug!("extract_node_id res not filtered");
return Some(if self.has_self.contains(&res.def_id()) { return Some(if self.has_self.contains(&res.def_id()) {
AssocSuggestion::MethodWithSelf AssocSuggestion::MethodWithSelf
} else { } else {

View File

@ -0,0 +1,7 @@
trait A {
type Bla;
fn to_bla(&self) -> Bla;
//~^ ERROR cannot find type `Bla` in this scope
}
fn main() {}

View File

@ -0,0 +1,9 @@
error[E0412]: cannot find type `Bla` in this scope
--> $DIR/assoc-type-in-method-return.rs:3:25
|
LL | fn to_bla(&self) -> Bla;
| ^^^ help: try: `Self::Bla`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0412`.