From c648884397bfdb779c447fa31964dc1fce94bd95 Mon Sep 17 00:00:00 2001 From: Zac Pullar-Strecker Date: Thu, 3 Sep 2020 19:55:24 +1200 Subject: Differentiate method/tymethod by determining 'defaultness' Currently a method only has defaultness if it is a provided trait method, but this will change when specialisation is available and may need to become a concept known to hir. I opted to go for a 'fewest changes' approach given specialisation is still under development. --- crates/hir/src/code_model.rs | 9 ++++++++- crates/hir/src/lib.rs | 4 ++-- crates/hir_def/src/data.rs | 2 ++ crates/hir_def/src/item_tree.rs | 1 + crates/hir_def/src/item_tree/lower.rs | 3 +++ crates/ide/src/doc_links.rs | 19 ++++++++++++++----- 6 files changed, 30 insertions(+), 8 deletions(-) (limited to 'crates') diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 1dd6d73f3..0b24f247c 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs @@ -772,7 +772,14 @@ impl Function { hir_ty::diagnostics::validate_body(db, self.id.into(), sink) } - pub fn parent_def(self, db: &dyn HirDatabase) -> Option { + /// Whether this function declaration has a definition. + /// + /// This is false in the case of required (not provided) trait methods. + pub fn has_body(self, db: &dyn HirDatabase) -> bool { + db.function_data(self.id).has_body + } + + pub fn method_owner(self, db: &dyn HirDatabase) -> Option { match self.as_assoc_item(db).map(|assoc| assoc.container(db)) { Some(AssocItemContainer::Trait(t)) => Some(t.into()), Some(AssocItemContainer::ImplDef(imp)) => { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 4094a76cb..687abe6ca 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -35,8 +35,8 @@ pub use crate::{ code_model::{ Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const, Crate, CrateDependency, DefWithBody, Enum, EnumVariant, Field, FieldSource, Function, - GenericDef, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, - Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, + GenericDef, HasVisibility, ImplDef, Local, MacroDef, MethodOwner, Module, ModuleDef, + ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, }, has_source::HasSource, semantics::{original_range, PathResolution, Semantics, SemanticsScope}, diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index 6190906da..ff1ef0df6 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs @@ -25,6 +25,7 @@ pub struct FunctionData { /// True if the first param is `self`. This is relevant to decide whether this /// can be called as a method. pub has_self_param: bool, + pub has_body: bool, pub is_unsafe: bool, pub is_varargs: bool, pub visibility: RawVisibility, @@ -42,6 +43,7 @@ impl FunctionData { ret_type: func.ret_type.clone(), attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(), has_self_param: func.has_self_param, + has_body: func.has_body, is_unsafe: func.is_unsafe, is_varargs: func.is_varargs, visibility: item_tree[func.visibility].clone(), diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 0fd91b9d0..8a1121bbd 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -505,6 +505,7 @@ pub struct Function { pub visibility: RawVisibilityId, pub generic_params: GenericParamsId, pub has_self_param: bool, + pub has_body: bool, pub is_unsafe: bool, pub params: Box<[TypeRef]>, pub is_varargs: bool, diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 54814f141..3328639cf 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -330,12 +330,15 @@ impl Ctx { ret_type }; + let has_body = func.body().is_some(); + let ast_id = self.source_ast_id_map.ast_id(func); let mut res = Function { name, visibility, generic_params: GenericParamsId::EMPTY, has_self_param, + has_body, is_unsafe: func.unsafe_token().is_some(), params: params.into_boxed_slice(), is_varargs, diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 512c42c4d..2f6c59c40 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -13,7 +13,7 @@ use ide_db::{defs::Definition, RootDatabase}; use hir::{ db::{DefDatabase, HirDatabase}, - Adt, AsName, AssocItem, Crate, Field, HasAttrs, ItemInNs, ModuleDef, + Adt, AsName, AssocItem, Crate, Field, HasAttrs, ItemInNs, MethodOwner, ModuleDef, }; use ide_db::{ defs::{classify_name, classify_name_ref, Definition}, @@ -117,7 +117,7 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option { let target_def: ModuleDef = match definition { Definition::ModuleDef(moddef) => match moddef { ModuleDef::Function(f) => { - f.parent_def(db).map(|mowner| mowner.into()).unwrap_or_else(|| f.clone().into()) + f.method_owner(db).map(|mowner| mowner.into()).unwrap_or_else(|| f.clone().into()) } moddef => moddef, }, @@ -401,9 +401,18 @@ fn get_symbol_fragment(db: &dyn HirDatabase, field_or_assoc: &FieldOrAssocItem) Some(match field_or_assoc { FieldOrAssocItem::Field(field) => format!("#structfield.{}", field.name(db)), FieldOrAssocItem::AssocItem(assoc) => match assoc { - // TODO: Rustdoc sometimes uses tymethod instead of method. This case needs to be investigated. - AssocItem::Function(function) => format!("#method.{}", function.name(db)), - // TODO: This might be the old method for documenting associated constants, i32::MAX uses a separate page... + AssocItem::Function(function) => { + let is_trait_method = + matches!(function.method_owner(db), Some(MethodOwner::Trait(..))); + // This distinction may get more complicated when specialisation is available. + // In particular this decision is made based on whether a method 'has defaultness'. + // Currently this is only the case for provided trait methods. + if is_trait_method && !function.has_body(db) { + format!("#tymethod.{}", function.name(db)) + } else { + format!("#method.{}", function.name(db)) + } + } AssocItem::Const(constant) => format!("#associatedconstant.{}", constant.name(db)?), AssocItem::TypeAlias(ty) => format!("#associatedtype.{}", ty.name(db)), }, -- cgit v1.2.3