diff --git a/Cargo.lock b/Cargo.lock index 960ad27dbcb..e60fd889174 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2047,6 +2047,7 @@ dependencies = [ "async-lsp", "codespan-lsp", "codespan-reporting", + "fm", "lsp-types 0.94.0", "noirc_driver", "noirc_errors", @@ -2064,6 +2065,7 @@ dependencies = [ "acvm", "build-data", "console_error_panic_hook", + "fm", "getrandom", "gloo-utils", "log", diff --git a/crates/fm/src/file_reader.rs b/crates/fm/src/file_reader.rs index f0a5fe625aa..df4e49b919a 100644 --- a/crates/fm/src/file_reader.rs +++ b/crates/fm/src/file_reader.rs @@ -11,6 +11,16 @@ use std::path::Path; #[cfg_attr(target_os = "windows", prefix = r"std\")] // Note reversed slash direction struct StdLibAssets; +#[cfg(target_os = "windows")] +pub(super) fn is_stdlib_asset(path: &Path) -> bool { + path.starts_with("std\\") +} + +#[cfg(not(target_os = "windows"))] +pub(super) fn is_stdlib_asset(path: &Path) -> bool { + path.starts_with("std/") +} + cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { use wasm_bindgen::{prelude::*, JsValue}; @@ -41,6 +51,7 @@ cfg_if::cfg_if! { } } } else { + pub(crate) fn read_file_to_string(path_to_file: &Path) -> Result { match StdLibAssets::get(path_to_file.to_str().unwrap()) { diff --git a/crates/fm/src/lib.rs b/crates/fm/src/lib.rs index d22d74c757a..368043ea601 100644 --- a/crates/fm/src/lib.rs +++ b/crates/fm/src/lib.rs @@ -7,10 +7,11 @@ mod file_map; mod file_reader; pub use file_map::{File, FileId, FileMap}; +use file_reader::is_stdlib_asset; use std::{ collections::HashMap, - path::{Path, PathBuf}, + path::{Component, Path, PathBuf}, }; pub const FILE_EXTENSION: &str = "nr"; @@ -18,30 +19,43 @@ pub const FILE_EXTENSION: &str = "nr"; #[derive(Clone, Debug, PartialEq, Eq, Hash)] struct VirtualPath(PathBuf); -#[derive(Debug, Default)] +#[derive(Debug)] pub struct FileManager { + root: PathBuf, file_map: file_map::FileMap, id_to_path: HashMap, path_to_id: HashMap, } impl FileManager { - // XXX: Maybe use a AsRef here, for API ergonomics - pub fn add_file(&mut self, path_to_file: &Path) -> Option { + pub fn new(root: &Path) -> Self { + Self { + root: normalize_path(root), + file_map: Default::default(), + id_to_path: Default::default(), + path_to_id: Default::default(), + } + } + + pub fn add_file(&mut self, file_name: &Path) -> Option { // Handle both relative file paths and std/lib virtual paths. - let base = Path::new(".").canonicalize().expect("Base path canonicalize failed"); - let res = path_to_file.canonicalize().unwrap_or_else(|_| path_to_file.to_path_buf()); - let resolved_path = res.strip_prefix(base).unwrap_or(&res); + let resolved_path: PathBuf = if is_stdlib_asset(file_name) { + // Special case for stdlib where we want to read specifically the `std/` relative path + // TODO: The stdlib path should probably be an absolute path rooted in something people would never create + file_name.to_path_buf() + } else { + normalize_path(&self.root.join(file_name)) + }; // Check that the resolved path already exists in the file map, if it is, we return it. - let path_to_file = virtualize_path(resolved_path); + let path_to_file = virtualize_path(&resolved_path); if let Some(file_id) = self.path_to_id.get(&path_to_file) { return Some(*file_id); } // Otherwise we add the file - let source = file_reader::read_file_to_string(resolved_path).ok()?; - let file_id = self.file_map.add_file(resolved_path.to_path_buf().into(), source); + let source = file_reader::read_file_to_string(&resolved_path).ok()?; + let file_id = self.file_map.add_file(resolved_path.into(), source); self.register_path(file_id, path_to_file); Some(file_id) } @@ -87,6 +101,37 @@ impl FileManager { } } +/// Replacement for `std::fs::canonicalize` that doesn't verify the path exists. +/// +/// Plucked from https://github.com/rust-lang/cargo/blob/fede83ccf973457de319ba6fa0e36ead454d2e20/src/cargo/util/paths.rs#L61 +/// Advice from https://www.reddit.com/r/rust/comments/hkkquy/comment/fwtw53s/ +fn normalize_path(path: &Path) -> PathBuf { + let mut components = path.components().peekable(); + let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { + components.next(); + PathBuf::from(c.as_os_str()) + } else { + PathBuf::new() + }; + + for component in components { + match component { + Component::Prefix(..) => unreachable!(), + Component::RootDir => { + ret.push(component.as_os_str()); + } + Component::CurDir => {} + Component::ParentDir => { + ret.pop(); + } + Component::Normal(c) => { + ret.push(c); + } + } + } + ret +} + /// Takes a path to a noir file. This will panic on paths to directories /// Returns the file path with the extension removed fn virtualize_path(path: &Path) -> VirtualPath { @@ -102,46 +147,49 @@ mod tests { use super::*; use tempfile::{tempdir, TempDir}; - fn dummy_file_path(dir: &TempDir, file_name: &str) -> PathBuf { + fn create_dummy_file(dir: &TempDir, file_name: &Path) { let file_path = dir.path().join(file_name); let _file = std::fs::File::create(file_path.clone()).unwrap(); - - file_path } #[test] fn path_resolve_file_module() { let dir = tempdir().unwrap(); - let file_path = dummy_file_path(&dir, "my_dummy_file.nr"); - let mut fm = FileManager::default(); + let entry_file_name = Path::new("my_dummy_file.nr"); + create_dummy_file(&dir, entry_file_name); + + let mut fm = FileManager::new(dir.path()); - let file_id = fm.add_file(&file_path).unwrap(); + let file_id = fm.add_file(entry_file_name).unwrap(); - let _foo_file_path = dummy_file_path(&dir, "foo.nr"); + let dep_file_name = Path::new("foo.nr"); + create_dummy_file(&dir, dep_file_name); fm.resolve_path(file_id, "foo").unwrap(); } #[test] fn path_resolve_file_module_other_ext() { let dir = tempdir().unwrap(); - let file_path = dummy_file_path(&dir, "foo.noir"); + let file_name = Path::new("foo.noir"); + create_dummy_file(&dir, file_name); - let mut fm = FileManager::default(); + let mut fm = FileManager::new(dir.path()); - let file_id = fm.add_file(&file_path).unwrap(); + let file_id = fm.add_file(&file_name).unwrap(); assert!(fm.path(file_id).ends_with("foo")); } #[test] fn path_resolve_sub_module() { - let mut fm = FileManager::default(); - let dir = tempdir().unwrap(); + let mut fm = FileManager::new(dir.path()); + // Create a lib.nr file at the root. // we now have dir/lib.nr - let file_path = dummy_file_path(&dir, "lib.nr"); + let file_name = Path::new("lib.nr"); + create_dummy_file(&dir, file_name); - let file_id = fm.add_file(&file_path).unwrap(); + let file_id = fm.add_file(&file_name).unwrap(); // Create a sub directory // we now have: @@ -154,14 +202,14 @@ mod tests { // we no have: // - dir/lib.nr // - dir/sub_dir/foo.nr - let _foo_file_path = dummy_file_path(&sub_dir, "foo.nr"); + create_dummy_file(&sub_dir, Path::new("foo.nr")); // Add a parent module for the sub_dir // we no have: // - dir/lib.nr // - dir/sub_dir.nr // - dir/sub_dir/foo.nr - let _sub_dir_root_file_path = dummy_file_path(&dir, &format!("{}.nr", sub_dir_name)); + create_dummy_file(&dir, Path::new(&format!("{}.nr", sub_dir_name))); // First check for the sub_dir.nr file and add it to the FileManager let sub_dir_file_id = fm.resolve_path(file_id, sub_dir_name).unwrap(); @@ -176,20 +224,22 @@ mod tests { /// they should both resolve to ../foo.nr #[test] fn path_resolve_modules_with_different_paths_as_same_file() { - let mut fm = FileManager::default(); - - // Create a lib.nr file at the root. let dir = tempdir().unwrap(); let sub_dir = TempDir::new_in(&dir).unwrap(); let sub_sub_dir = TempDir::new_in(&sub_dir).unwrap(); - let file_path = dummy_file_path(&dir, "lib.nr"); - // Create another file in a subdirectory with a convoluted path - let second_file_path = dummy_file_path(&sub_sub_dir, "./../../lib.nr"); + let mut fm = FileManager::new(dir.path()); + + // Create a lib.nr file at the root. + let file_name = Path::new("lib.nr"); + create_dummy_file(&dir, file_name); + + // Create another path with `./` and `../` inside it + let second_file_name = PathBuf::from(sub_sub_dir.path()).join("./../../lib.nr"); // Add both files to the file manager - let file_id = fm.add_file(&file_path).unwrap(); - let second_file_id = fm.add_file(&second_file_path).unwrap(); + let file_id = fm.add_file(&file_name).unwrap(); + let second_file_id = fm.add_file(&second_file_name).unwrap(); assert_eq!(file_id, second_file_id); } diff --git a/crates/lsp/Cargo.toml b/crates/lsp/Cargo.toml index 47278ab5291..2611abd67dc 100644 --- a/crates/lsp/Cargo.toml +++ b/crates/lsp/Cargo.toml @@ -11,6 +11,7 @@ edition.workspace = true acvm.workspace = true codespan-lsp.workspace = true codespan-reporting.workspace = true +fm.workspace = true lsp-types.workspace = true noirc_driver.workspace = true noirc_errors.workspace = true diff --git a/crates/lsp/src/lib.rs b/crates/lsp/src/lib.rs index cab74bd5560..953540226ad 100644 --- a/crates/lsp/src/lib.rs +++ b/crates/lsp/src/lib.rs @@ -2,17 +2,19 @@ mod lib_hacky; use std::env; use std::{ - future::Future, + future::{self, Future}, ops::{self, ControlFlow}, + path::PathBuf, pin::Pin, task::{self, Poll}, }; use async_lsp::{ - router::Router, AnyEvent, AnyNotification, AnyRequest, ClientSocket, Error, LanguageClient, - LspService, ResponseError, + router::Router, AnyEvent, AnyNotification, AnyRequest, ClientSocket, Error, ErrorCode, + LanguageClient, LspService, ResponseError, }; use codespan_reporting::files; +use fm::FileManager; use lsp_types::{ notification, request, CodeLens, CodeLensOptions, CodeLensParams, Command, Diagnostic, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeTextDocumentParams, @@ -22,7 +24,10 @@ use lsp_types::{ }; use noirc_driver::{check_crate, create_local_crate}; use noirc_errors::{DiagnosticKind, FileDiagnostic}; -use noirc_frontend::{graph::CrateType, hir::Context}; +use noirc_frontend::{ + graph::{CrateGraph, CrateType}, + hir::Context, +}; use serde_json::Value as JsonValue; use tower::Service; @@ -31,12 +36,13 @@ const TEST_CODELENS_TITLE: &str = "▶\u{fe0e} Run Test"; // State for the LSP gets implemented on this struct and is internal to the implementation pub struct LspState { + root_path: Option, client: ClientSocket, } impl LspState { fn new(client: &ClientSocket) -> Self { - Self { client: client.clone() } + Self { client: client.clone(), root_path: None } } } @@ -131,9 +137,13 @@ impl LspService for NargoLspService { // and params passed in. fn on_initialize( - _state: &mut LspState, - _params: InitializeParams, + state: &mut LspState, + params: InitializeParams, ) -> impl Future> { + if let Some(root_uri) = params.root_uri { + state.root_path = root_uri.to_file_path().ok(); + } + async { let text_document_sync = TextDocumentSyncOptions { save: Some(true.into()), ..Default::default() }; @@ -160,12 +170,25 @@ fn on_shutdown( } fn on_code_lens_request( - _state: &mut LspState, + state: &mut LspState, params: CodeLensParams, ) -> impl Future>, ResponseError>> { let file_path = ¶ms.text_document.uri.to_file_path().unwrap(); - let mut context = Context::default(); + let mut context = match &state.root_path { + Some(root_path) => { + let fm = FileManager::new(root_path); + let graph = CrateGraph::default(); + Context::new(fm, graph) + } + None => { + let err = ResponseError::new( + ErrorCode::REQUEST_FAILED, + "Unable to determine the project root path", + ); + return future::ready(Err(err)); + } + }; let crate_id = create_local_crate(&mut context, file_path, CrateType::Binary); @@ -202,13 +225,9 @@ fn on_code_lens_request( lenses.push(lens); } - async move { - if lenses.is_empty() { - Ok(None) - } else { - Ok(Some(lenses)) - } - } + let res = if lenses.is_empty() { Ok(None) } else { Ok(Some(lenses)) }; + + future::ready(res) } fn on_initialized( @@ -251,8 +270,20 @@ fn on_did_save_text_document( params: DidSaveTextDocumentParams, ) -> ControlFlow> { let file_path = ¶ms.text_document.uri.to_file_path().unwrap(); - - let mut context = Context::default(); + let mut context = match &state.root_path { + Some(root_path) => { + let fm = FileManager::new(root_path); + let graph = CrateGraph::default(); + Context::new(fm, graph) + } + None => { + let err = ResponseError::new( + ErrorCode::REQUEST_FAILED, + "Unable to determine the project root path", + ); + return ControlFlow::Break(Err(err.into())); + } + }; let crate_id = create_local_crate(&mut context, file_path, CrateType::Binary); diff --git a/crates/lsp/src/lib_hacky.rs b/crates/lsp/src/lib_hacky.rs index 36feee27348..0fea6c43144 100644 --- a/crates/lsp/src/lib_hacky.rs +++ b/crates/lsp/src/lib_hacky.rs @@ -4,13 +4,14 @@ use std::{ collections::HashMap, fs, - future::Future, + future::{self, Future}, ops::{self, ControlFlow}, path::{Path, PathBuf}, }; -use async_lsp::{LanguageClient, ResponseError}; +use async_lsp::{ErrorCode, LanguageClient, ResponseError}; use codespan_reporting::files; +use fm::FileManager; use lsp_types::{ CodeLens, CodeLensOptions, CodeLensParams, Command, Diagnostic, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeTextDocumentParams, DidCloseTextDocumentParams, @@ -21,10 +22,11 @@ use lsp_types::{ use noirc_driver::{check_crate, create_local_crate, create_non_local_crate, propagate_dep}; use noirc_errors::{DiagnosticKind, FileDiagnostic}; use noirc_frontend::{ - graph::{CrateId, CrateName, CrateType}, + graph::{CrateGraph, CrateId, CrateName, CrateType}, hir::Context, }; +// I'm guessing this is here so the `lib.rs` file compiles use crate::LspState; const TEST_COMMAND: &str = "nargo.test"; @@ -41,9 +43,13 @@ const TEST_CODELENS_TITLE: &str = "▶\u{fe0e} Run Test"; // and params passed in. pub fn on_initialize( - _state: &mut LspState, - _params: InitializeParams, + state: &mut LspState, + params: InitializeParams, ) -> impl Future> { + if let Some(root_uri) = params.root_uri { + state.root_path = root_uri.to_file_path().ok(); + } + async { let text_document_sync = TextDocumentSyncOptions { save: Some(true.into()), ..Default::default() }; @@ -70,30 +76,33 @@ pub fn on_shutdown( } pub fn on_code_lens_request( - _state: &mut LspState, + state: &mut LspState, params: CodeLensParams, ) -> impl Future>, ResponseError>> { let actual_path = params.text_document.uri.to_file_path().unwrap(); - let (mut driver, current_crate_id) = create_context_at_path(actual_path); + let (mut context, crate_id) = match create_context_at_path(&state.root_path, &actual_path) { + Err(err) => return future::ready(Err(err)), + Ok(res) => res, + }; // We ignore the warnings and errors produced by compilation for producing codelenses // because we can still get the test functions even if compilation fails - let _ = check_crate(&mut driver, current_crate_id, false, false); + let _ = check_crate(&mut context, crate_id, false, false); - let fm = &driver.file_manager; + let fm = &context.file_manager; let files = fm.as_simple_files(); - let tests = driver.get_all_test_functions_in_crate_matching(¤t_crate_id, ""); + let tests = context.get_all_test_functions_in_crate_matching(&crate_id, ""); let mut lenses: Vec = vec![]; for func_id in tests { - let location = driver.function_meta(&func_id).name.location; + let location = context.function_meta(&func_id).name.location; let file_id = location.file; // TODO(#1681): This file_id never be 0 because the "path" where it maps is the directory, not a file if file_id.as_usize() != 0 { continue; } - let func_name = driver.function_name(&func_id); + let func_name = context.function_name(&func_id); let range = byte_span_to_range(files, file_id.as_usize(), location.span.into()).unwrap_or_default(); @@ -109,13 +118,9 @@ pub fn on_code_lens_request( lenses.push(lens); } - async move { - if lenses.is_empty() { - Ok(None) - } else { - Ok(Some(lenses)) - } - } + let res = if lenses.is_empty() { Ok(None) } else { Ok(Some(lenses)) }; + + future::ready(res) } pub fn on_initialized( @@ -197,7 +202,11 @@ pub fn on_did_save_text_document( params: DidSaveTextDocumentParams, ) -> ControlFlow> { let actual_path = params.text_document.uri.to_file_path().unwrap(); - let (mut context, crate_id) = create_context_at_path(actual_path.clone()); + let actual_file_name = actual_path.file_name(); + let (mut context, crate_id) = match create_context_at_path(&state.root_path, &actual_path) { + Err(err) => return ControlFlow::Break(Err(err.into())), + Ok(res) => res, + }; let file_diagnostics = match check_crate(&mut context, crate_id, false, false) { Ok(warnings) => warnings, @@ -213,13 +222,13 @@ pub fn on_did_save_text_document( // TODO(AD): HACK, undo these total hacks once we have a proper approach if file_id.as_usize() == 0 { // main.nr case - if actual_path.file_name().unwrap().to_str() != Some("main.nr") - && actual_path.file_name().unwrap().to_str() != Some("lib.nr") + if actual_file_name.unwrap().to_str() != Some("main.nr") + && actual_file_name.unwrap().to_str() != Some("lib.nr") { continue; } } else if fm.path(file_id).file_name().unwrap().to_str().unwrap() - != actual_path.file_name().unwrap().to_str().unwrap().replace(".nr", "") + != actual_file_name.unwrap().to_str().unwrap().replace(".nr", "") { // every other file case continue; // TODO(AD): HACK, we list all errors, filter by hacky final path component @@ -256,19 +265,30 @@ pub fn on_did_save_text_document( ControlFlow::Continue(()) } -fn create_context_at_path(actual_path: PathBuf) -> (Context, CrateId) { - // TODO: Requiring `Language` and `is_opcode_supported` to construct a driver makes for some real stinky code - // The driver should not require knowledge of the backend; instead should be implemented as an independent pass (in nargo?) - let mut context = Context::default(); +fn create_context_at_path( + root_path: &Option, + actual_path: &Path, +) -> Result<(Context, CrateId), ResponseError> { + let mut context = match &root_path { + Some(root_path) => { + let fm = FileManager::new(root_path); + let graph = CrateGraph::default(); + Context::new(fm, graph) + } + None => { + let err = ResponseError::new(ErrorCode::REQUEST_FAILED, "Project has not been built"); + return Err(err); + } + }; - let mut file_path: PathBuf = actual_path; + let mut file_path = actual_path.to_path_buf(); // TODO better naming/unhacking if let Some(new_path) = find_nearest_parent_file(&file_path, &["lib.nr", "main.nr"]) { file_path = new_path; // TODO unhack } let nargo_toml_path = find_nearest_parent_file(&file_path, &["Nargo.toml"]); - let current_crate_id = create_local_crate(&mut context, file_path, CrateType::Binary); + let current_crate_id = create_local_crate(&mut context, &file_path, CrateType::Binary); // TODO(AD): undo hacky dependency resolution if let Some(nargo_toml_path) = nargo_toml_path { @@ -280,12 +300,12 @@ fn create_context_at_path(actual_path: PathBuf) -> (Context, CrateId) { .unwrap() // TODO .join(PathBuf::from(&dependency_path).join("src").join("lib.nr")); let library_crate = - create_non_local_crate(&mut context, path_to_lib, CrateType::Library); + create_non_local_crate(&mut context, &path_to_lib, CrateType::Library); propagate_dep(&mut context, library_crate, &CrateName::new(crate_name).unwrap()); } } } - (context, current_crate_id) + Ok((context, current_crate_id)) } pub fn on_exit(_state: &mut LspState, _params: ()) -> ControlFlow> { diff --git a/crates/nargo_cli/src/cli/mod.rs b/crates/nargo_cli/src/cli/mod.rs index 2edf36b1166..d952280f98c 100644 --- a/crates/nargo_cli/src/cli/mod.rs +++ b/crates/nargo_cli/src/cli/mod.rs @@ -88,9 +88,13 @@ pub fn start_cli() -> eyre::Result<()> { // FIXME: I not sure that this is the right place for this tests. #[cfg(test)] mod tests { + use fm::FileManager; use noirc_driver::{check_crate, create_local_crate}; use noirc_errors::reporter; - use noirc_frontend::{graph::CrateType, hir::Context}; + use noirc_frontend::{ + graph::{CrateGraph, CrateType}, + hir::Context, + }; use std::path::{Path, PathBuf}; @@ -99,9 +103,11 @@ mod tests { /// Compiles a file and returns true if compilation was successful /// /// This is used for tests. - fn file_compiles>(root_file: P) -> bool { - let mut context = Context::default(); - let crate_id = create_local_crate(&mut context, &root_file, CrateType::Binary); + fn file_compiles(root_dir: &Path, root_file: &Path) -> bool { + let fm = FileManager::new(root_dir); + let graph = CrateGraph::default(); + let mut context = Context::new(fm, graph); + let crate_id = create_local_crate(&mut context, root_file, CrateType::Binary); let result = check_crate(&mut context, crate_id, false, false); let success = result.is_ok(); @@ -120,10 +126,10 @@ mod tests { let pass_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!("{TEST_DATA_DIR}/pass")); - let paths = std::fs::read_dir(pass_dir).unwrap(); + let paths = std::fs::read_dir(&pass_dir).unwrap(); for path in paths.flatten() { let path = path.path(); - assert!(file_compiles(&path), "path: {}", path.display()); + assert!(file_compiles(&pass_dir, &path), "path: {}", path.display()); } } @@ -132,10 +138,10 @@ mod tests { let fail_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!("{TEST_DATA_DIR}/fail")); - let paths = std::fs::read_dir(fail_dir).unwrap(); + let paths = std::fs::read_dir(&fail_dir).unwrap(); for path in paths.flatten() { let path = path.path(); - assert!(!file_compiles(&path), "path: {}", path.display()); + assert!(!file_compiles(&fail_dir, &path), "path: {}", path.display()); } } } diff --git a/crates/nargo_cli/src/resolver.rs b/crates/nargo_cli/src/resolver.rs index 9598031b193..63e4cb0ae73 100644 --- a/crates/nargo_cli/src/resolver.rs +++ b/crates/nargo_cli/src/resolver.rs @@ -3,10 +3,11 @@ use std::{ path::{Path, PathBuf}, }; +use fm::FileManager; use nargo::manifest::{Dependency, Manifest, PackageManifest, WorkspaceConfig}; use noirc_driver::{add_dep, create_local_crate, create_non_local_crate}; use noirc_frontend::{ - graph::{CrateId, CrateName, CrateType}, + graph::{CrateGraph, CrateId, CrateName, CrateType}, hir::Context, }; use thiserror::Error; @@ -88,7 +89,9 @@ pub(crate) fn resolve_root_manifest( dir_path: &std::path::Path, package: Option, ) -> Result<(Context, CrateId), DependencyResolutionError> { - let mut context = Context::default(); + let fm = FileManager::new(dir_path); + let graph = CrateGraph::default(); + let mut context = Context::new(fm, graph); let manifest_path = super::find_package_manifest(dir_path)?; let manifest = super::manifest::parse(&manifest_path)?; @@ -97,7 +100,7 @@ pub(crate) fn resolve_root_manifest( Manifest::Package(package) => { let (entry_path, crate_type) = super::lib_or_bin(dir_path)?; - let crate_id = create_local_crate(&mut context, entry_path, crate_type); + let crate_id = create_local_crate(&mut context, &entry_path, crate_type); let pkg_root = manifest_path.parent().expect("Every manifest path has a parent."); resolve_package_manifest(&mut context, crate_id, package, pkg_root)?; @@ -222,11 +225,11 @@ fn resolve_workspace_manifest( .ok_or_else(|| DependencyResolutionError::PackageNotFound(crate_name(local_package)))?; let (entry_path, _crate_type) = super::lib_or_bin(local_crate)?; - let crate_id = create_local_crate(context, entry_path, CrateType::Workspace); + let crate_id = create_local_crate(context, &entry_path, CrateType::Workspace); for (_, package_path) in packages.drain() { let (entry_path, crate_type) = super::lib_or_bin(package_path)?; - create_non_local_crate(context, entry_path, crate_type); + create_non_local_crate(context, &entry_path, crate_type); } Ok(crate_id) diff --git a/crates/noirc_driver/src/lib.rs b/crates/noirc_driver/src/lib.rs index 525c15af1e8..05e4cfd28e3 100644 --- a/crates/noirc_driver/src/lib.rs +++ b/crates/noirc_driver/src/lib.rs @@ -14,7 +14,7 @@ use noirc_frontend::hir::Context; use noirc_frontend::monomorphization::monomorphize; use noirc_frontend::node_interner::FuncId; use serde::{Deserialize, Serialize}; -use std::path::{Path, PathBuf}; +use std::path::Path; mod contract; mod program; @@ -67,7 +67,7 @@ pub type ErrorsAndWarnings = Vec; // with the restricted version which only uses one file pub fn compile_file( context: &mut Context, - root_file: PathBuf, + root_file: &Path, ) -> Result<(CompiledProgram, Warnings), ErrorsAndWarnings> { let crate_id = create_local_crate(context, root_file, CrateType::Binary); compile_main(context, crate_id, &CompileOptions::default()) @@ -79,26 +79,24 @@ pub fn compile_file( /// we have multiple binaries in one workspace /// A Fix would be for the driver instance to store the local crate id. // Granted that this is the only place which relies on the local crate being first -pub fn create_local_crate>( +pub fn create_local_crate( context: &mut Context, - root_file: P, + file_name: &Path, crate_type: CrateType, ) -> CrateId { - let dir_path = root_file.as_ref().to_path_buf(); - let root_file_id = context.file_manager.add_file(&dir_path).unwrap(); + let root_file_id = context.file_manager.add_file(file_name).unwrap(); context.crate_graph.add_crate_root(crate_type, root_file_id) } /// Creates a Non Local Crate. A Non Local Crate is any crate which is the not the crate that /// the compiler is compiling. -pub fn create_non_local_crate>( +pub fn create_non_local_crate( context: &mut Context, - root_file: P, + file_name: &Path, crate_type: CrateType, ) -> CrateId { - let dir_path = root_file.as_ref().to_path_buf(); - let root_file_id = context.file_manager.add_file(&dir_path).unwrap(); + let root_file_id = context.file_manager.add_file(file_name).unwrap(); // You can add any crate type to the crate graph // but you cannot depend on Binaries @@ -153,7 +151,7 @@ pub fn check_crate( // however, the `create_non_local_crate` panics if you add the stdlib as the first crate in the graph and other // parts of the code expect the `0` FileID to be the crate root. See also #1681 let std_crate_name = "std"; - let path_to_std_lib_file = PathBuf::from(std_crate_name).join("lib.nr"); + let path_to_std_lib_file = Path::new(std_crate_name).join("lib.nr"); let root_file_id = context.file_manager.add_file(&path_to_std_lib_file).unwrap(); // You can add any crate type to the crate graph diff --git a/crates/noirc_frontend/src/hir/mod.rs b/crates/noirc_frontend/src/hir/mod.rs index cd188f46392..6c9237bff48 100644 --- a/crates/noirc_frontend/src/hir/mod.rs +++ b/crates/noirc_frontend/src/hir/mod.rs @@ -14,12 +14,10 @@ use std::collections::HashMap; /// Helper object which groups together several useful context objects used /// during name resolution. Once name resolution is finished, only the /// def_interner is required for type inference and monomorphization. -#[derive(Default)] pub struct Context { pub def_interner: NodeInterner, pub crate_graph: CrateGraph, pub(crate) def_maps: HashMap, - // TODO(#1599): Remove default impl and move creation/control of the FileManager into places that construct Context pub file_manager: FileManager, /// Maps a given (contract) module id to the next available storage slot diff --git a/crates/wasm/Cargo.toml b/crates/wasm/Cargo.toml index 5c139d96164..2b834318168 100644 --- a/crates/wasm/Cargo.toml +++ b/crates/wasm/Cargo.toml @@ -13,6 +13,7 @@ crate-type = ["cdylib"] [dependencies] acvm.workspace = true +fm.workspace = true noirc_driver.workspace = true noirc_frontend.workspace = true wasm-bindgen.workspace = true @@ -27,4 +28,4 @@ gloo-utils = { version = "0.1", features = ["serde"] } getrandom = { version = "*", features = ["js"] } [build-dependencies] -build-data = "0.1.3" \ No newline at end of file +build-data = "0.1.3" diff --git a/crates/wasm/src/compile.rs b/crates/wasm/src/compile.rs index 5496920cf56..7321b4f31e4 100644 --- a/crates/wasm/src/compile.rs +++ b/crates/wasm/src/compile.rs @@ -1,4 +1,5 @@ use acvm::acir::circuit::Circuit; +use fm::FileManager; use gloo_utils::format::JsValueSerdeExt; use log::debug; use noirc_driver::{ @@ -6,11 +7,11 @@ use noirc_driver::{ propagate_dep, CompileOptions, CompiledContract, }; use noirc_frontend::{ - graph::{CrateName, CrateType}, + graph::{CrateGraph, CrateName, CrateType}, hir::Context, }; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; +use std::path::Path; use wasm_bindgen::prelude::*; #[derive(Debug, Serialize, Deserialize)] @@ -61,8 +62,8 @@ impl Default for WASMCompileOptions { } fn add_noir_lib(context: &mut Context, crate_name: &str) { - let path_to_lib = PathBuf::from(&crate_name).join("lib.nr"); - let library_crate = create_non_local_crate(context, path_to_lib, CrateType::Library); + let path_to_lib = Path::new(&crate_name).join("lib.nr"); + let library_crate = create_non_local_crate(context, &path_to_lib, CrateType::Library); propagate_dep(context, library_crate, &CrateName::new(crate_name).unwrap()); } @@ -80,9 +81,12 @@ pub fn compile(args: JsValue) -> JsValue { debug!("Compiler configuration {:?}", &options); - let mut context = Context::default(); + let root = Path::new("/"); + let fm = FileManager::new(root); + let graph = CrateGraph::default(); + let mut context = Context::new(fm, graph); - let path = PathBuf::from(&options.entry_point); + let path = Path::new(&options.entry_point); let crate_id = create_local_crate(&mut context, path, CrateType::Binary); for dependency in options.optional_dependencies_set {