Commit 8c6b3c80 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Detection of undefined return value.

parent aca60237
Loading
Loading
Loading
Loading
+37 −11
Original line number Diff line number Diff line
@@ -12,12 +12,24 @@ module JsDuck
    #
    # For now there are three possible detected return values:
    #
    # * :void - the code can finish without explicitly returning anything
    # * :this - the code contins 'return this;'
    #
    # * "undefined" - the code finishes by returning undefined or
    #   without explicitly returning anything
    #
    # * :other - some other value is returned.
    #
    def return_types(ast)
      return_types_hash(ast["body"]["body"]).keys
      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
@@ -25,11 +37,9 @@ module JsDuck
    def return_types_hash(body)
      rvalues = {}
      body.each do |ast|
        if return_this?(ast)
          rvalues[:this] = true
          return rvalues
        elsif return?(ast)
          rvalues[:other] = true
        if return?(ast)
          type = value_type(ast["argument"])
          rvalues[type] = true
          return rvalues
        elsif possibly_blocking?(ast)
          extract_bodies(ast).each do |b|
@@ -52,14 +62,30 @@ module JsDuck
      return rvalues
    end

    def return_this?(ast)
      return?(ast) && !!ast["argument"] && this?(ast["argument"])
    end

    def return?(ast)
      ast["type"] == "ReturnStatement"
    end

    def value_type(ast)
      if !ast
        :void
      elsif undefined?(ast) || void?(ast)
        "undefined"
      elsif this?(ast)
        :this
      else
        :other
      end
    end

    def undefined?(ast)
      ast["type"] == "Identifier" && ast["name"] == "undefined"
    end

    def void?(ast)
      ast["type"] == "UnaryExpression" && ast["operator"] == "void"
    end

    def this?(ast)
      ast["type"] == "ThisExpression"
    end
+23 −1
Original line number Diff line number Diff line
@@ -161,7 +161,7 @@ describe "JsDuck::FunctionAst#return_types" do
  end

  describe "doesn't return [:this] when function body" do
    it "has no return statement." do
    it "is empty" do
      returns("/** */ function foo() {}").should_not == [:this]
    end

@@ -209,4 +209,26 @@ describe "JsDuck::FunctionAst#return_types" do
    end
  end

  describe "returns ['undefined'] when function body" do
    it "is empty" do
      returns("/** */ function foo() {}").should == ["undefined"]
    end

    it "has no return statement" do
      returns("/** */ function foo() { bar(); baz(); }").should == ["undefined"]
    end

    it "has empty return statement" do
      returns("/** */ function foo() { return; }").should == ["undefined"]
    end

    it "has RETURN UNDEFINED statement" do
      returns("/** */ function foo() { return undefined; }").should == ["undefined"]
    end

    it "has RETURN VOID statement" do
      returns("/** */ function foo() { return void(blah); }").should == ["undefined"]
    end
  end

end