Libraries are currently not covered and they are somewhat non-trivial to map instructions for.
Generally, there are two types of library functions:
- Internal functions are inlined in the contract that use them
- External functions are called via
delegatecall
The way coverage works currently is in a few passes:
- First, we detect any sections of code that we want to include in our coverage report
- Second, we also take the base contracts of a contract into account, since all behavior of base contracts are inherited. We need this to map instructions to the items we generated in the first pass using source maps
- Third, we use the source maps of concrete contracts to find instructions that mark the items gathered in our first and second step as covered. One item might be covered by multiple instructions in different contracts because of inheritance
The issue with adding library coverage is that the Solidity compiler does not give us any easily digestable information about what libraries are used in contracts. This is in contrast to base contracts which are easily identified using a property of contract AST nodes called linearizedBaseContracts.
In order to reliably find what libraries are used in contracts so we can map instructions onto coverage items present in libraries, we need to walk each node in the AST to find call expression nodes.
These call expression nodes include some crude type information, and in the case of library calls, the type information will show up as e.g. type(library LibraryName).
After parsing this information, we need to map the library name back onto an AST node ID so we can find what coverage items are present in the library. The rest of the analysis step in forge coverage uses AST node IDs to refer to contracts.
Some unknowns:
- Is the type information correct, i.e. does it take shadowing and import aliases into account?
- Are library names from the type information easily mappable onto AST node IDs corresponding to the library's AST?
Libraries are currently not covered and they are somewhat non-trivial to map instructions for.
Generally, there are two types of library functions:
delegatecallThe way coverage works currently is in a few passes:
The issue with adding library coverage is that the Solidity compiler does not give us any easily digestable information about what libraries are used in contracts. This is in contrast to base contracts which are easily identified using a property of contract AST nodes called
linearizedBaseContracts.In order to reliably find what libraries are used in contracts so we can map instructions onto coverage items present in libraries, we need to walk each node in the AST to find call expression nodes.
These call expression nodes include some crude type information, and in the case of library calls, the type information will show up as e.g.
type(library LibraryName).After parsing this information, we need to map the library name back onto an AST node ID so we can find what coverage items are present in the library. The rest of the analysis step in
forge coverageuses AST node IDs to refer to contracts.Some unknowns: