Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Walk local functions #1268

Merged
merged 5 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ Options:
--dump-candidates Dump removal candidates and exit (for debugging)
--framework <FRAMEWORK> Assume testing framework is <FRAMEWORK> [possible values: anchor, auto, foundry, go, hardhat, rust]
--no-dry-run Do not perform dry runs
--no-local-functions Do not walk local functions
--no-sqlite Do not output to an sqlite database
--quiet Do not output to the console
--reset Discard sqlite database contents
Expand Down
3 changes: 2 additions & 1 deletion backends/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ once_cell = "1.19"
paste = "1.0"
regex = "1.10"
remain = "0.2"
serde_json = "1.0"
strum = "0.26"
strum_macros = "0.26"
strip-ansi-escapes = "0.2"
Expand All @@ -39,7 +40,7 @@ toml_edit = "0.22"
# Foundry
# smoelius: New `solang-parser` releases may contain breaking changes:
# https://github.com/hyperledger/solang/pull/1213
solang-parser = "=0.3.4"
solang-parser = { version = "=0.3.4", features = ["pt-serde"] }

# Go
tree-sitter = "0.22"
Expand Down
34 changes: 31 additions & 3 deletions backends/src/foundry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ use necessist_core::{
framework::{SpanTestMaps, TestSet},
util, LightContext, LineColumn, SourceFile, Span, __Rewriter as Rewriter,
};
use solang_parser::pt::{CodeLocation, Expression, Identifier, Loc, SourceUnit, Statement};
use std::{cell::RefCell, convert::Infallible, fs::read_to_string, path::Path, process::Command};
use solang_parser::pt::{
CodeLocation, Expression, FunctionDefinition, Identifier, Loc, SourceUnit, Statement,
};
use std::{
cell::RefCell, collections::BTreeMap, convert::Infallible, fs::read_to_string, hash::Hash,
path::Path, process::Command,
};

mod storage;
use storage::Storage;

mod visitor;
use visitor::{visit, Statements};
use visitor::{collect_local_functions, visit, Statements};

#[derive(Debug)]
pub struct Foundry;
Expand All @@ -40,6 +45,20 @@ pub struct Test<'ast> {
statements: Statements<'ast>,
}

#[derive(Clone, Copy, Eq, PartialEq)]
pub struct LocalFunction<'ast> {
function_definition: &'ast FunctionDefinition,
}

impl<'ast> Hash for LocalFunction<'ast> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
// smoelius: Hack.
let bytes = serde_json::to_vec(self.function_definition)
.expect("failed to serialize function definition");
bytes.hash(state);
}
}

#[derive(Clone, Copy, Eq, PartialEq)]
pub struct WithContents<'ast, T> {
contents: &'ast str,
Expand Down Expand Up @@ -87,6 +106,7 @@ impl AbstractTypes for Types {
type Storage<'ast> = Storage<'ast>;
type File = (String, SourceUnit);
type Test<'ast> = Test<'ast>;
type LocalFunction<'ast> = LocalFunction<'ast>;
type Statement<'ast> = WithContents<'ast, &'ast Statement>;
type Expression<'ast> = WithContents<'ast, &'ast Expression>;
type Await<'ast> = Infallible;
Expand Down Expand Up @@ -178,6 +198,14 @@ impl ParseLow for Foundry {
Storage::new(file)
}

fn local_functions<'ast>(
&self,
_storage: &RefCell<<Self::Types as AbstractTypes>::Storage<'ast>>,
file: &'ast <Self::Types as AbstractTypes>::File,
) -> Result<BTreeMap<String, Vec<<Self::Types as AbstractTypes>::LocalFunction<'ast>>>> {
Ok(collect_local_functions(&file.1))
}

fn visit_file<'ast>(
generic_visitor: GenericVisitor<'_, '_, '_, 'ast, Self>,
storage: &RefCell<<Self::Types as AbstractTypes>::Storage<'ast>>,
Expand Down
47 changes: 44 additions & 3 deletions backends/src/foundry/visitor/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#![cfg_attr(dylint_lib = "general", allow(non_local_effect_before_error_return))]

use super::{Foundry, FunctionCall, GenericVisitor, Storage, Test, WithContents};
use super::{Foundry, FunctionCall, GenericVisitor, LocalFunction, Storage, Test, WithContents};
use anyhow::Result;
use if_chain::if_chain;
use necessist_core::framework::{SpanTestMaps, TestSet};
use solang_parser::pt::{Expression, FunctionDefinition, Identifier, Loc, SourceUnit, Statement};
use std::{cell::RefCell, convert::Infallible};
use std::{cell::RefCell, collections::BTreeMap, convert::Infallible};

mod visit;
use visit::{self as visit_fns, Visitor as _};
Expand All @@ -24,14 +24,49 @@ impl<'ast> Statements<'ast> {
}
}

pub(super) fn collect_local_functions(
source_unit: &SourceUnit,
) -> BTreeMap<String, Vec<LocalFunction>> {
let mut collector = FunctionDefinitionCollector::default();
collector.visit_source_unit(source_unit).unwrap();
collector.function_definitions.split_off(&String::new())
}

#[derive(Default)]
struct FunctionDefinitionCollector<'ast> {
function_definitions: BTreeMap<String, Vec<LocalFunction<'ast>>>,
}

impl<'ast> visit_fns::Visitor<'ast> for FunctionDefinitionCollector<'ast> {
type Error = Infallible;

fn visit_function_definition(
&mut self,
function_definition: &'ast FunctionDefinition,
) -> Result<(), Self::Error> {
if let Some(name) = &function_definition.name {
self.function_definitions
.entry(name.to_string())
.or_default()
.push(LocalFunction {
function_definition,
});
}
Ok(())
}
}

pub(super) fn visit<'ast>(
generic_visitor: GenericVisitor<'_, '_, '_, 'ast, Foundry>,
storage: &RefCell<Storage<'ast>>,
source_unit: &'ast SourceUnit,
) -> Result<(TestSet, SpanTestMaps)> {
let mut visitor = Visitor::new(generic_visitor, storage);
visitor.visit_source_unit(source_unit)?;
Ok(visitor.generic_visitor.results())
while let Some(local_function) = visitor.generic_visitor.next_local_function() {
visitor.visit_local_function(local_function)?;
}
visitor.generic_visitor.results()
}

struct Visitor<'context, 'config, 'backend, 'ast, 'storage> {
Expand All @@ -51,6 +86,12 @@ impl<'context, 'config, 'backend, 'ast, 'storage>
storage,
}
}

fn visit_local_function(&mut self, local_function: LocalFunction<'ast>) -> Result<()> {
visit_fns::visit_function_definition(self, local_function.function_definition)?;

Ok(())
}
}

impl<'context, 'config, 'backend, 'ast, 'storage> visit_fns::Visitor<'ast>
Expand Down
Loading