]> git.lizzy.rs Git - rust.git/commitdiff
Goto definition works for `S { a: }` case
authorAleksey Kladov <aleksey.kladov@gmail.com>
Mon, 15 Mar 2021 12:02:48 +0000 (15:02 +0300)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Mon, 15 Mar 2021 12:12:39 +0000 (15:12 +0300)
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
crates/ide/src/goto_definition.rs

index d546512cbdb341069bd33ad9b9650fe669f7274d..055a3e5d0c7c30808d575905edbf20529a4aa5e5 100644 (file)
@@ -24,7 +24,7 @@
 };
 use syntax::{
     ast::{self, AstNode},
-    SyntaxNode, TextRange, TextSize,
+    AstPtr, SyntaxNode, TextRange, TextSize,
 };
 
 use crate::{
@@ -161,8 +161,27 @@ pub(crate) fn resolve_record_field(
         db: &dyn HirDatabase,
         field: &ast::RecordExprField,
     ) -> Option<(Field, Option<Local>)> {
-        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 {
index abed1969e37c524f13f4a4026167fe91ea80d77b..e8f31e4b1be5b751ed02bf53def591f5e1c51e0c 100644 (file)
@@ -1158,6 +1158,17 @@ fn goto_def_for_intra_doc_link_inner() {
 
 //- /m.rs
 //! [`super::S$0`]
+"#,
+        )
+    }
+
+    #[test]
+    fn goto_incomplete_field() {
+        check(
+            r#"
+struct A { a: u32 }
+         //^
+fn foo() { A { a$0: }; }
 "#,
         )
     }