Skip to content

Commit

Permalink
feat: emit vm.skip(true) when passing -S (#63)
Browse files Browse the repository at this point in the history
Co-authored-by: Alexander González <alexfertel97@gmail.com>
  • Loading branch information
simon-something and alexfertel authored Apr 18, 2024
1 parent 9f27e80 commit a4cb16a
Show file tree
Hide file tree
Showing 18 changed files with 840 additions and 59 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ contents of the file.
$ bulloak scaffold -wf ./**/*.tree
```

Note all tests are showing as passing when their body is empty. To prevent this,
you can use the `-S` (or `--vm-skip`) option to add a `vm.skip(true);` at the
beginning of each test function. This option will also add an import for
forge-std's `Test.sol` and all test contracts will inherit from it.

### Check That Your Code And Spec Match

You can use `bulloak check` to make sure that your Solidity files match your
Expand Down
2 changes: 1 addition & 1 deletion benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn big_tree(c: &mut Criterion) {
let tree = std::fs::read_to_string("benches/bench_data/cancel.tree").unwrap();

let scaffolder = bulloak::scaffold::Scaffolder::new("some_version");
let scaffolder = bulloak::scaffold::Scaffolder::new("some_version", true);
let mut group = c.benchmark_group("sample-size-10");
// group.sample_size(10);
group.bench_function("big-tree", |b| {
Expand Down
2 changes: 1 addition & 1 deletion src/check/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl Context {
pub(crate) fn new(tree: PathBuf) -> Result<Self, Violation> {
let tree_path_cow = tree.to_string_lossy();
let tree_contents = try_read_to_string(&tree)?;
let hir = crate::hir::translate(&tree_contents).map_err(|e| {
let hir = crate::hir::translate(&tree_contents, false).map_err(|e| {
Violation::new(
ViolationKind::ParsingFailed(e),
Location::File(tree_path_cow.into_owned()),
Expand Down
3 changes: 2 additions & 1 deletion src/check/violation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ impl ViolationKind {
pub(crate) fn fix(&self, mut ctx: Context) -> Context {
match self {
ViolationKind::ContractMissing(_) => {
let pt = sol::Translator::new(INTERNAL_DEFAULT_SOL_VERSION).translate(&ctx.hir);
let pt =
sol::Translator::new(INTERNAL_DEFAULT_SOL_VERSION, false).translate(&ctx.hir);
let source = sol::Formatter::new().emit(pt.clone());
let parsed = parse(&source).expect("should parse solidity string");
ctx.from_parsed(parsed)
Expand Down
48 changes: 38 additions & 10 deletions src/hir/combiner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,13 @@ mod tests {
use crate::syntax::parser::Parser;
use crate::syntax::tokenizer::Tokenizer;

fn translate(text: &str) -> Result<Hir> {
fn translate(text: &str, with_vm_skip: bool) -> Result<Hir> {
let tokens = Tokenizer::new().tokenize(&text)?;
let ast = Parser::new().parse(&text, &tokens)?;
let mut discoverer = modifiers::ModifierDiscoverer::new();
let modifiers = discoverer.discover(&ast);

Ok(hir::translator::Translator::new().translate(&ast, modifiers))
Ok(hir::translator::Translator::new().translate(&ast, modifiers, with_vm_skip))
}

fn combine(text: &str, hirs: Vec<Hir>) -> Result<Hir, Error> {
Expand Down Expand Up @@ -307,6 +307,10 @@ mod tests {
})
}

fn statement(ty: hir::StatementType) -> Hir {
Hir::Statement(hir::Statement { ty })
}

fn comment(lexeme: String) -> Hir {
Hir::Comment(hir::Comment { lexeme })
}
Expand All @@ -317,7 +321,10 @@ mod tests {
"::orphanedFunction\n└── when something bad happens\n └── it should revert",
"Contract::function\n└── when something bad happens\n └── it should revert",
];
let hirs = trees.iter().map(|tree| translate(tree).unwrap()).collect();
let hirs = trees
.iter()
.map(|tree| translate(tree, true).unwrap())
.collect();
let text = trees.join("\n\n");
let result = combine(&text, hirs);

Expand All @@ -330,7 +337,10 @@ mod tests {
"Contract::function\n└── when something bad happens\n └── it should revert",
"::orphanedFunction\n└── when something bad happens\n └── it should revert",
];
let hirs = trees.iter().map(|tree| translate(tree).unwrap()).collect();
let hirs = trees
.iter()
.map(|tree| translate(tree, true).unwrap())
.collect();

let expected = r"•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
bulloak error: contract name missing at tree root #2";
Expand All @@ -348,7 +358,10 @@ bulloak error: contract name missing at tree root #2";
"Contract::function1\n└── when something bad happens\n └── it should revert",
"Contract::function2\n└── when something shit happens\n └── it should revert",
];
let mut hirs: Vec<_> = trees.iter().map(|tree| translate(tree).unwrap()).collect();
let mut hirs: Vec<_> = trees
.iter()
.map(|tree| translate(tree, true).unwrap())
.collect();

// Append a comment HIR to the hirs.
hirs.push(root(vec![comment("this is a random comment".to_owned())]));
Expand All @@ -369,14 +382,20 @@ bulloak error: contract name missing at tree root #2";
hir::FunctionTy::Function,
Span::new(Position::new(20, 2, 1), Position::new(86, 3, 24)),
None,
Some(vec![comment("it should revert".to_owned())])
Some(vec![
comment("it should revert".to_owned()),
statement(hir::StatementType::VmSkip)
])
),
function(
"test_Function2RevertWhen_SomethingShitHappens".to_owned(),
hir::FunctionTy::Function,
Span::new(Position::new(20, 2, 1), Position::new(87, 3, 24)),
None,
Some(vec![comment("it should revert".to_owned())])
Some(vec![
comment("it should revert".to_owned()),
statement(hir::StatementType::VmSkip)
])
),
]
)]
Expand All @@ -389,7 +408,10 @@ bulloak error: contract name missing at tree root #2";
"Contract::function1\n└── when something bad happens\n └── given something else happens\n └── it should revert",
"Contract::function2\n└── when something bad happens\n └── given the caller is 0x1337\n └── it should revert",
];
let mut hirs: Vec<_> = trees.iter().map(|tree| translate(tree).unwrap()).collect();
let mut hirs: Vec<_> = trees
.iter()
.map(|tree| translate(tree, true).unwrap())
.collect();

// Append a comment HIR to the hirs.
hirs.push(root(vec![comment("this is a random comment".to_owned())]));
Expand Down Expand Up @@ -418,14 +440,20 @@ bulloak error: contract name missing at tree root #2";
hir::FunctionTy::Function,
Span::new(Position::new(61, 3, 5), Position::new(133, 4, 28)),
Some(vec!["whenSomethingBadHappens".to_owned()]),
Some(vec![comment("it should revert".to_owned())])
Some(vec![
comment("it should revert".to_owned()),
statement(hir::StatementType::VmSkip)
])
),
function(
"test_Function2RevertGiven_TheCallerIs0x1337".to_owned(),
hir::FunctionTy::Function,
Span::new(Position::new(61, 3, 5), Position::new(131, 4, 28)),
Some(vec!["whenSomethingBadHappens".to_owned()]),
Some(vec![comment("it should revert".to_owned())])
Some(vec![
comment("it should revert".to_owned()),
statement(hir::StatementType::VmSkip)
])
),
]
)]
Expand Down
16 changes: 16 additions & 0 deletions src/hir/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub enum Hir {
FunctionDefinition(FunctionDefinition),
/// A comment.
Comment(Comment),
/// A Statement.
Statement(Statement),
}

impl Default for Hir {
Expand Down Expand Up @@ -129,3 +131,17 @@ pub struct Comment {
/// The contract name.
pub lexeme: String,
}

/// The statements which are currently supported.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StatementType {
/// The `vm.skip(true);` statement.
VmSkip,
}

/// A statement node.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Statement {
/// The statement.
pub ty: StatementType,
}
18 changes: 12 additions & 6 deletions src/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,35 @@ pub use hir::*;
///
/// This function leverages `translate_tree_to_hir` to generate the HIR for each tree,
/// and `crate::hir::combiner::Combiner::combine` to combine the HIRs into a single HIR.
pub fn translate(text: &str) -> anyhow::Result<Hir> {
Ok(translate_and_combine_trees(text)?)
pub fn translate(text: &str, add_vm_skip: bool) -> anyhow::Result<Hir> {

Check warning on line 15 in src/hir/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

docs for function returning `Result` missing `# Errors` section

warning: docs for function returning `Result` missing `# Errors` section --> src/hir/mod.rs:15:1 | 15 | pub fn translate(text: &str, add_vm_skip: bool) -> anyhow::Result<Hir> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
Ok(translate_and_combine_trees(text, add_vm_skip)?)
}

/// Generates the HIR for a single tree.
///
/// This function leverages `crate::syntax::parse` and `crate::hir::translator::Translator::translate`
/// to hide away most of the complexity of `bulloak`'s internal compiler.
pub fn translate_tree_to_hir(tree: &str) -> crate::error::Result<crate::hir::Hir> {
pub fn translate_tree_to_hir(

Check warning on line 23 in src/hir/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

item name ends with its containing module's name

warning: item name ends with its containing module's name --> src/hir/mod.rs:23:8 | 23 | pub fn translate_tree_to_hir( | ^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions = note: `#[warn(clippy::module_name_repetitions)]` implied by `#[warn(clippy::pedantic)]`
tree: &str,
add_vm_skip: bool,
) -> crate::error::Result<crate::hir::Hir> {

Check warning on line 26 in src/hir/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

docs for function returning `Result` missing `# Errors` section

warning: docs for function returning `Result` missing `# Errors` section --> src/hir/mod.rs:23:1 | 23 | / pub fn translate_tree_to_hir( 24 | | tree: &str, 25 | | add_vm_skip: bool, 26 | | ) -> crate::error::Result<crate::hir::Hir> { | |__________________________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
let ast = crate::syntax::parse(tree)?;
let mut discoverer = crate::scaffold::modifiers::ModifierDiscoverer::new();
let modifiers = discoverer.discover(&ast);
Ok(crate::hir::translator::Translator::new().translate(&ast, modifiers))
Ok(crate::hir::translator::Translator::new().translate(&ast, modifiers, add_vm_skip))
}

/// High-level function that returns a HIR given the contents of a `.tree` file.
///
/// This function leverages `translate_tree_to_hir` to generate the HIR for each tree,
/// and `crate::hir::combiner::Combiner::combine` to combine the HIRs into a single HIR.
pub(crate) fn translate_and_combine_trees(text: &str) -> crate::error::Result<crate::hir::Hir> {
pub(crate) fn translate_and_combine_trees(
text: &str,
add_vm_skip: bool,
) -> crate::error::Result<crate::hir::Hir> {
let trees = crate::utils::split_trees(text);
let hirs = trees
.map(translate_tree_to_hir)
.map(|tree| translate_tree_to_hir(tree, add_vm_skip))
.collect::<crate::error::Result<Vec<crate::hir::Hir>>>()?;
Ok(crate::hir::combiner::Combiner::new().combine(text, hirs)?)
}
Loading

0 comments on commit a4cb16a

Please sign in to comment.