1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
import * as _ from 'lodash';
import * as Parser from 'solidity-parser-antlr';
import { BranchMap, FnMap, LocationByOffset, SingleFileSourceRange, StatementMap } from './types';
export interface CoverageEntriesDescription {
fnMap: FnMap;
branchMap: BranchMap;
statementMap: StatementMap;
modifiersStatementIds: number[];
}
enum BranchType {
If = 'if',
ConditionalExpression = 'cond-expr',
BinaryExpression = 'binary-expr',
}
export class ASTVisitor {
private _entryId = 0;
private _fnMap: FnMap = {};
private _branchMap: BranchMap = {};
private _modifiersStatementIds: number[] = [];
private _statementMap: StatementMap = {};
private _locationByOffset: LocationByOffset;
constructor(locationByOffset: LocationByOffset) {
this._locationByOffset = locationByOffset;
}
public getCollectedCoverageEntries(): CoverageEntriesDescription {
const coverageEntriesDescription = {
fnMap: this._fnMap,
branchMap: this._branchMap,
statementMap: this._statementMap,
modifiersStatementIds: this._modifiersStatementIds,
};
return coverageEntriesDescription;
}
public IfStatement(ast: Parser.IfStatement): void {
this._visitStatement(ast);
this._visitBinaryBranch(ast, ast.trueBody, ast.falseBody || ast, BranchType.If);
}
public FunctionDefinition(ast: Parser.FunctionDefinition): void {
this._visitFunctionLikeDefinition(ast);
}
public ModifierDefinition(ast: Parser.ModifierDefinition): void {
this._visitFunctionLikeDefinition(ast);
}
public ForStatement(ast: Parser.ForStatement): void {
this._visitStatement(ast);
}
public ReturnStatement(ast: Parser.ReturnStatement): void {
this._visitStatement(ast);
}
public BreakStatement(ast: Parser.BreakStatement): void {
this._visitStatement(ast);
}
public ExpressionStatement(ast: Parser.ExpressionStatement): void {
this._visitStatement(ast.expression);
}
public BinaryOperation(ast: Parser.BinaryOperation): void {
const BRANCHING_BIN_OPS = ['&&', '||'];
if (_.includes(BRANCHING_BIN_OPS, ast.operator)) {
this._visitBinaryBranch(ast, ast.left, ast.right, BranchType.BinaryExpression);
}
}
public Conditional(ast: Parser.Conditional): void {
this._visitBinaryBranch(ast, ast.trueExpression, ast.falseExpression, BranchType.ConditionalExpression);
}
public ModifierInvocation(ast: Parser.ModifierInvocation): 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);
}
}
private _visitBinaryBranch(
ast: Parser.ASTNode,
left: Parser.ASTNode,
right: Parser.ASTNode,
type: BranchType,
): void {
this._branchMap[this._entryId++] = {
line: this._getExpressionRange(ast).start.line,
type,
locations: [this._getExpressionRange(left), this._getExpressionRange(right)],
};
}
private _visitStatement(ast: Parser.ASTNode): void {
this._statementMap[this._entryId++] = this._getExpressionRange(ast);
}
private _getExpressionRange(ast: Parser.ASTNode): SingleFileSourceRange {
const start = this._locationByOffset[ast.range[0] - 1];
const end = this._locationByOffset[ast.range[1]];
const range = {
start,
end,
};
return range;
}
private _visitFunctionLikeDefinition(ast: Parser.ModifierDefinition | Parser.FunctionDefinition): void {
const loc = this._getExpressionRange(ast);
this._fnMap[this._entryId++] = {
name: ast.name,
line: loc.start.line,
loc,
};
this._visitStatement(ast);
}
}
|