class JsDuck::Js::Returns
Analyzes the AST of a Function for possible return values.
Constants
- BOOLEAN_RETURNING_OPERATORS
- CONTROL_FLOW
- POSSIBLY_BLOCKING
Public Instance Methods
chainable?(ast)
click to toggle source
True when function always finishes with returning this.
# File lib/jsduck/js/returns.rb, line 11 def chainable?(ast) detect(ast) == [:this] end
detect(ast)
click to toggle source
Detects possible return types of the given function.
For now there are three possible detected return values:
-
:this - the code contins 'return this;'
-
“undefined” - the code finishes by returning undefined or without explicitly returning anything
-
:other - some other value is returned.
# File lib/jsduck/js/returns.rb, line 26 def detect(ast) h = return_types_hash(ast["body"]["body"]) # Replace the special :void value that signifies possibility of # exiting without explicitly returning anything if h[:void] h["undefined"] = true h.delete(:void) end h.keys end
Private Instance Methods
boolean?(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 103 def boolean?(ast) if boolean_literal?(ast) true elsif ast["type"] == "UnaryExpression" || ast["type"] == "BinaryExpression" !!BOOLEAN_RETURNING_OPERATORS[ast["operator"]] elsif ast["type"] == "LogicalExpression" boolean?(ast["left"]) && boolean?(ast["right"]) elsif ast["type"] == "ConditionalExpression" boolean?(ast["consequent"]) && boolean?(ast["alternate"]) elsif ast["type"] == "AssignmentExpression" && ast["operator"] == "=" boolean?(ast["right"]) else false end end
boolean_literal?(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 119 def boolean_literal?(ast) ast["type"] == "Literal" && (ast["value"] == true || ast["value"] == false) end
control_flow?(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 143 def control_flow?(ast) CONTROL_FLOW[ast["type"]] end
extract_bodies(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 147 def extract_bodies(ast) body = [] CONTROL_FLOW[ast["type"]].each do |name| statements = ast[name] if statements.is_a?(Hash) body << [statements] else body << Array(statements) end end body end
possibly_blocking?(ast)
click to toggle source
True if the node is a control structure which will block further program flow when all its branches finish with a return statement.
# File lib/jsduck/js/returns.rb, line 163 def possibly_blocking?(ast) if POSSIBLY_BLOCKING[ast["type"]] CONTROL_FLOW[ast["type"]].all? {|key| ast[key] } else false end end
regexp?(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 139 def regexp?(ast) ast["type"] == "Literal" && ast["raw"] =~ /^\// end
return?(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 69 def return?(ast) ast["type"] == "ReturnStatement" end
return_types_hash(body)
click to toggle source
# File lib/jsduck/js/returns.rb, line 41 def return_types_hash(body) rvalues = {} body.each do |ast| if return?(ast) type = value_type(ast["argument"]) rvalues[type] = true return rvalues elsif possibly_blocking?(ast) extract_bodies(ast).each do |b| rvalues.merge!(return_types_hash(b)) end if !rvalues[:void] return rvalues else rvalues.delete(:void) end elsif control_flow?(ast) extract_bodies(ast).each do |b| rvalues.merge!(return_types_hash(b)) end rvalues.delete(:void) end end rvalues[:void] = true return rvalues end
string?(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 123 def string?(ast) if string_literal?(ast) true elsif ast["type"] == "BinaryExpression" && ast["operator"] == "+" string?(ast["left"]) || string?(ast["right"]) elsif ast["type"] == "UnaryExpression" && ast["operator"] == "typeof" true else false end end
string_literal?(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 135 def string_literal?(ast) ast["type"] == "Literal" && ast["value"].is_a?(String) end
this?(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 99 def this?(ast) ast["type"] == "ThisExpression" end
undefined?(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 91 def undefined?(ast) ast["type"] == "Identifier" && ast["name"] == "undefined" end
value_type(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 73 def value_type(ast) if !ast :void elsif undefined?(ast) || void?(ast) "undefined" elsif this?(ast) :this elsif boolean?(ast) "Boolean" elsif regexp?(ast) "RegExp" elsif string?(ast) "String" else :other end end
void?(ast)
click to toggle source
# File lib/jsduck/js/returns.rb, line 95 def void?(ast) ast["type"] == "UnaryExpression" && ast["operator"] == "void" end