@@ -23,73 +23,89 @@ function isInlineThenFunctionExpression (node) {
2323 )
2424}
2525
26- function includes ( arr , val ) {
27- return arr . indexOf ( val ) !== - 1
28- }
29-
30- function last ( arr ) {
26+ function peek ( arr ) {
3127 return arr [ arr . length - 1 ]
3228}
3329
3430module . exports = {
3531 create : function ( context ) {
32+ // funcInfoStack is a stack representing the stack of currently executing
33+ // functions
34+ // funcInfoStack[i].branchIDStack is a stack representing the currently
35+ // executing branches ("codePathSegment"s) within the given function
36+ // funcInfoStack[i].branchInfoMap is an object representing information
37+ // about all branches within the given function
38+ // funcInfoStack[i].branchInfoMap[j].good is a boolean representing whether
39+ // the given branch explictly `return`s or `throw`s. It starts as `false`
40+ // for every branch and is updated to `true` if a `return` or `throw`
41+ // statement is found
42+ // funcInfoStack[i].branchInfoMap[j].loc is a eslint SourceLocation object
43+ // for the given branch
44+ // example:
45+ // funcInfoStack = [ { branchIDStack: [ 's1_1' ],
46+ // branchInfoMap:
47+ // { s1_1:
48+ // { good: false,
49+ // loc: <loc> } } },
50+ // { branchIDStack: ['s2_1', 's2_4'],
51+ // branchInfoMap:
52+ // { s2_1:
53+ // { good: false,
54+ // loc: <loc> },
55+ // s2_2:
56+ // { good: true,
57+ // loc: <loc> },
58+ // s2_4:
59+ // { good: false,
60+ // loc: <loc> } } } ]
3661 var funcInfoStack = [ ]
37- var CPSIDStack = [ ]
38-
39- function isEveryBranchReturning ( funcInfo ) {
40- // We need to check noCurrentCPSIsOnTheCPSStack because of what
41- // seems like a bug in eslint where 'FunctionExpression:exit' events occur
42- // before all of their constituent codePathSegments have fired their
43- // 'onCodePathSegmentEnd' events
44- var currentIDs = funcInfo . codePath . currentSegments . map ( x => x . id )
45- var noCurrentCPSIsOnTheCPSStack = ! currentIDs . some ( ( id ) => includes ( CPSIDStack , id ) )
4662
47- var finalIDs = funcInfo . codePath . finalSegments . map ( x => x . id )
48- var everyFinalCPSIsReturning = finalIDs . every ( ( id ) => includes ( funcInfo . explicitlyReturningCPSIDs , id ) )
49-
50- return noCurrentCPSIsOnTheCPSStack && everyFinalCPSIsReturning
63+ function markCurrentBranchAsGood ( ) {
64+ var funcInfo = peek ( funcInfoStack )
65+ var currentBranchID = peek ( funcInfo . branchIDStack )
66+ funcInfo . branchInfoMap [ currentBranchID ] . good = true
5167 }
5268
53- function onFunctionExpressionExit ( node ) {
54- if ( ! isInlineThenFunctionExpression ( node ) ) {
55- return
56- }
69+ return {
70+ ReturnStatement : markCurrentBranchAsGood ,
71+ ThrowStatement : markCurrentBranchAsGood ,
5772
58- var funcInfo = last ( funcInfoStack )
59- if ( ! isEveryBranchReturning ( funcInfo ) ) {
60- context . report ( node , 'Each then() should return a value or throw' )
61- }
62- }
73+ onCodePathSegmentStart : function ( segment , node ) {
74+ var funcInfo = peek ( funcInfoStack )
75+ funcInfo . branchIDStack . push ( segment . id )
76+ funcInfo . branchInfoMap [ segment . id ] = { good : false , loc : node . loc }
77+ } ,
6378
64- function markCurrentCodePathSegmentAsReturning ( ) {
65- var funcInfo = last ( funcInfoStack )
66- var currentCPSID = last ( CPSIDStack )
67- funcInfo . explicitlyReturningCPSIDs . push ( currentCPSID )
68- }
79+ onCodePathSegmentEnd : function ( segment , node ) {
80+ var funcInfo = peek ( funcInfoStack )
81+ funcInfo . branchIDStack . pop ( )
82+ } ,
6983
70- return {
71- onCodePathStart : function ( codePath , node ) {
84+ onCodePathStart : function ( path , node ) {
7285 funcInfoStack . push ( {
73- codePath : codePath ,
74- explicitlyReturningCPSIDs : [ ]
86+ branchIDStack : [ ] ,
87+ branchInfoMap : { }
7588 } )
7689 } ,
7790
78- onCodePathEnd : function ( codePath , node ) {
79- funcInfoStack . pop ( )
80- } ,
91+ onCodePathEnd : function ( path , node ) {
92+ var funcInfo = funcInfoStack . pop ( )
8193
82- onCodePathSegmentEnd : function ( segment , node ) {
83- CPSIDStack . pop ( )
84- } ,
85- onCodePathSegmentStart : function ( segment , node ) {
86- CPSIDStack . push ( segment . id )
87- } ,
94+ if ( ! isInlineThenFunctionExpression ( node ) ) {
95+ return
96+ }
8897
89- ReturnStatement : markCurrentCodePathSegmentAsReturning ,
90- ThrowStatement : markCurrentCodePathSegmentAsReturning ,
91- 'FunctionExpression:exit' : onFunctionExpressionExit ,
92- 'ArrowFunctionExpression:exit' : onFunctionExpressionExit
98+ var finalBranchIDs = path . finalSegments . map ( x => x . id )
99+ finalBranchIDs . forEach ( ( id ) => {
100+ var branch = funcInfo . branchInfoMap [ id ]
101+ if ( ! branch . good ) {
102+ context . report ( {
103+ message : 'Each then() should return a value or throw' ,
104+ loc : branch . loc
105+ } )
106+ }
107+ } )
108+ }
93109 }
94110 }
95111}
0 commit comments