Skip to content

Commit 474d305

Browse files
authored
feat: Add ast to output selection (#1154)
* Add ast to output selection, bridge it from source to contract struct * consume ast from sources following solc standard * fix zksync tests prefixes * adjust test name * update zksync tests check list
1 parent fda6aa0 commit 474d305

File tree

2 files changed

+217
-33
lines changed

2 files changed

+217
-33
lines changed

crates/zksync/compilers/tests/zksync_tests.rs

Lines changed: 199 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ use foundry_test_utils::foundry_compilers::{
1111
};
1212

1313
use foundry_zksync_compilers::{
14-
artifacts::{contract::Contract, error::Error},
14+
artifacts::{
15+
contract::Contract,
16+
error::Error,
17+
output_selection::{FileOutputSelection, OutputSelection, OutputSelectionFlag},
18+
},
1519
compilers::{
1620
artifact_output::zk::ZkArtifactOutput,
1721
zksolc::{
@@ -24,7 +28,7 @@ use foundry_zksync_compilers::{
2428
use semver::Version;
2529

2630
#[test]
27-
fn zksync_can_compile_dapp_sample() {
31+
fn test_zk_can_compile_dapp_sample() {
2832
// let _ = tracing_subscriber::fmt()
2933
// .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
3034
// .try_init()
@@ -55,7 +59,7 @@ fn zksync_can_compile_dapp_sample() {
5559
}
5660

5761
#[test]
58-
fn zksync_can_compile_dapp_sample_with_supported_zksolc_versions() {
62+
fn test_zk_can_compile_dapp_sample_with_supported_zksolc_versions() {
5963
for version in ZkSolc::zksolc_supported_versions() {
6064
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/dapp-sample");
6165
let paths = ProjectPathsConfig::builder().sources(root.join("src")).lib(root.join("lib"));
@@ -82,7 +86,7 @@ fn zksync_can_compile_dapp_sample_with_supported_zksolc_versions() {
8286
}
8387

8488
#[test]
85-
fn zksync_can_set_hash_type_with_supported_versions() {
89+
fn test_zk_can_set_hash_type_with_supported_versions() {
8690
for version in ZkSolc::zksolc_supported_versions() {
8791
let mut project = TempProject::<ZkSolcCompiler, ZkArtifactOutput>::dapptools().unwrap();
8892
project.project_mut().settings.set_zksolc_version(version.clone()).unwrap();
@@ -167,14 +171,14 @@ fn test_zksync_can_compile_contract_with_suppressed_errors(zksolc_version: Versi
167171
}
168172

169173
#[test]
170-
fn zksync_can_compile_contract_with_suppressed_errors() {
174+
fn test_zk_can_compile_contract_with_suppressed_errors() {
171175
test_zksync_can_compile_contract_with_suppressed_errors(
172176
ZkSolc::zksolc_latest_supported_version(),
173177
);
174178
}
175179

176180
#[test]
177-
fn zksync_pre_1_5_7_can_compile_contract_with_suppressed_errors() {
181+
fn test_zk_pre_1_5_7_can_compile_contract_with_suppressed_errors() {
178182
test_zksync_can_compile_contract_with_suppressed_errors(Version::new(1, 5, 6));
179183
}
180184

@@ -231,14 +235,14 @@ fn test_zksync_can_compile_contract_with_suppressed_warnings(zksolc_version: Ver
231235
}
232236

233237
#[test]
234-
fn zksync_can_compile_contract_with_suppressed_warnings() {
238+
fn test_zk_can_compile_contract_with_suppressed_warnings() {
235239
test_zksync_can_compile_contract_with_suppressed_warnings(
236240
ZkSolc::zksolc_latest_supported_version(),
237241
);
238242
}
239243

240244
#[test]
241-
fn zksync_pre_1_5_7_can_compile_contract_with_suppressed_warnings() {
245+
fn test_zk_pre_1_5_7_can_compile_contract_with_suppressed_warnings() {
242246
test_zksync_can_compile_contract_with_suppressed_warnings(Version::new(1, 5, 6));
243247
}
244248

@@ -297,14 +301,14 @@ fn test_zksync_can_compile_contract_with_assembly_create_suppressed_warnings(
297301
}
298302

299303
#[test]
300-
fn zksync_can_compile_contract_with_assembly_create_suppressed_warnings_1_5_10() {
304+
fn test_zk_can_compile_contract_with_assembly_create_suppressed_warnings_1_5_10() {
301305
test_zksync_can_compile_contract_with_assembly_create_suppressed_warnings(Version::new(
302306
1, 5, 10,
303307
));
304308
}
305309

306310
#[test]
307-
fn zksync_can_compile_dapp_detect_changes_in_libs() {
311+
fn test_zk_can_compile_dapp_detect_changes_in_libs() {
308312
// let _ = tracing_subscriber::fmt()
309313
// .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
310314
// .try_init()
@@ -381,7 +385,7 @@ fn zksync_can_compile_dapp_detect_changes_in_libs() {
381385
}
382386

383387
#[test]
384-
fn zksync_can_compile_dapp_detect_changes_in_sources() {
388+
fn test_zk_can_compile_dapp_detect_changes_in_sources() {
385389
let project = TempProject::<ZkSolcCompiler, ZkArtifactOutput>::dapptools().unwrap();
386390

387391
let src = project
@@ -470,7 +474,7 @@ fn zksync_can_compile_dapp_detect_changes_in_sources() {
470474
}
471475

472476
#[test]
473-
fn zksync_can_emit_build_info() {
477+
fn test_zk_can_emit_build_info() {
474478
let mut project = TempProject::<ZkSolcCompiler, ZkArtifactOutput>::dapptools().unwrap();
475479
project.project_mut().build_info = true;
476480
project
@@ -512,7 +516,7 @@ contract B { }
512516
}
513517

514518
#[test]
515-
fn zksync_can_clean_build_info() {
519+
fn test_zk_can_clean_build_info() {
516520
let mut project = TempProject::<ZkSolcCompiler, ZkArtifactOutput>::dapptools().unwrap();
517521

518522
project.project_mut().build_info = true;
@@ -559,7 +563,7 @@ contract B { }
559563
}
560564

561565
#[test]
562-
fn zksync_cant_compile_a_file_outside_allowed_paths() {
566+
fn test_zk_cant_compile_a_file_outside_allowed_paths() {
563567
// For this test we should create the following directory structure:
564568
// project_root/
565569
// ├── outer/
@@ -636,7 +640,7 @@ contract Util {}
636640
}
637641

638642
#[test]
639-
fn zksync_can_compile_a_file_in_allowed_paths_successfully() {
643+
fn test_zk_can_compile_a_file_in_allowed_paths_successfully() {
640644
let tmp_dir = tempfile::tempdir().unwrap();
641645
let project_root = tmp_dir.path().to_path_buf();
642646
let contracts_dir = tempfile::tempdir_in(&project_root).unwrap();
@@ -699,7 +703,7 @@ contract Util {}
699703
}
700704

701705
#[test]
702-
fn zksync_can_compile_yul_sample() {
706+
fn test_zk_can_compile_yul_sample() {
703707
// let _ = tracing_subscriber::fmt()
704708
// .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
705709
// .try_init()
@@ -732,7 +736,7 @@ fn zksync_can_compile_yul_sample() {
732736
}
733737

734738
#[test]
735-
fn zksync_detects_change_on_cache_if_zksolc_version_changes() {
739+
fn test_zk_detects_change_on_cache_if_zksolc_version_changes() {
736740
let mut project = TempProject::<ZkSolcCompiler, ZkArtifactOutput>::dapptools().unwrap();
737741

738742
project.project_mut().build_info = true;
@@ -784,3 +788,181 @@ contract B { }
784788
assert_eq!(zksolc_version, "\"1.5.7\"");
785789
}
786790
}
791+
792+
#[test]
793+
fn test_zk_can_compile_with_ast_output() {
794+
let mut project = TempProject::<ZkSolcCompiler, ZkArtifactOutput>::dapptools().unwrap();
795+
796+
// Configure output selection to include AST
797+
let mut settings = project.project().settings.clone();
798+
settings.settings.output_selection = OutputSelection {
799+
all: FileOutputSelection {
800+
per_file: [OutputSelectionFlag::AST].into(),
801+
per_contract: [OutputSelectionFlag::ABI, OutputSelectionFlag::Metadata].into(),
802+
},
803+
};
804+
project.project_mut().settings = settings;
805+
806+
project
807+
.add_source(
808+
"TestContract",
809+
r#"
810+
// SPDX-License-Identifier: MIT
811+
pragma solidity ^0.8.10;
812+
813+
contract TestContract {
814+
uint256 public value;
815+
816+
event ValueChanged(uint256 indexed newValue);
817+
818+
constructor(uint256 _initialValue) {
819+
value = _initialValue;
820+
}
821+
822+
function setValue(uint256 _newValue) public {
823+
value = _newValue;
824+
emit ValueChanged(_newValue);
825+
}
826+
827+
function getValue() public view returns (uint256) {
828+
return value;
829+
}
830+
}
831+
"#,
832+
)
833+
.unwrap();
834+
835+
let compiled = project.compile().unwrap();
836+
compiled.assert_success();
837+
838+
let sources = &compiled.output().sources;
839+
let (_path, versioned_files) = sources
840+
.0
841+
.iter()
842+
.find(|(path, _)| {
843+
path.file_name().and_then(|name| name.to_str()) == Some("TestContract.sol")
844+
})
845+
.expect("TestContract.sol source not found");
846+
847+
let versioned_source_file = &versioned_files[0]; // Get first version
848+
let source_file = &versioned_source_file.source_file;
849+
850+
assert!(source_file.ast.is_some(), "AST should be present in source file");
851+
let ast =
852+
serde_json::to_value(source_file.ast.as_ref().unwrap()).expect("Failed to serialize AST");
853+
854+
assert_eq!(ast["nodeType"].as_str(), Some("SourceUnit"), "AST root should be SourceUnit");
855+
assert!(ast["src"].is_string(), "AST should have src field");
856+
assert!(ast["nodes"].is_array(), "AST should have nodes array");
857+
858+
let nodes = ast["nodes"].as_array().expect("nodes should be array");
859+
assert!(!nodes.is_empty(), "AST nodes should not be empty");
860+
861+
// Find the contract definition node
862+
let contract_node = nodes
863+
.iter()
864+
.find(|node| {
865+
node["nodeType"].as_str() == Some("ContractDefinition")
866+
&& node["name"].as_str() == Some("TestContract")
867+
})
868+
.expect("Should find TestContract definition in AST");
869+
870+
assert!(contract_node["src"].is_string(), "Contract node should have src field");
871+
assert!(contract_node["nodes"].is_array(), "Contract should have nodes array");
872+
873+
let contract_nodes = contract_node["nodes"].as_array().expect("Contract nodes should be array");
874+
875+
let has_constructor = contract_nodes.iter().any(|node| {
876+
node["nodeType"].as_str() == Some("FunctionDefinition")
877+
&& node["kind"].as_str() == Some("constructor")
878+
});
879+
assert!(has_constructor, "Should find constructor in AST");
880+
881+
let has_set_value_function = contract_nodes.iter().any(|node| {
882+
node["nodeType"].as_str() == Some("FunctionDefinition")
883+
&& node["name"].as_str() == Some("setValue")
884+
});
885+
assert!(has_set_value_function, "Should find setValue function in AST");
886+
887+
let has_value_variable = contract_nodes.iter().any(|node| {
888+
node["nodeType"].as_str() == Some("VariableDeclaration")
889+
&& node["name"].as_str() == Some("value")
890+
});
891+
assert!(has_value_variable, "Should find value variable in AST");
892+
893+
let has_event = contract_nodes.iter().any(|node| {
894+
node["nodeType"].as_str() == Some("EventDefinition")
895+
&& node["name"].as_str() == Some("ValueChanged")
896+
});
897+
assert!(has_event, "Should find ValueChanged event in AST");
898+
}
899+
900+
#[test]
901+
fn test_zk_ast_available_in_sources() {
902+
let mut project = TempProject::<ZkSolcCompiler, ZkArtifactOutput>::dapptools().unwrap();
903+
904+
// Configure output selection to include AST
905+
let mut settings = project.project().settings.clone();
906+
settings.settings.output_selection = OutputSelection {
907+
all: FileOutputSelection {
908+
per_file: [OutputSelectionFlag::AST].into(),
909+
per_contract: [OutputSelectionFlag::ABI].into(),
910+
},
911+
};
912+
project.project_mut().settings = settings;
913+
914+
project
915+
.add_source(
916+
"SimpleAstTest",
917+
r#"
918+
// SPDX-License-Identifier: MIT
919+
pragma solidity ^0.8.10;
920+
921+
contract SimpleAstTest {
922+
uint256 public counter;
923+
924+
function increment() public {
925+
counter += 1;
926+
}
927+
}
928+
"#,
929+
)
930+
.unwrap();
931+
932+
let compiled = project.compile().unwrap();
933+
compiled.assert_success();
934+
935+
let sources = &compiled.output().sources;
936+
let (_path, versioned_files) = sources
937+
.0
938+
.iter()
939+
.find(|(path, _)| {
940+
path.file_name().and_then(|name| name.to_str()) == Some("SimpleAstTest.sol")
941+
})
942+
.expect("SimpleAstTest.sol source not found");
943+
944+
let versioned_source_file = &versioned_files[0]; // Get first version
945+
let source_file = &versioned_source_file.source_file;
946+
947+
assert!(source_file.ast.is_some(), "AST should be present in source file");
948+
let ast =
949+
serde_json::to_value(source_file.ast.as_ref().unwrap()).expect("Failed to serialize AST");
950+
951+
assert_eq!(ast["nodeType"].as_str(), Some("SourceUnit"));
952+
953+
let nodes = ast["nodes"].as_array().expect("AST should have nodes");
954+
let contract_node = nodes
955+
.iter()
956+
.find(|node| {
957+
node["nodeType"].as_str() == Some("ContractDefinition")
958+
&& node["name"].as_str() == Some("SimpleAstTest")
959+
})
960+
.expect("Should find SimpleAstTest in AST");
961+
962+
let contract_elements = contract_node["nodes"].as_array().expect("Contract should have nodes");
963+
let has_counter_var = contract_elements.iter().any(|node| {
964+
node["nodeType"].as_str() == Some("VariableDeclaration")
965+
&& node["name"].as_str() == Some("counter")
966+
});
967+
assert!(has_counter_var, "Should find counter variable in AST");
968+
}

zksync-tests

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -156,22 +156,24 @@ foundry-zksync-compilers:
156156
dual_compiled_contracts::tests::find_by_path
157157
dual_compiled_contracts::tests::find_nothing
158158
foundry-zksync-compilers::zksync_tests:
159-
zksync_can_clean_build_info
160-
zksync_can_compile_a_file_in_allowed_paths_successfully
161-
zksync_can_compile_contract_with_assembly_create_suppressed_warnings_1_5_10
162-
zksync_can_compile_contract_with_suppressed_errors
163-
zksync_can_compile_contract_with_suppressed_warnings
164-
zksync_can_compile_dapp_detect_changes_in_libs
165-
zksync_can_compile_dapp_detect_changes_in_sources
166-
zksync_can_compile_dapp_sample
167-
zksync_can_compile_dapp_sample_with_supported_zksolc_versions
168-
zksync_can_compile_yul_sample
169-
zksync_can_emit_build_info
170-
zksync_can_set_hash_type_with_supported_versions
171-
zksync_cant_compile_a_file_outside_allowed_paths
172-
zksync_detects_change_on_cache_if_zksolc_version_changes
173-
zksync_pre_1_5_7_can_compile_contract_with_suppressed_errors
174-
zksync_pre_1_5_7_can_compile_contract_with_suppressed_warnings
159+
test_zk_ast_available_in_sources
160+
test_zk_can_clean_build_info
161+
test_zk_can_compile_a_file_in_allowed_paths_successfully
162+
test_zk_can_compile_contract_with_assembly_create_suppressed_warnings_1_5_10
163+
test_zk_can_compile_contract_with_suppressed_errors
164+
test_zk_can_compile_contract_with_suppressed_warnings
165+
test_zk_can_compile_dapp_detect_changes_in_libs
166+
test_zk_can_compile_dapp_detect_changes_in_sources
167+
test_zk_can_compile_dapp_sample
168+
test_zk_can_compile_dapp_sample_with_supported_zksolc_versions
169+
test_zk_can_compile_with_ast_output
170+
test_zk_can_compile_yul_sample
171+
test_zk_can_emit_build_info
172+
test_zk_can_set_hash_type_with_supported_versions
173+
test_zk_cant_compile_a_file_outside_allowed_paths
174+
test_zk_detects_change_on_cache_if_zksolc_version_changes
175+
test_zk_pre_1_5_7_can_compile_contract_with_suppressed_errors
176+
test_zk_pre_1_5_7_can_compile_contract_with_suppressed_warnings
175177
foundry-zksync-core:
176178
cheatcodes::tests::test_etch_panics_when_bytecode_not_aligned_on_32_bytes
177179
convert::test::test_160_conversion

0 commit comments

Comments
 (0)