use foundry_compilers::artifacts::{
ast::SourceLocation, Block, BlockOrStatement, StateMutability, Statement, TypeName, Visibility,
};
use semver::VersionReq;
use crate::analysis::stmt_src;
pub fn source_string_at_location<'a>(
id: u32,
source: &'a str,
location: &SourceLocation,
) -> &'a str {
if let Some(index) = location.index {
assert_eq!(index as u32, id, "Source index mismatch");
}
source_string_at_location_unchecked(source, location)
}
pub fn source_string_at_location_unchecked<'a>(
source: &'a str,
location: &SourceLocation,
) -> &'a str {
let start = location.start.unwrap_or(0);
let end = location.length.map(|l| start + l).unwrap_or(source.len() - 1);
&source[start..end]
}
pub fn slice_source_location(src: &SourceLocation, start: usize, length: usize) -> SourceLocation {
assert!(
src.length.map(|l| l >= start).unwrap_or(true),
"Sliced start is greater than the original source length"
);
assert!(
src.length.map(|l| l >= start + length).unwrap_or(true),
"Sliced source length is greater than the original source length"
);
SourceLocation {
start: src.start.map(|s| s + start).or(Some(start)),
length: Some(length),
index: src.index,
}
}
pub fn visibility_to_str(visibility: &Visibility) -> &'static str {
match visibility {
Visibility::Public => "public",
Visibility::Internal => "internal",
Visibility::Private => "private",
Visibility::External => "external",
}
}
pub fn mutability_to_str(mutability: &StateMutability) -> &'static str {
match mutability {
StateMutability::Pure => "pure",
StateMutability::View => "view",
StateMutability::Payable => "payable",
StateMutability::Nonpayable => "nonpayable",
}
}
pub fn find_next_index_of_source_location(src: &SourceLocation) -> Option<usize> {
if let Some(start) = src.start {
if let Some(length) = src.length {
return Some(start + length);
}
}
None
}
pub fn find_next_semicolon_after_source_location(
source: &str,
src: &SourceLocation,
) -> Option<usize> {
let start = src.start.unwrap_or(0);
let end = src.length.map(|l| start + l).unwrap_or(start);
let substr = &source[end..];
substr.find(";").map(|i| i + end)
}
pub fn find_next_index_of_block_or_statement(
source: &str,
block_or_statement: &BlockOrStatement,
) -> Option<usize> {
match block_or_statement {
BlockOrStatement::Statement(statement) => find_next_index_of_statement(source, statement),
BlockOrStatement::Block(block) => find_next_index_of_source_location(&block.src),
}
}
pub fn find_index_of_first_statement_in_block_or_statement(
block_or_statement: &BlockOrStatement,
) -> Option<usize> {
match block_or_statement {
BlockOrStatement::Statement(statement) => match statement {
Statement::Block(block) => find_index_of_first_statement_in_block(block),
_ => stmt_src(statement).start,
},
BlockOrStatement::Block(block) => find_index_of_first_statement_in_block(block),
}
}
pub fn find_index_of_first_statement_in_block(block: &Block) -> Option<usize> {
block.statements.first().map_or(
block.src.start.map(|s| s + 1),
|stmt| stmt_src(stmt).start,
)
}
pub fn find_next_index_of_last_statement_in_block(source: &str, block: &Block) -> Option<usize> {
block.statements.last().map_or(
find_next_index_of_source_location(&block.src).map(|s| s - 1),
|stmt| find_next_index_of_statement(source, stmt),
)
}
pub fn find_next_index_of_statement(source: &str, stmt: &Statement) -> Option<usize> {
match stmt {
Statement::Block(block) => find_next_index_of_source_location(&block.src),
Statement::Break(break_stmt) => {
find_next_semicolon_after_source_location(source, &break_stmt.src).map(|i| i + 1)
}
Statement::Continue(continue_stmt) => {
find_next_semicolon_after_source_location(source, &continue_stmt.src).map(|i| i + 1)
}
Statement::DoWhileStatement(do_while_statement) => {
find_next_index_of_source_location(&do_while_statement.src)
}
Statement::EmitStatement(emit_statement) => {
find_next_semicolon_after_source_location(source, &emit_statement.src).map(|i| i + 1)
}
Statement::ExpressionStatement(expression_statement) => {
find_next_semicolon_after_source_location(source, &expression_statement.src)
.map(|i| i + 1)
}
Statement::ForStatement(for_statement) => {
find_next_index_of_block_or_statement(source, &for_statement.body)
}
Statement::IfStatement(if_statement) => match &if_statement.false_body {
Some(false_body) => find_next_index_of_block_or_statement(source, false_body),
None => find_next_index_of_block_or_statement(source, &if_statement.true_body),
},
Statement::InlineAssembly(inline_assembly) => {
find_next_index_of_source_location(&inline_assembly.src)
}
Statement::PlaceholderStatement(placeholder_statement) => {
find_next_semicolon_after_source_location(source, &placeholder_statement.src)
.map(|i| i + 1)
}
Statement::Return(return_stmt) => {
let return_str = source_string_at_location_unchecked(source, &return_stmt.src);
if return_str.trim_end().ends_with(";") {
find_next_index_of_source_location(&return_stmt.src)
} else {
find_next_semicolon_after_source_location(source, &return_stmt.src).map(|i| i + 1)
}
}
Statement::RevertStatement(revert_statement) => {
find_next_semicolon_after_source_location(source, &revert_statement.src).map(|i| i + 1)
}
Statement::TryStatement(try_statement) => {
find_next_index_of_source_location(&try_statement.src)
}
Statement::UncheckedBlock(unchecked_block) => {
find_next_index_of_source_location(&unchecked_block.src)
}
Statement::VariableDeclarationStatement(variable_declaration_statement) => {
find_next_semicolon_after_source_location(source, &variable_declaration_statement.src)
.map(|i| i + 1)
}
Statement::WhileStatement(while_statement) => {
find_next_index_of_block_or_statement(source, &while_statement.body)
}
}
}
pub fn contains_user_defined_type(type_name: &TypeName) -> bool {
match type_name {
TypeName::ArrayTypeName(array_type_name) => {
contains_user_defined_type(&array_type_name.base_type)
}
TypeName::ElementaryTypeName(_) => false,
TypeName::FunctionTypeName(_) => false,
TypeName::Mapping(mapping) => {
contains_user_defined_type(&mapping.key_type)
|| contains_user_defined_type(&mapping.value_type)
}
TypeName::UserDefinedTypeName(_) => true,
}
}
pub fn contains_function_type(type_name: &TypeName) -> bool {
match type_name {
TypeName::FunctionTypeName(_) => true,
TypeName::ArrayTypeName(array_type_name) => {
contains_function_type(&array_type_name.base_type)
}
TypeName::ElementaryTypeName(_) => false,
TypeName::Mapping(mapping) => {
contains_function_type(&mapping.key_type) || contains_function_type(&mapping.value_type)
}
TypeName::UserDefinedTypeName(_) => false,
}
}
pub fn contains_mapping_type(type_name: &TypeName) -> bool {
match type_name {
TypeName::Mapping(_) => true,
TypeName::ArrayTypeName(array_type_name) => {
contains_mapping_type(&array_type_name.base_type)
}
TypeName::ElementaryTypeName(_) => false,
TypeName::FunctionTypeName(_) => false,
TypeName::UserDefinedTypeName(_) => false,
}
}
pub fn abi_encode_available(version_req: &VersionReq) -> bool {
let min_version = semver::Version::parse("0.4.24").unwrap();
!version_req.comparators.iter().all(|cmp| allows_any_version_lt(&min_version, cmp))
}
fn allows_any_version_lt(min_version: &semver::Version, comparator: &semver::Comparator) -> bool {
use semver::Op;
match comparator.op {
Op::Exact | Op::Greater | Op::GreaterEq | Op::Tilde | Op::Caret | Op::Wildcard => {
let exact_version = semver::Version {
major: comparator.major,
minor: comparator.minor.unwrap_or(0),
patch: comparator.patch.unwrap_or(0),
pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY,
};
exact_version < *min_version
}
Op::Less | Op::LessEq => true,
_ => true,
}
}
#[cfg(test)]
mod tests {
use super::*;
use semver::VersionReq;
#[test]
fn test_abi_encode_available_exact_versions() {
assert!(abi_encode_available(&VersionReq::parse("=0.4.24").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("=0.4.25").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("=0.5.0").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("=0.5.10").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("=0.6.0").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("=0.7.0").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("=0.8.0").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("=0.8.19").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("=0.4.23").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("=0.4.20").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("=0.3.0").unwrap()));
}
#[test]
fn test_abi_encode_available_greater_than() {
assert!(!abi_encode_available(&VersionReq::parse(">0.4.23").unwrap())); assert!(!abi_encode_available(&VersionReq::parse(">0.4.20").unwrap())); assert!(!abi_encode_available(&VersionReq::parse(">0.3.0").unwrap())); assert!(!abi_encode_available(&VersionReq::parse(">0.1.0").unwrap()));
assert!(abi_encode_available(&VersionReq::parse(">0.4.24").unwrap())); assert!(abi_encode_available(&VersionReq::parse(">0.4.25").unwrap())); assert!(abi_encode_available(&VersionReq::parse(">0.5.0").unwrap())); }
#[test]
fn test_abi_encode_available_greater_equal() {
assert!(abi_encode_available(&VersionReq::parse(">=0.4.24").unwrap()));
assert!(abi_encode_available(&VersionReq::parse(">=0.4.25").unwrap()));
assert!(abi_encode_available(&VersionReq::parse(">=0.5.0").unwrap()));
assert!(abi_encode_available(&VersionReq::parse(">=0.6.0").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse(">=0.4.23").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse(">=0.4.20").unwrap()));
}
#[test]
fn test_abi_encode_available_less_than() {
assert!(!abi_encode_available(&VersionReq::parse("<0.4.24").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("<0.4.23").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("<0.3.0").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("<0.4.25").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("<0.5.0").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("<0.6.0").unwrap()));
}
#[test]
fn test_abi_encode_available_less_equal() {
assert!(!abi_encode_available(&VersionReq::parse("<=0.4.24").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("<=0.4.25").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("<=0.5.0").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("<=0.4.23").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("<=0.4.20").unwrap()));
}
#[test]
fn test_abi_encode_available_tilde() {
assert!(abi_encode_available(&VersionReq::parse("~0.4.24").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("~0.4.25").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("~0.5.0").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("~0.5.1").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("~0.4.23").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("~0.4.20").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("~0").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("~0.3").unwrap()));
}
#[test]
fn test_abi_encode_available_caret() {
assert!(abi_encode_available(&VersionReq::parse("^0.4.24").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("^0.4.25").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("^0.5.0").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("^0.6.0").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("^0.4.23").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("^0.4.20").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("^0").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("^0.3").unwrap()));
}
#[test]
fn test_abi_encode_available_wildcard() {
assert!(abi_encode_available(&VersionReq::parse("0.4.24").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("0.4.25").unwrap()));
assert!(abi_encode_available(&VersionReq::parse("0.5.0").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("0.4.23").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse("0.4.20").unwrap()));
}
#[test]
fn test_abi_encode_available_complex_ranges() {
assert!(abi_encode_available(&VersionReq::parse(">=0.4.24, <0.9.0").unwrap()));
assert!(abi_encode_available(&VersionReq::parse(">=0.5.0, <0.8.0").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse(">=0.4.20, <0.4.24").unwrap()));
assert!(!abi_encode_available(&VersionReq::parse(">=0.3.0, <0.4.24").unwrap()));
}
#[test]
fn test_abi_encode_available_edge_cases() {
assert!(abi_encode_available(&VersionReq::parse(">=0.4.24").unwrap())); assert!(abi_encode_available(&VersionReq::parse(">0.4.24").unwrap())); assert!(!abi_encode_available(&VersionReq::parse("<=0.4.24").unwrap())); assert!(!abi_encode_available(&VersionReq::parse("<0.4.24").unwrap()));
assert!(abi_encode_available(&VersionReq::parse(">=0.4.24-alpha").unwrap()));
}
}