diff --git a/Cargo.lock b/Cargo.lock index 8fb282dda98..037db4f72ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2532,6 +2532,7 @@ name = "noir_debugger" version = "0.17.0" dependencies = [ "acvm", + "codespan-reporting", "easy-repl", "nargo", "noirc_printable_type", @@ -2623,6 +2624,7 @@ version = "0.17.0" dependencies = [ "acvm", "base64", + "build-data", "clap", "fm", "fxhash", diff --git a/compiler/noirc_driver/Cargo.toml b/compiler/noirc_driver/Cargo.toml index f1c21f74aab..f1c120e1687 100644 --- a/compiler/noirc_driver/Cargo.toml +++ b/compiler/noirc_driver/Cargo.toml @@ -7,6 +7,9 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[build-dependencies] +build-data = "0.1.3" + [dependencies] clap.workspace = true noirc_errors.workspace = true diff --git a/compiler/noirc_driver/build.rs b/compiler/noirc_driver/build.rs new file mode 100644 index 00000000000..7b5a645c026 --- /dev/null +++ b/compiler/noirc_driver/build.rs @@ -0,0 +1,14 @@ +const GIT_COMMIT: &&str = &"GIT_COMMIT"; + +fn main() { + // Rebuild if the tests have changed + println!("cargo:rerun-if-changed=tests"); + + // Only use build_data if the environment variable isn't set + // The environment variable is always set when working via Nix + if std::env::var(GIT_COMMIT).is_err() { + build_data::set_GIT_COMMIT(); + build_data::set_GIT_DIRTY(); + build_data::no_debug_rebuilds(); + } +} diff --git a/compiler/noirc_driver/src/contract.rs b/compiler/noirc_driver/src/contract.rs index a16da313ff6..da097d4cb86 100644 --- a/compiler/noirc_driver/src/contract.rs +++ b/compiler/noirc_driver/src/contract.rs @@ -28,6 +28,8 @@ pub enum ContractFunctionType { #[derive(Serialize, Deserialize)] pub struct CompiledContract { + pub noir_version: String, + /// The name of the contract. pub name: String, /// Each of the contract's functions are compiled into a separate `CompiledProgram` diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 44e50f94874..f8908efc596 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -28,6 +28,15 @@ pub use program::CompiledProgram; const STD_CRATE_NAME: &str = "std"; +pub const GIT_COMMIT: &str = env!("GIT_COMMIT"); +pub const GIT_DIRTY: &str = env!("GIT_DIRTY"); +pub const NOIRC_VERSION: &str = env!("CARGO_PKG_VERSION"); + +/// Version string that gets placed in artifacts that Noir builds. This is semver compatible. +/// Note: You can't directly use the value of a constant produced with env! inside a concat! macro. +pub const NOIR_ARTIFACT_VERSION_STRING: &str = + concat!(env!("CARGO_PKG_VERSION"), "+", env!("GIT_COMMIT")); + #[derive(Args, Clone, Debug, Default, Serialize, Deserialize)] pub struct CompileOptions { /// Emit debug information for the intermediate SSA IR @@ -305,6 +314,7 @@ fn compile_contract_inner( .collect(), functions, file_map, + noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), }) } else { Err(errors) @@ -342,5 +352,12 @@ pub fn compile_no_check( let file_map = filter_relevant_files(&[debug.clone()], &context.file_manager); - Ok(CompiledProgram { hash, circuit, debug, abi, file_map }) + Ok(CompiledProgram { + hash, + circuit, + debug, + abi, + file_map, + noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), + }) } diff --git a/compiler/noirc_driver/src/program.rs b/compiler/noirc_driver/src/program.rs index 8a13092aeb6..fe991567180 100644 --- a/compiler/noirc_driver/src/program.rs +++ b/compiler/noirc_driver/src/program.rs @@ -12,6 +12,7 @@ use super::debug::DebugFile; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct CompiledProgram { + pub noir_version: String, /// Hash of the [`Program`][noirc_frontend::monomorphization::ast::Program] from which this [`CompiledProgram`] /// was compiled. /// diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index d846ede566f..9c820220e07 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -427,20 +427,10 @@ impl AcirContext { let diff_expr = &lhs_expr - &rhs_expr; // Check to see if equality can be determined at compile-time. - if diff_expr.is_const() { - if diff_expr.is_zero() { - // Constraint is always true - assertion is unnecessary. - self.mark_variables_equivalent(lhs, rhs)?; - return Ok(()); - } else { - // Constraint is always false - this program is unprovable. - return Err(RuntimeError::FailedConstraint { - lhs: Box::new(lhs_expr), - rhs: Box::new(rhs_expr), - call_stack: self.get_call_stack(), - assert_message, - }); - }; + if diff_expr.is_zero() { + // Constraint is always true - assertion is unnecessary. + self.mark_variables_equivalent(lhs, rhs)?; + return Ok(()); } self.acir_ir.assert_is_zero(diff_expr); diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index a458814ce9e..aa2bedb37b7 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -1173,6 +1173,11 @@ fn resolve_function_set( resolver.set_self_type(self_type.clone()); resolver.set_trait_id(unresolved_functions.trait_id); + // Without this, impl methods can accidentally be placed in contracts. See #3254 + if self_type.is_some() { + resolver.set_in_contract(false); + } + let (hir_func, func_meta, errs) = resolver.resolve_function(func, func_id); interner.push_fn_meta(func_meta, func_id); interner.update_fn(func_id, hir_func); diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index a4a85d85929..d527433630f 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -87,6 +87,14 @@ pub struct Resolver<'a> { /// Set to the current type if we're resolving an impl self_type: Option, + /// True if the current module is a contract. + /// This is usually determined by self.path_resolver.module_id(), but it can + /// be overriden for impls. Impls are an odd case since the methods within resolve + /// as if they're in the parent module, but should be placed in a child module. + /// Since they should be within a child module, in_contract is manually set to false + /// for these so we can still resolve them in the parent module without them being in a contract. + in_contract: bool, + /// Contains a mapping of the current struct or functions's generics to /// unique type variables if we're resolving a struct. Empty otherwise. /// This is a Vec rather than a map to preserve the order a functions generics @@ -119,6 +127,9 @@ impl<'a> Resolver<'a> { def_maps: &'a BTreeMap, file: FileId, ) -> Resolver<'a> { + let module_id = path_resolver.module_id(); + let in_contract = module_id.module(def_maps).is_contract; + Self { path_resolver, def_maps, @@ -131,6 +142,7 @@ impl<'a> Resolver<'a> { errors: Vec::new(), lambda_stack: Vec::new(), file, + in_contract, } } @@ -805,9 +817,16 @@ impl<'a> Resolver<'a> { } } + /// Override whether this name resolver is within a contract or not. + /// This will affect which types are allowed as parameters to methods as well + /// as which modifiers are allowed on a function. + pub(crate) fn set_in_contract(&mut self, in_contract: bool) { + self.in_contract = in_contract; + } + /// True if the 'pub' keyword is allowed on parameters in this function fn pub_allowed(&self, func: &NoirFunction) -> bool { - if self.in_contract() { + if self.in_contract { !func.def.is_unconstrained } else { func.name() == MAIN_FUNCTION @@ -815,7 +834,7 @@ impl<'a> Resolver<'a> { } fn is_entry_point_function(&self, func: &NoirFunction) -> bool { - if self.in_contract() { + if self.in_contract { func.attributes().is_contract_entry_point() } else { func.name() == MAIN_FUNCTION @@ -824,7 +843,7 @@ impl<'a> Resolver<'a> { /// True if the `distinct` keyword is allowed on a function's return type fn distinct_allowed(&self, func: &NoirFunction) -> bool { - if self.in_contract() { + if self.in_contract { // "open" and "unconstrained" functions are compiled to brillig and thus duplication of // witness indices in their abis is not a concern. !func.def.is_unconstrained && !func.def.is_open @@ -836,7 +855,7 @@ impl<'a> Resolver<'a> { fn handle_function_type(&mut self, function: &FuncId) { let function_type = self.interner.function_modifiers(function).contract_function_type; - if !self.in_contract() && function_type == Some(ContractFunctionType::Open) { + if !self.in_contract && function_type == Some(ContractFunctionType::Open) { let span = self.interner.function_ident(function).span(); self.errors.push(ResolverError::ContractFunctionTypeInNormalFunction { span }); self.interner.function_modifiers_mut(function).contract_function_type = None; @@ -844,7 +863,7 @@ impl<'a> Resolver<'a> { } fn handle_is_function_internal(&mut self, function: &FuncId) { - if !self.in_contract() { + if !self.in_contract { if self.interner.function_modifiers(function).is_internal == Some(true) { let span = self.interner.function_ident(function).span(); self.push_err(ResolverError::ContractFunctionInternalInNormalFunction { span }); @@ -1605,11 +1624,6 @@ impl<'a> Resolver<'a> { } } - fn in_contract(&self) -> bool { - let module_id = self.path_resolver.module_id(); - module_id.module(self.def_maps).is_contract - } - fn resolve_fmt_str_literal(&mut self, str: String, call_expr_span: Span) -> HirLiteral { let re = Regex::new(r"\{([a-zA-Z0-9_]+)\}") .expect("ICE: an invalid regex pattern was used for checking format strings"); diff --git a/compiler/wasm/build.rs b/compiler/wasm/build.rs index 3b96be74ef3..dc46037a1d9 100644 --- a/compiler/wasm/build.rs +++ b/compiler/wasm/build.rs @@ -1,14 +1,4 @@ -const GIT_COMMIT: &&str = &"GIT_COMMIT"; - fn main() { - // Only use build_data if the environment variable isn't set - // The environment variable is always set when working via Nix - if std::env::var(GIT_COMMIT).is_err() { - build_data::set_GIT_COMMIT(); - build_data::set_GIT_DIRTY(); - build_data::no_debug_rebuilds(); - } - build_data::set_SOURCE_TIMESTAMP(); build_data::no_debug_rebuilds(); } diff --git a/compiler/wasm/src/compile.rs b/compiler/wasm/src/compile.rs index 0f7baff4819..66f08dbabf5 100644 --- a/compiler/wasm/src/compile.rs +++ b/compiler/wasm/src/compile.rs @@ -7,7 +7,7 @@ use nargo::artifacts::{ }; use noirc_driver::{ add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, CompileOptions, - CompiledContract, CompiledProgram, + CompiledContract, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING, }; use noirc_frontend::{graph::CrateGraph, hir::Context}; use std::path::Path; @@ -118,6 +118,7 @@ fn preprocess_program(program: CompiledProgram) -> PreprocessedProgram { hash: program.hash, backend: String::from(BACKEND_IDENTIFIER), abi: program.abi, + noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), bytecode: program.circuit, } } @@ -136,6 +137,7 @@ fn preprocess_contract(contract: CompiledContract) -> PreprocessedContract { .collect(); PreprocessedContract { + noir_version: String::from(NOIR_ARTIFACT_VERSION_STRING), name: contract.name, backend: String::from(BACKEND_IDENTIFIER), functions: preprocessed_functions, diff --git a/compiler/wasm/src/lib.rs b/compiler/wasm/src/lib.rs index 3a8e00bc6dd..9f2f558f85c 100644 --- a/compiler/wasm/src/lib.rs +++ b/compiler/wasm/src/lib.rs @@ -7,6 +7,7 @@ use getrandom as _; use gloo_utils::format::JsValueSerdeExt; use log::Level; +use noirc_driver::{GIT_COMMIT, GIT_DIRTY, NOIRC_VERSION}; use serde::{Deserialize, Serialize}; use std::str::FromStr; use wasm_bindgen::prelude::*; @@ -37,11 +38,8 @@ pub fn init_log_level(level: String) { }); } -const BUILD_INFO: BuildInfo = BuildInfo { - git_hash: env!("GIT_COMMIT"), - version: env!("CARGO_PKG_VERSION"), - dirty: env!("GIT_DIRTY"), -}; +const BUILD_INFO: BuildInfo = + BuildInfo { git_hash: GIT_COMMIT, version: NOIRC_VERSION, dirty: GIT_DIRTY }; #[wasm_bindgen] pub fn build_info() -> JsValue { diff --git a/docs/docs/language_concepts/01_functions.md b/docs/docs/language_concepts/01_functions.md index 2168c9203b6..22ee43681e5 100644 --- a/docs/docs/language_concepts/01_functions.md +++ b/docs/docs/language_concepts/01_functions.md @@ -148,19 +148,21 @@ See [Lambdas](./08_lambdas.md) for more details. Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. Supported attributes include: + - **builtin**: the function is implemented by the compiler, for efficiency purposes. -- **deprecated**: mark the function as *deprecated*. Calling the function will generate a warning: `warning: use of deprecated function` +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` - **field**: Used to enable conditional compilation of code depending on the field size. See below for more details -- **oracle**: mark the function as *oracle*; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./05_unconstrained.md) and [Noir js](../noir_js/noir_js.md) for more details. +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./05_unconstrained.md) and [NoirJS](../noir_js/noir_js.md) for more details. - **test**: mark the function as unit tests. See [Tests](../nargo/02_testing.md) for more details ### Field Attribute + The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. - Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + ```rust #[field(bn254)] fn foo() -> u32 { @@ -184,4 +186,4 @@ fn foo() -> u32 { } ``` -If the field name is not known to Noir, it will discard the function. Field names are case insensitive. \ No newline at end of file +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/docs/docs/nargo/01_commands.md b/docs/docs/nargo/01_commands.md index fbbe2b27666..65e2bdb44e3 100644 --- a/docs/docs/nargo/01_commands.md +++ b/docs/docs/nargo/01_commands.md @@ -20,12 +20,12 @@ keywords: ## General options -| Option | Description | -|---------------------|------------------------------------------------------| -| `--show-ssa` | Emit debug information for the intermediate SSA IR | -| `--deny-warnings` | Quit execution when warnings are emitted | -| `--silence-warnings`| Suppress warnings | -| `-h, --help` | Print help | +| Option | Description | +| -------------------- | -------------------------------------------------- | +| `--show-ssa` | Emit debug information for the intermediate SSA IR | +| `--deny-warnings` | Quit execution when warnings are emitted | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo help [subcommand]` @@ -33,9 +33,9 @@ Prints the list of available commands or specific information of a subcommand. _Arguments_ -| Argument | Description | -|---------------|--------------------------------------------------------| -| ``| The subcommand whose help message to display | +| Argument | Description | +| -------------- | -------------------------------------------- | +| `` | The subcommand whose help message to display | ## `nargo backend` @@ -43,20 +43,20 @@ Installs and selects custom backends used to generate and verify proofs. ### Commands -| Command | Description | -|------------|--------------------------------------------------| -| `current` | Prints the name of the currently active backend | -| `ls` | Prints the list of currently installed backends | -| `use` | Select the backend to use | -| `install` | Install a new backend from a URL | -| `uninstall`| Uninstalls a backend | -| `help` | Print this message or the help of the given subcommand(s) | +| Command | Description | +| ----------- | --------------------------------------------------------- | +| `current` | Prints the name of the currently active backend | +| `ls` | Prints the list of currently installed backends | +| `use` | Select the backend to use | +| `install` | Install a new backend from a URL | +| `uninstall` | Uninstalls a backend | +| `help` | Print this message or the help of the given subcommand(s) | ### Options -| Option | Description | -|---------------|--------------| -| `-h, --help` | Print help | +| Option | Description | +| ------------ | ----------- | +| `-h, --help` | Print help | ## `nargo check` @@ -65,14 +65,14 @@ values of the Noir program respectively. ### Options -| Option | Description | -|----------------------|---------------------------------------------------| -| `--package `| The name of the package to check | -| `--workspace` | Check all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to check | +| `--workspace` | Check all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ### `nargo codegen-verifier` @@ -80,14 +80,14 @@ Generate a Solidity verifier smart contract for the program. ### Options -| Option | Description | -|----------------------|---------------------------------------------------| -| `--package `| The name of the package to codegen | -| `--workspace` | Codegen all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to codegen | +| `--workspace` | Codegen all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo compile` @@ -98,15 +98,15 @@ You can also use "build" as an alias for compile (e.g. `nargo build`). ### Options -| Option | Description | -|----------------------|----------------------------------------------------| -| `--include-keys` | Include Proving and Verification keys in the build artifacts | -| `--package `| The name of the package to compile | -| `--workspace` | Compile all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| Option | Description | +| --------------------- | ------------------------------------------------------------ | +| `--include-keys` | Include Proving and Verification keys in the build artifacts | +| `--package ` | The name of the package to compile | +| `--workspace` | Compile all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo new ` @@ -114,19 +114,19 @@ Creates a new Noir project in a new folder. **Arguments** -| Argument | Description | -|-----------|------------------------------------| -| `` | The path to save the new project | +| Argument | Description | +| -------- | -------------------------------- | +| `` | The path to save the new project | ### Options -| Option | Description | -|-------------------|-------------------------------------------------| -| `--name ` | Name of the package [default: package directory name] | -| `--lib` | Use a library template | -| `--bin` | Use a binary template [default] | -| `--contract` | Use a contract template | -| `-h, --help` | Print help | +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: package directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | ## `nargo init` @@ -134,13 +134,13 @@ Creates a new Noir project in the current directory. ### Options -| Option | Description | -|-------------------|-------------------------------------------------| -| `--name ` | Name of the package [default: current directory name] | -| `--lib` | Use a library template | -| `--bin` | Use a binary template [default] | -| `--contract` | Use a contract template | -| `-h, --help` | Print help | +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: current directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | ## `nargo execute [WITNESS_NAME]` @@ -148,21 +148,21 @@ Runs the Noir program and prints its return value. **Arguments** -| Argument | Description | -|-----------------|------------------------------------------------| -| `[WITNESS_NAME]`| Write the execution witness to named file | +| Argument | Description | +| ---------------- | ----------------------------------------- | +| `[WITNESS_NAME]` | Write the execution witness to named file | ### Options -| Option | Description | -|-------------------------------|------------------------------------------------------------------| +| Option | Description | +| --------------------------------- | ------------------------------------------------------------------------------------ | | `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | -| `--package ` | The name of the package to execute | -| `--workspace` | Execute all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| `--package ` | The name of the package to execute | +| `--workspace` | Execute all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | _Usage_ @@ -178,17 +178,17 @@ Creates a proof for the program. ### Options -| Option | Description | -|-------------------------------|------------------------------------------------------------------| -| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | | `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | -| `--verify` | Verify proof after proving | -| `--package ` | The name of the package to prove | -| `--workspace` | Prove all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| `--verify` | Verify proof after proving | +| `--package ` | The name of the package to prove | +| `--workspace` | Prove all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo verify` @@ -196,15 +196,15 @@ Given a proof and a program, verify whether the proof is valid. ### Options -| Option | Description | -|-------------------------------|------------------------------------------------------------------| +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | | `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | -| `--package ` | The name of the package to verify | -| `--workspace` | Verify all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| `--package ` | The name of the package to verify | +| `--workspace` | Verify all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo test [TEST_NAME]` @@ -217,16 +217,16 @@ See an example on the [testing page](./testing). ### Options -| Option | Description | -|----------------------|---------------------------------------------------| -| `--show-output` | Display output of `println` statements | -| `--exact` | Only run tests that match exactly | -| `--package `| The name of the package to test | -| `--workspace` | Test all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | +| Option | Description | +| --------------------- | -------------------------------------- | +| `--show-output` | Display output of `println` statements | +| `--exact` | Only run tests that match exactly | +| `--package ` | The name of the package to test | +| `--workspace` | Test all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | ## `nargo info` @@ -244,3 +244,7 @@ above information about each function of the contract. Start a long-running Language Server process that communicates over stdin/stdout. Usually this command is not run by a user, but instead will be run by a Language Client, such as [vscode-noir](https://github.com/noir-lang/vscode-noir). + +## `nargo fmt` + +Automatically formats your Noir source code based on the default formatting settings. diff --git a/docs/docs/nargo/04_language_server.md b/docs/docs/nargo/04_language_server.md index 543af9ec5ff..144cd249c4b 100644 --- a/docs/docs/nargo/04_language_server.md +++ b/docs/docs/nargo/04_language_server.md @@ -25,7 +25,7 @@ Currently, Noir provides a Language Client for Visual Studio Code via the [vscod > > If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. -When you language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, execution, and tests: +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: ![Compile and Execute](./../../static/img/codelens_compile_execute.png) ![Run test](../../static/img/codelens_run_test.png) diff --git a/docs/docs/noir_js/getting_started/01_tiny_noir_app.md b/docs/docs/noir_js/getting_started/01_tiny_noir_app.md index 9878554dd60..c450427e8f7 100644 --- a/docs/docs/noir_js/getting_started/01_tiny_noir_app.md +++ b/docs/docs/noir_js/getting_started/01_tiny_noir_app.md @@ -1,10 +1,10 @@ --- -title: Full Stack Noir App +title: End-to-end description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs] --- -Noir JS works both on the browser and on the server, and works for both ESM and CJS module systems. In this page, we will learn how can we write a simple test and a simple web app to verify the standard Noir example. +NoirJS works both on the browser and on the server, and works for both ESM and CJS module systems. In this page, we will learn how can we write a simple test and a simple web app to verify the standard Noir example. ## Before we start diff --git a/docs/docs/noir_js/noir_js.md b/docs/docs/noir_js/noir_js.md index baaf409b522..23ea550e156 100644 --- a/docs/docs/noir_js/noir_js.md +++ b/docs/docs/noir_js/noir_js.md @@ -1,22 +1,36 @@ --- -title: Noir JS -description: Learn how to use noir js to use Noir in a Typescript or Javascript environment +title: NoirJS +description: Interact with Noir in Typescript or Javascript keywords: [Noir project, javascript, typescript, node.js, browser, react] --- -Noir JS are a set of typescript libraries that make it easy to use Noir on your dapp, webapp, node.js server, website, etc. +NoirJS is a TypeScript library that make it easy to use Noir on your dapp, webapp, Node.js server, website, etc. -It is composed of two major elements: +A typical workflow would be composed of two major elements: -- Noir -- Backend proving system +- NoirJS +- Proving backend of choice's JavaScript package -Your only concern should be to write Noir. Noir.js will work out-of-the box and abstract all the components, such as the ACVM and others. +To install NoirJS, install Node.js if you have not already and run this in your JavaScript project: -## Barretenberg +```bash +npm i @noir-lang/noir_js +``` -Since Noir is backend agnostic, you can instantiate `noir_js` without any backend (i.e. to execute a function). But for proving, you should instantiate it with any of the supported backends through their own `js` interface. +## Proving backend -Aztec Labs maintains the `barretenberg` backend. You can use it to instantiate your `Noir` class. +Since Noir is backend agnostic, you can instantiate NoirJS without any backend (i.e. to execute a function). But for proving, you would have to instantiate NoirJS with any of the supported backends through their own `js` interface. + +### Barretenberg + +Aztec Labs maintains the `barretenberg` proving backend, which you can instantiate and make use of alongside NoirJS. It is also the default proving backend installed and used with Nargo, the Noir CLI tool. + +To install its JavaScript library, run this in your project: + +```bash +npm i @noir-lang/backend_barretenberg +``` + +For more details on how to instantiate and use the libraries, refer to the [Full Noir App Guide](./getting_started/01_tiny_noir_app.md) and [Reference](./reference/01_noirjs.md) sections. diff --git a/docs/docs/noir_js/reference/02_noirjs.md b/docs/docs/noir_js/reference/01_noirjs.md similarity index 100% rename from docs/docs/noir_js/reference/02_noirjs.md rename to docs/docs/noir_js/reference/01_noirjs.md diff --git a/docs/docs/noir_js/reference/01_bb_backend.md b/docs/docs/noir_js/reference/02_bb_backend.md similarity index 99% rename from docs/docs/noir_js/reference/01_bb_backend.md rename to docs/docs/noir_js/reference/02_bb_backend.md index 446bf9820ea..21c2ff32b57 100644 --- a/docs/docs/noir_js/reference/01_bb_backend.md +++ b/docs/docs/noir_js/reference/02_bb_backend.md @@ -41,7 +41,7 @@ constructor(acirCircuit, (numberOfThreads = 1)); | Parameter | Type | Description | | ----------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `acirCircuit` | Object | A circuit represented in a `json` format, containing the ABI and bytecode Tipically obtained by running [`nargo compile`](../../nargo/01_commands.md). This is the same circuit expected to be passed to [the Noir class](02_noirjs.md) | +| `acirCircuit` | Object | A circuit represented in a `json` format, containing the ABI and bytecode Tipically obtained by running [`nargo compile`](../../nargo/01_commands.md). This is the same circuit expected to be passed to [the Noir class](01_noirjs.md) | | `numberOfThreads` | Number (optional) | The number of threads to be used by the backend. Defaults to 1. | ### Usage diff --git a/docs/sidebars.js b/docs/sidebars.js index 205ecb76038..8fddb677a58 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -98,7 +98,7 @@ const sidebars = { }, { type: 'category', - label: 'Noir JS', + label: 'NoirJS', link: { type: 'doc', id: 'noir_js/noir_js', @@ -106,21 +106,21 @@ const sidebars = { items: [ { type: 'category', - label: 'Reference', + label: 'Guides', items: [ { type: 'autogenerated', - dirName: 'noir_js/reference', + dirName: 'noir_js/getting_started', }, ], }, { type: 'category', - label: 'Guides', + label: 'Reference', items: [ { type: 'autogenerated', - dirName: 'noir_js/getting_started', + dirName: 'noir_js/reference', }, ], }, diff --git a/docs/static/img/codelens_compile_execute.png b/docs/static/img/codelens_compile_execute.png index 347490f529f..040e3af2704 100644 Binary files a/docs/static/img/codelens_compile_execute.png and b/docs/static/img/codelens_compile_execute.png differ diff --git a/docs/static/img/codelens_run_test.png b/docs/static/img/codelens_run_test.png index 3ff94df0903..568869fb839 100644 Binary files a/docs/static/img/codelens_run_test.png and b/docs/static/img/codelens_run_test.png differ diff --git a/noir_stdlib/src/sha256.nr b/noir_stdlib/src/sha256.nr index f6c22aa1d5f..d2afd21db8a 100644 --- a/noir_stdlib/src/sha256.nr +++ b/noir_stdlib/src/sha256.nr @@ -5,7 +5,9 @@ // Auxiliary mappings; names as in FIPS PUB 180-4 fn rotr32(a: u32, b: u32) -> u32 // 32-bit right rotation { - (a >> b) | (a << (32 as u32 - b)) + // None of the bits overlap between `(a >> b)` and `(a << (32 - b))` + // Addition is then equivalent to OR, with fewer constraints. + (a >> b) + (a << (32 - b)) } fn ch(x: u32, y: u32, z: u32) -> u32 diff --git a/noir_stdlib/src/sha512.nr b/noir_stdlib/src/sha512.nr index e5cac7b1554..c565b16c098 100644 --- a/noir_stdlib/src/sha512.nr +++ b/noir_stdlib/src/sha512.nr @@ -5,7 +5,9 @@ // Auxiliary mappings; names as in FIPS PUB 180-4 fn rotr64(a: u64, b: u64) -> u64 // 64-bit right rotation { - (a >> b) | (a << (64 - b)) + // None of the bits overlap between `(a >> b)` and `(a << (64 - b))` + // Addition is then equivalent to OR, with fewer constraints. + (a >> b) + (a << (64 - b)) } fn sha_ch(x: u64, y: u64, z: u64) -> u64 diff --git a/release-tests/test/version.test.js b/release-tests/test/version.test.js index 07051d1edce..7a70639d83e 100644 --- a/release-tests/test/version.test.js +++ b/release-tests/test/version.test.js @@ -21,9 +21,12 @@ test("promise resolved", async () => { test("prints version", async () => { const processOutput = (await $`${NARGO_BIN} --version`).toString(); - assert.match(processOutput, /nargo\s\d{1,2}.\d{1,2}/); + + // Regex to match the "nargo version" part of the output + assert.match(processOutput, /nargo version = \d{1,2}\.\d{1,2}\.\d{1,2}/); }); + test("reports a clean commit", async () => { const processOutput = (await $`${NARGO_BIN} --version`).toString(); assert.not.match(processOutput, /is dirty: true/) diff --git a/tooling/debugger/Cargo.toml b/tooling/debugger/Cargo.toml index e01abd9ac61..1f9a54b4fe8 100644 --- a/tooling/debugger/Cargo.toml +++ b/tooling/debugger/Cargo.toml @@ -13,5 +13,6 @@ acvm.workspace = true nargo.workspace = true noirc_printable_type.workspace = true thiserror.workspace = true +codespan-reporting.workspace = true easy-repl = "0.2.1" -owo-colors = "3" \ No newline at end of file +owo-colors = "3" diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index f8e9db19234..3da9c5c7989 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -4,16 +4,21 @@ use acvm::BlackBoxFunctionSolver; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use nargo::artifacts::debug::DebugArtifact; -use nargo::errors::ExecutionError; +use nargo::errors::{ExecutionError, Location}; use nargo::NargoError; use nargo::ops::ForeignCallExecutor; use easy_repl::{command, CommandStatus, Critical, Repl}; -use std::cell::{Cell, RefCell}; +use std::{ + cell::{Cell, RefCell}, + ops::Range, +}; use owo_colors::OwoColorize; +use codespan_reporting::files::Files; + enum SolveResult { Done, Ok, @@ -74,25 +79,72 @@ impl<'backend, B: BlackBoxFunctionSolver> DebugContext<'backend, B> { println!("Finished execution"); } else { println!("Stopped at opcode {}: {}", ip, opcodes[ip]); - Self::show_source_code_location(&OpcodeLocation::Acir(ip), &self.debug_artifact); + self.show_source_code_location(&OpcodeLocation::Acir(ip), &self.debug_artifact); } } - fn show_source_code_location(location: &OpcodeLocation, debug_artifact: &DebugArtifact) { + fn print_location_path(&self, loc: Location) { + let line_number = self.debug_artifact.location_line_number(loc).unwrap(); + let column_number = self.debug_artifact.location_column_number(loc).unwrap(); + + println!( + "At {}:{line_number}:{column_number}", + self.debug_artifact.name(loc.file).unwrap() + ); + } + + fn show_source_code_location(&self, location: &OpcodeLocation, debug_artifact: &DebugArtifact) { let locations = debug_artifact.debug_symbols[0].opcode_location(location); - if let Some(locations) = locations { - for loc in locations { - let file = &debug_artifact.file_map[&loc.file]; - let source = &file.source.as_str(); - let start = loc.span.start() as usize; - let end = loc.span.end() as usize; - println!("At {}:{start}-{end}", file.path.as_path().display()); - println!( - "\n{}{}{}\n", - &source[0..start].to_string().dimmed(), - &source[start..end], - &source[end..].to_string().dimmed(), - ); + let Some(locations) = locations else { return }; + for loc in locations { + self.print_location_path(loc); + + let loc_line_index = debug_artifact.location_line_index(loc).unwrap(); + + // How many lines before or after the location's line we + // print + let context_lines = 5; + + let first_line_to_print = + if loc_line_index < context_lines { 0 } else { loc_line_index - context_lines }; + + let last_line_index = debug_artifact.last_line_index(loc).unwrap(); + let last_line_to_print = std::cmp::min(loc_line_index + context_lines, last_line_index); + + let source = debug_artifact.location_source_code(loc).unwrap(); + for (current_line_index, line) in source.lines().enumerate() { + let current_line_number = current_line_index + 1; + + if current_line_index < first_line_to_print { + // Ignore lines before range starts + continue; + } else if current_line_index == first_line_to_print && current_line_index > 0 { + // Denote that there's more lines before but we're not showing them + print_line_of_ellipsis(current_line_index); + } + + if current_line_index > last_line_to_print { + // Denote that there's more lines after but we're not showing them, + // and stop printing + print_line_of_ellipsis(current_line_number); + break; + } + + if current_line_index == loc_line_index { + // Highlight current location + let Range { start: loc_start, end: loc_end } = + debug_artifact.location_in_line(loc).unwrap(); + println!( + "{:>3} {:2} {}{}{}", + current_line_number, + "->", + &line[0..loc_start].to_string().dimmed(), + &line[loc_start..loc_end], + &line[loc_end..].to_string().dimmed() + ); + } else { + print_dimmed_line(current_line_number, line); + } } } } @@ -112,6 +164,14 @@ impl<'backend, B: BlackBoxFunctionSolver> DebugContext<'backend, B> { } } +fn print_line_of_ellipsis(line_number: usize) { + println!("{}", format!("{:>3} {}", line_number, "...").dimmed()); +} + +fn print_dimmed_line(line_number: usize, line: &str) { + println!("{}", format!("{:>3} {:2} {}", line_number, "", line).dimmed()); +} + fn map_command_status(result: SolveResult) -> CommandStatus { match result { SolveResult::Ok => CommandStatus::Done, diff --git a/tooling/nargo/src/artifacts/contract.rs b/tooling/nargo/src/artifacts/contract.rs index fa161b63a5b..4f1ae0e10a0 100644 --- a/tooling/nargo/src/artifacts/contract.rs +++ b/tooling/nargo/src/artifacts/contract.rs @@ -10,6 +10,8 @@ use serde::{Deserialize, Serialize}; /// - Proving and verification keys have been pregenerated based on this ACIR. #[derive(Serialize, Deserialize)] pub struct PreprocessedContract { + /// Version of noir used to compile this contract + pub noir_version: String, /// The name of the contract. pub name: String, /// The identifier of the proving backend which this contract has been compiled for. diff --git a/tooling/nargo/src/artifacts/debug.rs b/tooling/nargo/src/artifacts/debug.rs index 31ce1d7bb6a..d2e2eb9f311 100644 --- a/tooling/nargo/src/artifacts/debug.rs +++ b/tooling/nargo/src/artifacts/debug.rs @@ -1,6 +1,6 @@ use codespan_reporting::files::{Error, Files, SimpleFile}; use noirc_driver::DebugFile; -use noirc_errors::debug_info::DebugInfo; +use noirc_errors::{debug_info::DebugInfo, Location}; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, BTreeSet}, @@ -45,6 +45,53 @@ impl DebugArtifact { Self { debug_symbols, file_map } } + + /// Given a location, returns its file's source code + pub fn location_source_code(&self, location: Location) -> Result<&str, Error> { + self.source(location.file) + } + + /// Given a location, returns the index of the line it starts at + pub fn location_line_index(&self, location: Location) -> Result { + let location_start = location.span.start() as usize; + self.line_index(location.file, location_start) + } + + /// Given a location, returns the line number it starts at + pub fn location_line_number(&self, location: Location) -> Result { + let location_start = location.span.start() as usize; + let line_index = self.line_index(location.file, location_start)?; + self.line_number(location.file, line_index) + } + + /// Given a location, returns the column number it starts at + pub fn location_column_number(&self, location: Location) -> Result { + let location_start = location.span.start() as usize; + let line_index = self.line_index(location.file, location_start)?; + self.column_number(location.file, line_index, location_start) + } + + /// Given a location, returns a Span relative to its line's + /// position in the file. This is useful when processing a file's + /// contents on a per-line-basis. + pub fn location_in_line(&self, location: Location) -> Result, Error> { + let location_start = location.span.start() as usize; + let location_end = location.span.end() as usize; + let line_index = self.line_index(location.file, location_start)?; + let line_span = self.line_range(location.file, line_index)?; + + let start_in_line = location_start - line_span.start; + let end_in_line = location_end - line_span.start; + + Ok(Range { start: start_in_line, end: end_in_line }) + } + + /// Given a location, returns the last line index + /// of its file + pub fn last_line_index(&self, location: Location) -> Result { + let source = self.source(location.file)?; + self.line_index(location.file, source.len()) + } } impl<'a> Files<'a> for DebugArtifact { diff --git a/tooling/nargo/src/artifacts/program.rs b/tooling/nargo/src/artifacts/program.rs index 190b4c76897..5988f3f59cb 100644 --- a/tooling/nargo/src/artifacts/program.rs +++ b/tooling/nargo/src/artifacts/program.rs @@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize}; /// - Proving and verification keys have been pregenerated based on this ACIR. #[derive(Serialize, Deserialize, Debug)] pub struct PreprocessedProgram { + pub noir_version: String, + /// Hash of the [`Program`][noirc_frontend::monomorphization::ast::Program] from which this [`PreprocessedProgram`] /// was compiled. /// diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index ea6e7fa8108..0c920716f2a 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -2,7 +2,10 @@ use acvm::{ acir::circuit::OpcodeLocation, pwg::{ErrorLocation, OpcodeResolutionError}, }; -use noirc_errors::{debug_info::DebugInfo, CustomDiagnostic, FileDiagnostic, Location}; +use noirc_errors::{debug_info::DebugInfo, CustomDiagnostic, FileDiagnostic}; + +pub use noirc_errors::Location; + use noirc_printable_type::ForeignCallError; use thiserror::Error; diff --git a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs index 35538ef1a83..856970544b8 100644 --- a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs +++ b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs @@ -76,7 +76,6 @@ fn smart_contract_for_package( workspace, package, compile_options, - false, np_language, &is_opcode_supported, )?; diff --git a/tooling/nargo_cli/src/cli/compile_cmd.rs b/tooling/nargo_cli/src/cli/compile_cmd.rs index a332d63e062..56f91843914 100644 --- a/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeMap; use std::path::Path; use acvm::acir::circuit::Opcode; @@ -14,8 +13,8 @@ use nargo::package::Package; use nargo::prepare_package; use nargo::workspace::Workspace; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; +use noirc_driver::NOIR_ARTIFACT_VERSION_STRING; use noirc_driver::{CompilationResult, CompileOptions, CompiledContract, CompiledProgram}; -use noirc_errors::debug_info::DebugInfo; use noirc_frontend::graph::CrateName; use clap::Args; @@ -23,9 +22,9 @@ use clap::Args; use crate::backends::Backend; use crate::errors::{CliError, CompileError}; -use super::fs::program::read_program_from_file; use super::fs::program::{ - save_contract_to_file, save_debug_artifact_to_file, save_program_to_file, + read_debug_artifact_from_file, read_program_from_file, save_contract_to_file, + save_debug_artifact_to_file, save_program_to_file, }; use super::NargoConfig; use rayon::prelude::*; @@ -40,10 +39,6 @@ pub(crate) struct CompileCommand { #[arg(long)] include_keys: bool, - /// Output debug files - #[arg(long, hide = true)] - output_debug: bool, - /// The name of the package to compile #[clap(long, conflicts_with = "workspace")] package: Option, @@ -82,12 +77,11 @@ pub(crate) fn run( np_language, &opcode_support, &args.compile_options, - args.output_debug, )?; // Save build artifacts to disk. for (package, contract) in contract_packages.into_iter().zip(compiled_contracts) { - save_contract(contract, &package, &circuit_dir, args.output_debug); + save_contract(contract, &package, &circuit_dir); } Ok(()) @@ -100,7 +94,6 @@ pub(super) fn compile_workspace( np_language: Language, opcode_support: &BackendOpcodeSupport, compile_options: &CompileOptions, - output_debug: bool, ) -> Result<(Vec, Vec), CliError> { let is_opcode_supported = |opcode: &_| opcode_support.is_opcode_supported(opcode); @@ -108,14 +101,7 @@ pub(super) fn compile_workspace( let program_results: Vec<(FileManager, CompilationResult)> = binary_packages .par_iter() .map(|package| { - compile_program( - workspace, - package, - compile_options, - output_debug, - np_language, - &is_opcode_supported, - ) + compile_program(workspace, package, compile_options, np_language, &is_opcode_supported) }) .collect(); let contract_results: Vec<(FileManager, CompilationResult)> = @@ -157,7 +143,6 @@ pub(crate) fn compile_bin_package( workspace: &Workspace, package: &Package, compile_options: &CompileOptions, - output_debug: bool, np_language: Language, is_opcode_supported: &impl Fn(&Opcode) -> bool, ) -> Result { @@ -165,14 +150,8 @@ pub(crate) fn compile_bin_package( return Err(CompileError::LibraryCrate(package.name.clone()).into()); } - let (file_manager, compilation_result) = compile_program( - workspace, - package, - compile_options, - output_debug, - np_language, - &is_opcode_supported, - ); + let (file_manager, compilation_result) = + compile_program(workspace, package, compile_options, np_language, &is_opcode_supported); let program = report_errors( compilation_result, @@ -188,31 +167,33 @@ fn compile_program( workspace: &Workspace, package: &Package, compile_options: &CompileOptions, - output_debug: bool, np_language: Language, is_opcode_supported: &impl Fn(&Opcode) -> bool, ) -> (FileManager, CompilationResult) { let (mut context, crate_id) = prepare_package(package, Box::new(|path| std::fs::read_to_string(path))); - let cached_program = if let Ok(preprocessed_program) = - read_program_from_file(workspace.package_build_path(package)) - { - // TODO: Load debug information. + let program_artifact_path = workspace.package_build_path(package); + let mut debug_artifact_path = program_artifact_path.clone(); + debug_artifact_path.set_file_name(format!("debug_{}.json", package.name)); + let cached_program = if let (Ok(preprocessed_program), Ok(mut debug_artifact)) = ( + read_program_from_file(program_artifact_path), + read_debug_artifact_from_file(debug_artifact_path), + ) { Some(CompiledProgram { hash: preprocessed_program.hash, circuit: preprocessed_program.bytecode, abi: preprocessed_program.abi, - debug: DebugInfo::default(), - file_map: BTreeMap::new(), + noir_version: preprocessed_program.noir_version, + debug: debug_artifact.debug_symbols.remove(0), + file_map: debug_artifact.file_map, }) } else { None }; - // If we want to output the debug information then we need to perform a full recompilation of the ACIR. - let force_recompile = output_debug; - + let force_recompile = + cached_program.as_ref().map_or(false, |p| p.noir_version != NOIR_ARTIFACT_VERSION_STRING); let (program, warnings) = match noirc_driver::compile_main( &mut context, crate_id, @@ -231,12 +212,7 @@ fn compile_program( nargo::ops::optimize_program(program, np_language, &is_opcode_supported) .expect("Backend does not support an opcode that is in the IR"); - save_program( - optimized_program.clone(), - package, - &workspace.target_directory_path(), - output_debug, - ); + save_program(optimized_program.clone(), package, &workspace.target_directory_path()); (context.file_manager, Ok((optimized_program, warnings))) } @@ -264,35 +240,24 @@ fn compile_contract( (context.file_manager, Ok((optimized_contract, warnings))) } -fn save_program( - program: CompiledProgram, - package: &Package, - circuit_dir: &Path, - output_debug: bool, -) { +fn save_program(program: CompiledProgram, package: &Package, circuit_dir: &Path) { let preprocessed_program = PreprocessedProgram { hash: program.hash, backend: String::from(BACKEND_IDENTIFIER), abi: program.abi, + noir_version: program.noir_version, bytecode: program.circuit, }; save_program_to_file(&preprocessed_program, &package.name, circuit_dir); - if output_debug { - let debug_artifact = - DebugArtifact { debug_symbols: vec![program.debug], file_map: program.file_map }; - let circuit_name: String = (&package.name).into(); - save_debug_artifact_to_file(&debug_artifact, &circuit_name, circuit_dir); - } + let debug_artifact = + DebugArtifact { debug_symbols: vec![program.debug], file_map: program.file_map }; + let circuit_name: String = (&package.name).into(); + save_debug_artifact_to_file(&debug_artifact, &circuit_name, circuit_dir); } -fn save_contract( - contract: CompiledContract, - package: &Package, - circuit_dir: &Path, - output_debug: bool, -) { +fn save_contract(contract: CompiledContract, package: &Package, circuit_dir: &Path) { // TODO(#1389): I wonder if it is incorrect for nargo-core to know anything about contracts. // As can be seen here, It seems like a leaky abstraction where ContractFunctions (essentially CompiledPrograms) // are compiled via nargo-core and then the PreprocessedContract is constructed here. @@ -311,6 +276,7 @@ fn save_contract( }); let preprocessed_contract = PreprocessedContract { + noir_version: contract.noir_version, name: contract.name, backend: String::from(BACKEND_IDENTIFIER), functions: preprocessed_functions, @@ -323,13 +289,11 @@ fn save_contract( circuit_dir, ); - if output_debug { - save_debug_artifact_to_file( - &debug_artifact, - &format!("{}-{}", package.name, preprocessed_contract.name), - circuit_dir, - ); - } + save_debug_artifact_to_file( + &debug_artifact, + &format!("{}-{}", package.name, preprocessed_contract.name), + circuit_dir, + ); } /// Helper function for reporting any errors in a `CompilationResult` diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 7d8c0dc9a15..82cd3349ec4 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -52,14 +52,10 @@ pub(crate) fn run( return Ok(()); }; - let compiled_program = compile_bin_package( - &workspace, - package, - &args.compile_options, - true, - np_language, - &|opcode| opcode_support.is_opcode_supported(opcode), - )?; + let compiled_program = + compile_bin_package(&workspace, package, &args.compile_options, np_language, &|opcode| { + opcode_support.is_opcode_supported(opcode) + })?; println!("[{}] Starting debugger", package.name); let (return_value, solved_witness) = diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index c61dc6db69c..1819c9a6f06 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -57,7 +57,6 @@ pub(crate) fn run( &workspace, package, &args.compile_options, - false, np_language, &|opcode| opcode_support.is_opcode_supported(opcode), )?; diff --git a/tooling/nargo_cli/src/cli/fs/program.rs b/tooling/nargo_cli/src/cli/fs/program.rs index 377786627be..e82f2d55264 100644 --- a/tooling/nargo_cli/src/cli/fs/program.rs +++ b/tooling/nargo_cli/src/cli/fs/program.rs @@ -60,3 +60,14 @@ pub(crate) fn read_program_from_file>( Ok(program) } + +pub(crate) fn read_debug_artifact_from_file>( + debug_artifact_path: P, +) -> Result { + let input_string = std::fs::read(&debug_artifact_path) + .map_err(|_| FilesystemError::PathNotValid(debug_artifact_path.as_ref().into()))?; + let program = serde_json::from_slice(&input_string) + .map_err(|err| FilesystemError::ProgramSerializationError(err.to_string()))?; + + Ok(program) +} diff --git a/tooling/nargo_cli/src/cli/info_cmd.rs b/tooling/nargo_cli/src/cli/info_cmd.rs index e55b1b7886f..50021e842c4 100644 --- a/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/tooling/nargo_cli/src/cli/info_cmd.rs @@ -63,7 +63,6 @@ pub(crate) fn run( np_language, &opcode_support, &args.compile_options, - false, )?; let program_info = binary_packages diff --git a/tooling/nargo_cli/src/cli/init_cmd.rs b/tooling/nargo_cli/src/cli/init_cmd.rs index 6dc7b9bd98e..9d7700a6598 100644 --- a/tooling/nargo_cli/src/cli/init_cmd.rs +++ b/tooling/nargo_cli/src/cli/init_cmd.rs @@ -2,10 +2,11 @@ use crate::backends::Backend; use crate::errors::CliError; use super::fs::{create_named_dir, write_to_file}; -use super::{NargoConfig, CARGO_PKG_VERSION}; +use super::NargoConfig; use clap::Args; use nargo::constants::{PKG_FILE, SRC_DIR}; use nargo::package::PackageType; +use noirc_driver::NOIRC_VERSION; use noirc_frontend::graph::CrateName; use std::path::PathBuf; @@ -72,7 +73,7 @@ pub(crate) fn initialize_project( name = "{package_name}" type = "{package_type}" authors = [""] -compiler_version = "{CARGO_PKG_VERSION}" +compiler_version = "{NOIRC_VERSION}" [dependencies]"# ); diff --git a/tooling/nargo_cli/src/cli/mod.rs b/tooling/nargo_cli/src/cli/mod.rs index a0ef778e1a5..8d22fb1b204 100644 --- a/tooling/nargo_cli/src/cli/mod.rs +++ b/tooling/nargo_cli/src/cli/mod.rs @@ -1,6 +1,7 @@ use clap::{Args, Parser, Subcommand}; use const_format::formatcp; use nargo_toml::find_package_root; +use noirc_driver::NOIR_ARTIFACT_VERSION_STRING; use std::path::PathBuf; use color_eyre::eyre; @@ -26,10 +27,15 @@ mod verify_cmd; const GIT_HASH: &str = env!("GIT_COMMIT"); const IS_DIRTY: &str = env!("GIT_DIRTY"); -const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); - -static VERSION_STRING: &str = - formatcp!("{} (git version hash: {}, is dirty: {})", CARGO_PKG_VERSION, GIT_HASH, IS_DIRTY); +const NARGO_VERSION: &str = env!("CARGO_PKG_VERSION"); + +static VERSION_STRING: &str = formatcp!( + "version = {}\nnoirc version = {}\n(git version hash: {}, is dirty: {})", + NARGO_VERSION, + NOIR_ARTIFACT_VERSION_STRING, + GIT_HASH, + IS_DIRTY +); #[derive(Parser, Debug)] #[command(name="nargo", author, version=VERSION_STRING, about, long_about = None)] diff --git a/tooling/nargo_cli/src/cli/prove_cmd.rs b/tooling/nargo_cli/src/cli/prove_cmd.rs index 5571117e2d4..af300b7ebe0 100644 --- a/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -59,7 +59,6 @@ pub(crate) fn run( &workspace, package, &args.compile_options, - false, np_language, &|opcode| opcode_support.is_opcode_supported(opcode), )?; diff --git a/tooling/nargo_cli/src/cli/verify_cmd.rs b/tooling/nargo_cli/src/cli/verify_cmd.rs index a5a39e9aef9..6ae2b78fd0c 100644 --- a/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -50,7 +50,6 @@ pub(crate) fn run( &workspace, package, &args.compile_options, - false, np_language, &|opcode| opcode_support.is_opcode_supported(opcode), )?; diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/acir.gz index e6800ef5893..1d12077353e 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/witness.gz index 40ca3d3f63f..6bedf3922b0 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz index 5b5ac8f865a..b13e4554bd3 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz index 66d8125a852..746c6fc56a2 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/Nargo.toml b/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/Nargo.toml new file mode 100644 index 00000000000..99340cf80b5 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "simple_contract" +type = "contract" +authors = [""] +compiler_version = "0.1" + +[dependencies] + diff --git a/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/src/main.nr b/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/src/main.nr new file mode 100644 index 00000000000..fa04e0d3e7b --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_contract/contract_with_impl/src/main.nr @@ -0,0 +1,8 @@ + +contract Foo { + struct T { x: [Field] } + + impl T { + fn t(self){} + } +} diff --git a/tooling/nargo_fmt/src/config.rs b/tooling/nargo_fmt/src/config.rs index aaa5cbac3d0..dd57778da92 100644 --- a/tooling/nargo_fmt/src/config.rs +++ b/tooling/nargo_fmt/src/config.rs @@ -47,6 +47,7 @@ config! { remove_nested_parens: bool, true, "Remove nested parens"; short_array_element_width_threshold: usize, 10, "Width threshold for an array element to be considered short"; array_width: usize, 100, "Maximum width of an array literal before falling back to vertical formatting"; + single_line_if_else_max_width: usize, 100, "Maximum line length for single line if-else expressions"; } impl Config { diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index d1c642b87d2..68c6b8bd1e3 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -218,7 +218,7 @@ impl Item for Expression { } fn format(self, visitor: &FmtVisitor) -> String { - visitor.format_expr(self) + visitor.format_subexpr(self) } } @@ -232,7 +232,7 @@ impl Item for (Ident, Expression) { let (name, expr) = self; let name = name.0.contents; - let expr = visitor.format_expr(expr); + let expr = visitor.format_subexpr(expr); if name == expr { name diff --git a/tooling/nargo_fmt/src/visitor.rs b/tooling/nargo_fmt/src/visitor.rs index 32e41cb34db..d1c87d311e7 100644 --- a/tooling/nargo_fmt/src/visitor.rs +++ b/tooling/nargo_fmt/src/visitor.rs @@ -164,7 +164,7 @@ impl<'me> FmtVisitor<'me> { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] struct Indent { block_indent: usize, } @@ -197,3 +197,9 @@ struct Shape { width: usize, indent: Indent, } + +#[derive(PartialEq, Eq, Debug)] +pub(crate) enum ExpressionType { + Statement, + SubExpression, +} diff --git a/tooling/nargo_fmt/src/visitor/expr.rs b/tooling/nargo_fmt/src/visitor/expr.rs index 89674f3f17a..dcf4dbbd3b1 100644 --- a/tooling/nargo_fmt/src/visitor/expr.rs +++ b/tooling/nargo_fmt/src/visitor/expr.rs @@ -1,26 +1,35 @@ use noirc_frontend::{ hir::resolution::errors::Span, token::Token, ArrayLiteral, BlockExpression, - ConstructorExpression, Expression, ExpressionKind, Literal, Statement, UnaryOp, + ConstructorExpression, Expression, ExpressionKind, IfExpression, Literal, Statement, + StatementKind, UnaryOp, }; -use super::{FmtVisitor, Shape}; +use super::{ExpressionType, FmtVisitor, Indent, Shape}; use crate::{ utils::{self, Expr, FindToken, Item}, Config, }; impl FmtVisitor<'_> { - pub(crate) fn visit_expr(&mut self, expr: Expression) { + pub(crate) fn visit_expr(&mut self, expr: Expression, expr_type: ExpressionType) { let span = expr.span; - let rewrite = self.format_expr(expr); + let rewrite = self.format_expr(expr, expr_type); let rewrite = utils::recover_comment_removed(self.slice(span), rewrite); self.push_rewrite(rewrite, span); self.last_position = span.end(); } - pub(crate) fn format_expr(&self, Expression { kind, mut span }: Expression) -> String { + pub(crate) fn format_subexpr(&self, expression: Expression) -> String { + self.format_expr(expression, ExpressionType::SubExpression) + } + + pub(crate) fn format_expr( + &self, + Expression { kind, mut span }: Expression, + expr_type: ExpressionType, + ) -> String { match kind { ExpressionKind::Block(block) => { let mut visitor = self.fork(); @@ -41,24 +50,24 @@ impl FmtVisitor<'_> { } }; - format!("{op}{}", self.format_expr(prefix.rhs)) + format!("{op}{}", self.format_subexpr(prefix.rhs)) } ExpressionKind::Cast(cast) => { - format!("{} as {}", self.format_expr(cast.lhs), cast.r#type) + format!("{} as {}", self.format_subexpr(cast.lhs), cast.r#type) } ExpressionKind::Infix(infix) => { format!( "{} {} {}", - self.format_expr(infix.lhs), + self.format_subexpr(infix.lhs), infix.operator.contents.as_string(), - self.format_expr(infix.rhs) + self.format_subexpr(infix.rhs) ) } ExpressionKind::Call(call_expr) => { let args_span = self.span_before(call_expr.func.span.end()..span.end(), Token::LeftParen); - let callee = self.format_expr(*call_expr.func); + let callee = self.format_subexpr(*call_expr.func); let args = format_parens(self.fork(), false, call_expr.arguments, args_span); format!("{callee}{args}") @@ -69,21 +78,21 @@ impl FmtVisitor<'_> { Token::LeftParen, ); - let object = self.format_expr(method_call_expr.object); + let object = self.format_subexpr(method_call_expr.object); let method = method_call_expr.method_name.to_string(); let args = format_parens(self.fork(), false, method_call_expr.arguments, args_span); format!("{object}.{method}{args}") } ExpressionKind::MemberAccess(member_access_expr) => { - let lhs_str = self.format_expr(member_access_expr.lhs); + let lhs_str = self.format_subexpr(member_access_expr.lhs); format!("{}.{}", lhs_str, member_access_expr.rhs) } ExpressionKind::Index(index_expr) => { let index_span = self .span_before(index_expr.collection.span.end()..span.end(), Token::LeftBracket); - let collection = self.format_expr(index_expr.collection); + let collection = self.format_subexpr(index_expr.collection); let index = format_brackets(self.fork(), false, vec![index_expr.index], index_span); format!("{collection}{index}") @@ -96,8 +105,8 @@ impl FmtVisitor<'_> { self.slice(span).to_string() } Literal::Array(ArrayLiteral::Repeated { repeated_element, length }) => { - let repeated = self.format_expr(*repeated_element); - let length = self.format_expr(*length); + let repeated = self.format_subexpr(*repeated_element); + let length = self.format_subexpr(*length); format!("[{repeated}; {length}]") } @@ -131,7 +140,7 @@ impl FmtVisitor<'_> { } if !leading.contains("//") && !trailing.contains("//") { - let sub_expr = self.format_expr(*sub_expr); + let sub_expr = self.format_subexpr(*sub_expr); format!("({leading}{sub_expr}{trailing})") } else { let mut visitor = self.fork(); @@ -140,7 +149,7 @@ impl FmtVisitor<'_> { visitor.indent.block_indent(self.config); let nested_indent = visitor.indent.to_string_with_newline(); - let sub_expr = visitor.format_expr(*sub_expr); + let sub_expr = visitor.format_subexpr(*sub_expr); let mut result = String::new(); result.push('('); @@ -171,11 +180,66 @@ impl FmtVisitor<'_> { self.format_struct_lit(type_name, fields_span, *constructor) } - // TODO: - _expr => self.slice(span).to_string(), + ExpressionKind::If(if_expr) => { + let allow_single_line = expr_type == ExpressionType::SubExpression; + + if allow_single_line { + let mut visitor = self.fork(); + visitor.indent = Indent::default(); + if let Some(line) = visitor.format_if_single_line(*if_expr.clone()) { + return line; + } + } + + self.format_if(*if_expr) + } + _ => self.slice(span).to_string(), } } + fn format_if(&self, if_expr: IfExpression) -> String { + let condition_str = self.format_subexpr(if_expr.condition); + let consequence_str = self.format_subexpr(if_expr.consequence); + + let mut result = format!("if {condition_str} {consequence_str}"); + + if let Some(alternative) = if_expr.alternative { + let alternative = if let Some(ExpressionKind::If(if_expr)) = + extract_simple_expr(alternative.clone()).map(|expr| expr.kind) + { + self.format_if(*if_expr) + } else { + self.format_expr(alternative, ExpressionType::Statement) + }; + + result.push_str(" else "); + result.push_str(&alternative); + }; + + result + } + + fn format_if_single_line(&self, if_expr: IfExpression) -> Option { + let condition_str = self.format_subexpr(if_expr.condition); + let consequence_str = self.format_subexpr(extract_simple_expr(if_expr.consequence)?); + + let if_str = if let Some(alternative) = if_expr.alternative { + let alternative_str = if let Some(ExpressionKind::If(_)) = + extract_simple_expr(alternative.clone()).map(|expr| expr.kind) + { + return None; + } else { + self.format_expr(extract_simple_expr(alternative)?, ExpressionType::Statement) + }; + + format!("if {} {{ {} }} else {{ {} }}", condition_str, consequence_str, alternative_str) + } else { + format!("if {{{}}} {{{}}}", condition_str, consequence_str) + }; + + (if_str.len() <= self.config.single_line_if_else_max_width).then_some(if_str) + } + fn format_struct_lit( &self, type_name: &str, @@ -515,3 +579,15 @@ fn has_single_line_comment(slice: &str) -> bool { fn no_long_exprs(exprs: &[Expr], max_width: usize) -> bool { exprs.iter().all(|expr| expr.value.len() <= max_width) } + +fn extract_simple_expr(expr: Expression) -> Option { + if let ExpressionKind::Block(mut block) = expr.kind { + if block.len() == 1 { + if let StatementKind::Expression(expr) = block.pop().unwrap() { + return expr.into(); + } + } + } + + None +} diff --git a/tooling/nargo_fmt/src/visitor/stmt.rs b/tooling/nargo_fmt/src/visitor/stmt.rs index 724f6bc7156..ca28c8a5c06 100644 --- a/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/tooling/nargo_fmt/src/visitor/stmt.rs @@ -1,18 +1,30 @@ +use std::iter::zip; + use noirc_frontend::{Statement, StatementKind}; +use super::ExpressionType; + impl super::FmtVisitor<'_> { pub(crate) fn visit_stmts(&mut self, stmts: Vec) { - for Statement { kind, span } in stmts { + let len = stmts.len(); + + for (Statement { kind, span }, index) in zip(stmts, 1..) { + let is_last = index == len; + match kind { - StatementKind::Expression(expr) => self.visit_expr(expr), + StatementKind::Expression(expr) => self.visit_expr( + expr, + if is_last { ExpressionType::SubExpression } else { ExpressionType::Statement }, + ), StatementKind::Semi(expr) => { - self.visit_expr(expr); + self.visit_expr(expr, ExpressionType::Statement); self.push_str(";"); } StatementKind::Let(let_stmt) => { let let_str = self.slice(span.start()..let_stmt.expression.span.start()).trim_end(); - let expr_str = self.format_expr(let_stmt.expression); + let expr_str = + self.format_expr(let_stmt.expression, ExpressionType::SubExpression); self.push_rewrite(format!("{let_str} {expr_str};"), span); } diff --git a/tooling/nargo_fmt/tests/expected/expr.nr b/tooling/nargo_fmt/tests/expected/expr.nr index 6c95b6925ef..20eb6ad547d 100644 --- a/tooling/nargo_fmt/tests/expected/expr.nr +++ b/tooling/nargo_fmt/tests/expected/expr.nr @@ -94,3 +94,29 @@ fn parenthesized() { fn constructor() { Point { x: 5, y: 10 }; } + +fn if_expr() { + if true { + println("Hello :D"); + } +} + +fn return_if_expr() { + if true { 42 } else { 40 + 2 } +} + +fn return_if_expr() { + if true { + 42 + }; + + if true { 42 } else { 40 + 2 } +} + +fn if_if() { + if cond { + some(); + } else { + none(); + }.bar().baz(); +} diff --git a/tooling/nargo_fmt/tests/expected/if.nr b/tooling/nargo_fmt/tests/expected/if.nr new file mode 100644 index 00000000000..9893239750c --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/if.nr @@ -0,0 +1,40 @@ +fn main() { + if false { + (); + (); + } + + if false // lone if comment + { + (); + (); + } + + let a = if 0 > 1 { 0 } else { 0 }; + + if true { + (); + } else if false { + (); + (); + } else { + (); + (); + (); + } + + if true // else-if-chain if comment + { + (); + } + else if false // else-if-chain else-if comment + { + (); + (); + } else // else-if-chain else comment + { + (); + (); + (); + } +} diff --git a/tooling/nargo_fmt/tests/expected/nested_if_else.nr b/tooling/nargo_fmt/tests/expected/nested_if_else.nr index 8aa120e3b18..dfd203189e8 100644 --- a/tooling/nargo_fmt/tests/expected/nested_if_else.nr +++ b/tooling/nargo_fmt/tests/expected/nested_if_else.nr @@ -1,3 +1,9 @@ fn nested_if_else() { - if false { 1 } else if false { 2 } else { 3 } + if false { + 1 + } else if false { + 2 + } else { + 3 + } } diff --git a/tooling/nargo_fmt/tests/input/expr.nr b/tooling/nargo_fmt/tests/input/expr.nr index ff2ac1e10a2..28ba9cb0585 100644 --- a/tooling/nargo_fmt/tests/input/expr.nr +++ b/tooling/nargo_fmt/tests/input/expr.nr @@ -103,4 +103,33 @@ fn parenthesized() { fn constructor() { Point{x :5, y: 10 }; +} + +fn if_expr() { + if true { println("Hello :D"); } +} + +fn return_if_expr() { + if true { +42 +} +else +{ 40 + 2 } +} + +fn return_if_expr() { + if true {42}; + + if true { + 42 + } + else { + 40 + + 2 } +} + +fn if_if() { +if cond { some(); } else { none(); } + .bar() + .baz(); } \ No newline at end of file diff --git a/tooling/nargo_fmt/tests/input/if.nr b/tooling/nargo_fmt/tests/input/if.nr new file mode 100644 index 00000000000..0985928396d --- /dev/null +++ b/tooling/nargo_fmt/tests/input/if.nr @@ -0,0 +1,52 @@ +fn main() { + if false + { + (); + (); + } + + if false // lone if comment + { + (); + (); + } + + + let a = + if 0 > 1 { + 0 + } + else + { + 0 + }; + + + if true + { + (); + } else if false { + (); + (); + } + else { + (); + (); + (); + } + + if true // else-if-chain if comment + { + (); + } + else if false // else-if-chain else-if comment + { + (); + (); + } else // else-if-chain else comment + { + (); + (); + (); + } +}