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

feat: vm.skip(true) #63

Merged
merged 19 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
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 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> {
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(
tree: &str,
add_vm_skip: bool,
) -> crate::error::Result<crate::hir::Hir> {
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
Loading