diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/adt.rs | 70 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 16 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 55 | ||||
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 34 | ||||
-rw-r--r-- | crates/ra_ide_api/src/marks.rs | 6 | ||||
-rw-r--r-- | crates/ra_ide_api/src/navigation_target.rs | 13 |
7 files changed, 150 insertions, 46 deletions
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::{ | |||
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
13 | Name, AsName, Struct, Enum, EnumVariant, Crate, | 13 | Name, AsName, Struct, Enum, EnumVariant, Crate, |
14 | HirDatabase, HirFileId, | 14 | HirDatabase, HirFileId, StructField, FieldSource, |
15 | type_ref::TypeRef, | 15 | type_ref::TypeRef, |
16 | }; | 16 | }; |
17 | 17 | ||
@@ -150,7 +150,7 @@ impl VariantData { | |||
150 | impl VariantData { | 150 | impl VariantData { |
151 | fn new(flavor: StructFlavor) -> Self { | 151 | fn new(flavor: StructFlavor) -> Self { |
152 | let inner = match flavor { | 152 | let inner = match flavor { |
153 | StructFlavor::Tuple(fl) => { | 153 | ast::StructFlavor::Tuple(fl) => { |
154 | let fields = fl | 154 | let fields = fl |
155 | .fields() | 155 | .fields() |
156 | .enumerate() | 156 | .enumerate() |
@@ -161,7 +161,7 @@ impl VariantData { | |||
161 | .collect(); | 161 | .collect(); |
162 | VariantDataInner::Tuple(fields) | 162 | VariantDataInner::Tuple(fields) |
163 | } | 163 | } |
164 | StructFlavor::Named(fl) => { | 164 | ast::StructFlavor::Named(fl) => { |
165 | let fields = fl | 165 | let fields = fl |
166 | .fields() | 166 | .fields() |
167 | .map(|fd| StructFieldData { | 167 | .map(|fd| StructFieldData { |
@@ -171,8 +171,70 @@ impl VariantData { | |||
171 | .collect(); | 171 | .collect(); |
172 | VariantDataInner::Struct(fields) | 172 | VariantDataInner::Struct(fields) |
173 | } | 173 | } |
174 | StructFlavor::Unit => VariantDataInner::Unit, | 174 | ast::StructFlavor::Unit => VariantDataInner::Unit, |
175 | }; | 175 | }; |
176 | VariantData(inner) | 176 | VariantData(inner) |
177 | } | 177 | } |
178 | } | 178 | } |
179 | |||
180 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
181 | pub enum VariantDef { | ||
182 | Struct(Struct), | ||
183 | EnumVariant(EnumVariant), | ||
184 | } | ||
185 | impl_froms!(VariantDef: Struct, EnumVariant); | ||
186 | |||
187 | impl VariantDef { | ||
188 | pub(crate) fn field(self, db: &impl HirDatabase, name: &Name) -> Option<StructField> { | ||
189 | match self { | ||
190 | VariantDef::Struct(it) => it.field(db, name), | ||
191 | VariantDef::EnumVariant(it) => it.field(db, name), | ||
192 | } | ||
193 | } | ||
194 | pub(crate) fn variant_data(self, db: &impl HirDatabase) -> Arc<VariantData> { | ||
195 | match self { | ||
196 | VariantDef::Struct(it) => it.variant_data(db), | ||
197 | VariantDef::EnumVariant(it) => it.variant_data(db), | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | |||
202 | impl StructField { | ||
203 | pub(crate) fn source_impl(&self, db: &impl HirDatabase) -> (HirFileId, FieldSource) { | ||
204 | let var_data = self.parent.variant_data(db); | ||
205 | let fields = var_data.fields().unwrap(); | ||
206 | let ss; | ||
207 | let es; | ||
208 | let (file_id, struct_flavor) = match self.parent { | ||
209 | VariantDef::Struct(s) => { | ||
210 | let (file_id, source) = s.source(db); | ||
211 | ss = source; | ||
212 | (file_id, ss.flavor()) | ||
213 | } | ||
214 | VariantDef::EnumVariant(e) => { | ||
215 | let (file_id, source) = e.source(db); | ||
216 | es = source; | ||
217 | (file_id, es.flavor()) | ||
218 | } | ||
219 | }; | ||
220 | |||
221 | let field_sources = match struct_flavor { | ||
222 | ast::StructFlavor::Tuple(fl) => fl | ||
223 | .fields() | ||
224 | .map(|it| FieldSource::Pos(it.to_owned())) | ||
225 | .collect(), | ||
226 | ast::StructFlavor::Named(fl) => fl | ||
227 | .fields() | ||
228 | .map(|it| FieldSource::Named(it.to_owned())) | ||
229 | .collect(), | ||
230 | ast::StructFlavor::Unit => Vec::new(), | ||
231 | }; | ||
232 | let field = field_sources | ||
233 | .into_iter() | ||
234 | .zip(fields.iter()) | ||
235 | .find(|(_syntax, (id, _))| *id == self.id) | ||
236 | .unwrap() | ||
237 | .0; | ||
238 | (file_id, field) | ||
239 | } | ||
240 | } | ||
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::{ | |||
10 | nameres::{ModuleScope, lower::ImportId}, | 10 | nameres::{ModuleScope, lower::ImportId}, |
11 | db::HirDatabase, | 11 | db::HirDatabase, |
12 | expr::BodySyntaxMapping, | 12 | expr::BodySyntaxMapping, |
13 | ty::{InferenceResult, VariantDef}, | 13 | ty::InferenceResult, |
14 | adt::{EnumVariantId, StructFieldId}, | 14 | adt::{EnumVariantId, StructFieldId, VariantDef}, |
15 | generics::GenericParams, | 15 | generics::GenericParams, |
16 | docs::{Documentation, Docs, docs_from_ast}, | 16 | docs::{Documentation, Docs, docs_from_ast}, |
17 | module_tree::ModuleId, | 17 | module_tree::ModuleId, |
@@ -179,10 +179,16 @@ impl Module { | |||
179 | 179 | ||
180 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 180 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
181 | pub struct StructField { | 181 | pub struct StructField { |
182 | parent: VariantDef, | 182 | pub(crate) parent: VariantDef, |
183 | pub(crate) id: StructFieldId, | 183 | pub(crate) id: StructFieldId, |
184 | } | 184 | } |
185 | 185 | ||
186 | #[derive(Debug)] | ||
187 | pub enum FieldSource { | ||
188 | Named(TreeArc<ast::NamedFieldDef>), | ||
189 | Pos(TreeArc<ast::PosField>), | ||
190 | } | ||
191 | |||
186 | impl StructField { | 192 | impl StructField { |
187 | pub fn name(&self, db: &impl HirDatabase) -> Name { | 193 | pub fn name(&self, db: &impl HirDatabase) -> Name { |
188 | self.parent.variant_data(db).fields().unwrap()[self.id] | 194 | self.parent.variant_data(db).fields().unwrap()[self.id] |
@@ -190,6 +196,10 @@ impl StructField { | |||
190 | .clone() | 196 | .clone() |
191 | } | 197 | } |
192 | 198 | ||
199 | pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, FieldSource) { | ||
200 | self.source_impl(db) | ||
201 | } | ||
202 | |||
193 | pub fn ty(&self, db: &impl HirDatabase) -> Ty { | 203 | pub fn ty(&self, db: &impl HirDatabase) -> Ty { |
194 | db.type_for_field(*self) | 204 | db.type_for_field(*self) |
195 | } | 205 | } |
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::{ | |||
68 | Module, ModuleDef, ModuleSource, Problem, | 68 | Module, ModuleDef, ModuleSource, Problem, |
69 | Struct, Enum, EnumVariant, | 69 | Struct, Enum, EnumVariant, |
70 | Function, FnSignature, ScopeEntryWithSyntax, | 70 | Function, FnSignature, ScopeEntryWithSyntax, |
71 | StructField, | 71 | StructField, FieldSource, |
72 | Static, Const, | 72 | Static, Const, |
73 | Trait, Type, | 73 | Trait, Type, |
74 | }; | 74 | }; |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index c57e222dd..714eaaae5 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -38,7 +38,7 @@ use crate::{ | |||
38 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, | 38 | expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, |
39 | generics::GenericParams, | 39 | generics::GenericParams, |
40 | path::GenericArg, | 40 | path::GenericArg, |
41 | adt::VariantData, | 41 | adt::VariantDef, |
42 | }; | 42 | }; |
43 | 43 | ||
44 | /// The ID of a type variable. | 44 | /// The ID of a type variable. |
@@ -696,28 +696,6 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def: TypableDef) -> Ty { | |||
696 | } | 696 | } |
697 | } | 697 | } |
698 | 698 | ||
699 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
700 | pub enum VariantDef { | ||
701 | Struct(Struct), | ||
702 | EnumVariant(EnumVariant), | ||
703 | } | ||
704 | impl_froms!(VariantDef: Struct, EnumVariant); | ||
705 | |||
706 | impl VariantDef { | ||
707 | pub(crate) fn field(self, db: &impl HirDatabase, name: &Name) -> Option<StructField> { | ||
708 | match self { | ||
709 | VariantDef::Struct(it) => it.field(db, name), | ||
710 | VariantDef::EnumVariant(it) => it.field(db, name), | ||
711 | } | ||
712 | } | ||
713 | pub(crate) fn variant_data(self, db: &impl HirDatabase) -> Arc<VariantData> { | ||
714 | match self { | ||
715 | VariantDef::Struct(it) => it.variant_data(db), | ||
716 | VariantDef::EnumVariant(it) => it.variant_data(db), | ||
717 | } | ||
718 | } | ||
719 | } | ||
720 | |||
721 | pub(super) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty { | 699 | pub(super) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty { |
722 | let parent_def = field.parent_def(db); | 700 | let parent_def = field.parent_def(db); |
723 | let (generics, module) = match parent_def { | 701 | let (generics, module) = match parent_def { |
@@ -732,8 +710,10 @@ pub(super) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty { | |||
732 | /// The result of type inference: A mapping from expressions and patterns to types. | 710 | /// The result of type inference: A mapping from expressions and patterns to types. |
733 | #[derive(Clone, PartialEq, Eq, Debug)] | 711 | #[derive(Clone, PartialEq, Eq, Debug)] |
734 | pub struct InferenceResult { | 712 | pub struct InferenceResult { |
735 | /// For each method call expr, record the function it resolved to. | 713 | /// For each method call expr, records the function it resolves to. |
736 | method_resolutions: FxHashMap<ExprId, Function>, | 714 | method_resolutions: FxHashMap<ExprId, Function>, |
715 | /// For each field access expr, records the field it resolves to. | ||
716 | field_resolutions: FxHashMap<ExprId, StructField>, | ||
737 | type_of_expr: ArenaMap<ExprId, Ty>, | 717 | type_of_expr: ArenaMap<ExprId, Ty>, |
738 | type_of_pat: ArenaMap<PatId, Ty>, | 718 | type_of_pat: ArenaMap<PatId, Ty>, |
739 | } | 719 | } |
@@ -742,6 +722,9 @@ impl InferenceResult { | |||
742 | pub fn method_resolution(&self, expr: ExprId) -> Option<Function> { | 722 | pub fn method_resolution(&self, expr: ExprId) -> Option<Function> { |
743 | self.method_resolutions.get(&expr).map(|it| *it) | 723 | self.method_resolutions.get(&expr).map(|it| *it) |
744 | } | 724 | } |
725 | pub fn field_resolution(&self, expr: ExprId) -> Option<StructField> { | ||
726 | self.field_resolutions.get(&expr).map(|it| *it) | ||
727 | } | ||
745 | } | 728 | } |
746 | 729 | ||
747 | impl Index<ExprId> for InferenceResult { | 730 | impl Index<ExprId> for InferenceResult { |
@@ -770,6 +753,7 @@ struct InferenceContext<'a, D: HirDatabase> { | |||
770 | impl_block: Option<ImplBlock>, | 753 | impl_block: Option<ImplBlock>, |
771 | var_unification_table: InPlaceUnificationTable<TypeVarId>, | 754 | var_unification_table: InPlaceUnificationTable<TypeVarId>, |
772 | method_resolutions: FxHashMap<ExprId, Function>, | 755 | method_resolutions: FxHashMap<ExprId, Function>, |
756 | field_resolutions: FxHashMap<ExprId, StructField>, | ||
773 | type_of_expr: ArenaMap<ExprId, Ty>, | 757 | type_of_expr: ArenaMap<ExprId, Ty>, |
774 | type_of_pat: ArenaMap<PatId, Ty>, | 758 | type_of_pat: ArenaMap<PatId, Ty>, |
775 | /// The return type of the function being inferred. | 759 | /// The return type of the function being inferred. |
@@ -861,6 +845,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
861 | ) -> Self { | 845 | ) -> Self { |
862 | InferenceContext { | 846 | InferenceContext { |
863 | method_resolutions: FxHashMap::default(), | 847 | method_resolutions: FxHashMap::default(), |
848 | field_resolutions: FxHashMap::default(), | ||
864 | type_of_expr: ArenaMap::default(), | 849 | type_of_expr: ArenaMap::default(), |
865 | type_of_pat: ArenaMap::default(), | 850 | type_of_pat: ArenaMap::default(), |
866 | var_unification_table: InPlaceUnificationTable::new(), | 851 | var_unification_table: InPlaceUnificationTable::new(), |
@@ -886,6 +871,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
886 | } | 871 | } |
887 | InferenceResult { | 872 | InferenceResult { |
888 | method_resolutions: mem::replace(&mut self.method_resolutions, Default::default()), | 873 | method_resolutions: mem::replace(&mut self.method_resolutions, Default::default()), |
874 | field_resolutions: mem::replace(&mut self.field_resolutions, Default::default()), | ||
889 | type_of_expr: expr_types, | 875 | type_of_expr: expr_types, |
890 | type_of_pat: pat_types, | 876 | type_of_pat: pat_types, |
891 | } | 877 | } |
@@ -899,6 +885,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
899 | self.method_resolutions.insert(expr, func); | 885 | self.method_resolutions.insert(expr, func); |
900 | } | 886 | } |
901 | 887 | ||
888 | fn write_field_resolution(&mut self, expr: ExprId, field: StructField) { | ||
889 | self.field_resolutions.insert(expr, field); | ||
890 | } | ||
891 | |||
902 | fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { | 892 | fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { |
903 | self.type_of_pat.insert(pat, ty); | 893 | self.type_of_pat.insert(pat, ty); |
904 | } | 894 | } |
@@ -1251,9 +1241,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1251 | ty | 1241 | ty |
1252 | } | 1242 | } |
1253 | 1243 | ||
1254 | fn infer_expr(&mut self, expr: ExprId, expected: &Expectation) -> Ty { | 1244 | fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { |
1255 | let body = Arc::clone(&self.body); // avoid borrow checker problem | 1245 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
1256 | let ty = match &body[expr] { | 1246 | let ty = match &body[tgt_expr] { |
1257 | Expr::Missing => Ty::Unknown, | 1247 | Expr::Missing => Ty::Unknown, |
1258 | Expr::If { | 1248 | Expr::If { |
1259 | condition, | 1249 | condition, |
@@ -1344,7 +1334,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1344 | let resolved = receiver_ty.clone().lookup_method(self.db, method_name); | 1334 | let resolved = receiver_ty.clone().lookup_method(self.db, method_name); |
1345 | let method_ty = match resolved { | 1335 | let method_ty = match resolved { |
1346 | Some(func) => { | 1336 | Some(func) => { |
1347 | self.write_method_resolution(expr, func); | 1337 | self.write_method_resolution(tgt_expr, func); |
1348 | self.db.type_for_def(func.into()) | 1338 | self.db.type_for_def(func.into()) |
1349 | } | 1339 | } |
1350 | None => Ty::Unknown, | 1340 | None => Ty::Unknown, |
@@ -1389,7 +1379,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1389 | 1379 | ||
1390 | expected.ty | 1380 | expected.ty |
1391 | } | 1381 | } |
1392 | Expr::Path(p) => self.infer_path_expr(expr, p).unwrap_or(Ty::Unknown), | 1382 | Expr::Path(p) => self.infer_path_expr(tgt_expr, p).unwrap_or(Ty::Unknown), |
1393 | Expr::Continue => Ty::Never, | 1383 | Expr::Continue => Ty::Never, |
1394 | Expr::Break { expr } => { | 1384 | Expr::Break { expr } => { |
1395 | if let Some(expr) = expr { | 1385 | if let Some(expr) = expr { |
@@ -1436,9 +1426,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1436 | def_id: AdtDef::Struct(s), | 1426 | def_id: AdtDef::Struct(s), |
1437 | ref substs, | 1427 | ref substs, |
1438 | .. | 1428 | .. |
1439 | } => s | 1429 | } => s.field(self.db, name).map(|field| { |
1440 | .field(self.db, name) | 1430 | self.write_field_resolution(tgt_expr, field); |
1441 | .map(|field| field.ty(self.db).subst(substs)), | 1431 | field.ty(self.db).subst(substs) |
1432 | }), | ||
1442 | _ => None, | 1433 | _ => None, |
1443 | }) | 1434 | }) |
1444 | .unwrap_or(Ty::Unknown); | 1435 | .unwrap_or(Ty::Unknown); |
@@ -1545,7 +1536,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1545 | let ty = self.insert_type_vars_shallow(ty); | 1536 | let ty = self.insert_type_vars_shallow(ty); |
1546 | self.unify(&ty, &expected.ty); | 1537 | self.unify(&ty, &expected.ty); |
1547 | let ty = self.resolve_ty_as_possible(ty); | 1538 | let ty = self.resolve_ty_as_possible(ty); |
1548 | self.write_expr_ty(expr, ty.clone()); | 1539 | self.write_expr_ty(tgt_expr, ty.clone()); |
1549 | ty | 1540 | ty |
1550 | } | 1541 | } |
1551 | 1542 | ||
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::{ | |||
3 | AstNode, ast, | 3 | AstNode, ast, |
4 | algo::find_node_at_offset, | 4 | algo::find_node_at_offset, |
5 | }; | 5 | }; |
6 | use test_utils::tested_by; | ||
6 | 7 | ||
7 | use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; | 8 | use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; |
8 | 9 | ||
@@ -60,6 +61,7 @@ pub(crate) fn reference_definition( | |||
60 | .parent() | 61 | .parent() |
61 | .and_then(ast::MethodCallExpr::cast) | 62 | .and_then(ast::MethodCallExpr::cast) |
62 | { | 63 | { |
64 | tested_by!(goto_definition_works_for_methods); | ||
63 | let infer_result = function.infer(db); | 65 | let infer_result = function.infer(db); |
64 | let syntax_mapping = function.body_syntax_mapping(db); | 66 | let syntax_mapping = function.body_syntax_mapping(db); |
65 | let expr = ast::Expr::cast(method_call.syntax()).unwrap(); | 67 | let expr = ast::Expr::cast(method_call.syntax()).unwrap(); |
@@ -70,6 +72,19 @@ pub(crate) fn reference_definition( | |||
70 | return Exact(NavigationTarget::from_function(db, func)); | 72 | return Exact(NavigationTarget::from_function(db, func)); |
71 | }; | 73 | }; |
72 | } | 74 | } |
75 | // It could also be a field access | ||
76 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { | ||
77 | tested_by!(goto_definition_works_for_fields); | ||
78 | let infer_result = function.infer(db); | ||
79 | let syntax_mapping = function.body_syntax_mapping(db); | ||
80 | let expr = ast::Expr::cast(field_expr.syntax()).unwrap(); | ||
81 | if let Some(field) = syntax_mapping | ||
82 | .node_expr(expr) | ||
83 | .and_then(|it| infer_result.field_resolution(it)) | ||
84 | { | ||
85 | return Exact(NavigationTarget::from_field(db, field)); | ||
86 | }; | ||
87 | } | ||
73 | } | 88 | } |
74 | // Then try module name resolution | 89 | // Then try module name resolution |
75 | if let Some(module) = hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax()) | 90 | if let Some(module) = hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax()) |
@@ -117,6 +132,8 @@ fn name_definition( | |||
117 | 132 | ||
118 | #[cfg(test)] | 133 | #[cfg(test)] |
119 | mod tests { | 134 | mod tests { |
135 | use test_utils::covers; | ||
136 | |||
120 | use crate::mock_analysis::analysis_and_position; | 137 | use crate::mock_analysis::analysis_and_position; |
121 | 138 | ||
122 | fn check_goto(fixuture: &str, expected: &str) { | 139 | fn check_goto(fixuture: &str, expected: &str) { |
@@ -183,6 +200,7 @@ mod tests { | |||
183 | 200 | ||
184 | #[test] | 201 | #[test] |
185 | fn goto_definition_works_for_methods() { | 202 | fn goto_definition_works_for_methods() { |
203 | covers!(goto_definition_works_for_methods); | ||
186 | check_goto( | 204 | check_goto( |
187 | " | 205 | " |
188 | //- /lib.rs | 206 | //- /lib.rs |
@@ -197,15 +215,23 @@ mod tests { | |||
197 | ", | 215 | ", |
198 | "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)", | 216 | "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)", |
199 | ); | 217 | ); |
218 | } | ||
200 | 219 | ||
220 | #[test] | ||
221 | fn goto_definition_works_for_fields() { | ||
222 | covers!(goto_definition_works_for_fields); | ||
201 | check_goto( | 223 | check_goto( |
202 | " | 224 | " |
203 | //- /lib.rs | 225 | //- /lib.rs |
204 | mod <|>foo; | 226 | struct Foo { |
205 | //- /foo/mod.rs | 227 | spam: u32, |
206 | // empty | 228 | } |
229 | |||
230 | fn bar(foo: &Foo) { | ||
231 | foo.spam<|>; | ||
232 | } | ||
207 | ", | 233 | ", |
208 | "foo SOURCE_FILE FileId(2) [0; 10)", | 234 | "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)", |
209 | ); | 235 | ); |
210 | } | 236 | } |
211 | } | 237 | } |
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 @@ | |||
1 | test_utils::marks!(inserts_parens_for_function_calls); | 1 | test_utils::marks!( |
2 | inserts_parens_for_function_calls | ||
3 | goto_definition_works_for_methods | ||
4 | goto_definition_works_for_fields | ||
5 | ); | ||
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::{ | |||
3 | SyntaxNode, AstNode, SmolStr, TextRange, ast, | 3 | SyntaxNode, AstNode, SmolStr, TextRange, ast, |
4 | SyntaxKind::{self, NAME}, | 4 | SyntaxKind::{self, NAME}, |
5 | }; | 5 | }; |
6 | use hir::{ModuleSource}; | 6 | use hir::{ModuleSource, FieldSource}; |
7 | 7 | ||
8 | use crate::{FileSymbol, db::RootDatabase}; | 8 | use crate::{FileSymbol, db::RootDatabase}; |
9 | 9 | ||
@@ -101,6 +101,17 @@ impl NavigationTarget { | |||
101 | NavigationTarget::from_named(file_id.original_file(db), &*fn_def) | 101 | NavigationTarget::from_named(file_id.original_file(db), &*fn_def) |
102 | } | 102 | } |
103 | 103 | ||
104 | pub(crate) fn from_field(db: &RootDatabase, field: hir::StructField) -> NavigationTarget { | ||
105 | let (file_id, field) = field.source(db); | ||
106 | let file_id = file_id.original_file(db); | ||
107 | match field { | ||
108 | FieldSource::Named(it) => NavigationTarget::from_named(file_id, &*it), | ||
109 | FieldSource::Pos(it) => { | ||
110 | NavigationTarget::from_syntax(file_id, "".into(), None, it.syntax()) | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
104 | // TODO once Def::Item is gone, this should be able to always return a NavigationTarget | 115 | // TODO once Def::Item is gone, this should be able to always return a NavigationTarget |
105 | pub(crate) fn from_def( | 116 | pub(crate) fn from_def( |
106 | db: &RootDatabase, | 117 | db: &RootDatabase, |