Skip to content

Commit fc57bf6

Browse files
committed
Skip pure functions in Foundry backend
1 parent b7b6f6b commit fc57bf6

6 files changed

Lines changed: 48 additions & 2 deletions

File tree

backends/src/foundry/visitor/mod.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
use super::{Foundry, FunctionCall, GenericVisitor, LocalFunction, Storage, Test, WithContents};
44
use anyhow::Result;
55
use necessist_core::framework::{SpanTestMaps, TestSet};
6-
use solang_parser::pt::{Expression, FunctionDefinition, Identifier, Loc, SourceUnit, Statement};
6+
use solang_parser::pt::{
7+
Expression, FunctionAttribute, FunctionDefinition, Identifier, Loc, Mutability, SourceUnit,
8+
Statement,
9+
};
710
use std::{cell::RefCell, collections::BTreeMap, convert::Infallible};
811

912
mod visit;
@@ -43,7 +46,9 @@ impl<'ast> visit_fns::Visitor<'ast> for FunctionDefinitionCollector<'ast> {
4346
&mut self,
4447
function_definition: &'ast FunctionDefinition,
4548
) -> Result<(), Self::Error> {
46-
if let Some(name) = &function_definition.name {
49+
if let Some(name) = &function_definition.name
50+
&& !is_pure(function_definition)
51+
{
4752
self.function_definitions
4853
.entry(name.to_string())
4954
.or_default()
@@ -172,6 +177,7 @@ impl<'ast> visit_fns::Visitor<'ast> for Visitor<'_, '_, '_, 'ast, '_> {
172177
fn is_test_function(function_definition: &FunctionDefinition) -> Option<Test<'_>> {
173178
if let Some(Identifier { name, .. }) = &function_definition.name
174179
&& name.starts_with("test")
180+
&& !is_pure(function_definition)
175181
&& let Some(Statement::Block { statements, .. }) = &function_definition.body
176182
{
177183
Some(Test {
@@ -183,6 +189,17 @@ fn is_test_function(function_definition: &FunctionDefinition) -> Option<Test<'_>
183189
}
184190
}
185191

192+
// smoelius: Skip `pure` functions. @smonicas noticed that instrumenting them is a bug because
193+
// `vm.envBytes` is a `view` function. See: https://github.com/trailofbits/necessist/issues/1728
194+
fn is_pure(function_definition: &FunctionDefinition) -> bool {
195+
function_definition.attributes.iter().any(|attribute| {
196+
matches!(
197+
attribute,
198+
FunctionAttribute::Mutability(Mutability::Pure(_))
199+
)
200+
})
201+
}
202+
186203
fn filter_statements<'ast, I: IntoIterator<Item = &'ast Statement>>(
187204
statements: I,
188205
) -> impl Iterator<Item = &'ast Statement> {

fixtures/pure_function/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Compiler files
2+
cache/
3+
out/
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[profile.default]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
pragma solidity ^0.8.0;
2+
3+
contract PureFunction {
4+
function testPureFunction() public pure {
5+
string memory s;
6+
s = "This is a `pure` function.";
7+
s = "";
8+
}
9+
10+
function testViewFunction() public view {
11+
string memory s;
12+
s = "This is a `view` function.";
13+
s = "";
14+
}
15+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fixtures/pure_function/test/PureFunction.t.sol:12:9-12:42: `s = "This is a `view` function.";`
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
args = ["--no-sqlite", "--root=fixtures/pure_function", "--dump-candidates"]
2+
3+
stderr = ""
4+
5+
[bin]
6+
name = "necessist"
7+
8+
[fs]
9+
cwd = "../../.."

0 commit comments

Comments
 (0)