Skip to content

Commit d2f92bc

Browse files
authored
chore(common::shell): finish implementation + enforce in clippy (foundry-rs#9268)
* enforce for script and verify crates * complete and enforce common shell * permit eprintln! due to circular dependency outside of common path * avoid code duplication
1 parent 9df5939 commit d2f92bc

File tree

39 files changed

+188
-160
lines changed

39 files changed

+188
-160
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

clippy.toml

+7-7
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ msrv = "1.80"
33
# so it is safe to ignore it as well
44
ignore-interior-mutability = ["bytes::Bytes", "alloy_primitives::Bytes"]
55

6-
# disallowed-macros = [
7-
# # See `foundry_common::shell`
8-
# { path = "std::print", reason = "use `sh_print` or similar macros instead" },
9-
# { path = "std::eprint", reason = "use `sh_eprint` or similar macros instead" },
10-
# { path = "std::println", reason = "use `sh_println` or similar macros instead" },
11-
# { path = "std::eprintln", reason = "use `sh_eprintln` or similar macros instead" },
12-
# ]
6+
disallowed-macros = [
7+
# See `foundry_common::shell`
8+
{ path = "std::print", reason = "use `sh_print` or similar macros instead" },
9+
{ path = "std::eprint", reason = "use `sh_eprint` or similar macros instead" },
10+
{ path = "std::println", reason = "use `sh_println` or similar macros instead" },
11+
{ path = "std::eprintln", reason = "use `sh_eprintln` or similar macros instead" },
12+
]

crates/anvil/tests/it/eip4844.rs

+2
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ async fn can_check_blob_fields_on_genesis() {
205205
assert_eq!(block.header.excess_blob_gas, Some(0));
206206
}
207207

208+
#[allow(clippy::disallowed_macros)]
208209
#[tokio::test(flavor = "multi_thread")]
209210
async fn can_correctly_estimate_blob_gas_with_recommended_fillers() {
210211
let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into()));
@@ -247,6 +248,7 @@ async fn can_correctly_estimate_blob_gas_with_recommended_fillers() {
247248
);
248249
}
249250

251+
#[allow(clippy::disallowed_macros)]
250252
#[tokio::test(flavor = "multi_thread")]
251253
async fn can_correctly_estimate_blob_gas_with_recommended_fillers_with_signer() {
252254
let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into()));

crates/anvil/tests/it/pubsub.rs

+1
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ async fn test_subscriptions() {
247247
assert_eq!(blocks, vec![1, 2, 3])
248248
}
249249

250+
#[allow(clippy::disallowed_macros)]
250251
#[tokio::test(flavor = "multi_thread")]
251252
async fn test_sub_new_heads_fast() {
252253
let (api, handle) = spawn(NodeConfig::test()).await;

crates/cast/bin/main.rs

+25-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#[macro_use]
22
extern crate tracing;
33

4+
use alloy_dyn_abi::DynSolValue;
45
use alloy_primitives::{eip191_hash_message, hex, keccak256, Address, B256};
56
use alloy_provider::Provider;
67
use alloy_rpc_types::{BlockId, BlockNumberOrTag::Latest};
@@ -12,7 +13,7 @@ use foundry_cli::{handler, utils};
1213
use foundry_common::{
1314
abi::get_event,
1415
ens::{namehash, ProviderEnsExt},
15-
fmt::{format_uint_exp, print_tokens},
16+
fmt::{format_tokens, format_tokens_raw, format_uint_exp},
1617
fs,
1718
selectors::{
1819
decode_calldata, decode_event_topic, decode_function_selector, decode_selectors,
@@ -135,11 +136,11 @@ async fn main_args(args: CastArgs) -> Result<()> {
135136
}
136137
CastSubcommand::ParseUnits { value, unit } => {
137138
let value = stdin::unwrap_line(value)?;
138-
println!("{}", SimpleCast::parse_units(&value, unit)?);
139+
sh_println!("{}", SimpleCast::parse_units(&value, unit)?)?;
139140
}
140141
CastSubcommand::FormatUnits { value, unit } => {
141142
let value = stdin::unwrap_line(value)?;
142-
println!("{}", SimpleCast::format_units(&value, unit)?);
143+
sh_println!("{}", SimpleCast::format_units(&value, unit)?)?;
143144
}
144145
CastSubcommand::FromWei { value, unit } => {
145146
let value = stdin::unwrap_line(value)?;
@@ -189,7 +190,7 @@ async fn main_args(args: CastArgs) -> Result<()> {
189190
// ABI encoding & decoding
190191
CastSubcommand::AbiDecode { sig, calldata, input } => {
191192
let tokens = SimpleCast::abi_decode(&sig, &calldata, input)?;
192-
print_tokens(&tokens, shell::is_json())
193+
print_tokens(&tokens);
193194
}
194195
CastSubcommand::AbiEncode { sig, packed, args } => {
195196
if !packed {
@@ -200,14 +201,14 @@ async fn main_args(args: CastArgs) -> Result<()> {
200201
}
201202
CastSubcommand::CalldataDecode { sig, calldata } => {
202203
let tokens = SimpleCast::calldata_decode(&sig, &calldata, true)?;
203-
print_tokens(&tokens, shell::is_json())
204+
print_tokens(&tokens);
204205
}
205206
CastSubcommand::CalldataEncode { sig, args } => {
206207
sh_println!("{}", SimpleCast::calldata_encode(sig, &args)?)?;
207208
}
208209
CastSubcommand::StringDecode { data } => {
209210
let tokens = SimpleCast::calldata_decode("Any(string)", &data, true)?;
210-
print_tokens(&tokens, shell::is_json())
211+
print_tokens(&tokens);
211212
}
212213
CastSubcommand::Interface(cmd) => cmd.run().await?,
213214
CastSubcommand::CreationCode(cmd) => cmd.run().await?,
@@ -482,7 +483,7 @@ async fn main_args(args: CastArgs) -> Result<()> {
482483
};
483484

484485
let tokens = SimpleCast::calldata_decode(sig, &calldata, true)?;
485-
print_tokens(&tokens, shell::is_json())
486+
print_tokens(&tokens);
486487
}
487488
CastSubcommand::FourByteEvent { topic } => {
488489
let topic = stdin::unwrap_line(topic)?;
@@ -618,5 +619,22 @@ async fn main_args(args: CastArgs) -> Result<()> {
618619
sh_println!("{}", SimpleCast::decode_eof(&eof)?)?
619620
}
620621
};
622+
623+
/// Prints slice of tokens using [`format_tokens`] or [`format_tokens_raw`] depending whether
624+
/// the shell is in JSON mode.
625+
///
626+
/// This is included here to avoid a cyclic dependency between `fmt` and `common`.
627+
fn print_tokens(tokens: &[DynSolValue]) {
628+
if shell::is_json() {
629+
let tokens: Vec<String> = format_tokens_raw(tokens).collect();
630+
let _ = sh_println!("{}", serde_json::to_string_pretty(&tokens).unwrap());
631+
} else {
632+
let tokens = format_tokens(tokens);
633+
tokens.for_each(|t| {
634+
let _ = sh_println!("{t}");
635+
});
636+
}
637+
}
638+
621639
Ok(())
622640
}

crates/cheatcodes/spec/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ impl Cheatcodes<'static> {
103103
}
104104

105105
#[cfg(test)]
106+
#[allow(clippy::disallowed_macros)]
106107
mod tests {
107108
use super::*;
108109
use std::{fs, path::Path};

crates/cheatcodes/src/fs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -620,11 +620,11 @@ fn prompt(
620620

621621
match rx.recv_timeout(timeout) {
622622
Ok(res) => res.map_err(|err| {
623-
println!();
623+
let _ = sh_println!();
624624
err.to_string().into()
625625
}),
626626
Err(_) => {
627-
println!();
627+
let _ = sh_eprintln!();
628628
Err("Prompt timed out".into())
629629
}
630630
}

crates/cheatcodes/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
77
#![allow(elided_lifetimes_in_paths)] // Cheats context uses 3 lifetimes
88

9+
#[macro_use]
10+
extern crate foundry_common;
11+
912
#[macro_use]
1013
pub extern crate foundry_cheatcodes_spec as spec;
14+
1115
#[macro_use]
1216
extern crate tracing;
1317

crates/cli/src/utils/cmd.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,8 @@ pub fn needs_setup(abi: &JsonAbi) -> bool {
128128

129129
for setup_fn in setup_fns.iter() {
130130
if setup_fn.name != "setUp" {
131-
println!(
132-
"{} Found invalid setup function \"{}\" did you mean \"setUp()\"?",
133-
"Warning:".yellow().bold(),
131+
let _ = sh_warn!(
132+
"Found invalid setup function \"{}\" did you mean \"setUp()\"?",
134133
setup_fn.signature()
135134
);
136135
}
@@ -450,19 +449,19 @@ pub async fn print_traces(
450449
) -> Result<()> {
451450
let traces = result.traces.as_mut().expect("No traces found");
452451

453-
println!("Traces:");
452+
sh_println!("Traces:")?;
454453
for (_, arena) in traces {
455454
decode_trace_arena(arena, decoder).await?;
456-
println!("{}", render_trace_arena_with_bytecodes(arena, verbose));
455+
sh_println!("{}", render_trace_arena_with_bytecodes(arena, verbose))?;
457456
}
458-
println!();
457+
sh_println!()?;
459458

460459
if result.success {
461-
println!("{}", "Transaction successfully executed.".green());
460+
sh_println!("{}", "Transaction successfully executed.".green())?;
462461
} else {
463-
println!("{}", "Transaction failed.".red());
462+
sh_err!("Transaction failed.")?;
464463
}
465464

466-
println!("Gas used: {}", result.gas_used);
465+
sh_println!("Gas used: {}", result.gas_used)?;
467466
Ok(())
468467
}

crates/common/fmt/src/dynamic.rs

-12
Original file line numberDiff line numberDiff line change
@@ -132,18 +132,6 @@ pub fn format_tokens_raw(tokens: &[DynSolValue]) -> impl Iterator<Item = String>
132132
tokens.iter().map(format_token_raw)
133133
}
134134

135-
/// Prints slice of tokens using [`format_tokens`] or [`format_tokens_raw`] depending on `json`
136-
/// parameter.
137-
pub fn print_tokens(tokens: &[DynSolValue], json: bool) {
138-
if json {
139-
let tokens: Vec<String> = format_tokens_raw(tokens).collect();
140-
println!("{}", serde_json::to_string_pretty(&tokens).unwrap());
141-
} else {
142-
let tokens = format_tokens(tokens);
143-
tokens.for_each(|t| println!("{t}"));
144-
}
145-
}
146-
147135
/// Pretty-prints the given value into a string suitable for user output.
148136
pub fn format_token(value: &DynSolValue) -> String {
149137
DynValueDisplay::new(value, false).to_string()

crates/common/fmt/src/lib.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ mod console;
44
pub use console::{console_format, ConsoleFmt, FormatSpec};
55

66
mod dynamic;
7-
pub use dynamic::{
8-
format_token, format_token_raw, format_tokens, format_tokens_raw, parse_tokens, print_tokens,
9-
};
7+
pub use dynamic::{format_token, format_token_raw, format_tokens, format_tokens_raw, parse_tokens};
108

119
mod exp;
1210
pub use exp::{format_int_exp, format_uint_exp, to_exp_notation};

crates/common/src/abi.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ pub fn find_source(
146146
Ok(source)
147147
} else {
148148
let implementation = metadata.implementation.unwrap();
149-
println!(
149+
sh_println!(
150150
"Contract at {address} is a proxy, trying to fetch source at {implementation}..."
151-
);
151+
)?;
152152
match find_source(client, implementation).await {
153153
impl_source @ Ok(_) => impl_source,
154154
Err(e) => {

crates/common/src/compile.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ impl ProjectCompiler {
127127
pub fn compile<C: Compiler>(mut self, project: &Project<C>) -> Result<ProjectCompileOutput<C>> {
128128
// TODO: Avoid process::exit
129129
if !project.paths.has_input_files() && self.files.is_empty() {
130-
println!("Nothing to compile");
130+
sh_println!("Nothing to compile")?;
131131
// nothing to do here
132132
std::process::exit(0);
133133
}
@@ -182,10 +182,10 @@ impl ProjectCompiler {
182182

183183
if !quiet {
184184
if output.is_unchanged() {
185-
println!("No files changed, compilation skipped");
185+
sh_println!("No files changed, compilation skipped")?;
186186
} else {
187187
// print the compiler output / warnings
188-
println!("{output}");
188+
sh_println!("{output}")?;
189189
}
190190

191191
self.handle_output(&output);
@@ -206,20 +206,22 @@ impl ProjectCompiler {
206206
artifacts.entry(version).or_default().push(name);
207207
}
208208
for (version, names) in artifacts {
209-
println!(
209+
let _ = sh_println!(
210210
" compiler version: {}.{}.{}",
211-
version.major, version.minor, version.patch
211+
version.major,
212+
version.minor,
213+
version.patch
212214
);
213215
for name in names {
214-
println!(" - {name}");
216+
let _ = sh_println!(" - {name}");
215217
}
216218
}
217219
}
218220

219221
if print_sizes {
220222
// add extra newline if names were already printed
221223
if print_names {
222-
println!();
224+
let _ = sh_println!();
223225
}
224226

225227
let mut size_report = SizeReport { contracts: BTreeMap::new() };
@@ -252,7 +254,7 @@ impl ProjectCompiler {
252254
.insert(name, ContractInfo { runtime_size, init_size, is_dev_contract });
253255
}
254256

255-
println!("{size_report}");
257+
let _ = sh_println!("{size_report}");
256258

257259
// TODO: avoid process::exit
258260
// exit with error if any contract exceeds the size limit, excluding test contracts.

crates/common/src/ens.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ pub trait ProviderEnsExt<T: Transport + Clone, N: Network, P: Provider<T, N>> {
123123
.call()
124124
.await
125125
.map_err(EnsError::Resolve)
126-
.inspect_err(|e| eprintln!("{e:?}"))?
126+
.inspect_err(|e| {
127+
let _ = sh_eprintln!("{e:?}");
128+
})?
127129
._0;
128130
Ok(addr)
129131
}

crates/common/src/selectors.rs

+15-18
Original file line numberDiff line numberDiff line change
@@ -492,24 +492,20 @@ pub struct SelectorImportResponse {
492492
impl SelectorImportResponse {
493493
/// Print info about the functions which were uploaded or already known
494494
pub fn describe(&self) {
495-
self.result
496-
.function
497-
.imported
498-
.iter()
499-
.for_each(|(k, v)| println!("Imported: Function {k}: {v}"));
500-
self.result.event.imported.iter().for_each(|(k, v)| println!("Imported: Event {k}: {v}"));
501-
self.result
502-
.function
503-
.duplicated
504-
.iter()
505-
.for_each(|(k, v)| println!("Duplicated: Function {k}: {v}"));
506-
self.result
507-
.event
508-
.duplicated
509-
.iter()
510-
.for_each(|(k, v)| println!("Duplicated: Event {k}: {v}"));
511-
512-
println!("Selectors successfully uploaded to OpenChain");
495+
self.result.function.imported.iter().for_each(|(k, v)| {
496+
let _ = sh_println!("Imported: Function {k}: {v}");
497+
});
498+
self.result.event.imported.iter().for_each(|(k, v)| {
499+
let _ = sh_println!("Imported: Event {k}: {v}");
500+
});
501+
self.result.function.duplicated.iter().for_each(|(k, v)| {
502+
let _ = sh_println!("Duplicated: Function {k}: {v}");
503+
});
504+
self.result.event.duplicated.iter().for_each(|(k, v)| {
505+
let _ = sh_println!("Duplicated: Event {k}: {v}");
506+
});
507+
508+
let _ = sh_println!("Selectors successfully uploaded to OpenChain");
513509
}
514510
}
515511

@@ -579,6 +575,7 @@ pub fn parse_signatures(tokens: Vec<String>) -> ParsedSignatures {
579575
}
580576

581577
#[cfg(test)]
578+
#[allow(clippy::disallowed_macros)]
582579
#[allow(clippy::needless_return)]
583580
mod tests {
584581
use super::*;

crates/common/src/term.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl Spinner {
7272

7373
let indicator = self.indicator[self.idx % self.indicator.len()].green();
7474
let indicator = Paint::new(format!("[{indicator}]")).bold();
75-
print!("\r\x33[2K\r{indicator} {}", self.message);
75+
let _ = sh_print!("\r\x33[2K\r{indicator} {}", self.message);
7676
io::stdout().flush().unwrap();
7777

7878
self.idx = self.idx.wrapping_add(1);
@@ -112,11 +112,11 @@ impl SpinnerReporter {
112112
Ok(SpinnerMsg::Msg(msg)) => {
113113
spinner.message(msg);
114114
// new line so past messages are not overwritten
115-
println!();
115+
let _ = sh_println!();
116116
}
117117
Ok(SpinnerMsg::Shutdown(ack)) => {
118118
// end with a newline
119-
println!();
119+
let _ = sh_println!();
120120
let _ = ack.send(());
121121
break
122122
}

0 commit comments

Comments
 (0)