From af2366acdf1321702e54e01c88052ed5a674716c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 15 Mar 2021 15:02:48 +0300 Subject: [PATCH] Goto definition works for `S { a: }` case What happens here is that we lower `: ` to a missing expression, and then correctly record that the corresponding field expression resolves to a specific field. Where we fail is in the mapping of syntax to this missing expression. Doing it via `ast_field.expr()` fails, as that expression is `None`. Instead, we go in the opposite direcition and ask each lowered field about its source. This works, but has wrong complexity `O(N)` and, really, the implementation is just too complex. We need some better management of data here. --- crates/hir/src/source_analyzer.rs | 25 ++++++++++++++++++++++--- crates/ide/src/goto_definition.rs | 11 +++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index d546512cbdb..055a3e5d0c7 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -24,7 +24,7 @@ use hir_ty::{ }; use syntax::{ ast::{self, AstNode}, - SyntaxNode, TextRange, TextSize, + AstPtr, SyntaxNode, TextRange, TextSize, }; use crate::{ @@ -161,8 +161,27 @@ impl SourceAnalyzer { db: &dyn HirDatabase, field: &ast::RecordExprField, ) -> Option<(Field, Option)> { - let expr = field.expr()?; - let expr_id = self.expr_id(db, &expr)?; + let expr_id = { + let record_lit = field.parent_record_lit(); + let record_lit_expr = self.expr_id(db, &ast::Expr::from(record_lit))?; + let body = self.body.as_ref()?; + let body_source_map = self.body_source_map.as_ref()?; + match &body[record_lit_expr] { + hir_def::expr::Expr::RecordLit { fields, .. } => { + let field_ptr = InFile::new(self.file_id, AstPtr::new(field)); + fields.iter().enumerate().find_map(|(i, f)| { + let ptr = body_source_map.field_syntax(record_lit_expr, i); + if ptr == field_ptr { + Some(f.expr) + } else { + None + } + })? + } + _ => return None, + } + }; + let local = if field.name_ref().is_some() { None } else { diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index abed1969e37..e8f31e4b1be 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1158,6 +1158,17 @@ struct S; //- /m.rs //! [`super::S$0`] +"#, + ) + } + + #[test] + fn goto_incomplete_field() { + check( + r#" +struct A { a: u32 } + //^ +fn foo() { A { a$0: }; } "#, ) }