diff --git a/lib/jsduck/ast.rb b/lib/jsduck/ast.rb index 7bd4a5e83fbc1597d82f01fe8bc3ae0d7ccae214..1b1dc1edacf60c7bc548eec45bd28f202e4026fc 100644 --- a/lib/jsduck/ast.rb +++ b/lib/jsduck/ast.rb @@ -407,7 +407,7 @@ module JsDuck end def chainable?(ast) - FunctionAst.new.returns(ast) == "this" + FunctionAst.chainable?(ast) end def make_property(name=nil, ast=nil, tagname=:property) diff --git a/lib/jsduck/function_ast.rb b/lib/jsduck/function_ast.rb index 58121a3ad20ffc574c1ee65687ef71f5f3c516a7..fc348788676dd4ab09f737622305515e4d010923 100644 --- a/lib/jsduck/function_ast.rb +++ b/lib/jsduck/function_ast.rb @@ -1,3 +1,4 @@ +require "singleton" require "jsduck/serializer" require "jsduck/evaluator" @@ -5,12 +6,21 @@ module JsDuck # Analyzes the AST of a FunctionDeclaration or FunctionExpression. class FunctionAst - # Detects from function body what the function returns. - def returns(ast) + include Singleton + + # True when function always finishes by returning this. False + # doesn't neccessarily mean that the function doesn't return this + # - rather it means our static analyzes wasn't able to determine + # what the function returns. + def self.chainable?(ast) + FunctionAst.instance.chainable?(ast) + end + + def chainable?(ast) if ast && function?(ast) body_returns(ast["body"]["body"]) else - nil + false end end @@ -23,11 +33,7 @@ module JsDuck def body_returns(body) body = skip_non_control_flow_statements(body) - if body.length > 0 && return_this?(body[0]) - "this" - else - nil - end + return body.length > 0 && return_this?(body[0]) end def return_this?(ast) diff --git a/spec/function_ast_spec.rb b/spec/function_ast_spec.rb index 1c27968abcd00458d110b8080907c0e7dd3557e5..bc587c97566fd81a01646c8d64e5cd65da3a30f2 100644 --- a/spec/function_ast_spec.rb +++ b/spec/function_ast_spec.rb @@ -2,29 +2,29 @@ require "jsduck/js_parser" require "jsduck/function_ast" describe "JsDuck::FunctionAst#returns" do - def returns(string) + def chainable?(string) node = JsDuck::JsParser.new(string).parse[0] - return JsDuck::FunctionAst.new.returns(node[:code]) + return JsDuck::FunctionAst.chainable?(node[:code]) end it "fails when no AST given at all" do - returns("/** */").should == nil + chainable?("/** */").should == false end it "fails when no function AST given" do - returns("/** */ Ext.emptyFn;").should == nil + chainable?("/** */ Ext.emptyFn;").should == false end it "fails when body has no return statement." do - returns("/** */ function foo() {}").should == nil + chainable?("/** */ function foo() {}").should == false end it "returns this when single return this statement in body" do - returns("/** */ function foo() {return this;}").should == "this" + chainable?("/** */ function foo() {return this;}").should == true end it "returns this when return this after a few expression statements" do - returns(<<-EOJS).should == "this" + chainable?(<<-EOJS).should == true /** */ function foo() { doSomething(); @@ -36,7 +36,7 @@ describe "JsDuck::FunctionAst#returns" do end it "returns this when return this after a few declarations" do - returns(<<-EOJS).should == "this" + chainable?(<<-EOJS).should == true /** */ function foo() { var x = 10;