Skip to content

Commit

Permalink
feat: lsp rename/find-all-references for struct members (#5443)
Browse files Browse the repository at this point in the history
# Description

## Problem

Resolves #5459

## Summary


https://github.com/noir-lang/noir/assets/209371/0ddff997-f511-4eb6-8801-eba0741b1efd

## Additional Context

None.

## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
asterite authored Jul 10, 2024
1 parent c47242a commit a6d213d
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 4 deletions.
20 changes: 17 additions & 3 deletions compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,13 @@ impl<'context> Elaborator<'context> {
let mut unseen_fields = struct_type.borrow().field_names();

for (field_name, field) in fields {
let expected_type = field_types.iter().find(|(name, _)| name == &field_name.0.contents);
let expected_type = expected_type.map(|(_, typ)| typ).unwrap_or(&Type::Error);
let expected_field_with_index = field_types
.iter()
.enumerate()
.find(|(_, (name, _))| name == &field_name.0.contents);
let expected_index = expected_field_with_index.map(|(index, _)| index);
let expected_type =
expected_field_with_index.map(|(_, (_, typ))| typ).unwrap_or(&Type::Error);

let field_span = field.span;
let (resolved, field_type) = self.elaborate_expression(field);
Expand All @@ -468,6 +473,14 @@ impl<'context> Elaborator<'context> {
});
}

if let Some(expected_index) = expected_index {
let struct_id = struct_type.borrow().id;
let referenced = ReferenceId::StructMember(struct_id, expected_index);
let reference =
ReferenceId::Reference(Location::new(field_name.span(), self.file), false);
self.interner.add_reference(referenced, reference);
}

ret.push((field_name, resolved));
}

Expand All @@ -489,10 +502,11 @@ impl<'context> Elaborator<'context> {
) -> (ExprId, Type) {
let (lhs, lhs_type) = self.elaborate_expression(access.lhs);
let rhs = access.rhs;
let rhs_span = rhs.span();
// `is_offset` is only used when lhs is a reference and we want to return a reference to rhs
let access = HirMemberAccess { lhs, rhs, is_offset: false };
let expr_id = self.intern_expr(HirExpression::MemberAccess(access.clone()), span);
let typ = self.type_check_member_access(access, expr_id, lhs_type, span);
let typ = self.type_check_member_access(access, expr_id, lhs_type, rhs_span);
self.interner.push_expr_type(expr_id, typ.clone());
(expr_id, typ)
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,7 @@ impl<'context> Elaborator<'context> {
let span = typ.struct_def.span;

let fields = self.resolve_struct_fields(typ.struct_def, type_id);
let fields_len = fields.len();
self.interner.update_struct(type_id, |struct_def| {
struct_def.set_fields(fields);

Expand All @@ -1217,6 +1218,11 @@ impl<'context> Elaborator<'context> {
}
});

for field_index in 0..fields_len {
self.interner
.add_definition_location(ReferenceId::StructMember(type_id, field_index));
}

self.run_comptime_attributes_on_struct(attributes, type_id, span, &mut generated_items);
}

Expand Down
10 changes: 9 additions & 1 deletion compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,18 @@ impl<'context> Elaborator<'context> {
new_definitions,
);

let referenced = ReferenceId::Struct(struct_type.borrow().id);
let struct_id = struct_type.borrow().id;

let referenced = ReferenceId::Struct(struct_id);
let reference = ReferenceId::Reference(Location::new(name_span, self.file), is_self_type);
self.interner.add_reference(referenced, reference);

for (field_index, field) in fields.iter().enumerate() {
let referenced = ReferenceId::StructMember(struct_id, field_index);
let reference = ReferenceId::Reference(Location::new(field.0.span(), self.file), false);
self.interner.add_reference(referenced, reference);
}

HirPattern::Struct(expected_type, fields, location)
}

Expand Down
4 changes: 4 additions & 0 deletions compiler/noirc_frontend/src/elaborator/statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@ impl<'context> Elaborator<'context> {
Type::Struct(s, args) => {
let s = s.borrow();
if let Some((field, index)) = s.get_field(field_name, args) {
let referenced = ReferenceId::StructMember(s.id, index);
let reference = ReferenceId::Reference(Location::new(span, self.file), false);
self.interner.add_reference(referenced, reference);

return Some((field, index));
}
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/noirc_frontend/src/hir_def/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,11 @@ impl StructType {
vecmap(&self.fields, |(name, typ)| (name.0.contents.clone(), typ.clone()))
}

/// Returns the field at the given index. Panics if no field exists at the given index.
pub fn field_at(&self, index: usize) -> &(Ident, Type) {
&self.fields[index]
}

pub fn field_names(&self) -> BTreeSet<Ident> {
self.fields.iter().map(|(name, _)| name.clone()).collect()
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/noirc_frontend/src/locations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ impl NodeInterner {
let struct_type = struct_type.borrow();
Location::new(struct_type.name.span(), struct_type.location.file)
}
ReferenceId::StructMember(id, field_index) => {
let struct_type = self.get_struct(id);
let struct_type = struct_type.borrow();
Location::new(struct_type.field_at(field_index).0.span(), struct_type.location.file)
}
ReferenceId::Trait(id) => {
let trait_type = self.get_trait(id);
Location::new(trait_type.name.span(), trait_type.location.file)
Expand Down
1 change: 1 addition & 0 deletions compiler/noirc_frontend/src/node_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ pub enum DependencyId {
pub enum ReferenceId {
Module(ModuleId),
Struct(StructId),
StructMember(StructId, usize),
Trait(TraitId),
Global(GlobalId),
Function(FuncId),
Expand Down
5 changes: 5 additions & 0 deletions tooling/lsp/src/requests/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,9 @@ mod rename_tests {
async fn test_rename_local_variable() {
check_rename_succeeds("local_variable", "some_var").await;
}

#[test]
async fn test_rename_struct_member() {
check_rename_succeeds("struct_member", "some_member").await;
}
}
6 changes: 6 additions & 0 deletions tooling/lsp/test_programs/struct_member/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "struct_member"
type = "bin"
authors = [""]

[dependencies]
12 changes: 12 additions & 0 deletions tooling/lsp/test_programs/struct_member/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
struct Foo {
some_member: Field
}

fn main() {
let mut foo = Foo { some_member: 1 };
foo.some_member = 2;
let _ = foo.some_member;

let Foo { some_member } = foo;
let Foo { some_member: some_var } = foo;
}

0 comments on commit a6d213d

Please sign in to comment.