diff --git a/.prettierignore b/.prettierignore index 5138b38cc6c5..297c599d56f4 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,6 +2,32 @@ bellman-cuda sdk/zksync-rs/CHANGELOG.md CHANGELOG.md +core/lib/dal/.sqlx +prover/lib/dal/.sqlx +node_modules # Ignore contract submodules contracts + +**/target/** +**/node_modules +volumes +**/build/** +dist +.git +generated +grafonnet-lib +prettier-config +lint-config +**/cache +**/artifacts +**/typechain +binaryen +system-contracts +artifacts-zk +cache-zk +// Ignore directories with OZ and forge submodules. +contracts/l1-contracts/lib + +**/.git +**/node_modules diff --git a/core/tests/vm-benchmark/deployment_benchmarks_sources/heap_read_write.sol b/core/tests/vm-benchmark/deployment_benchmarks_sources/heap_read_write.sol index d5a503eb7087..6aa7ca59a0c4 100644 --- a/core/tests/vm-benchmark/deployment_benchmarks_sources/heap_read_write.sol +++ b/core/tests/vm-benchmark/deployment_benchmarks_sources/heap_read_write.sol @@ -11,11 +11,15 @@ contract HeapBenchmark { mstore(add(array, sub(n, 1)), 4242) let j := 0 - for {} lt(j, n) {} { + for { + + } lt(j, n) { + + } { v1 := mload(add(array, mod(mul(j, j), n))) v2 := mload(add(array, j)) mstore(add(array, j), add(add(v1, v2), 42)) - j := add(j, 1) + j := add(j, 1) if gt(j, sub(n, 1)) { j := 0 } diff --git a/etc/contracts-test-data/counter/counter.sol b/etc/contracts-test-data/counter/counter.sol index 3c4e19222762..ec9219d7a199 100644 --- a/etc/contracts-test-data/counter/counter.sol +++ b/etc/contracts-test-data/counter/counter.sol @@ -9,13 +9,13 @@ contract Counter { value += x; } - function incrementWithRevertPayable(uint256 x, bool shouldRevert) payable public returns (uint256) { + function incrementWithRevertPayable(uint256 x, bool shouldRevert) public payable returns (uint256) { return incrementWithRevert(x, shouldRevert); } function incrementWithRevert(uint256 x, bool shouldRevert) public returns (uint256) { value += x; - if(shouldRevert) { + if (shouldRevert) { revert("This method always reverts"); } return value; @@ -24,4 +24,4 @@ contract Counter { function get() public view returns (uint256) { return value; } -} \ No newline at end of file +} diff --git a/zk_toolbox/Cargo.lock b/zk_toolbox/Cargo.lock index 04a29f5b0f42..140ef509194d 100644 --- a/zk_toolbox/Cargo.lock +++ b/zk_toolbox/Cargo.lock @@ -6294,6 +6294,7 @@ dependencies = [ "clap-markdown", "common", "config", + "futures", "human-panic", "serde", "strum", diff --git a/zk_toolbox/crates/zk_supervisor/Cargo.toml b/zk_toolbox/crates/zk_supervisor/Cargo.toml index 911fba2248a0..e1225de96d32 100644 --- a/zk_toolbox/crates/zk_supervisor/Cargo.toml +++ b/zk_toolbox/crates/zk_supervisor/Cargo.toml @@ -22,3 +22,4 @@ url.workspace = true xshell.workspace = true serde.workspace = true clap-markdown.workspace = true +futures.workspace = true diff --git a/zk_toolbox/crates/zk_supervisor/src/commands/fmt.rs b/zk_toolbox/crates/zk_supervisor/src/commands/fmt.rs new file mode 100644 index 000000000000..fa0f4cef7bfe --- /dev/null +++ b/zk_toolbox/crates/zk_supervisor/src/commands/fmt.rs @@ -0,0 +1,127 @@ +use std::path::PathBuf; + +use clap::Parser; +use common::{cmd::Cmd, logger, spinner::Spinner}; +use config::EcosystemConfig; +use xshell::{cmd, Shell}; + +use crate::{ + commands::lint_utils::{get_unignored_files, Extension}, + messages::{ + msg_running_fmt_for_extension_spinner, msg_running_fmt_for_extensions_spinner, + msg_running_rustfmt_for_dir_spinner, MSG_RUNNING_CONTRACTS_FMT_SPINNER, + }, +}; + +async fn prettier(shell: Shell, extension: Extension, check: bool) -> anyhow::Result<()> { + let spinner = Spinner::new(&msg_running_fmt_for_extension_spinner(extension)); + let files = get_unignored_files(&shell, &extension)?; + + if files.is_empty() { + return Ok(()); + } + + spinner.freeze(); + let mode = if check { "--check" } else { "--write" }; + let config = format!("etc/prettier-config/{extension}.js"); + Ok( + Cmd::new(cmd!(shell, "yarn --silent prettier {mode} --config {config}").args(files)) + .run()?, + ) +} + +async fn prettier_contracts(shell: Shell, check: bool) -> anyhow::Result<()> { + let spinner = Spinner::new(MSG_RUNNING_CONTRACTS_FMT_SPINNER); + spinner.freeze(); + let prettier_command = cmd!(shell, "yarn --silent --cwd contracts") + .arg(format!("prettier:{}", if check { "check" } else { "fix" })); + + Ok(Cmd::new(prettier_command).run()?) +} + +async fn rustfmt(shell: Shell, check: bool, link_to_code: PathBuf) -> anyhow::Result<()> { + for dir in [".", "prover", "zk_toolbox"] { + let spinner = Spinner::new(&msg_running_rustfmt_for_dir_spinner(dir)); + let _dir = shell.push_dir(link_to_code.join(dir)); + let mut cmd = cmd!(shell, "cargo fmt -- --config imports_granularity=Crate --config group_imports=StdExternalCrate"); + if check { + cmd = cmd.arg("--check"); + } + spinner.freeze(); + Cmd::new(cmd).run()?; + } + Ok(()) +} + +async fn run_all_rust_formatters( + shell: Shell, + check: bool, + link_to_code: PathBuf, +) -> anyhow::Result<()> { + rustfmt(shell.clone(), check, link_to_code).await?; + Ok(()) +} + +#[derive(Debug, Parser)] +pub enum Formatter { + Rustfmt, + Contract, + Prettier { + #[arg(short, long)] + extensions: Vec, + }, +} + +#[derive(Debug, Parser)] +pub struct FmtArgs { + #[clap(long, short = 'c')] + pub check: bool, + #[clap(subcommand)] + pub formatter: Option, +} + +pub async fn run(shell: Shell, args: FmtArgs) -> anyhow::Result<()> { + let ecosystem = EcosystemConfig::from_file(&shell)?; + match args.formatter { + None => { + let mut tasks = vec![]; + let extensions: Vec<_> = + vec![Extension::Js, Extension::Ts, Extension::Md, Extension::Sol]; + let spinner = Spinner::new(&msg_running_fmt_for_extensions_spinner(&extensions)); + spinner.freeze(); + for ext in extensions { + tasks.push(tokio::spawn(prettier(shell.clone(), ext, args.check))); + } + tasks.push(tokio::spawn(rustfmt( + shell.clone(), + args.check, + ecosystem.link_to_code, + ))); + tasks.push(tokio::spawn(prettier_contracts(shell.clone(), args.check))); + + futures::future::join_all(tasks) + .await + .iter() + .for_each(|res| { + if let Err(err) = res { + logger::error(err) + } + }); + } + Some(Formatter::Prettier { mut extensions }) => { + if extensions.is_empty() { + extensions = vec![Extension::Js, Extension::Ts, Extension::Md, Extension::Sol]; + } + let spinner = Spinner::new(&msg_running_fmt_for_extensions_spinner(&extensions)); + for ext in extensions { + prettier(shell.clone(), ext, args.check).await? + } + spinner.finish() + } + Some(Formatter::Rustfmt) => { + run_all_rust_formatters(shell.clone(), args.check, ".".into()).await? + } + Some(Formatter::Contract) => prettier_contracts(shell.clone(), args.check).await?, + } + Ok(()) +} diff --git a/zk_toolbox/crates/zk_supervisor/src/commands/lint.rs b/zk_toolbox/crates/zk_supervisor/src/commands/lint.rs index bbad72f65377..17c8680f1d24 100644 --- a/zk_toolbox/crates/zk_supervisor/src/commands/lint.rs +++ b/zk_toolbox/crates/zk_supervisor/src/commands/lint.rs @@ -1,43 +1,16 @@ -use clap::{Parser, ValueEnum}; +use clap::Parser; use common::{cmd::Cmd, logger, spinner::Spinner}; use config::EcosystemConfig; -use strum::EnumIter; use xshell::{cmd, Shell}; -use crate::messages::{ - msg_running_linter_for_extension_spinner, msg_running_linters_for_files, - MSG_LINT_CONFIG_PATH_ERR, MSG_RUNNING_CONTRACTS_LINTER_SPINNER, +use crate::{ + commands::lint_utils::{get_unignored_files, Extension}, + messages::{ + msg_running_linter_for_extension_spinner, msg_running_linters_for_files, + MSG_LINT_CONFIG_PATH_ERR, MSG_RUNNING_CONTRACTS_LINTER_SPINNER, + }, }; -const IGNORED_DIRS: [&str; 18] = [ - "target", - "node_modules", - "volumes", - "build", - "dist", - ".git", - "generated", - "grafonnet-lib", - "prettier-config", - "lint-config", - "cache", - "artifacts", - "typechain", - "binaryen", - "system-contracts", - "artifacts-zk", - "cache-zk", - // Ignore directories with OZ and forge submodules. - "contracts/l1-contracts/lib", -]; - -const IGNORED_FILES: [&str; 4] = [ - "KeysWithPlonkVerifier.sol", - "TokenInit.sol", - ".tslintrc.js", - ".prettierrc.js", -]; - const CONFIG_PATH: &str = "etc/lint-config"; #[derive(Debug, Parser)] @@ -48,16 +21,6 @@ pub struct LintArgs { pub extensions: Vec, } -#[derive(Debug, ValueEnum, EnumIter, strum::Display, PartialEq, Eq, Clone)] -#[strum(serialize_all = "lowercase")] -pub enum Extension { - Rs, - Md, - Sol, - Js, - Ts, -} - pub fn run(shell: &Shell, args: LintArgs) -> anyhow::Result<()> { let extensions = if args.extensions.is_empty() { vec![ @@ -77,7 +40,7 @@ pub fn run(shell: &Shell, args: LintArgs) -> anyhow::Result<()> { for extension in extensions { match extension { - Extension::Rs => lint_rs(shell, &ecosystem)?, + Extension::Rs => lint_rs(shell, &ecosystem, args.check)?, Extension::Sol => lint_contracts(shell, &ecosystem, args.check)?, ext => lint(shell, &ecosystem, &ext, args.check)?, } @@ -86,7 +49,7 @@ pub fn run(shell: &Shell, args: LintArgs) -> anyhow::Result<()> { Ok(()) } -fn lint_rs(shell: &Shell, ecosystem: &EcosystemConfig) -> anyhow::Result<()> { +fn lint_rs(shell: &Shell, ecosystem: &EcosystemConfig, check: bool) -> anyhow::Result<()> { let spinner = Spinner::new(&msg_running_linter_for_extension_spinner(&Extension::Rs)); let link_to_code = &ecosystem.link_to_code; @@ -94,17 +57,25 @@ fn lint_rs(shell: &Shell, ecosystem: &EcosystemConfig) -> anyhow::Result<()> { let link_to_toolbox = &ecosystem.link_to_code.join("zk_toolbox"); let paths = vec![link_to_code, lint_to_prover, link_to_toolbox]; + spinner.freeze(); for path in paths { let _dir_guard = shell.push_dir(path); - Cmd::new(cmd!( - shell, - "cargo clippy --locked -- -D warnings -D unstable_features" - )) - .run()?; + let mut cmd = cmd!(shell, "cargo clippy"); + let common_args = &[ + "--locked", + "--", + "-D", + "warnings", + "-D", + "unstable_features", + ]; + if !check { + cmd = cmd.args(&["--fix", "--allow-dirty"]); + } + cmd = cmd.args(common_args); + Cmd::new(cmd).with_force_run().run()?; } - spinner.finish(); - Ok(()) } @@ -127,7 +98,6 @@ fn lint( let spinner = Spinner::new(&msg_running_linter_for_extension_spinner(extension)); let _dir_guard = shell.push_dir(&ecosystem.link_to_code); let files = get_unignored_files(shell, extension)?; - let cmd = cmd!(shell, "yarn"); let config_path = ecosystem.link_to_code.join(CONFIG_PATH); let config_path = config_path.join(format!("{}.js", extension)); @@ -170,20 +140,3 @@ fn lint_contracts(shell: &Shell, ecosystem: &EcosystemConfig, check: bool) -> an Ok(()) } - -fn get_unignored_files(shell: &Shell, extension: &Extension) -> anyhow::Result> { - let mut files = Vec::new(); - let output = cmd!(shell, "git ls-files").read()?; - - for line in output.lines() { - let path = line.to_string(); - if !IGNORED_DIRS.iter().any(|dir| path.contains(dir)) - && !IGNORED_FILES.contains(&path.as_str()) - && path.ends_with(&format!(".{}", extension)) - { - files.push(path); - } - } - - Ok(files) -} diff --git a/zk_toolbox/crates/zk_supervisor/src/commands/lint_utils.rs b/zk_toolbox/crates/zk_supervisor/src/commands/lint_utils.rs new file mode 100644 index 000000000000..92fac6ea815f --- /dev/null +++ b/zk_toolbox/crates/zk_supervisor/src/commands/lint_utils.rs @@ -0,0 +1,59 @@ +use clap::ValueEnum; +use strum::EnumIter; +use xshell::{cmd, Shell}; + +const IGNORED_DIRS: [&str; 18] = [ + "target", + "node_modules", + "volumes", + "build", + "dist", + ".git", + "generated", + "grafonnet-lib", + "prettier-config", + "lint-config", + "cache", + "artifacts", + "typechain", + "binaryen", + "system-contracts", + "artifacts-zk", + "cache-zk", + // Ignore directories with OZ and forge submodules. + "contracts/l1-contracts/lib", +]; + +const IGNORED_FILES: [&str; 4] = [ + "KeysWithPlonkVerifier.sol", + "TokenInit.sol", + ".tslintrc.js", + ".prettierrc.js", +]; + +#[derive(Debug, ValueEnum, EnumIter, strum::Display, PartialEq, Eq, Clone, Copy)] +#[strum(serialize_all = "lowercase")] +pub enum Extension { + Md, + Sol, + Js, + Ts, + Rs, +} + +pub fn get_unignored_files(shell: &Shell, extension: &Extension) -> anyhow::Result> { + let mut files = Vec::new(); + let output = cmd!(shell, "git ls-files --recurse-submodules").read()?; + + for line in output.lines() { + let path = line.to_string(); + if !IGNORED_DIRS.iter().any(|dir| path.contains(dir)) + && !IGNORED_FILES.contains(&path.as_str()) + && path.ends_with(&format!(".{}", extension)) + { + files.push(path); + } + } + + Ok(files) +} diff --git a/zk_toolbox/crates/zk_supervisor/src/commands/mod.rs b/zk_toolbox/crates/zk_supervisor/src/commands/mod.rs index b7a6a54f1211..99a8fa5e0a5f 100644 --- a/zk_toolbox/crates/zk_supervisor/src/commands/mod.rs +++ b/zk_toolbox/crates/zk_supervisor/src/commands/mod.rs @@ -1,5 +1,7 @@ pub mod clean; pub mod database; +pub mod fmt; pub mod lint; +pub(crate) mod lint_utils; pub mod snapshot; pub mod test; diff --git a/zk_toolbox/crates/zk_supervisor/src/main.rs b/zk_toolbox/crates/zk_supervisor/src/main.rs index 51b8f00ef373..d2c4b2320f41 100644 --- a/zk_toolbox/crates/zk_supervisor/src/main.rs +++ b/zk_toolbox/crates/zk_supervisor/src/main.rs @@ -11,11 +11,12 @@ use common::{ use config::EcosystemConfig; use messages::{ msg_global_chain_does_not_exist, MSG_SUBCOMMAND_CLEAN, MSG_SUBCOMMAND_DATABASE_ABOUT, - MSG_SUBCOMMAND_LINT_ABOUT, MSG_SUBCOMMAND_TESTS_ABOUT, + MSG_SUBCOMMAND_FMT_ABOUT, MSG_SUBCOMMAND_LINT_ABOUT, MSG_SUBCOMMAND_SNAPSHOTS_CREATOR_ABOUT, + MSG_SUBCOMMAND_TESTS_ABOUT, }; use xshell::Shell; -use crate::commands::clean::CleanCommands; +use crate::commands::{clean::CleanCommands, fmt::FmtArgs}; mod commands; mod dals; @@ -38,10 +39,12 @@ enum SupervisorSubcommands { Test(TestCommands), #[command(subcommand, about = MSG_SUBCOMMAND_CLEAN)] Clean(CleanCommands), - #[command(subcommand, about = "Snapshots creator")] + #[command(subcommand, about = MSG_SUBCOMMAND_SNAPSHOTS_CREATOR_ABOUT)] Snapshot(SnapshotCommands), #[command(about = MSG_SUBCOMMAND_LINT_ABOUT, alias = "l")] Lint(LintArgs), + #[command(about = MSG_SUBCOMMAND_FMT_ABOUT)] + Fmt(FmtArgs), #[command(hide = true)] Markdown, } @@ -99,6 +102,7 @@ async fn run_subcommand(args: Supervisor, shell: &Shell) -> anyhow::Result<()> { clap_markdown::print_help_markdown::(); } SupervisorSubcommands::Lint(args) => commands::lint::run(shell, args)?, + SupervisorSubcommands::Fmt(args) => commands::fmt::run(shell.clone(), args).await?, } Ok(()) } diff --git a/zk_toolbox/crates/zk_supervisor/src/messages.rs b/zk_toolbox/crates/zk_supervisor/src/messages.rs index 6368cb4e3d53..9f057c0b73aa 100644 --- a/zk_toolbox/crates/zk_supervisor/src/messages.rs +++ b/zk_toolbox/crates/zk_supervisor/src/messages.rs @@ -1,4 +1,4 @@ -use crate::commands::lint::Extension; +use crate::commands::lint_utils::Extension; // Ecosystem related messages pub(super) const MSG_CHAIN_NOT_FOUND_ERR: &str = "Chain not found"; @@ -13,6 +13,10 @@ pub(super) const MSG_SUBCOMMAND_TESTS_ABOUT: &str = "Run tests"; pub(super) const MSG_SUBCOMMAND_CLEAN: &str = "Clean artifacts"; pub(super) const MSG_SUBCOMMAND_LINT_ABOUT: &str = "Lint code"; +pub(super) const MSG_SUBCOMMAND_FMT_ABOUT: &str = "Format code"; + +pub(super) const MSG_SUBCOMMAND_SNAPSHOTS_CREATOR_ABOUT: &str = "Snapshots creator"; + // Database related messages pub(super) const MSG_NO_DATABASES_SELECTED: &str = "No databases selected"; @@ -152,5 +156,18 @@ pub(super) fn msg_running_linter_for_extension_spinner(extension: &Extension) -> format!("Running linter for files with extension: .{}", extension) } +pub(super) fn msg_running_fmt_for_extension_spinner(extension: Extension) -> String { + format!("Running prettier for: {extension:?}") +} + +pub(super) fn msg_running_rustfmt_for_dir_spinner(dir: &str) -> String { + format!("Running rustfmt for: {dir:?}") +} + +pub(super) fn msg_running_fmt_for_extensions_spinner(extensions: &[Extension]) -> String { + format!("Running prettier for: {extensions:?} and rustfmt") +} + pub(super) const MSG_LINT_CONFIG_PATH_ERR: &str = "Lint config path error"; pub(super) const MSG_RUNNING_CONTRACTS_LINTER_SPINNER: &str = "Running contracts linter.."; +pub(super) const MSG_RUNNING_CONTRACTS_FMT_SPINNER: &str = "Running prettier for contracts..";