@@ -5,6 +5,7 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
5
5
import { createRule , isFunction } from '../util' ;
6
6
import type {
7
7
FunctionExpression ,
8
+ FunctionInfo ,
8
9
FunctionNode ,
9
10
} from '../util/explicitReturnTypeUtils' ;
10
11
import {
@@ -101,13 +102,31 @@ export default createRule<Options, MessageIds>({
101
102
// tracks all of the functions we've already checked
102
103
const checkedFunctions = new Set < FunctionNode > ( ) ;
103
104
104
- // tracks functions that were found whilst traversing
105
- const foundFunctions : FunctionNode [ ] = [ ] ;
105
+ const functionStack : FunctionNode [ ] = [ ] ;
106
+ const functionReturnsMap = new Map <
107
+ FunctionNode ,
108
+ TSESTree . ReturnStatement [ ]
109
+ > ( ) ;
106
110
107
111
// all nodes visited, avoids infinite recursion for cyclic references
108
112
// (such as class member referring to itself)
109
113
const alreadyVisited = new Set < TSESTree . Node > ( ) ;
110
114
115
+ function getReturnsInFunction (
116
+ node : FunctionNode ,
117
+ ) : TSESTree . ReturnStatement [ ] {
118
+ return functionReturnsMap . get ( node ) ?? [ ] ;
119
+ }
120
+
121
+ function enterFunction ( node : FunctionNode ) : void {
122
+ functionStack . push ( node ) ;
123
+ functionReturnsMap . set ( node , [ ] ) ;
124
+ }
125
+
126
+ function exitFunction ( ) : void {
127
+ functionStack . pop ( ) ;
128
+ }
129
+
111
130
/*
112
131
# How the rule works:
113
132
@@ -120,10 +139,10 @@ export default createRule<Options, MessageIds>({
120
139
*/
121
140
122
141
return {
123
- ExportDefaultDeclaration ( node ) : void {
142
+ ' ExportDefaultDeclaration:exit' ( node ) : void {
124
143
checkNode ( node . declaration ) ;
125
144
} ,
126
- 'ExportNamedDeclaration:not([source])' (
145
+ 'ExportNamedDeclaration:not([source]):exit ' (
127
146
node : TSESTree . ExportNamedDeclaration ,
128
147
) : void {
129
148
if ( node . declaration ) {
@@ -134,21 +153,25 @@ export default createRule<Options, MessageIds>({
134
153
}
135
154
}
136
155
} ,
137
- TSExportAssignment ( node ) : void {
156
+ ' TSExportAssignment:exit' ( node ) : void {
138
157
checkNode ( node . expression ) ;
139
158
} ,
140
- 'ArrowFunctionExpression, FunctionDeclaration, FunctionExpression' (
141
- node : FunctionNode ,
142
- ) : void {
143
- foundFunctions . push ( node ) ;
144
- } ,
159
+ 'ArrowFunctionExpression, FunctionDeclaration, FunctionExpression' :
160
+ enterFunction ,
161
+ 'ArrowFunctionExpression:exit' : exitFunction ,
162
+ 'FunctionDeclaration:exit' : exitFunction ,
163
+ 'FunctionExpression:exit' : exitFunction ,
145
164
'Program:exit' ( ) : void {
146
- for ( const func of foundFunctions ) {
147
- if ( isExportedHigherOrderFunction ( func ) ) {
148
- checkNode ( func ) ;
165
+ for ( const [ node , returns ] of functionReturnsMap ) {
166
+ if ( isExportedHigherOrderFunction ( { node , returns } ) ) {
167
+ checkNode ( node ) ;
149
168
}
150
169
}
151
170
} ,
171
+ ReturnStatement ( node ) : void {
172
+ const current = functionStack [ functionStack . length - 1 ] ;
173
+ functionReturnsMap . get ( current ) ?. push ( node ) ;
174
+ } ,
152
175
} ;
153
176
154
177
function checkParameters (
@@ -265,7 +288,9 @@ export default createRule<Options, MessageIds>({
265
288
return false ;
266
289
}
267
290
268
- function isExportedHigherOrderFunction ( node : FunctionNode ) : boolean {
291
+ function isExportedHigherOrderFunction ( {
292
+ node,
293
+ } : FunctionInfo < FunctionNode > ) : boolean {
269
294
let current : TSESTree . Node | undefined = node . parent ;
270
295
while ( current ) {
271
296
if ( current . type === AST_NODE_TYPES . ReturnStatement ) {
@@ -274,9 +299,12 @@ export default createRule<Options, MessageIds>({
274
299
continue ;
275
300
}
276
301
302
+ if ( ! isFunction ( current ) ) {
303
+ return false ;
304
+ }
305
+ const returns = getReturnsInFunction ( current ) ;
277
306
if (
278
- ! isFunction ( current ) ||
279
- ! doesImmediatelyReturnFunctionExpression ( current )
307
+ ! doesImmediatelyReturnFunctionExpression ( { node : current , returns } )
280
308
) {
281
309
return false ;
282
310
}
@@ -335,8 +363,10 @@ export default createRule<Options, MessageIds>({
335
363
336
364
switch ( node . type ) {
337
365
case AST_NODE_TYPES . ArrowFunctionExpression :
338
- case AST_NODE_TYPES . FunctionExpression :
339
- return checkFunctionExpression ( node ) ;
366
+ case AST_NODE_TYPES . FunctionExpression : {
367
+ const returns = getReturnsInFunction ( node ) ;
368
+ return checkFunctionExpression ( { node, returns } ) ;
369
+ }
340
370
341
371
case AST_NODE_TYPES . ArrayExpression :
342
372
for ( const element of node . elements ) {
@@ -360,8 +390,10 @@ export default createRule<Options, MessageIds>({
360
390
}
361
391
return ;
362
392
363
- case AST_NODE_TYPES . FunctionDeclaration :
364
- return checkFunction ( node ) ;
393
+ case AST_NODE_TYPES . FunctionDeclaration : {
394
+ const returns = getReturnsInFunction ( node ) ;
395
+ return checkFunction ( { node, returns } ) ;
396
+ }
365
397
366
398
case AST_NODE_TYPES . MethodDefinition :
367
399
case AST_NODE_TYPES . TSAbstractMethodDefinition :
@@ -419,7 +451,10 @@ export default createRule<Options, MessageIds>({
419
451
checkParameters ( node ) ;
420
452
}
421
453
422
- function checkFunctionExpression ( node : FunctionExpression ) : void {
454
+ function checkFunctionExpression ( {
455
+ node,
456
+ returns,
457
+ } : FunctionInfo < FunctionExpression > ) : void {
423
458
if ( checkedFunctions . has ( node ) ) {
424
459
return ;
425
460
}
@@ -434,7 +469,7 @@ export default createRule<Options, MessageIds>({
434
469
}
435
470
436
471
checkFunctionExpressionReturnType (
437
- node ,
472
+ { node, returns } ,
438
473
options ,
439
474
context . sourceCode ,
440
475
loc => {
@@ -449,7 +484,10 @@ export default createRule<Options, MessageIds>({
449
484
checkParameters ( node ) ;
450
485
}
451
486
452
- function checkFunction ( node : TSESTree . FunctionDeclaration ) : void {
487
+ function checkFunction ( {
488
+ node,
489
+ returns,
490
+ } : FunctionInfo < TSESTree . FunctionDeclaration > ) : void {
453
491
if ( checkedFunctions . has ( node ) ) {
454
492
return ;
455
493
}
@@ -459,13 +497,18 @@ export default createRule<Options, MessageIds>({
459
497
return ;
460
498
}
461
499
462
- checkFunctionReturnType ( node , options , context . sourceCode , loc => {
463
- context . report ( {
464
- node,
465
- loc,
466
- messageId : 'missingReturnType' ,
467
- } ) ;
468
- } ) ;
500
+ checkFunctionReturnType (
501
+ { node, returns } ,
502
+ options ,
503
+ context . sourceCode ,
504
+ loc => {
505
+ context . report ( {
506
+ node,
507
+ loc,
508
+ messageId : 'missingReturnType' ,
509
+ } ) ;
510
+ } ,
511
+ ) ;
469
512
470
513
checkParameters ( node ) ;
471
514
}
0 commit comments