@@ -68,6 +68,96 @@ impl Stats {
6868 }
6969}
7070
71+ #[ inline( always) ]
72+ fn is_child_identifier ( node : & Node , id : u16 ) -> bool {
73+ let mut cursor = node. object ( ) . walk ( ) ;
74+ for child in node. object ( ) . children ( & mut cursor) {
75+ if child. kind_id ( ) == id {
76+ return true ;
77+ }
78+ }
79+ false
80+ }
81+
82+ macro_rules! function {
83+ ( $node: ident, $stats: ident) => {
84+ // Consider named functions as functions and unnamed ones as
85+ // closures.
86+ if count_specific_ancestors!(
87+ $node,
88+ VariableDeclarator | AssignmentExpression | LabeledStatement | Pair ,
89+ StatementBlock | ReturnStatement | NewExpression | Arguments
90+ ) > 0
91+ || is_child_identifier( $node, Identifier as u16 )
92+ {
93+ $stats. functions += 1 ;
94+ } else {
95+ $stats. closures += 1 ;
96+ }
97+ } ;
98+ }
99+
100+ macro_rules! arrow_function {
101+ ( $node: ident, $stats: ident) => {
102+ // Consider named functions as functions and unnamed ones as
103+ // closures.
104+ if count_specific_ancestors!(
105+ $node,
106+ VariableDeclarator | AssignmentExpression | LabeledStatement ,
107+ StatementBlock | ReturnStatement | NewExpression | CallExpression
108+ ) > 0
109+ {
110+ $stats. functions += 1 ;
111+ } else {
112+ $stats. closures += 1 ;
113+ }
114+ } ;
115+ }
116+
117+ macro_rules! js_grammar {
118+ ( $grammar: ident, $node: ident, $stats: ident) => {
119+ use $grammar:: * ;
120+
121+ match $node. object( ) . kind_id( ) . into( ) {
122+ FunctionDeclaration | MethodDefinition => {
123+ $stats. functions += 1 ;
124+ }
125+ GeneratorFunction | GeneratorFunctionDeclaration => {
126+ $stats. closures += 1 ;
127+ }
128+ Function => {
129+ function!( $node, $stats) ;
130+ }
131+ ArrowFunction => {
132+ arrow_function!( $node, $stats) ;
133+ }
134+ _ => { }
135+ }
136+ } ;
137+ }
138+
139+ macro_rules! typescript_grammar {
140+ ( $grammar: ident, $node: ident, $stats: ident) => {
141+ use $grammar:: * ;
142+
143+ match $node. object( ) . kind_id( ) . into( ) {
144+ FunctionDeclaration | MethodDefinition => {
145+ $stats. functions += 1 ;
146+ }
147+ GeneratorFunction | GeneratorFunctionDeclaration => {
148+ $stats. closures += 1 ;
149+ }
150+ Function => {
151+ function!( $node, $stats) ;
152+ }
153+ ArrowFunction => {
154+ arrow_function!( $node, $stats) ;
155+ }
156+ _ => { }
157+ }
158+ } ;
159+ }
160+
71161#[ doc( hidden) ]
72162pub trait Nom
73163where
@@ -94,65 +184,25 @@ impl Nom for PythonCode {
94184
95185impl Nom for MozjsCode {
96186 fn compute ( node : & Node , stats : & mut Stats ) {
97- use Mozjs :: * ;
98-
99- match node. object ( ) . kind_id ( ) . into ( ) {
100- Function | FunctionDeclaration | MethodDefinition => {
101- stats. functions += 1 ;
102- }
103- GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
104- stats. closures += 1 ;
105- }
106- _ => { }
107- }
187+ js_grammar ! ( Mozjs , node, stats) ;
108188 }
109189}
110190
111191impl Nom for JavascriptCode {
112192 fn compute ( node : & Node , stats : & mut Stats ) {
113- use Javascript :: * ;
114-
115- match node. object ( ) . kind_id ( ) . into ( ) {
116- Function | FunctionDeclaration | MethodDefinition => {
117- stats. functions += 1 ;
118- }
119- GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
120- stats. closures += 1 ;
121- }
122- _ => { }
123- }
193+ js_grammar ! ( Javascript , node, stats) ;
124194 }
125195}
126196
127197impl Nom for TypescriptCode {
128198 fn compute ( node : & Node , stats : & mut Stats ) {
129- use Typescript :: * ;
130-
131- match node. object ( ) . kind_id ( ) . into ( ) {
132- Function | FunctionDeclaration | MethodDefinition => {
133- stats. functions += 1 ;
134- }
135- GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
136- stats. closures += 1 ;
137- }
138- _ => { }
139- }
199+ typescript_grammar ! ( Typescript , node, stats) ;
140200 }
141201}
142202
143203impl Nom for TsxCode {
144204 fn compute ( node : & Node , stats : & mut Stats ) {
145- use Tsx :: * ;
146-
147- match node. object ( ) . kind_id ( ) . into ( ) {
148- Function | FunctionDeclaration | MethodDefinition => {
149- stats. functions += 1 ;
150- }
151- GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
152- stats. closures += 1 ;
153- }
154- _ => { }
155- }
205+ typescript_grammar ! ( Tsx , node, stats) ;
156206 }
157207}
158208
@@ -279,15 +329,72 @@ mod tests {
279329 }
280330
281331 #[ test]
282- fn c_nom ( ) {
332+ fn javascript_nom ( ) {
283333 check_metrics ! (
284- "int foo();
334+ "function f(a, b) {
335+ function foo(a) {
336+ return a;
337+ }
338+ var bar = (function () {
339+ var counter = 0;
340+ return function () {
341+ counter += 1;
342+ return counter
343+ }
344+ })();
345+ return bar(foo(a), a);
346+ }" ,
347+ "foo.js" ,
348+ JavascriptParser ,
349+ nom,
350+ [
351+ ( functions, 3 , usize ) , // f, foo, bar
352+ ( closures, 1 , usize ) , // return function ()
353+ ( total, 4 , usize )
354+ ]
355+ ) ;
356+ }
285357
286- int foo() {
287- return 0;
358+ #[ test]
359+ fn javascript_call_nom ( ) {
360+ check_metrics ! (
361+ "add_task(async function test_safe_mode() {
362+ gAppInfo.inSafeMode = true;
363+ });" ,
364+ "foo.js" ,
365+ JavascriptParser ,
366+ nom,
367+ [
368+ ( functions, 1 , usize ) , // test_safe_mode
369+ ( closures, 0 , usize ) ,
370+ ( total, 1 , usize )
371+ ]
372+ ) ;
373+ }
374+
375+ #[ test]
376+ fn javascript_assignment_nom ( ) {
377+ check_metrics ! (
378+ "AnimationTest.prototype.enableDisplay = function(element) {};" ,
379+ "foo.js" ,
380+ JavascriptParser ,
381+ nom,
382+ [
383+ ( functions, 1 , usize ) ,
384+ ( closures, 0 , usize ) ,
385+ ( total, 1 , usize )
386+ ]
387+ ) ;
388+ }
389+
390+ #[ test]
391+ fn javascript_labeled_nom ( ) {
392+ check_metrics ! (
393+ "toJSON: function() {
394+ return this.inspect(true);
288395 }" ,
289- "foo.c " ,
290- CppParser ,
396+ "foo.js " ,
397+ JavascriptParser ,
291398 nom,
292399 [
293400 ( functions, 1 , usize ) ,
@@ -296,4 +403,106 @@ mod tests {
296403 ]
297404 ) ;
298405 }
406+
407+ #[ test]
408+ fn javascript_pair_nom ( ) {
409+ check_metrics ! (
410+ "return {
411+ initialize: function(object) {
412+ this._object = object.toObject();
413+ },
414+ }" ,
415+ "foo.js" ,
416+ JavascriptParser ,
417+ nom,
418+ [
419+ ( functions, 1 , usize ) ,
420+ ( closures, 0 , usize ) ,
421+ ( total, 1 , usize )
422+ ]
423+ ) ;
424+ }
425+
426+ #[ test]
427+ fn javascript_unnamed_nom ( ) {
428+ check_metrics ! (
429+ "Ajax.getTransport = Try.these(
430+ function() {
431+ return function(){ return new XMLHttpRequest()}
432+ }
433+ );" ,
434+ "foo.js" ,
435+ JavascriptParser ,
436+ nom,
437+ [
438+ ( functions, 0 , usize ) ,
439+ ( closures, 2 , usize ) ,
440+ ( total, 2 , usize )
441+ ]
442+ ) ;
443+ }
444+
445+ #[ test]
446+ fn javascript_arrow_nom ( ) {
447+ check_metrics ! (
448+ "var materials = [\" Hydrogen\" ];
449+ materials.map(material => material.length);
450+ let add = (a, b) => a + b;" ,
451+ "foo.js" ,
452+ JavascriptParser ,
453+ nom,
454+ [
455+ ( functions, 1 , usize ) , // add
456+ ( closures, 1 , usize ) , // materials.map
457+ ( total, 2 , usize )
458+ ]
459+ ) ;
460+ }
461+
462+ #[ test]
463+ fn javascript_arrow_assignment_nom ( ) {
464+ check_metrics ! (
465+ "sink.onPull = () => { };" ,
466+ "foo.js" ,
467+ JavascriptParser ,
468+ nom,
469+ [
470+ ( functions, 1 , usize ) ,
471+ ( closures, 0 , usize ) ,
472+ ( total, 1 , usize )
473+ ]
474+ ) ;
475+ }
476+
477+ #[ test]
478+ fn javascript_arrow_new_nom ( ) {
479+ check_metrics ! (
480+ "const response = new Promise(resolve => channel.port1.onmessage = resolve);" ,
481+ "foo.js" ,
482+ JavascriptParser ,
483+ nom,
484+ [
485+ ( functions, 0 , usize ) ,
486+ ( closures, 1 , usize ) ,
487+ ( total, 1 , usize )
488+ ]
489+ ) ;
490+ }
491+
492+ #[ test]
493+ fn javascript_arrow_call_nom ( ) {
494+ check_metrics ! (
495+ "let notDisabled = TestUtils.waitForCondition(
496+ () => !backbutton.hasAttribute(\" disabled\" )
497+ );" ,
498+ "foo.js" ,
499+ JavascriptParser ,
500+ nom,
501+ [
502+ ( functions, 0 , usize ) ,
503+ ( closures, 1 , usize ) ,
504+ ( total, 1 , usize )
505+ ]
506+ ) ;
507+ }
299508}
0 commit comments