Skip to content

Commit ca2250a

Browse files
authored
Merge pull request #17 from esnext-coverage/feat/visitors-hoist-functions
feat(visitors): hoist function declarations
2 parents e0e873c + 929e25a commit ca2250a

File tree

8 files changed

+52
-28
lines changed

8 files changed

+52
-28
lines changed

src/visitors.js

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,18 @@ export default safeguardVisitors({
173173
instrumentStatement(path, state);
174174
},
175175

176-
Function(path, state) {
177-
if (path.node.kind === 'constructor') {
178-
instrumentBlock('body', path.get('body'), state, ['function', 'constructor']);
179-
} else {
180-
instrumentBlock('body', path.get('body'), state, ['function']);
181-
}
182-
instrumentStatement(path, state);
176+
FunctionExpression(path, state) {
177+
instrumentBlock('body', path.get('body'), state, ['function']);
178+
},
179+
180+
FunctionDeclaration(path, state) {
181+
const loc = path.node.loc;
182+
const marker = createMarker(state, {loc, tags: ['statement']});
183+
path.findParent(parent => parent.isBlock())
184+
.unshiftContainer('body', markAsInstrumented(
185+
t.expressionStatement(marker)
186+
));
187+
instrumentBlock('body', path.get('body'), state, ['function']);
183188
},
184189

185190
// Source: x => {}
@@ -214,11 +219,18 @@ export default safeguardVisitors({
214219
instrumentObjectProperty(path, state);
215220
},
216221

222+
ClassProperty(path, state) {
223+
instrumentClassProperty(path, state);
224+
},
225+
226+
ClassMethod(path, state) {
227+
instrumentBlock('body', path.get('body'), state, ['function']);
228+
},
229+
217230
ClassBody(path, state) {
218231
const parent = path.parentPath;
219232
const body = path.get('body');
220233
const methods = body.filter(node => node.isMethod());
221-
const props = body.filter(node => !node.isMethod());
222234
const methodMarkers = methods.map(method => {
223235
const tags = ['statement', 'method'];
224236
const loc = method.node.loc;
@@ -237,10 +249,6 @@ export default safeguardVisitors({
237249
t.sequenceExpression([...methodMarkers, parent.node])
238250
));
239251
}
240-
241-
props.forEach(prop => {
242-
instrumentClassProperty(prop, state);
243-
});
244252
}
245253

246254
});

test/fixture/classes.fixture.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class A {
55
p = // one statement (executed twice)
66
1; // one expression (executed twice)
77
constructor() { // one statement
8-
// one constructor function (executed twice)
8+
// one function (executed twice)
99
}
1010
foo() { // one statement
1111
// one method function

test/fixture/export-statements.fixture.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
export const a = 'a'; // one expression
33

44
// one export statement
5+
// one hoisted function declaration statement
56
export function foo() {} // one function
67

78
// one export statement
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// One statement:
2+
function foo() {
3+
// One statement:
4+
return bar();
5+
// One statement:
6+
function bar() {}
7+
}
8+
9+
// One statement:
10+
foo();

test/spec/classes.spec.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import test from 'tape';
22
import runFixture from './helpers/run';
33
import {
4-
isConstructor,
54
isExpression,
65
isStatement,
76
isFunction
@@ -55,15 +54,6 @@ test('coverage should count expressions in classes', t => {
5554
// Class methods
5655
// --------------------
5756

58-
test('coverage should count class constructors', t => {
59-
t.plan(1);
60-
runFixture('classes').then(locations => {
61-
const constructorLocations = locations.filter(isConstructor);
62-
// There is only one constructor:
63-
t.equal(constructorLocations.length, 1);
64-
});
65-
});
66-
6757
test('coverage should count class methods and track their executions', t => {
6858
t.plan(3);
6959
runFixture('classes').then(locations => {

test/spec/exports.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ test('coverage should count exports', t => {
2525
test('coverage should count exports as statements', t => {
2626
t.plan(2);
2727
runFixture('export-statements').then(locations => {
28-
const exportStatements = locations.filter(isStatement);
28+
const exportStatements = locations.filter(isExport).filter(isStatement);
2929
const executedOnceStatements = exportStatements
3030
.filter(el => el.count === 1);
3131

test/spec/functions.spec.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@ test('coverage should count function declarations', t => {
1616
});
1717
});
1818

19+
test('coverage should count functions declared after return statment', t => {
20+
t.plan(3);
21+
runFixture('function-after-return').then(locations => {
22+
const functionLocations = locations.filter(isFunction);
23+
const statementLocations = locations.filter(isStatement);
24+
const declaredOnceFunctionLocations = functionLocations
25+
.filter(el => el.count === 1);
26+
const executedOnceStatementLocations = statementLocations
27+
.filter(el => el.count === 1);
28+
29+
// There are two functions:
30+
t.equal(functionLocations.length, 2);
31+
// Both functions have been declared once:
32+
t.equal(declaredOnceFunctionLocations.length, 2);
33+
// All statements have been executed once:
34+
t.equal(executedOnceStatementLocations.length, 4);
35+
});
36+
});
37+
1938
test('coverage should count function executions', t => {
2039
t.plan(4);
2140
runFixture('function-executions').then(locations => {

test/spec/helpers/tag-assert.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ export function isFunction({tags}) {
66
return tags.some(tag => tag === 'function');
77
}
88

9-
export function isConstructor({tags}) {
10-
return tags.some(tag => tag === 'constructor');
11-
}
12-
139
export function isBranch({tags}) {
1410
return tags.some(tag => tag === 'branch');
1511
}

0 commit comments

Comments
 (0)