From 9f2574c97e55e2af1d1b93f60307aa9d41f55f42 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 25 Jan 2019 20:32:34 +0300 Subject: add ability to get strcut field source --- crates/ra_hir/src/adt.rs | 70 ++++++++++++++++++++++++++++-- crates/ra_hir/src/code_model_api.rs | 16 +++++-- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/ty.rs | 27 ++---------- crates/ra_ide_api/src/goto_definition.rs | 34 +++++++++++++-- crates/ra_ide_api/src/marks.rs | 6 ++- crates/ra_ide_api/src/navigation_target.rs | 13 +++++- 7 files changed, 131 insertions(+), 37 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index ec6a10353..22bbad964 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -11,7 +11,7 @@ use ra_syntax::{ use crate::{ Name, AsName, Struct, Enum, EnumVariant, Crate, - HirDatabase, HirFileId, + HirDatabase, HirFileId, StructField, FieldSource, type_ref::TypeRef, }; @@ -150,7 +150,7 @@ impl VariantData { impl VariantData { fn new(flavor: StructFlavor) -> Self { let inner = match flavor { - StructFlavor::Tuple(fl) => { + ast::StructFlavor::Tuple(fl) => { let fields = fl .fields() .enumerate() @@ -161,7 +161,7 @@ impl VariantData { .collect(); VariantDataInner::Tuple(fields) } - StructFlavor::Named(fl) => { + ast::StructFlavor::Named(fl) => { let fields = fl .fields() .map(|fd| StructFieldData { @@ -171,8 +171,70 @@ impl VariantData { .collect(); VariantDataInner::Struct(fields) } - StructFlavor::Unit => VariantDataInner::Unit, + ast::StructFlavor::Unit => VariantDataInner::Unit, }; VariantData(inner) } } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum VariantDef { + Struct(Struct), + EnumVariant(EnumVariant), +} +impl_froms!(VariantDef: Struct, EnumVariant); + +impl VariantDef { + pub(crate) fn field(self, db: &impl HirDatabase, name: &Name) -> Option { + match self { + VariantDef::Struct(it) => it.field(db, name), + VariantDef::EnumVariant(it) => it.field(db, name), + } + } + pub(crate) fn variant_data(self, db: &impl HirDatabase) -> Arc { + match self { + VariantDef::Struct(it) => it.variant_data(db), + VariantDef::EnumVariant(it) => it.variant_data(db), + } + } +} + +impl StructField { + pub(crate) fn source_impl(&self, db: &impl HirDatabase) -> (HirFileId, FieldSource) { + let var_data = self.parent.variant_data(db); + let fields = var_data.fields().unwrap(); + let ss; + let es; + let (file_id, struct_flavor) = match self.parent { + VariantDef::Struct(s) => { + let (file_id, source) = s.source(db); + ss = source; + (file_id, ss.flavor()) + } + VariantDef::EnumVariant(e) => { + let (file_id, source) = e.source(db); + es = source; + (file_id, es.flavor()) + } + }; + + let field_sources = match struct_flavor { + ast::StructFlavor::Tuple(fl) => fl + .fields() + .map(|it| FieldSource::Pos(it.to_owned())) + .collect(), + ast::StructFlavor::Named(fl) => fl + .fields() + .map(|it| FieldSource::Named(it.to_owned())) + .collect(), + ast::StructFlavor::Unit => Vec::new(), + }; + let field = field_sources + .into_iter() + .zip(fields.iter()) + .find(|(_syntax, (id, _))| *id == self.id) + .unwrap() + .0; + (file_id, field) + } +} diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 118562984..fdea5be57 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -10,8 +10,8 @@ use crate::{ nameres::{ModuleScope, lower::ImportId}, db::HirDatabase, expr::BodySyntaxMapping, - ty::{InferenceResult, VariantDef}, - adt::{EnumVariantId, StructFieldId}, + ty::InferenceResult, + adt::{EnumVariantId, StructFieldId, VariantDef}, generics::GenericParams, docs::{Documentation, Docs, docs_from_ast}, module_tree::ModuleId, @@ -179,10 +179,16 @@ impl Module { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct StructField { - parent: VariantDef, + pub(crate) parent: VariantDef, pub(crate) id: StructFieldId, } +#[derive(Debug)] +pub enum FieldSource { + Named(TreeArc), + Pos(TreeArc), +} + impl StructField { pub fn name(&self, db: &impl HirDatabase) -> Name { self.parent.variant_data(db).fields().unwrap()[self.id] @@ -190,6 +196,10 @@ impl StructField { .clone() } + pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, FieldSource) { + self.source_impl(db) + } + pub fn ty(&self, db: &impl HirDatabase) -> Ty { db.type_for_field(*self) } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 596f9c38c..eaf8565ee 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -68,7 +68,7 @@ pub use self::code_model_api::{ Module, ModuleDef, ModuleSource, Problem, Struct, Enum, EnumVariant, Function, FnSignature, ScopeEntryWithSyntax, - StructField, + StructField, FieldSource, Static, Const, Trait, Type, }; diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 97a876da8..714eaaae5 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -38,7 +38,7 @@ use crate::{ expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, generics::GenericParams, path::GenericArg, - adt::VariantData, + adt::VariantDef, }; /// The ID of a type variable. @@ -696,28 +696,6 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def: TypableDef) -> Ty { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum VariantDef { - Struct(Struct), - EnumVariant(EnumVariant), -} -impl_froms!(VariantDef: Struct, EnumVariant); - -impl VariantDef { - pub(crate) fn field(self, db: &impl HirDatabase, name: &Name) -> Option { - match self { - VariantDef::Struct(it) => it.field(db, name), - VariantDef::EnumVariant(it) => it.field(db, name), - } - } - pub(crate) fn variant_data(self, db: &impl HirDatabase) -> Arc { - match self { - VariantDef::Struct(it) => it.variant_data(db), - VariantDef::EnumVariant(it) => it.variant_data(db), - } - } -} - pub(super) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty { let parent_def = field.parent_def(db); let (generics, module) = match parent_def { @@ -744,6 +722,9 @@ impl InferenceResult { pub fn method_resolution(&self, expr: ExprId) -> Option { self.method_resolutions.get(&expr).map(|it| *it) } + pub fn field_resolution(&self, expr: ExprId) -> Option { + self.field_resolutions.get(&expr).map(|it| *it) + } } impl Index for InferenceResult { diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 46bdde00d..45b4c56ef 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -3,6 +3,7 @@ use ra_syntax::{ AstNode, ast, algo::find_node_at_offset, }; +use test_utils::tested_by; use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; @@ -60,6 +61,7 @@ pub(crate) fn reference_definition( .parent() .and_then(ast::MethodCallExpr::cast) { + tested_by!(goto_definition_works_for_methods); let infer_result = function.infer(db); let syntax_mapping = function.body_syntax_mapping(db); let expr = ast::Expr::cast(method_call.syntax()).unwrap(); @@ -70,6 +72,19 @@ pub(crate) fn reference_definition( return Exact(NavigationTarget::from_function(db, func)); }; } + // It could also be a field access + if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { + tested_by!(goto_definition_works_for_fields); + let infer_result = function.infer(db); + let syntax_mapping = function.body_syntax_mapping(db); + let expr = ast::Expr::cast(field_expr.syntax()).unwrap(); + if let Some(field) = syntax_mapping + .node_expr(expr) + .and_then(|it| infer_result.field_resolution(it)) + { + return Exact(NavigationTarget::from_field(db, field)); + }; + } } // Then try module name resolution if let Some(module) = hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax()) @@ -117,6 +132,8 @@ fn name_definition( #[cfg(test)] mod tests { + use test_utils::covers; + use crate::mock_analysis::analysis_and_position; fn check_goto(fixuture: &str, expected: &str) { @@ -183,6 +200,7 @@ mod tests { #[test] fn goto_definition_works_for_methods() { + covers!(goto_definition_works_for_methods); check_goto( " //- /lib.rs @@ -197,15 +215,23 @@ mod tests { ", "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)", ); + } + #[test] + fn goto_definition_works_for_fields() { + covers!(goto_definition_works_for_fields); check_goto( " //- /lib.rs - mod <|>foo; - //- /foo/mod.rs - // empty + struct Foo { + spam: u32, + } + + fn bar(foo: &Foo) { + foo.spam<|>; + } ", - "foo SOURCE_FILE FileId(2) [0; 10)", + "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)", ); } } diff --git a/crates/ra_ide_api/src/marks.rs b/crates/ra_ide_api/src/marks.rs index dc5b2702a..e33bf6c91 100644 --- a/crates/ra_ide_api/src/marks.rs +++ b/crates/ra_ide_api/src/marks.rs @@ -1 +1,5 @@ -test_utils::marks!(inserts_parens_for_function_calls); +test_utils::marks!( + inserts_parens_for_function_calls + goto_definition_works_for_methods + goto_definition_works_for_fields +); diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs index c5be8e01b..ae2175dbc 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/navigation_target.rs @@ -3,7 +3,7 @@ use ra_syntax::{ SyntaxNode, AstNode, SmolStr, TextRange, ast, SyntaxKind::{self, NAME}, }; -use hir::{ModuleSource}; +use hir::{ModuleSource, FieldSource}; use crate::{FileSymbol, db::RootDatabase}; @@ -101,6 +101,17 @@ impl NavigationTarget { NavigationTarget::from_named(file_id.original_file(db), &*fn_def) } + pub(crate) fn from_field(db: &RootDatabase, field: hir::StructField) -> NavigationTarget { + let (file_id, field) = field.source(db); + let file_id = file_id.original_file(db); + match field { + FieldSource::Named(it) => NavigationTarget::from_named(file_id, &*it), + FieldSource::Pos(it) => { + NavigationTarget::from_syntax(file_id, "".into(), None, it.syntax()) + } + } + } + // TODO once Def::Item is gone, this should be able to always return a NavigationTarget pub(crate) fn from_def( db: &RootDatabase, -- cgit v1.2.3