Commit cab57d1f authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Replace FunctionAst#returns with #chainable?

As we're only detecting 'return this;' using a predicate is better
than a function returning 'this' or nil.

FunctionAst is now also a singleton, but that fact is hidden away behind
static #chainable? method.
parent 31bf26a2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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)
+14 −8
Original line number Diff line number Diff line
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)
+8 −8
Original line number Diff line number Diff line
@@ -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;