Skip to content

Commit 48df36d

Browse files
committed
check
1 parent 5377589 commit 48df36d

File tree

2 files changed

+99
-2
lines changed

2 files changed

+99
-2
lines changed

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
/target
33
/Cargo.lock
44

5-
cache/
5+
/cache
6+
test-data/**/cache/
67

78
.vscode
89
/.envrc
@@ -12,4 +13,4 @@ cache/
1213
devenv.local.nix
1314
.direnv
1415
.pre-commit-config.yaml
15-
.lock
16+
.lock
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use crate::{parse_one_source, replace_source_content};
2+
use solar_sema::{
3+
ast::{self, Span},
4+
interface::diagnostics::EmittedDiagnostics,
5+
};
6+
use std::path::Path;
7+
8+
pub(crate) fn interface_repr_hash(content: &str, path: &Path) -> Option<String> {
9+
let src = interface_repr(content, path).ok()?;
10+
Some(foundry_compilers_artifacts::Source::content_hash_of(&src))
11+
}
12+
13+
pub(crate) fn interface_repr(content: &str, path: &Path) -> Result<String, EmittedDiagnostics> {
14+
parse_one_source(content, path, |ast| interface_representation_ast(content, &ast))
15+
}
16+
17+
/// Helper function to remove parts of the contract which do not alter its interface:
18+
/// - Internal functions
19+
/// - External functions bodies
20+
///
21+
/// Preserves all libraries and interfaces.
22+
pub(crate) fn interface_representation_ast(
23+
content: &str,
24+
ast: &solar_parse::ast::SourceUnit<'_>,
25+
) -> String {
26+
let mut spans_to_remove: Vec<Span> = Vec::new();
27+
for item in ast.items.iter() {
28+
let ast::ItemKind::Contract(contract) = &item.kind else {
29+
continue;
30+
};
31+
32+
if contract.kind.is_interface() || contract.kind.is_library() {
33+
continue;
34+
}
35+
36+
for contract_item in contract.body.iter() {
37+
if let ast::ItemKind::Function(function) = &contract_item.kind {
38+
let is_exposed = match function.kind {
39+
// Function with external or public visibility
40+
ast::FunctionKind::Function => {
41+
function.header.visibility >= Some(ast::Visibility::Public)
42+
}
43+
ast::FunctionKind::Constructor
44+
| ast::FunctionKind::Fallback
45+
| ast::FunctionKind::Receive => true,
46+
ast::FunctionKind::Modifier => false,
47+
};
48+
49+
// If function is not exposed we remove the entire span (signature and
50+
// body). Otherwise we keep function signature and
51+
// remove only the body.
52+
if !is_exposed {
53+
spans_to_remove.push(contract_item.span);
54+
} else {
55+
spans_to_remove.push(function.body_span);
56+
}
57+
}
58+
}
59+
}
60+
let content =
61+
replace_source_content(content, spans_to_remove.iter().map(|span| (span.to_range(), "")))
62+
.replace("\n", "");
63+
crate::utils::RE_TWO_OR_MORE_SPACES.replace_all(&content, "").into_owned()
64+
}
65+
66+
#[cfg(test)]
67+
mod tests {
68+
use super::*;
69+
70+
#[test]
71+
fn test_interface_representation() {
72+
let content = r#"
73+
library Lib {
74+
function libFn() internal {
75+
// logic to keep
76+
}
77+
}
78+
contract A {
79+
function a() external {}
80+
function b() public {}
81+
function c() internal {
82+
// logic logic logic
83+
}
84+
function d() private {}
85+
function e() external {
86+
// logic logic logic
87+
}
88+
}"#;
89+
90+
let result = interface_repr(content, Path::new("")).unwrap();
91+
assert_eq!(
92+
result,
93+
r#"library Lib {function libFn() internal {// logic to keep}}contract A {function a() externalfunction b() publicfunction e() external }"#
94+
);
95+
}
96+
}

0 commit comments

Comments
 (0)