From 2ae17f2177380244f695575c169cc591496cf3ad Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:24:44 -0400 Subject: [PATCH] feat: Sync from noir (#7308) Automated pull of development from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec. BEGIN_COMMIT_OVERRIDE feat: build releases for `aarch64-unknown-linux-gnu` target (https://github.com/noir-lang/noir/pull/5289) fix: go to definition from `use` statement (https://github.com/noir-lang/noir/pull/5390) feat: lsp rename struct (https://github.com/noir-lang/noir/pull/5380) END_COMMIT_OVERRIDE --------- Co-authored-by: TomAFrench --- .noir-sync-commit | 2 +- .../.github/workflows/publish-nargo.yml | 2 +- .../src/elaborator/expressions.rs | 7 +- .../noirc_frontend/src/elaborator/patterns.rs | 11 +- .../noirc_frontend/src/elaborator/types.rs | 11 +- .../src/hir/def_collector/dc_crate.rs | 14 +- .../src/hir/def_collector/dc_mod.rs | 5 + .../compiler/noirc_frontend/src/hir/mod.rs | 5 + .../compiler/noirc_frontend/src/locations.rs | 76 ++++--- .../noirc_frontend/src/node_interner.rs | 34 ++- .../noirc_frontend/src/resolve_locations.rs | 16 +- noir/noir-repo/tooling/lsp/src/lib.rs | 12 + .../tooling/lsp/src/notifications/mod.rs | 4 +- .../lsp/src/requests/goto_declaration.rs | 47 +--- .../lsp/src/requests/goto_definition.rs | 129 +++++------ .../noir-repo/tooling/lsp/src/requests/mod.rs | 58 ++++- .../tooling/lsp/src/requests/rename.rs | 207 +++++------------- .../tooling/lsp/src/requests/test_run.rs | 3 +- .../tooling/lsp/src/requests/tests.rs | 4 +- noir/noir-repo/tooling/lsp/src/test_utils.rs | 24 +- .../go_to_definition/src/main.nr | 12 +- .../test_programs/rename_function/Nargo.toml | 6 + .../test_programs/rename_function/src/main.nr | 21 ++ .../rename_function_use/Nargo.toml | 6 + .../rename_function_use/src/main.nr | 12 + .../rename_qualified_function/Nargo.toml | 6 + .../rename_qualified_function/src/main.nr | 9 + .../test_programs/rename_struct/Nargo.toml | 6 + .../test_programs/rename_struct/src/main.nr | 18 ++ 29 files changed, 432 insertions(+), 335 deletions(-) create mode 100644 noir/noir-repo/tooling/lsp/test_programs/rename_function/Nargo.toml create mode 100644 noir/noir-repo/tooling/lsp/test_programs/rename_function/src/main.nr create mode 100644 noir/noir-repo/tooling/lsp/test_programs/rename_function_use/Nargo.toml create mode 100644 noir/noir-repo/tooling/lsp/test_programs/rename_function_use/src/main.nr create mode 100644 noir/noir-repo/tooling/lsp/test_programs/rename_qualified_function/Nargo.toml create mode 100644 noir/noir-repo/tooling/lsp/test_programs/rename_qualified_function/src/main.nr create mode 100644 noir/noir-repo/tooling/lsp/test_programs/rename_struct/Nargo.toml create mode 100644 noir/noir-repo/tooling/lsp/test_programs/rename_struct/src/main.nr diff --git a/.noir-sync-commit b/.noir-sync-commit index c288f5b3cd9..9e595d82423 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -32029f91f6aae4d2f6b08b4ea40481f5837e50bc +f35614a43cf8c5cfb244d9f6ffc9d63282a63e6d diff --git a/noir/noir-repo/.github/workflows/publish-nargo.yml b/noir/noir-repo/.github/workflows/publish-nargo.yml index 26a4c701c5b..55ba754e6ae 100644 --- a/noir/noir-repo/.github/workflows/publish-nargo.yml +++ b/noir/noir-repo/.github/workflows/publish-nargo.yml @@ -110,7 +110,7 @@ jobs: strategy: fail-fast: false matrix: - target: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl] + target: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl, aarch64-unknown-linux-gnu] timeout-minutes: 30 steps: diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs index 9c72529e11a..791cb8913d6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -27,7 +27,7 @@ use crate::{ HirLiteral, HirStatement, Ident, IndexExpression, Literal, MemberAccessExpression, MethodCallExpression, PrefixExpression, }, - node_interner::{DefinitionKind, ExprId, FuncId}, + node_interner::{DefinitionKind, DependencyId, ExprId, FuncId}, token::Tokens, Kind, QuotedType, Shared, StructType, Type, }; @@ -431,6 +431,11 @@ impl<'context> Elaborator<'context> { r#type, struct_generics, }); + + let referenced = DependencyId::Struct(struct_type.borrow().id); + let reference = DependencyId::Variable(Location::new(span, self.file)); + self.interner.add_reference(referenced, reference); + (expr, Type::Struct(struct_type, generics)) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs index 8a2f305d8f6..94f03fb511b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -157,6 +157,8 @@ impl<'context> Elaborator<'context> { mutable: Option, new_definitions: &mut Vec, ) -> HirPattern { + let name_span = name.last_segment().span(); + let error_identifier = |this: &mut Self| { // Must create a name here to return a HirPattern::Identifier. Allowing // shadowing here lets us avoid further errors if we define ERROR_IDENT @@ -196,6 +198,10 @@ impl<'context> Elaborator<'context> { new_definitions, ); + let referenced = DependencyId::Struct(struct_type.borrow().id); + let reference = DependencyId::Variable(Location::new(name_span, self.file)); + self.interner.add_reference(referenced, reference); + HirPattern::Struct(expected_type, fields, location) } @@ -584,10 +590,7 @@ impl<'context> Elaborator<'context> { } pub fn get_ident_from_path(&mut self, path: Path) -> (HirIdent, usize) { - let location = Location::new( - path.segments.last().expect("ice: path without segments").span(), - self.file, - ); + let location = Location::new(path.last_segment().span(), self.file); let error = match path.as_ident().map(|ident| self.use_variable(ident)) { Some(Ok(found)) => return found, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs index 7f07e2a9538..924950bd0b6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -30,7 +30,9 @@ use crate::{ HirExpression, HirLiteral, HirStatement, Path, PathKind, SecondaryAttribute, Signedness, UnaryOp, UnresolvedType, UnresolvedTypeData, }, - node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, + node_interner::{ + DefinitionKind, DependencyId, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId, + }, Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeVariable, TypeVariableKind, }; @@ -242,6 +244,8 @@ impl<'context> Elaborator<'context> { return Type::Alias(alias, args); } + let last_segment = path.last_segment(); + match self.lookup_struct_or_error(path) { Some(struct_type) => { if self.resolving_ids.contains(&struct_type.borrow().id) { @@ -279,6 +283,11 @@ impl<'context> Elaborator<'context> { self.interner.add_type_dependency(current_item, dependency_id); } + let referenced = DependencyId::Struct(struct_type.borrow().id); + let reference = + DependencyId::Variable(Location::new(last_segment.span(), self.file)); + self.interner.add_reference(referenced, reference); + Type::Struct(struct_type, args) } None => Type::Error, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 6858e10a175..5f860e971c9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -483,9 +483,17 @@ fn add_import_reference( // 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); + + match def_id { + crate::macros_api::ModuleDefId::FunctionId(func_id) => { + let variable = DependencyId::Variable(Location::new(name.span(), file_id)); + interner.add_reference(DependencyId::Function(func_id), variable); + } + crate::macros_api::ModuleDefId::TypeId(struct_id) => { + let variable = DependencyId::Variable(Location::new(name.span(), file_id)); + interner.add_reference(DependencyId::Struct(struct_id), variable); + } + _ => (), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index aebc649b7b2..935a891170c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -14,6 +14,7 @@ use crate::ast::{ TypeImpl, }; use crate::macros_api::NodeInterner; +use crate::node_interner::DependencyId; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, @@ -267,6 +268,7 @@ impl<'a> ModCollector<'a> { let mut definition_errors = vec![]; for struct_definition in types { let name = struct_definition.name.clone(); + let name_location = Location::new(name.span(), self.file_id); let unresolved = UnresolvedStruct { file_id: self.file_id, @@ -310,6 +312,9 @@ impl<'a> ModCollector<'a> { // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); + + context.def_interner.add_struct_location(id, name_location); + context.def_interner.add_definition_location(DependencyId::Struct(id)); } definition_errors } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs index 71fdc6b30d2..87c4133d68e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs @@ -290,4 +290,9 @@ impl Context<'_, '_> { ResolvedGeneric { name, type_var, kind, span } }) } + + // Enables reference tracking (useful for tools like LSP). + pub fn track_references(&mut self) { + self.def_interner.track_references = true; + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index dd6a3412a40..88f3424906f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -32,7 +32,7 @@ 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::Struct(id) => self.struct_location(&id), DependencyId::Global(id) => self.get_global(id).location, DependencyId::Alias(id) => self.get_type_alias(id).borrow().location, DependencyId::Variable(location) => location, @@ -40,33 +40,23 @@ impl NodeInterner { } 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") - }; + if !self.track_references { + return; + } + let referenced_index = self.get_or_insert_reference(referenced); 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.reference_graph.add_edge(reference_index, referenced_index, ()); self.location_indices.add_location(reference_location, reference_index); } pub(crate) fn add_definition_location(&mut self, referenced: DependencyId) { + if !self.track_references { + return; + } + 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); @@ -83,40 +73,60 @@ impl NodeInterner { index } - pub fn check_rename_possible(&self, location: Location) -> bool { + // Given a reference location, find the location of the referenced node. + pub fn find_referenced_location(&self, reference_location: Location) -> Option { + self.location_indices + .get_node_from_location(reference_location) + .and_then(|node_index| self.referenced_index(node_index)) + .map(|node_index| self.dependency_location(self.reference_graph[node_index])) + } + + // Is the given location known to this interner? + pub fn is_location_known(&self, location: Location) -> bool { self.location_indices.get_node_from_location(location).is_some() } - pub fn find_rename_symbols_at(&self, location: Location) -> Option> { + // Starting at the given location, find the node referenced by it. Then, gather + // all locations that reference that node, and return all of them + // (the referenced node and the references). + // Returns `None` if the location is not known to this interner. + pub fn find_all_references(&self, location: Location) -> Option> { let node_index = self.location_indices.get_node_from_location(location)?; let reference_node = self.reference_graph[node_index]; let found_locations: Vec = match reference_node { - DependencyId::Alias(_) | DependencyId::Struct(_) | DependencyId::Global(_) => todo!(), - DependencyId::Function(_) => self.get_edit_locations(node_index), + DependencyId::Alias(_) | DependencyId::Global(_) => todo!(), + DependencyId::Function(_) | DependencyId::Struct(_) => { + self.find_all_references_for_index(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) + let referenced_node_index = self.referenced_index(node_index)?; + self.find_all_references_for_index(referenced_node_index) } }; Some(found_locations) } - fn get_edit_locations(&self, referenced_node_index: PetGraphIndex) -> Vec { + // Given a referenced node index, find all references to it and return their locations, together + // with the reference node's location. + fn find_all_references_for_index(&self, referenced_node_index: PetGraphIndex) -> Vec { 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) + .neighbors_directed(referenced_node_index, petgraph::Direction::Incoming) .for_each(|reference_node_index| { let id = self.reference_graph[reference_node_index]; edit_locations.push(self.dependency_location(id)); }); edit_locations } + + // Given a reference index, returns the referenced index, if any. + fn referenced_index(&self, reference_index: PetGraphIndex) -> Option { + self.reference_graph + .neighbors_directed(reference_index, petgraph::Direction::Outgoing) + .next() + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs index 2cb18a71d7a..618ed6a9ffa 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::fmt; +use std::hash::Hash; use std::ops::Deref; use fm::FileId; @@ -64,6 +65,9 @@ pub struct NodeInterner { // Contains the source module each function was defined in function_modules: HashMap, + // The location of each struct name + struct_name_locations: HashMap, + /// This graph tracks dependencies between different global definitions. /// This is used to ensure the absence of dependency cycles for globals and types. dependency_graph: DiGraph, @@ -184,7 +188,25 @@ pub struct NodeInterner { /// the actual type since types do not implement Send or Sync. quoted_types: noirc_arena::Arena, - /// Store the location of the references in the graph + /// Whether to track references. In regular compilations this is false, but tools set it to true. + pub(crate) track_references: bool, + + /// Store the location of the references in the graph. + /// Edges are directed from reference nodes to referenced nodes. + /// For example: + /// + /// ``` + /// let foo = 3; + /// // referenced + /// // ^ + /// // | + /// // +------------+ + /// let bar = foo; | + /// // reference | + /// // v | + /// // | | + /// // +------+ + /// ``` pub(crate) reference_graph: DiGraph, /// Tracks the index of the references in the graph @@ -504,6 +526,7 @@ impl Default for NodeInterner { function_definition_ids: HashMap::new(), function_modifiers: HashMap::new(), function_modules: HashMap::new(), + struct_name_locations: HashMap::new(), func_id_to_trait: HashMap::new(), dependency_graph: petgraph::graph::DiGraph::new(), dependency_graph_indices: HashMap::new(), @@ -531,6 +554,7 @@ impl Default for NodeInterner { type_alias_ref: Vec::new(), type_ref_locations: Vec::new(), quoted_types: Default::default(), + track_references: false, location_indices: LocationIndices::default(), reference_graph: petgraph::graph::DiGraph::new(), reference_graph_indices: HashMap::new(), @@ -928,6 +952,14 @@ impl NodeInterner { &self.struct_attributes[struct_id] } + pub fn add_struct_location(&mut self, struct_id: StructId, location: Location) { + self.struct_name_locations.insert(struct_id, location); + } + + pub fn struct_location(&self, struct_id: &StructId) -> Location { + self.struct_name_locations[struct_id] + } + pub fn global_attributes(&self, global_id: &GlobalId) -> &[SecondaryAttribute] { &self.global_attributes[global_id] } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs index 5efe2e4a041..efb430b75eb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs @@ -38,12 +38,16 @@ impl NodeInterner { location: Location, return_type_location_instead: bool, ) -> Option { - self.find_location_index(location) - .and_then(|index| self.resolve_location(index, return_type_location_instead)) - .or_else(|| self.try_resolve_trait_impl_location(location)) - .or_else(|| self.try_resolve_trait_method_declaration(location)) - .or_else(|| self.try_resolve_type_ref(location)) - .or_else(|| self.try_resolve_type_alias(location)) + // First try to find the location in the reference graph + self.find_referenced_location(location).or_else(|| { + // Otherwise fallback to the location indices + self.find_location_index(location) + .and_then(|index| self.resolve_location(index, return_type_location_instead)) + .or_else(|| self.try_resolve_trait_impl_location(location)) + .or_else(|| self.try_resolve_trait_method_declaration(location)) + .or_else(|| self.try_resolve_type_ref(location)) + .or_else(|| self.try_resolve_type_alias(location)) + }) } pub fn get_declaration_location_from(&self, location: Location) -> Option { diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index 92924e701a6..e9777261405 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -266,6 +266,16 @@ pub(crate) fn resolve_workspace_for_source_path(file_path: &Path) -> Result( + file_manager: &'file_manager FileManager, + parsed_files: &'parsed_files ParsedFiles, + package: &Package, +) -> (Context<'file_manager, 'parsed_files>, CrateId) { + let (mut context, crate_id) = nargo::prepare_package(file_manager, parsed_files, package); + context.track_references(); + (context, crate_id) +} + /// Prepares a package from a source string /// This is useful for situations when we don't need dependencies /// and just need to operate on single file. @@ -283,6 +293,8 @@ fn prepare_source(source: String, state: &mut LspState) -> (Context<'static, 'st let parsed_files = parse_diff(&file_manager, state); let mut context = Context::new(file_manager, parsed_files); + context.track_references(); + let root_crate_id = prepare_crate(&mut context, file_name); (context, root_crate_id) diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index 3856bdc79e9..da3b95ce8e0 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; -use nargo::{insert_all_files_for_workspace_into_file_manager, prepare_package}; +use nargo::insert_all_files_for_workspace_into_file_manager; use noirc_driver::{check_crate, file_manager_with_stdlib}; use noirc_errors::{DiagnosticKind, FileDiagnostic}; @@ -137,7 +137,7 @@ fn process_noir_document( .into_iter() .flat_map(|package| -> Vec { let (mut context, crate_id) = - prepare_package(&workspace_file_manager, &parsed_files, package); + crate::prepare_package(&workspace_file_manager, &parsed_files, package); let file_diagnostics = match check_crate(&mut context, crate_id, false, false, false) { Ok(((), warnings)) => warnings, diff --git a/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs b/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs index 4ffe6abf88a..627cd8d203e 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs @@ -2,16 +2,11 @@ use std::future::{self, Future}; use crate::types::GotoDeclarationResult; use crate::LspState; -use crate::{parse_diff, resolve_workspace_for_source_path}; -use async_lsp::{ErrorCode, ResponseError}; +use async_lsp::ResponseError; -use fm::PathString; use lsp_types::request::{GotoDeclarationParams, GotoDeclarationResponse}; -use nargo::insert_all_files_for_workspace_into_file_manager; -use noirc_driver::file_manager_with_stdlib; - -use super::{position_to_location, to_lsp_location}; +use super::{process_request, to_lsp_location}; pub(crate) fn on_goto_declaration_request( state: &mut LspState, @@ -25,42 +20,12 @@ fn on_goto_definition_inner( state: &mut LspState, params: GotoDeclarationParams, ) -> Result { - let file_path = - params.text_document_position_params.text_document.uri.to_file_path().map_err(|_| { - ResponseError::new(ErrorCode::REQUEST_FAILED, "URI is not a valid file path") - })?; - - let workspace = resolve_workspace_for_source_path(file_path.as_path()).unwrap(); - let package = workspace.members.first().unwrap(); - - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); - insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); - let parsed_files = parse_diff(&workspace_file_manager, state); - - let (mut context, crate_id) = - nargo::prepare_package(&workspace_file_manager, &parsed_files, package); - - let package_root_path = package.root_dir.as_os_str().to_string_lossy().into_owned(); - let interner = if let Some(def_interner) = state.cached_definitions.get(&package_root_path) { - def_interner - } else { - // We ignore the warnings and errors produced by compilation while resolving the definition - let _ = noirc_driver::check_crate(&mut context, crate_id, false, false, false); - &context.def_interner - }; - - let files = workspace_file_manager.as_file_map(); - let file_path = PathString::from(file_path); - let search_for_location = - position_to_location(files, &file_path, ¶ms.text_document_position_params.position)?; - - let goto_declaration_response = - interner.get_declaration_location_from(search_for_location).and_then(|found_location| { + process_request(state, params.text_document_position_params, |location, interner, files| { + interner.get_declaration_location_from(location).and_then(|found_location| { let file_id = found_location.file; let definition_position = to_lsp_location(files, file_id, found_location.span)?; let response = GotoDeclarationResponse::from(definition_position).to_owned(); Some(response) - }); - - Ok(goto_declaration_response) + }) + }) } diff --git a/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs b/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs index 4985c565e06..158b8a1dc35 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs @@ -1,16 +1,12 @@ use std::future::{self, Future}; -use crate::{parse_diff, resolve_workspace_for_source_path}; use crate::{types::GotoDefinitionResult, LspState}; -use async_lsp::{ErrorCode, ResponseError}; +use async_lsp::ResponseError; -use fm::PathString; use lsp_types::request::GotoTypeDefinitionParams; use lsp_types::{GotoDefinitionParams, GotoDefinitionResponse}; -use nargo::insert_all_files_for_workspace_into_file_manager; -use noirc_driver::file_manager_with_stdlib; -use super::{position_to_location, to_lsp_location}; +use super::{process_request, to_lsp_location}; pub(crate) fn on_goto_definition_request( state: &mut LspState, @@ -33,85 +29,68 @@ fn on_goto_definition_inner( params: GotoDefinitionParams, return_type_location_instead: bool, ) -> Result { - let file_path = - params.text_document_position_params.text_document.uri.to_file_path().map_err(|_| { - ResponseError::new(ErrorCode::REQUEST_FAILED, "URI is not a valid file path") - })?; - - let workspace = resolve_workspace_for_source_path(file_path.as_path()).unwrap(); - let package = workspace.members.first().unwrap(); - - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); - insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); - let parsed_files = parse_diff(&workspace_file_manager, state); - - let (mut context, crate_id) = - nargo::prepare_package(&workspace_file_manager, &parsed_files, package); - - let package_root_path = package.root_dir.as_os_str().to_string_lossy().into_owned(); - let interner = if let Some(def_interner) = state.cached_definitions.get(&package_root_path) { - def_interner - } else { - // We ignore the warnings and errors produced by compilation while resolving the definition - let _ = noirc_driver::check_crate(&mut context, crate_id, false, false, false); - &context.def_interner - }; - - let files = workspace_file_manager.as_file_map(); - let file_path = PathString::from(file_path); - let search_for_location = - position_to_location(files, &file_path, ¶ms.text_document_position_params.position)?; - - let goto_definition_response = interner - .get_definition_location_from(search_for_location, return_type_location_instead) - .and_then(|found_location| { - let file_id = found_location.file; - let definition_position = to_lsp_location(files, file_id, found_location.span)?; - let response = GotoDefinitionResponse::from(definition_position).to_owned(); - Some(response) - }); - - Ok(goto_definition_response) + process_request(state, params.text_document_position_params, |location, interner, files| { + interner.get_definition_location_from(location, return_type_location_instead).and_then( + |found_location| { + let file_id = found_location.file; + let definition_position = to_lsp_location(files, file_id, found_location.span)?; + let response = GotoDefinitionResponse::from(definition_position).to_owned(); + Some(response) + }, + ) + }) } #[cfg(test)] mod goto_definition_tests { use std::panic; - use crate::test_utils; - use lsp_types::{Position, Range}; + use crate::test_utils::{self, search_in_file}; use tokio::test; use super::*; - #[test] - async fn test_on_goto_definition() { - let (mut state, noir_text_document) = test_utils::init_lsp_server("go_to_definition").await; + async fn expect_goto(directory: &str, name: &str, definition_index: usize) { + let (mut state, noir_text_document) = test_utils::init_lsp_server(directory).await; + + let ranges = search_in_file(noir_text_document.path(), name); + let expected_range = ranges[definition_index]; + + for (index, range) in ranges.iter().enumerate() { + // Ideally "go to" at the definition should return the same location, but this isn't currently + // working. But it's also not that important, so we'll keep it for later. + if index == definition_index { + continue; + } + + let params = GotoDefinitionParams { + text_document_position_params: lsp_types::TextDocumentPositionParams { + text_document: lsp_types::TextDocumentIdentifier { + uri: noir_text_document.clone(), + }, + position: range.start, + }, + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + }; + + let response = on_goto_definition_request(&mut state, params) + .await + .expect("Could execute on_goto_definition_request") + .unwrap_or_else(|| { + panic!("Didn't get a goto definition response for index {index}") + }); + + if let GotoDefinitionResponse::Scalar(location) = response { + assert_eq!(location.range, expected_range); + } else { + panic!("Expected a scalar response"); + }; + } + } - let params = GotoDefinitionParams { - text_document_position_params: lsp_types::TextDocumentPositionParams { - text_document: lsp_types::TextDocumentIdentifier { uri: noir_text_document }, - position: Position { line: 9, character: 12 }, // Right at the beginning of "another_function" - }, - work_done_progress_params: Default::default(), - partial_result_params: Default::default(), - }; - - let response: GotoDefinitionResponse = on_goto_definition_request(&mut state, params) - .await - .expect("Could execute on_goto_definition_request") - .expect("Didn't get a goto definition response"); - - if let GotoDefinitionResponse::Scalar(location) = response { - assert_eq!( - location.range, - Range { - start: Position { line: 4, character: 3 }, - end: Position { line: 4, character: 19 }, - } - ); - } else { - panic!("Expected a scalar response"); - }; + #[test] + async fn goto_from_function_location_to_declaration() { + expect_goto("go_to_definition", "another_function", 0).await; } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index 545b5fef3d2..1d4b0982f93 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -1,13 +1,20 @@ use std::future::Future; -use crate::types::{CodeLensOptions, InitializeParams}; +use crate::{ + parse_diff, resolve_workspace_for_source_path, + types::{CodeLensOptions, InitializeParams}, +}; use async_lsp::{ErrorCode, ResponseError}; use fm::{codespan_files::Error, FileMap, PathString}; use lsp_types::{ - DeclarationCapability, Location, Position, TextDocumentSyncCapability, TextDocumentSyncKind, - TypeDefinitionProviderCapability, Url, WorkDoneProgressOptions, + DeclarationCapability, Location, Position, TextDocumentPositionParams, + TextDocumentSyncCapability, TextDocumentSyncKind, TypeDefinitionProviderCapability, Url, + WorkDoneProgressOptions, }; +use nargo::insert_all_files_for_workspace_into_file_manager; use nargo_fmt::Config; +use noirc_driver::file_manager_with_stdlib; +use noirc_frontend::macros_api::NodeInterner; use serde::{Deserialize, Serialize}; use crate::{ @@ -251,6 +258,51 @@ pub(crate) fn on_shutdown( async { Ok(()) } } +pub(crate) fn process_request( + state: &mut LspState, + text_document_position_params: TextDocumentPositionParams, + callback: F, +) -> Result +where + F: FnOnce(noirc_errors::Location, &NodeInterner, &FileMap) -> T, +{ + let file_path = + text_document_position_params.text_document.uri.to_file_path().map_err(|_| { + ResponseError::new(ErrorCode::REQUEST_FAILED, "URI is not a valid file path") + })?; + + let workspace = resolve_workspace_for_source_path(file_path.as_path()).unwrap(); + let package = workspace.members.first().unwrap(); + + let package_root_path: String = package.root_dir.as_os_str().to_string_lossy().into(); + + let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); + insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); + let parsed_files = parse_diff(&workspace_file_manager, state); + + let (mut context, crate_id) = + crate::prepare_package(&workspace_file_manager, &parsed_files, package); + + let interner; + if let Some(def_interner) = state.cached_definitions.get(&package_root_path) { + interner = def_interner; + } else { + // We ignore the warnings and errors produced by compilation while resolving the definition + let _ = noirc_driver::check_crate(&mut context, crate_id, false, false, false); + interner = &context.def_interner; + } + + let files = context.file_manager.as_file_map(); + + let location = position_to_location( + files, + &PathString::from(file_path), + &text_document_position_params.position, + )?; + + Ok(callback(location, interner, files)) +} + #[cfg(test)] mod initialization { use acvm::blackbox_solver::StubbedBlackBoxSolver; diff --git a/noir/noir-repo/tooling/lsp/src/requests/rename.rs b/noir/noir-repo/tooling/lsp/src/requests/rename.rs index 9f6416a2c63..729c51c6b19 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/rename.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/rename.rs @@ -3,26 +3,21 @@ use std::{ future::{self, Future}, }; -use async_lsp::{ErrorCode, ResponseError}; -use fm::FileMap; +use async_lsp::ResponseError; use lsp_types::{ PrepareRenameResponse, RenameParams, TextDocumentPositionParams, TextEdit, Url, WorkspaceEdit, }; -use nargo::insert_all_files_for_workspace_into_file_manager; -use noirc_driver::file_manager_with_stdlib; -use noirc_errors::Location; -use noirc_frontend::macros_api::NodeInterner; -use crate::{parse_diff, resolve_workspace_for_source_path, LspState}; +use crate::LspState; -use super::{position_to_byte_index, to_lsp_location}; +use super::{process_request, to_lsp_location}; pub(crate) fn on_prepare_rename_request( state: &mut LspState, params: TextDocumentPositionParams, ) -> impl Future, ResponseError>> { - let result = process_rename_request(state, params, |search_for_location, interner, _| { - let rename_possible = interner.check_rename_possible(search_for_location); + let result = process_request(state, params, |location, interner, _| { + let rename_possible = interner.is_location_known(location); Some(PrepareRenameResponse::DefaultBehavior { default_behavior: rename_possible }) }); future::ready(result) @@ -32,34 +27,31 @@ pub(crate) fn on_rename_request( state: &mut LspState, params: RenameParams, ) -> impl Future, ResponseError>> { - let result = process_rename_request( - state, - params.text_document_position, - |search_for_location, interner, files| { - let rename_changes = - interner.find_rename_symbols_at(search_for_location).map(|locations| { - let rs = locations.iter().fold( - HashMap::new(), - |mut acc: HashMap>, location| { - let file_id = location.file; - let span = location.span; - - let Some(lsp_location) = to_lsp_location(files, file_id, span) else { - return acc; - }; - - let edit = TextEdit { - range: lsp_location.range, - new_text: params.new_name.clone(), - }; - - acc.entry(lsp_location.uri).or_default().push(edit); - - acc - }, - ); - rs - }); + let result = + process_request(state, params.text_document_position, |location, interner, files| { + let rename_changes = interner.find_all_references(location).map(|locations| { + let rs = locations.iter().fold( + HashMap::new(), + |mut acc: HashMap>, location| { + let file_id = location.file; + let span = location.span; + + let Some(lsp_location) = to_lsp_location(files, file_id, span) else { + return acc; + }; + + let edit = TextEdit { + range: lsp_location.range, + new_text: params.new_name.clone(), + }; + + acc.entry(lsp_location.uri).or_default().push(edit); + + acc + }, + ); + rs + }); let response = WorkspaceEdit { changes: rename_changes, @@ -68,97 +60,27 @@ pub(crate) fn on_rename_request( }; Some(response) - }, - ); + }); future::ready(result) } -fn process_rename_request( - state: &mut LspState, - text_document_position_params: TextDocumentPositionParams, - callback: F, -) -> Result -where - F: FnOnce(Location, &NodeInterner, &FileMap) -> T, -{ - let file_path = - text_document_position_params.text_document.uri.to_file_path().map_err(|_| { - ResponseError::new(ErrorCode::REQUEST_FAILED, "URI is not a valid file path") - })?; - - let workspace = resolve_workspace_for_source_path(file_path.as_path()).unwrap(); - let package = workspace.members.first().unwrap(); - - let package_root_path: String = package.root_dir.as_os_str().to_string_lossy().into(); - - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); - insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); - let parsed_files = parse_diff(&workspace_file_manager, state); - - let (mut context, crate_id) = - nargo::prepare_package(&workspace_file_manager, &parsed_files, package); - - let interner; - if let Some(def_interner) = state.cached_definitions.get(&package_root_path) { - interner = def_interner; - } else { - // We ignore the warnings and errors produced by compilation while resolving the definition - let _ = noirc_driver::check_crate(&mut context, crate_id, false, false, false); - interner = &context.def_interner; - } - - let files = context.file_manager.as_file_map(); - let file_id = context.file_manager.name_to_id(file_path.clone()).ok_or(ResponseError::new( - ErrorCode::REQUEST_FAILED, - format!("Could not find file in file manager. File path: {:?}", file_path), - ))?; - let byte_index = - position_to_byte_index(files, file_id, &text_document_position_params.position).map_err( - |err| { - ResponseError::new( - ErrorCode::REQUEST_FAILED, - format!("Could not convert position to byte index. Error: {:?}", err), - ) - }, - )?; - - let search_for_location = noirc_errors::Location { - file: file_id, - span: noirc_errors::Span::single_char(byte_index as u32), - }; - - Ok(callback(search_for_location, interner, files)) -} - #[cfg(test)] mod rename_tests { use super::*; - use crate::test_utils; - use lsp_types::{Position, Range, WorkDoneProgressParams}; + use crate::test_utils::{self, search_in_file}; + use lsp_types::{Range, WorkDoneProgressParams}; use tokio::test; - async fn check_rename_succeeds(directory: &str, name: &str, ranges: &[Range]) { + async fn check_rename_succeeds(directory: &str, name: &str) { let (mut state, noir_text_document) = test_utils::init_lsp_server(directory).await; - let main_path = noir_text_document.path(); - - // As we process the rename requests we'll check that the request position actually - // includes the target name. - let file_contents = std::fs::read_to_string(main_path) - .unwrap_or_else(|_| panic!("Couldn't read file {}", main_path)); - - let file_lines: Vec<&str> = file_contents.lines().collect(); + // First we find out all of the occurrences of `name` in the main.nr file. + // Note that this only works if that name doesn't show up in other places where we don't + // expect a rename, but we craft our tests to avoid that. + let ranges = search_in_file(noir_text_document.path(), name); // Test renaming works on any instance of the symbol. - for target_range in ranges { - assert_eq!(target_range.start.line, target_range.end.line); - - // Check that the range includes the target name - let line = file_lines[target_range.start.line as usize]; - let chunk = - &line[target_range.start.character as usize..target_range.end.character as usize]; - assert_eq!(chunk, name); - + for target_range in &ranges { let target_position = target_range.start; let params = RenameParams { @@ -187,7 +109,7 @@ mod rename_tests { #[test] async fn test_on_prepare_rename_request_cannot_be_applied() { - let (mut state, noir_text_document) = test_utils::init_lsp_server("rename").await; + let (mut state, noir_text_document) = test_utils::init_lsp_server("rename_function").await; let params = TextDocumentPositionParams { text_document: lsp_types::TextDocumentIdentifier { uri: noir_text_document }, @@ -205,45 +127,22 @@ mod rename_tests { } #[test] - async fn test_on_rename_request() { - const ANOTHER_FUNCTION_REFERENCE: Range = Range { - start: Position { line: 9, character: 12 }, - end: Position { line: 9, character: 28 }, - }; - const ANOTHER_FUNCTION_DECLARATION: Range = Range { - start: Position { line: 4, character: 3 }, - end: Position { line: 4, character: 19 }, - }; - // The ranges of positions which represent the usage of the `another_function` symbol. - const ANOTHER_FUNCTION_RANGES: &[Range] = &[ - ANOTHER_FUNCTION_DECLARATION, - ANOTHER_FUNCTION_REFERENCE, - Range { - start: Position { line: 13, character: 12 }, - end: Position { line: 13, character: 28 }, - }, - Range { - start: Position { line: 19, character: 15 }, - end: Position { line: 19, character: 31 }, - }, - ]; - - check_rename_succeeds("rename", "another_function", ANOTHER_FUNCTION_RANGES).await; + async fn test_rename_function() { + check_rename_succeeds("rename_function", "another_function").await; } #[test] - async fn test_on_rename_request_works_with_qualified_path() { - const BAR_FUNCTION_REFERENCE: Range = Range { - start: Position { line: 1, character: 9 }, - end: Position { line: 1, character: 12 }, - }; - const BAR_FUNCTION_DECLARATION: Range = Range { - start: Position { line: 5, character: 11 }, - end: Position { line: 5, character: 14 }, - }; - // The ranges of positions which represent the usage of the `bar` symbol. - const BAR_FUNCTION_RANGES: &[Range] = &[BAR_FUNCTION_REFERENCE, BAR_FUNCTION_DECLARATION]; + async fn test_rename_qualified_function() { + check_rename_succeeds("rename_qualified_function", "bar").await; + } - check_rename_succeeds("rename_qualified", "bar", BAR_FUNCTION_RANGES).await; + #[test] + async fn test_rename_function_in_use_statement() { + check_rename_succeeds("rename_function_use", "some_function").await; + } + + #[test] + async fn test_rename_struct() { + check_rename_succeeds("rename_struct", "Foo").await; } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs index 83b05ba06a2..acd4f5800f3 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs @@ -4,7 +4,6 @@ use async_lsp::{ErrorCode, ResponseError}; use nargo::{ insert_all_files_for_workspace_into_file_manager, ops::{run_test, TestStatus}, - prepare_package, }; use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ @@ -59,7 +58,7 @@ fn on_test_run_request_inner( match workspace.into_iter().next() { Some(package) => { let (mut context, crate_id) = - prepare_package(&workspace_file_manager, &parsed_files, package); + crate::prepare_package(&workspace_file_manager, &parsed_files, package); if check_crate(&mut context, crate_id, false, false, false).is_err() { let result = NargoTestRunResult { id: params.id.clone(), diff --git a/noir/noir-repo/tooling/lsp/src/requests/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/tests.rs index cdf4ad338c4..a2aa3ebc0bf 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/tests.rs @@ -2,7 +2,7 @@ use std::future::{self, Future}; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; use lsp_types::{LogMessageParams, MessageType}; -use nargo::{insert_all_files_for_workspace_into_file_manager, prepare_package}; +use nargo::insert_all_files_for_workspace_into_file_manager; use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{check_crate, file_manager_with_stdlib, NOIR_ARTIFACT_VERSION_STRING}; @@ -58,7 +58,7 @@ fn on_tests_request_inner( .into_iter() .filter_map(|package| { let (mut context, crate_id) = - prepare_package(&workspace_file_manager, &parsed_files, package); + crate::prepare_package(&workspace_file_manager, &parsed_files, package); // We ignore the warnings and errors produced by compilation for producing tests // because we can still get the test functions even if compilation fails let _ = check_crate(&mut context, crate_id, false, false, false); diff --git a/noir/noir-repo/tooling/lsp/src/test_utils.rs b/noir/noir-repo/tooling/lsp/src/test_utils.rs index dcaec2fd615..fd1a9090965 100644 --- a/noir/noir-repo/tooling/lsp/src/test_utils.rs +++ b/noir/noir-repo/tooling/lsp/src/test_utils.rs @@ -1,7 +1,7 @@ use crate::LspState; use acvm::blackbox_solver::StubbedBlackBoxSolver; use async_lsp::ClientSocket; -use lsp_types::Url; +use lsp_types::{Position, Range, Url}; pub(crate) async fn init_lsp_server(directory: &str) -> (LspState, Url) { let client = ClientSocket::new_closed(); @@ -37,3 +37,25 @@ pub(crate) async fn init_lsp_server(directory: &str) -> (LspState, Url) { (state, noir_text_document) } + +/// Searches for all instances of `search_string` in file `file_name` and returns a list of their locations. +pub(crate) fn search_in_file(filename: &str, search_string: &str) -> Vec { + let file_contents = std::fs::read_to_string(filename) + .unwrap_or_else(|_| panic!("Couldn't read file {}", filename)); + let file_lines: Vec<&str> = file_contents.lines().collect(); + file_lines + .iter() + .enumerate() + .filter_map(|(line_num, line)| { + // Note: this only finds the first instance of `search_string` on this line. + line.find(search_string).map(|index| { + let start = Position { line: line_num as u32, character: index as u32 }; + let end = Position { + line: line_num as u32, + character: (index + search_string.len()) as u32, + }; + Range { start, end } + }) + }) + .collect() +} diff --git a/noir/noir-repo/tooling/lsp/test_programs/go_to_definition/src/main.nr b/noir/noir-repo/tooling/lsp/test_programs/go_to_definition/src/main.nr index c27f8fed868..01ea5a3eadf 100644 --- a/noir/noir-repo/tooling/lsp/test_programs/go_to_definition/src/main.nr +++ b/noir/noir-repo/tooling/lsp/test_programs/go_to_definition/src/main.nr @@ -1,9 +1,13 @@ -fn some_function() -> Field { - 1 + 2 +mod foo { + pub fn another_function() -> Field { + 3 + 4 + } } -fn another_function() -> Field { - 3 + 4 +use foo::another_function; + +fn some_function() -> Field { + 1 + 2 } fn main() { diff --git a/noir/noir-repo/tooling/lsp/test_programs/rename_function/Nargo.toml b/noir/noir-repo/tooling/lsp/test_programs/rename_function/Nargo.toml new file mode 100644 index 00000000000..529fde06128 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/rename_function/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rename_function" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/noir-repo/tooling/lsp/test_programs/rename_function/src/main.nr b/noir/noir-repo/tooling/lsp/test_programs/rename_function/src/main.nr new file mode 100644 index 00000000000..ad526f10966 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/rename_function/src/main.nr @@ -0,0 +1,21 @@ +fn some_function() -> Field { + 1 + 2 +} + +fn another_function() -> Field { + 3 + 4 +} + +fn main() { + let _ = another_function(); + + let _ = 1; + + let _ = another_function(); +} + +mod foo { + fn some_other_function() -> Field { + crate::another_function() + } +} diff --git a/noir/noir-repo/tooling/lsp/test_programs/rename_function_use/Nargo.toml b/noir/noir-repo/tooling/lsp/test_programs/rename_function_use/Nargo.toml new file mode 100644 index 00000000000..e2eb25886bf --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/rename_function_use/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rename_function_use" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/noir-repo/tooling/lsp/test_programs/rename_function_use/src/main.nr b/noir/noir-repo/tooling/lsp/test_programs/rename_function_use/src/main.nr new file mode 100644 index 00000000000..e1b7c98ab0f --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/rename_function_use/src/main.nr @@ -0,0 +1,12 @@ +mod foo { + pub fn some_function() -> Field { + 1 + 2 + } +} + +use foo::some_function; + +fn main() { + let _ = some_function(); +} + diff --git a/noir/noir-repo/tooling/lsp/test_programs/rename_qualified_function/Nargo.toml b/noir/noir-repo/tooling/lsp/test_programs/rename_qualified_function/Nargo.toml new file mode 100644 index 00000000000..c0aaa3ce658 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/rename_qualified_function/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rename_qualified_function" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/noir-repo/tooling/lsp/test_programs/rename_qualified_function/src/main.nr b/noir/noir-repo/tooling/lsp/test_programs/rename_qualified_function/src/main.nr new file mode 100644 index 00000000000..f1b77796210 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/rename_qualified_function/src/main.nr @@ -0,0 +1,9 @@ +fn main() -> pub Field { + foo::bar() +} + +mod foo { + pub fn bar() -> Field { + 1 + } +} diff --git a/noir/noir-repo/tooling/lsp/test_programs/rename_struct/Nargo.toml b/noir/noir-repo/tooling/lsp/test_programs/rename_struct/Nargo.toml new file mode 100644 index 00000000000..e5822098e58 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/rename_struct/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rename_struct" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/noir-repo/tooling/lsp/test_programs/rename_struct/src/main.nr b/noir/noir-repo/tooling/lsp/test_programs/rename_struct/src/main.nr new file mode 100644 index 00000000000..96cccb4d72a --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/rename_struct/src/main.nr @@ -0,0 +1,18 @@ +mod foo { + mod bar { + struct Foo { + field: Field, + } + } +} + +use foo::bar::Foo; + +fn main(x: Field) -> pub Field { + let foo1 = Foo { field: 1 }; + let foo2 = Foo { field: 2 }; + let Foo { field } = foo1; + x +} + +fn foo(foo: Foo) {}