aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/sol-cov/src/ast_visitor.ts4
-rw-r--r--packages/sol-cov/src/coverage_manager.ts24
2 files changed, 28 insertions, 0 deletions
diff --git a/packages/sol-cov/src/ast_visitor.ts b/packages/sol-cov/src/ast_visitor.ts
index 9193f6a18..701cbbc88 100644
--- a/packages/sol-cov/src/ast_visitor.ts
+++ b/packages/sol-cov/src/ast_visitor.ts
@@ -7,6 +7,7 @@ export interface CoverageEntriesDescription {
fnMap: FnMap;
branchMap: BranchMap;
statementMap: StatementMap;
+ modifiersStatementIds: number[];
}
enum BranchType {
@@ -19,6 +20,7 @@ export class ASTVisitor {
private _entryId = 0;
private _fnMap: FnMap = {};
private _branchMap: BranchMap = {};
+ private _modifiersStatementIds: number[] = [];
private _statementMap: StatementMap = {};
private _locationByOffset: LocationByOffset;
private static _doesLookLikeAnASTNode(ast: any): boolean {
@@ -49,6 +51,7 @@ export class ASTVisitor {
fnMap: this._fnMap,
branchMap: this._branchMap,
statementMap: this._statementMap,
+ modifiersStatementIds: this._modifiersStatementIds,
};
return coverageEntriesDescription;
}
@@ -91,6 +94,7 @@ export class ASTVisitor {
private _visitModifierArgument(ast: SolidityParser.AST): void {
const BUILTIN_MODIFIERS = ['public', 'view', 'payable', 'external', 'internal', 'pure', 'constant'];
if (!_.includes(BUILTIN_MODIFIERS, ast.name)) {
+ this._modifiersStatementIds.push(this._entryId);
this._visitStatement(ast);
}
}
diff --git a/packages/sol-cov/src/coverage_manager.ts b/packages/sol-cov/src/coverage_manager.ts
index 4ca6b0ec8..b70ca6f3f 100644
--- a/packages/sol-cov/src/coverage_manager.ts
+++ b/packages/sol-cov/src/coverage_manager.ts
@@ -69,6 +69,30 @@ export class CoverageManager {
);
functionCoverage[fnId] = isCovered;
}
+ // HACK: Solidity doesn't emit any opcodes that map back to modifiers with no args, that's why we map back to the
+ // function range and check if there is any covered statement within that range.
+ for (const modifierStatementId of coverageEntriesDescription.modifiersStatementIds) {
+ if (statementCoverage[modifierStatementId]) {
+ // Already detected as covered
+ continue;
+ }
+ const modifierDescription = coverageEntriesDescription.statementMap[modifierStatementId];
+ const enclosingFunction = _.find(coverageEntriesDescription.fnMap, functionDescription =>
+ utils.isRangeInside(modifierDescription, functionDescription.loc),
+ ) as FunctionDescription;
+ const isModifierCovered = _.some(
+ coverageEntriesDescription.statementMap,
+ (statementDescription: StatementDescription, statementId: number) => {
+ const isInsideTheModifierEnclosingFunction = utils.isRangeInside(
+ statementDescription,
+ enclosingFunction.loc,
+ );
+ const isCovered = statementCoverage[statementId];
+ return isInsideTheModifierEnclosingFunction && isCovered;
+ },
+ );
+ statementCoverage[modifierStatementId] = isModifierCovered;
+ }
const partialCoverage = {
[contractData.sources[fileIndex]]: {
...coverageEntriesDescription,