Skip to content

Commit

Permalink
feat(lsp): allow function rename (#4294)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves <!-- Link to GitHub Issue -->

Rename function Symbol with LSP #4192

## Summary\*

Allows to rename function symbol.

## Additional Context



## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[Exceptional Case]** 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.

---------

Co-authored-by: Jake Fecher <jfecher11@gmail.com>
Co-authored-by: jfecher <jake@aztecprotocol.com>
Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com>
Co-authored-by: Tom French <tom@tomfren.ch>
Co-authored-by: Ary Borenszweig <asterite@gmail.com>
  • Loading branch information
6 people authored Jul 2, 2024
1 parent b000598 commit 3d86dc6
Show file tree
Hide file tree
Showing 22 changed files with 607 additions and 55 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions compiler/noirc_frontend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ regex = "1.9.1"
cfg-if = "1.0.0"
tracing.workspace = true
petgraph = "0.6"
rangemap = "1.4.0"
lalrpop-util = { version = "0.20.2", features = ["lexer"] }


Expand Down
19 changes: 15 additions & 4 deletions compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
stmt::HirPattern,
},
macros_api::{HirExpression, Ident, Path, Pattern},
node_interner::{DefinitionId, DefinitionKind, ExprId, GlobalId, TraitImplKind},
node_interner::{DefinitionId, DefinitionKind, DependencyId, ExprId, GlobalId, TraitImplKind},
Shared, StructType, Type, TypeBindings,
};

Expand Down Expand Up @@ -418,10 +418,14 @@ impl<'context> Elaborator<'context> {

if hir_ident.id != DefinitionId::dummy_id() {
match self.interner.definition(hir_ident.id).kind {
DefinitionKind::Function(id) => {
DefinitionKind::Function(func_id) => {
if let Some(current_item) = self.current_item {
self.interner.add_function_dependency(current_item, id);
self.interner.add_function_dependency(current_item, func_id);
}

let variable = DependencyId::Variable(hir_ident.location);
let function = DependencyId::Function(func_id);
self.interner.add_reference(function, variable);
}
DefinitionKind::Global(global_id) => {
if let Some(global) = self.unresolved_globals.remove(&global_id) {
Expand All @@ -430,6 +434,10 @@ impl<'context> Elaborator<'context> {
if let Some(current_item) = self.current_item {
self.interner.add_global_dependency(current_item, global_id);
}

let variable = DependencyId::Variable(hir_ident.location);
let global = DependencyId::Global(global_id);
self.interner.add_reference(global, variable);
}
DefinitionKind::GenericType(_) => {
// Initialize numeric generics to a polymorphic integer type in case
Expand Down Expand Up @@ -575,7 +583,10 @@ impl<'context> Elaborator<'context> {
}

pub fn get_ident_from_path(&mut self, path: Path) -> (HirIdent, usize) {
let location = Location::new(path.span(), self.file);
let location = Location::new(
path.segments.last().expect("ice: path without segments").span(),
self.file,
);

let error = match path.as_ident().map(|ident| self.use_variable(ident)) {
Some(Ok(found)) => return found,
Expand Down
25 changes: 23 additions & 2 deletions compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@ use crate::hir::Context;

use crate::macros_api::{MacroError, MacroProcessor};
use crate::node_interner::{
FuncId, GlobalId, NodeInterner, StructId, TraitId, TraitImplId, TypeAliasId,
DependencyId, FuncId, GlobalId, NodeInterner, StructId, TraitId, TraitImplId, TypeAliasId,
};

use crate::ast::{
ExpressionKind, Ident, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait,
NoirTypeAlias, Path, PathKind, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType,
};

use crate::parser::{ParserError, SortedModule};
use fm::FileId;
use iter_extended::vecmap;
use noirc_errors::{CustomDiagnostic, Span};
use noirc_errors::{CustomDiagnostic, Location, Span};
use rustc_hash::FxHashMap as HashMap;
use std::collections::BTreeMap;

Expand Down Expand Up @@ -328,6 +329,7 @@ impl DefCollector {

// Resolve unresolved imports collected from the crate, one by one.
for collected_import in std::mem::take(&mut def_collector.imports) {
let module_id = collected_import.module_id;
match resolve_import(crate_id, &collected_import, &context.def_maps) {
Ok(resolved_import) => {
if let Some(error) = resolved_import.error {
Expand All @@ -345,6 +347,9 @@ impl DefCollector {
let result = current_def_map.modules[resolved_import.module_scope.0]
.import(name.clone(), ns, resolved_import.is_prelude);

let file_id = current_def_map.file_id(module_id);
add_import_reference(ns, &name, &mut context.def_interner, file_id);

if let Err((first_def, second_def)) = result {
let err = DefCollectorErrorKind::Duplicate {
typ: DuplicateType::Import,
Expand Down Expand Up @@ -468,6 +473,22 @@ impl DefCollector {
}
}

fn add_import_reference(
def_id: crate::macros_api::ModuleDefId,
name: &Ident,
interner: &mut NodeInterner,
file_id: FileId,
) {
if name.span() == Span::empty(0) {
// We ignore empty spans at 0 location, this must be Stdlib
return;
}
if let crate::macros_api::ModuleDefId::FunctionId(func_id) = def_id {
let variable = DependencyId::Variable(Location::new(name.span(), file_id));
interner.add_reference_for(DependencyId::Function(func_id), variable);
}
}

fn inject_prelude(
crate_id: CrateId,
context: &Context,
Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ impl<'a> ModCollector<'a> {
let func_id = context.def_interner.push_empty_fn();
method_ids.insert(name.to_string(), func_id);

let location = Location::new(name.span(), self.file_id);
let modifiers = FunctionModifiers {
name: name.to_string(),
visibility: ItemVisibility::Public,
Expand All @@ -423,9 +424,9 @@ impl<'a> ModCollector<'a> {
is_unconstrained: false,
generic_count: generics.len(),
is_comptime: false,
name_location: location,
};

let location = Location::new(name.span(), self.file_id);
context
.def_interner
.push_function_definition(func_id, modifiers, trait_id.0, location);
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1512,7 +1512,7 @@ impl<'a> Resolver<'a> {
// Otherwise, then it is referring to an Identifier
// This lookup allows support of such statements: let x = foo::bar::SOME_GLOBAL + 10;
// If the expression is a singular indent, we search the resolver's current scope as normal.
let (hir_ident, var_scope_index) = self.get_ident_from_path(path);
let (hir_ident, var_scope_index) = self.get_ident_from_path(path.clone());

if hir_ident.id != DefinitionId::dummy_id() {
match self.interner.definition(hir_ident.id).kind {
Expand Down
1 change: 1 addition & 0 deletions compiler/noirc_frontend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod debug;
pub mod elaborator;
pub mod graph;
pub mod lexer;
pub mod locations;
pub mod monomorphization;
pub mod node_interner;
pub mod parser;
Expand Down
122 changes: 122 additions & 0 deletions compiler/noirc_frontend/src/locations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use fm::FileId;
use noirc_errors::Location;
use rangemap::RangeMap;
use rustc_hash::FxHashMap;

use crate::{macros_api::NodeInterner, node_interner::DependencyId};
use petgraph::prelude::NodeIndex as PetGraphIndex;

#[derive(Debug, Default)]
pub(crate) struct LocationIndices {
map_file_to_range: FxHashMap<FileId, RangeMap<u32, PetGraphIndex>>,
}

impl LocationIndices {
pub(crate) fn add_location(&mut self, location: Location, node_index: PetGraphIndex) {
// Some location spans are empty: maybe they are from ficticious nodes?
if location.span.start() == location.span.end() {
return;
}

let range_map = self.map_file_to_range.entry(location.file).or_default();
range_map.insert(location.span.start()..location.span.end(), node_index);
}

pub(crate) fn get_node_from_location(&self, location: Location) -> Option<PetGraphIndex> {
let range_map = self.map_file_to_range.get(&location.file)?;
Some(*range_map.get(&location.span.start())?)
}
}

impl NodeInterner {
pub fn dependency_location(&self, dependency: DependencyId) -> Location {
match dependency {
DependencyId::Function(id) => self.function_modifiers(&id).name_location,
DependencyId::Struct(id) => self.get_struct(id).borrow().location,
DependencyId::Global(id) => self.get_global(id).location,
DependencyId::Alias(id) => self.get_type_alias(id).borrow().location,
DependencyId::Variable(location) => location,
}
}

pub(crate) fn add_reference(&mut self, referenced: DependencyId, reference: DependencyId) {
let referenced_index = self.get_or_insert_reference(referenced);
let reference_index = self.reference_graph.add_node(reference);

let referenced_location = self.dependency_location(referenced);
let reference_location = self.dependency_location(reference);

self.reference_graph.add_edge(referenced_index, reference_index, ());
self.location_indices.add_location(referenced_location, referenced_index);
self.location_indices.add_location(reference_location, reference_index);
}

pub(crate) fn add_reference_for(
&mut self,
referenced_id: DependencyId,
reference: DependencyId,
) {
let Some(referenced_index) = self.reference_graph_indices.get(&referenced_id) else {
panic!("Compiler Error: Referenced index not found")
};

let reference_location = self.dependency_location(reference);
let reference_index = self.reference_graph.add_node(reference);
self.reference_graph.add_edge(*referenced_index, reference_index, ());
self.location_indices.add_location(reference_location, reference_index);
}

pub(crate) fn add_definition_location(&mut self, referenced: DependencyId) {
let referenced_index = self.get_or_insert_reference(referenced);
let referenced_location = self.dependency_location(referenced);
self.location_indices.add_location(referenced_location, referenced_index);
}

#[tracing::instrument(skip(self), ret)]
pub(crate) fn get_or_insert_reference(&mut self, id: DependencyId) -> PetGraphIndex {
if let Some(index) = self.reference_graph_indices.get(&id) {
return *index;
}

let index = self.reference_graph.add_node(id);
self.reference_graph_indices.insert(id, index);
index
}

pub fn check_rename_possible(&self, location: Location) -> bool {
self.location_indices.get_node_from_location(location).is_some()
}

pub fn find_rename_symbols_at(&self, location: Location) -> Option<Vec<Location>> {
let node_index = self.location_indices.get_node_from_location(location)?;

let reference_node = self.reference_graph[node_index];
let found_locations: Vec<Location> = match reference_node {
DependencyId::Alias(_) | DependencyId::Struct(_) | DependencyId::Global(_) => todo!(),
DependencyId::Function(_) => self.get_edit_locations(node_index),

DependencyId::Variable(_) => {
let referenced_node_index = self
.reference_graph
.neighbors_directed(node_index, petgraph::Direction::Incoming)
.next()?;

self.get_edit_locations(referenced_node_index)
}
};
Some(found_locations)
}

fn get_edit_locations(&self, referenced_node_index: PetGraphIndex) -> Vec<Location> {
let id = self.reference_graph[referenced_node_index];
let mut edit_locations = vec![self.dependency_location(id)];

self.reference_graph
.neighbors_directed(referenced_node_index, petgraph::Direction::Outgoing)
.for_each(|reference_node_index| {
let id = self.reference_graph[reference_node_index];
edit_locations.push(self.dependency_location(id));
});
edit_locations
}
}
Loading

0 comments on commit 3d86dc6

Please sign in to comment.