UNPKG

coffeelint-multiple-callback

Version:

Coffeelint rule that checks for callbacks being called multiple times

186 lines (140 loc) 6.61 kB
prefix = 'prefix' _ = require 'lodash' class Branch constructor: (@parent_branch, @is_new_scope)-> @calls = {} parent_calls = @parent_branch?.calls or {} _.each parent_calls, (parent_call_obj, prefixed_func_name)=> @initBlankFunc(parent_call_obj.func_name) # inherited @calls[prefixed_func_name].min_hits = parent_call_obj.min_hits @calls[prefixed_func_name].max_hits = parent_call_obj.max_hits @calls[prefixed_func_name].is_defined_in_this_scope = parent_call_obj.is_defined_in_this_scope @calls[prefixed_func_name].might_not_be_func = parent_call_obj.might_not_be_func @calls[prefixed_func_name].is_defined_in_this_file = parent_call_obj.is_defined_in_this_file # inherited pass by ref!!! @calls[prefixed_func_name].called_at_nodes = _.clone parent_call_obj.called_at_nodes # array is cloned. nodes are not. major speed boost @calls[prefixed_func_name].triggered_errors = parent_call_obj.triggered_errors # emptied values: @calls[prefixed_func_name].min_hits_this_branch = 0 @calls[prefixed_func_name].max_hits_this_branch = 0 @calls[prefixed_func_name].called_at_nodes_this_branch = [] @calls[prefixed_func_name].is_defined_in_this_branch = false @calls[prefixed_func_name].is_modified_in_branch = false if @is_new_scope @calls[prefixed_func_name].is_defined_in_this_scope = false return _.each @calls, (call_obj)=> call_obj.min_hits_this_branch = 0 call_obj.max_hits_this_branch = 0 call_obj.called_at_nodes_this_branch = [] call_obj.is_defined_in_this_branch = false if @is_new_scope call_obj.is_defined_in_this_scope = false return return isDeadBranch: ()=> out = @is_dead_branch if @is_new_scope out = false return out getBranchDepth: ()=> branch_count = 0 next_branch_up = @ while branch_count < 99 and not _.isNull next_branch_up next_branch_up = next_branch_up.getParentBranch() branch_count += 1 return branch_count isEndOfFuncExistence: (call_obj)=> if @is_end_of_branch and call_obj.is_defined_in_this_branch return true return false isRootBranch: ()=> if _.isNull @parent_branch return true return false getParentBranch: ()=> return @parent_branch initBlankFunc: (func_name)=> if "#{prefix}-#{func_name}" is 'prefix-undefined' or "#{prefix}-#{func_name}" is 'prefix-null' throw new Error "undefined/null func_name passed to initBlankFunc()" return @calls["#{prefix}-#{func_name}"] ?= min_hits: 0 max_hits: 0 min_hits_this_branch: 0 max_hits_this_branch: 0 is_defined_in_this_scope: false is_defined_in_this_branch: false called_at_nodes: [] called_at_nodes_this_branch: [] func_name: func_name might_not_be_func: true triggered_errors: no_hits: false multiple_hits: false is_defined_in_this_file: false is_modified_in_branch: false return addFuncCall: (func_name, node, is_direct_call = true)=> @initBlankFunc func_name call_obj = @calls["#{prefix}-#{func_name}"] call_obj.min_hits += 1 call_obj.max_hits += 1 call_obj.min_hits_this_branch += 1 call_obj.max_hits_this_branch += 1 call_obj.called_at_nodes.push node call_obj.called_at_nodes_this_branch.push node # if it's being called, we have no doubt that its a function if is_direct_call call_obj.might_not_be_func = false call_obj.is_modified_in_branch = true # console.log "Calling #{func_name}: [#{call_obj.min_hits}, #{call_obj.max_hits}]" return # when a func is defined through assignment or as a param, trigger this addFuncDef: (func_name, node, might_not_be_func = true)=> @initBlankFunc func_name @calls["#{prefix}-#{func_name}"].is_defined_in_this_scope = true @calls["#{prefix}-#{func_name}"].is_defined_in_this_branch = true @calls["#{prefix}-#{func_name}"].def_node = node @calls["#{prefix}-#{func_name}"].might_not_be_func &= might_not_be_func @calls["#{prefix}-#{func_name}"].is_defined_in_this_file = true @calls["#{prefix}-#{func_name}"].is_modified_in_branch = true return mergeChildCalls: (child_branch)=> # FIXME: child_branch.is_dead_branch???? # if child_branch.is_dead_branch # console.log "Hit Return, skipping merge" # return child_calls = child_branch.getCalls() out_calls = @calls _.each child_calls, (call_obj, prefixed_func_name)=> # skip over variables defined deeper in the chain or variables that havent been touched if ( not call_obj.is_modified_in_branch ) or call_obj.is_defined_in_this_branch return @initBlankFunc call_obj.func_name out_calls[prefixed_func_name].triggered_errors.no_hits |= call_obj.no_hits out_calls[prefixed_func_name].triggered_errors.multiple_hits |= call_obj.multiple_hits # out_calls[prefixed_func_name].min_hits = call_obj.min_hits # out_calls[prefixed_func_name].max_hits = call_obj.max_hits # if its a dead branch, we don't need max_hits added up the chain if not child_branch.isDeadBranch() out_calls[prefixed_func_name].def_node ?= call_obj.def_node out_calls[prefixed_func_name].called_at_nodes_this_branch = out_calls[prefixed_func_name].called_at_nodes_this_branch.concat call_obj.called_at_nodes_this_branch out_calls[prefixed_func_name].called_at_nodes = out_calls[prefixed_func_name].called_at_nodes.concat call_obj.called_at_nodes_this_branch # out_calls[prefixed_func_name].max_hits = _.max [out_calls[prefixed_func_name].max_hits, call_obj.max_hits] out_calls[prefixed_func_name].max_hits += call_obj.max_hits_this_branch out_calls[prefixed_func_name].max_hits_this_branch += call_obj.max_hits_this_branch out_calls[prefixed_func_name].min_hits += call_obj.min_hits_this_branch out_calls[prefixed_func_name].min_hits_this_branch += call_obj.min_hits_this_branch # out_calls[prefixed_func_name].min_hits = _.min [out_calls[prefixed_func_name].min_hits, call_obj.min_hits] # out_calls[prefixed_func_name].is_defined_in_this_scope |= call_obj.is_defined_in_this_scope out_calls[prefixed_func_name].might_not_be_func &= call_obj.might_not_be_func out_calls[prefixed_func_name].is_modified_in_branch = true return child_branch.is_end_of_branch = true # child_branch.is_dead_branch = true return getCalls: ()=> return @calls module.exports = Branch