Skip to content

Commit b192f4b

Browse files
committed
merge in changes of #8513
2 parents a221bd1 + 4e4f35c commit b192f4b

File tree

21 files changed

+424
-307
lines changed

21 files changed

+424
-307
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cast/bin/main.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use foundry_cli::{handler, prompt, stdin, utils};
1212
use foundry_common::{
1313
abi::get_event,
1414
ens::{namehash, ProviderEnsExt},
15-
fmt::{format_tokens, format_uint_exp},
15+
fmt::{format_uint_exp, print_tokens},
1616
fs,
1717
selectors::{
1818
decode_calldata, decode_event_topic, decode_function_selector, decode_selectors,
@@ -163,10 +163,9 @@ async fn main() -> Result<()> {
163163
}
164164

165165
// ABI encoding & decoding
166-
CastSubcommand::AbiDecode { sig, calldata, input } => {
166+
CastSubcommand::AbiDecode { sig, calldata, input, json } => {
167167
let tokens = SimpleCast::abi_decode(&sig, &calldata, input)?;
168-
let tokens = format_tokens(&tokens);
169-
tokens.for_each(|t| println!("{t}"));
168+
print_tokens(&tokens, json)
170169
}
171170
CastSubcommand::AbiEncode { sig, packed, args } => {
172171
if !packed {
@@ -175,10 +174,9 @@ async fn main() -> Result<()> {
175174
println!("{}", SimpleCast::abi_encode_packed(&sig, &args)?);
176175
}
177176
}
178-
CastSubcommand::CalldataDecode { sig, calldata } => {
177+
CastSubcommand::CalldataDecode { sig, calldata, json } => {
179178
let tokens = SimpleCast::calldata_decode(&sig, &calldata, true)?;
180-
let tokens = format_tokens(&tokens);
181-
tokens.for_each(|t| println!("{t}"));
179+
print_tokens(&tokens, json)
182180
}
183181
CastSubcommand::CalldataEncode { sig, args } => {
184182
println!("{}", SimpleCast::calldata_encode(sig, &args)?);
@@ -425,7 +423,7 @@ async fn main() -> Result<()> {
425423
println!("{sig}");
426424
}
427425
}
428-
CastSubcommand::FourByteDecode { calldata } => {
426+
CastSubcommand::FourByteDecode { calldata, json } => {
429427
let calldata = stdin::unwrap_line(calldata)?;
430428
let sigs = decode_calldata(&calldata).await?;
431429
sigs.iter().enumerate().for_each(|(i, sig)| println!("{}) \"{sig}\"", i + 1));
@@ -440,9 +438,7 @@ async fn main() -> Result<()> {
440438
};
441439

442440
let tokens = SimpleCast::calldata_decode(sig, &calldata, true)?;
443-
for token in format_tokens(&tokens) {
444-
println!("{token}");
445-
}
441+
print_tokens(&tokens, json)
446442
}
447443
CastSubcommand::FourByteEvent { topic } => {
448444
let topic = stdin::unwrap_line(topic)?;

crates/cast/bin/opts.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,10 @@ pub enum CastSubcommand {
490490

491491
/// The ABI-encoded calldata.
492492
calldata: String,
493+
494+
/// Print the decoded calldata as JSON.
495+
#[arg(long, short, help_heading = "Display options")]
496+
json: bool,
493497
},
494498

495499
/// Decode ABI-encoded input or output data.
@@ -508,6 +512,10 @@ pub enum CastSubcommand {
508512
/// Whether to decode the input or output data.
509513
#[arg(long, short, help_heading = "Decode input data instead of output data")]
510514
input: bool,
515+
516+
/// Print the decoded calldata as JSON.
517+
#[arg(long, short, help_heading = "Display options")]
518+
json: bool,
511519
},
512520

513521
/// ABI encode the given function argument, excluding the selector.
@@ -594,6 +602,10 @@ pub enum CastSubcommand {
594602
FourByteDecode {
595603
/// The ABI-encoded calldata.
596604
calldata: Option<String>,
605+
606+
/// Print the decoded calldata as JSON.
607+
#[arg(long, short, help_heading = "Display options")]
608+
json: bool,
597609
},
598610

599611
/// Get the event signature for a given topic 0 from https://openchain.xyz.

crates/common/fmt/src/dynamic.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,23 @@ pub fn format_tokens(tokens: &[DynSolValue]) -> impl Iterator<Item = String> + '
119119
tokens.iter().map(format_token)
120120
}
121121

122+
/// Pretty-prints a slice of tokens using [`format_token_raw`].
123+
pub fn format_tokens_raw(tokens: &[DynSolValue]) -> impl Iterator<Item = String> + '_ {
124+
tokens.iter().map(format_token_raw)
125+
}
126+
127+
/// Prints slice of tokens using [`format_tokens`] or [`format_tokens_raw`] depending on `json`
128+
/// parameter.
129+
pub fn print_tokens(tokens: &[DynSolValue], json: bool) {
130+
if json {
131+
let tokens: Vec<String> = format_tokens_raw(tokens).collect();
132+
println!("{}", serde_json::to_string_pretty(&tokens).unwrap());
133+
} else {
134+
let tokens = format_tokens(tokens);
135+
tokens.for_each(|t| println!("{t}"));
136+
}
137+
}
138+
122139
/// Pretty-prints the given value into a string suitable for user output.
123140
pub fn format_token(value: &DynSolValue) -> String {
124141
DynValueDisplay::new(value, false).to_string()

crates/common/fmt/src/lib.rs

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

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

911
mod exp;
1012
pub use exp::{format_int_exp, format_uint_exp, to_exp_notation};

crates/common/src/abi.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
//! ABI related helper functions.
22
33
use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt, JsonAbiExt};
4-
use alloy_json_abi::{Event, Function};
4+
use alloy_json_abi::{Event, Function, Param};
55
use alloy_primitives::{hex, Address, LogData};
66
use eyre::{Context, ContextCompat, Result};
77
use foundry_block_explorers::{contract::ContractMetadata, errors::EtherscanError, Client};
88
use foundry_config::Chain;
99
use std::{future::Future, pin::Pin};
1010

11+
pub fn encode_args<I, S>(inputs: &[Param], args: I) -> Result<Vec<DynSolValue>>
12+
where
13+
I: IntoIterator<Item = S>,
14+
S: AsRef<str>,
15+
{
16+
std::iter::zip(inputs, args)
17+
.map(|(input, arg)| coerce_value(&input.selector_type(), arg.as_ref()))
18+
.collect()
19+
}
20+
1121
/// Given a function and a vector of string arguments, it proceeds to convert the args to alloy
1222
/// [DynSolValue]s and then ABI encode them.
1323
pub fn encode_function_args<I, S>(func: &Function, args: I) -> Result<Vec<u8>>
1424
where
1525
I: IntoIterator<Item = S>,
1626
S: AsRef<str>,
1727
{
18-
let params = std::iter::zip(&func.inputs, args)
19-
.map(|(input, arg)| coerce_value(&input.selector_type(), arg.as_ref()))
20-
.collect::<Result<Vec<_>>>()?;
21-
func.abi_encode_input(params.as_slice()).map_err(Into::into)
28+
Ok(func.abi_encode_input(&encode_args(&func.inputs, args)?)?)
2229
}
2330

2431
/// Given a function and a vector of string arguments, it proceeds to convert the args to alloy

crates/config/src/inline/conf_parser.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ where
3737
/// - `Err(InlineConfigParserError)` in case of wrong configuration.
3838
fn try_merge(&self, configs: &[String]) -> Result<Option<Self>, InlineConfigParserError>;
3939

40-
/// Validates and merges the natspec configs into the current config.
40+
/// Validates and merges the natspec configs for current profile into the current config.
4141
fn merge(&self, natspec: &NatSpec) -> Result<Option<Self>, InlineConfigError> {
4242
let config_key = Self::config_key();
4343

44-
let configs =
45-
natspec.config_lines().filter(|l| l.contains(&config_key)).collect::<Vec<String>>();
44+
let configs = natspec
45+
.current_profile_configs()
46+
.filter(|l| l.contains(&config_key))
47+
.collect::<Vec<String>>();
4648

4749
self.try_merge(&configs).map_err(|e| {
4850
let line = natspec.debug_context();

crates/debugger/src/tui/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ impl Debugger {
136136
}
137137
}
138138

139+
// TODO: Update once on 1.82
140+
#[allow(deprecated)]
139141
type PanicHandler = Box<dyn Fn(&std::panic::PanicInfo<'_>) + 'static + Sync + Send>;
140142

141143
/// Handles terminal state.

crates/evm/coverage/src/analysis.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ impl<'a> ContractVisitor<'a> {
417417
if let Some(NodeType::Identifier) = expr.as_ref().map(|expr| &expr.node_type) {
418418
// Might be a require call, add branch coverage.
419419
let name: Option<String> = expr.and_then(|expr| expr.attribute("name"));
420-
if let Some("require") = name.as_deref() {
420+
if let Some("require" | "assert") = name.as_deref() {
421421
let branch_id = self.branch_id;
422422
self.branch_id += 1;
423423
self.push_item(CoverageItem {

crates/evm/coverage/src/anchors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ pub fn find_anchor_branch(
149149
ItemAnchor {
150150
item_id,
151151
// The first branch is the opcode directly after JUMPI
152-
instruction: pc + 1,
152+
instruction: pc + 2,
153153
},
154154
ItemAnchor { item_id, instruction: pc_jump },
155155
));

crates/forge/tests/cli/coverage.rs

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ forgetest!(test_assert_coverage, |prj, cmd| {
173173
"AContract.sol",
174174
r#"
175175
contract AContract {
176-
function checkA() external pure returns (bool) {
177-
assert(10 > 2);
176+
function checkA(uint256 a) external pure returns (bool) {
177+
assert(a > 2);
178178
return true;
179179
}
180180
}
@@ -188,26 +188,66 @@ contract AContract {
188188
import "./test.sol";
189189
import {AContract} from "./AContract.sol";
190190
191+
interface Vm {
192+
function expectRevert() external;
193+
}
194+
191195
contract AContractTest is DSTest {
192-
function testA() external {
196+
Vm constant vm = Vm(HEVM_ADDRESS);
197+
function testAssertBranch() external {
193198
AContract a = new AContract();
194-
bool result = a.checkA();
199+
bool result = a.checkA(10);
195200
assertTrue(result);
196201
}
202+
203+
function testAssertRevertBranch() external {
204+
AContract a = new AContract();
205+
vm.expectRevert();
206+
a.checkA(1);
207+
}
197208
}
198209
"#,
199210
)
200211
.unwrap();
201212

213+
// Assert 50% branch coverage for assert failure.
214+
cmd.arg("coverage")
215+
.args(["--mt".to_string(), "testAssertRevertBranch".to_string()])
216+
.assert_success()
217+
.stdout_eq(str![[r#"
218+
...
219+
| File | % Lines | % Statements | % Branches | % Funcs |
220+
|-------------------|--------------|--------------|--------------|---------------|
221+
| src/AContract.sol | 50.00% (1/2) | 50.00% (1/2) | 50.00% (1/2) | 100.00% (1/1) |
222+
| Total | 50.00% (1/2) | 50.00% (1/2) | 50.00% (1/2) | 100.00% (1/1) |
223+
224+
"#]]);
225+
226+
// Assert 50% branch coverage for proper assert.
227+
cmd.forge_fuse()
228+
.arg("coverage")
229+
.args(["--mt".to_string(), "testAssertBranch".to_string()])
230+
.assert_success()
231+
.stdout_eq(str![[r#"
232+
...
233+
| File | % Lines | % Statements | % Branches | % Funcs |
234+
|-------------------|---------------|---------------|--------------|---------------|
235+
| src/AContract.sol | 100.00% (2/2) | 100.00% (2/2) | 50.00% (1/2) | 100.00% (1/1) |
236+
| Total | 100.00% (2/2) | 100.00% (2/2) | 50.00% (1/2) | 100.00% (1/1) |
237+
238+
"#]]);
239+
202240
// Assert 100% coverage (assert properly covered).
203-
cmd.arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq(str![[r#"
241+
cmd.forge_fuse().arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq(
242+
str![[r#"
204243
...
205244
| File | % Lines | % Statements | % Branches | % Funcs |
206245
|-------------------|---------------|---------------|---------------|---------------|
207-
| src/AContract.sol | 100.00% (2/2) | 100.00% (2/2) | 100.00% (0/0) | 100.00% (1/1) |
208-
| Total | 100.00% (2/2) | 100.00% (2/2) | 100.00% (0/0) | 100.00% (1/1) |
246+
| src/AContract.sol | 100.00% (2/2) | 100.00% (2/2) | 100.00% (2/2) | 100.00% (1/1) |
247+
| Total | 100.00% (2/2) | 100.00% (2/2) | 100.00% (2/2) | 100.00% (1/1) |
209248
210-
"#]]);
249+
"#]],
250+
);
211251
});
212252

213253
forgetest!(test_require_coverage, |prj, cmd| {
@@ -262,6 +302,20 @@ contract AContractTest is DSTest {
262302
| src/AContract.sol | 100.00% (1/1) | 100.00% (1/1) | 50.00% (1/2) | 100.00% (1/1) |
263303
| Total | 100.00% (1/1) | 100.00% (1/1) | 50.00% (1/2) | 100.00% (1/1) |
264304
305+
"#]]);
306+
307+
// Assert 50% branch coverage if only happy path tested.
308+
cmd.forge_fuse()
309+
.arg("coverage")
310+
.args(["--mt".to_string(), "testRequireNoRevert".to_string()])
311+
.assert_success()
312+
.stdout_eq(str![[r#"
313+
...
314+
| File | % Lines | % Statements | % Branches | % Funcs |
315+
|-------------------|---------------|---------------|--------------|---------------|
316+
| src/AContract.sol | 100.00% (1/1) | 100.00% (1/1) | 50.00% (1/2) | 100.00% (1/1) |
317+
| Total | 100.00% (1/1) | 100.00% (1/1) | 50.00% (1/2) | 100.00% (1/1) |
318+
265319
"#]]);
266320

267321
// Assert 100% branch coverage.
@@ -384,7 +438,7 @@ contract Foo {
384438
385439
function foo(uint256 a) external pure {
386440
if (a < 10) {
387-
if (a == 1) {
441+
if (a < 3) {
388442
assert(a == 1);
389443
} else {
390444
assert(a == 5);
@@ -423,16 +477,23 @@ import {Foo} from "./Foo.sol";
423477
424478
interface Vm {
425479
function expectRevert(bytes calldata revertData) external;
480+
function expectRevert() external;
426481
}
427482
428483
contract FooTest is DSTest {
429484
Vm constant vm = Vm(HEVM_ADDRESS);
430485
Foo internal foo = new Foo();
431486
432-
function test_issue_7784() external view {
487+
function test_issue_7784() external {
433488
foo.foo(1);
489+
vm.expectRevert();
490+
foo.foo(2);
491+
vm.expectRevert();
492+
foo.foo(4);
434493
foo.foo(5);
435494
foo.foo(60);
495+
vm.expectRevert();
496+
foo.foo(70);
436497
}
437498
438499
function test_issue_4310() external {
@@ -506,13 +567,16 @@ contract FooTest is DSTest {
506567
)
507568
.unwrap();
508569

509-
// Assert 100% coverage.
570+
// TODO: fix following issues for 100% coverage
571+
// https://github.com/foundry-rs/foundry/issues/4309
572+
// https://github.com/foundry-rs/foundry/issues/4310
573+
// https://github.com/foundry-rs/foundry/issues/4315
510574
cmd.arg("coverage").args(["--summary".to_string()]).assert_success().stdout_eq(str![[r#"
511575
...
512-
| File | % Lines | % Statements | % Branches | % Funcs |
513-
|-------------|-----------------|-----------------|-----------------|---------------|
514-
| src/Foo.sol | 100.00% (20/20) | 100.00% (23/23) | 100.00% (12/12) | 100.00% (7/7) |
515-
| Total | 100.00% (20/20) | 100.00% (23/23) | 100.00% (12/12) | 100.00% (7/7) |
576+
| File | % Lines | % Statements | % Branches | % Funcs |
577+
|-------------|-----------------|-----------------|----------------|---------------|
578+
| src/Foo.sol | 100.00% (20/20) | 100.00% (23/23) | 83.33% (15/18) | 100.00% (7/7) |
579+
| Total | 100.00% (20/20) | 100.00% (23/23) | 83.33% (15/18) | 100.00% (7/7) |
516580
517581
"#]]);
518582
});
@@ -681,10 +745,10 @@ contract FooTest is DSTest {
681745
cmd.arg("coverage").args(["--mt".to_string(), "happy".to_string()]).assert_success().stdout_eq(
682746
str![[r#"
683747
...
684-
| File | % Lines | % Statements | % Branches | % Funcs |
685-
|-------------|----------------|----------------|---------------|---------------|
686-
| src/Foo.sol | 66.67% (10/15) | 66.67% (14/21) | 100.00% (4/4) | 100.00% (5/5) |
687-
| Total | 66.67% (10/15) | 66.67% (14/21) | 100.00% (4/4) | 100.00% (5/5) |
748+
| File | % Lines | % Statements | % Branches | % Funcs |
749+
|-------------|----------------|----------------|--------------|---------------|
750+
| src/Foo.sol | 66.67% (10/15) | 66.67% (14/21) | 83.33% (5/6) | 100.00% (5/5) |
751+
| Total | 66.67% (10/15) | 66.67% (14/21) | 83.33% (5/6) | 100.00% (5/5) |
688752
689753
"#]],
690754
);
@@ -695,8 +759,8 @@ contract FooTest is DSTest {
695759
...
696760
| File | % Lines | % Statements | % Branches | % Funcs |
697761
|-------------|-----------------|-----------------|---------------|---------------|
698-
| src/Foo.sol | 100.00% (15/15) | 100.00% (21/21) | 100.00% (4/4) | 100.00% (5/5) |
699-
| Total | 100.00% (15/15) | 100.00% (21/21) | 100.00% (4/4) | 100.00% (5/5) |
762+
| src/Foo.sol | 100.00% (15/15) | 100.00% (21/21) | 100.00% (6/6) | 100.00% (5/5) |
763+
| Total | 100.00% (15/15) | 100.00% (21/21) | 100.00% (6/6) | 100.00% (5/5) |
700764
701765
"#]],
702766
);

0 commit comments

Comments
 (0)