foundry-rs / foundry

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.
https://getfoundry.sh
Apache License 2.0
8.29k stars 1.75k forks source link

Coverage doesn't cover functions that only return a value #2194

Closed iFrostizz closed 2 years ago

iFrostizz commented 2 years ago

Component

Forge

Have you ensured that all of these are up to date?

What version of Foundry are you on?

forge 0.2.0 (153ae19 2022-06-26T00:04:15.775569508Z)

What command(s) is the bug in?

forge coverage

Operating System

Linux

Describe the bug

Hello,

When working on a weird ERC20 and checking the coverage, I noticed that public functions that just return a value are not marked as covered in the coverage report even if properly tested.

Screenshot from 2022-07-02 23-48-19 Screenshot from 2022-07-02 23-49-22

I've prepared a repo in order for you to quickly reproduce the bug: https://github.com/iFrostizz/broken-token-cov-repro

Thanks for the amazing work

onbjerg commented 2 years ago

The constructors do not work yet (see https://github.com/foundry-rs/foundry/issues/1963), for the other functions I'm unsure but will check it out.

onbjerg commented 2 years ago

Note for later: Interestingly this is only the case if you return a 0-y value like false or 0. Seems like a Solidity optimization, but we lose the source maps

iFrostizz commented 2 years ago

@onbjerg I don't know if you've already started working on it, but I a new rustacean and would love to learn how it gets done.

onbjerg commented 2 years ago

I am working on this unfortunately, but I appreciate it :smile:

onbjerg commented 2 years ago

Seems to be fixed in #2504, at least with this smaller reproduction:

// Contract.sol
pragma solidity >=0.4.0;

contract Dummy {
    bool public stuff;
    function setStuff(bool _stuff) public {
        _setStuff(_stuff);
    }
    function _setStuff(bool _stuff) internal {
        stuff = _stuff;
    }
    function transfer(address, uint256) public returns (bool) {
        return false;
    }
}
// Contract.t.sol
pragma solidity ^0.8.13;

import "forge-std/Test.sol";
import "src/Contract.sol";

contract ContractTest is Test {
    Dummy d;
    function setUp() public {
        d = new Dummy();
    }

    function testExample() public {
        assertTrue(!d.transfer(address(100), 1));
    }
}
$ ../../foundry-rs/foundry/target/local/forge coverage
[⠊] Compiling...
[⠃] Compiling 9 files with 0.8.15
[⠊] Solc 0.8.15 finished in 826.92ms
Compiler run successful (with warnings)
warning[2018]: Warning: Function state mutability can be restricted to pure
  --> src/Contract.sol:12:5:
   |
12 |     function transfer(address, uint256) public returns (bool) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

Analysing contracts...
Running tests...
+-----------------------+---------------+---------------+---------------+---------------+
| File                  | % Lines       | % Statements  | % Branches    | % Funcs       |
+=======================================================================================+
| script/Contract.s.sol | 0.00% (0/1)   | 0.00% (0/1)   | 100.00% (0/0) | 0.00% (0/2)   |
|-----------------------+---------------+---------------+---------------+---------------|
| src/Contract.sol      | 100.00% (3/3) | 100.00% (3/3) | 100.00% (0/0) | 100.00% (3/3) |
|-----------------------+---------------+---------------+---------------+---------------|
| Total                 | 75.00% (3/4)  | 75.00% (3/4)  | 100.00% (0/0) | 60.00% (3/5)  |
+-----------------------+---------------+---------------+---------------+---------------+