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.
This commit is contained in:
Aleksey Kladov 2021-03-15 15:02:48 +03:00
parent 5138baf2ac
commit af2366acdf
2 changed files with 33 additions and 3 deletions

View File

@ -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<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 {

View File

@ -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: }; }
"#,
)
}