Commit 08b86793 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Detection of boolean return values.

parent 8c6b3c80
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -73,6 +73,8 @@ module JsDuck
        "undefined"
      elsif this?(ast)
        :this
      elsif boolean?(ast)
        "Boolean"
      else
        :other
      end
@@ -90,6 +92,26 @@ module JsDuck
      ast["type"] == "ThisExpression"
    end

    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

    def boolean_literal?(ast)
      ast["type"] == "Literal" && (ast["value"] == true || ast["value"] == false)
    end

    def control_flow?(ast)
      CONTROL_FLOW[ast["type"]]
    end
@@ -118,6 +140,21 @@ module JsDuck
      end
    end

    BOOLEAN_RETURNING_OPERATORS = {
      "!" => true,
      ">" => true,
      ">=" => true,
      "<" => true,
      "<=" => true,
      "==" => true,
      "!=" => true,
      "===" => true,
      "!==" => true,
      "in" => true,
      "instanceof" => true,
      "delete" => true,
    }

    POSSIBLY_BLOCKING = {
      "IfStatement" => true,
      "DoWhileStatement" => true,
+54 −0
Original line number Diff line number Diff line
@@ -231,4 +231,58 @@ describe "JsDuck::FunctionAst#return_types" do
    end
  end

  describe "returns ['Boolean'] when function body" do
    it "returns true" do
      returns("/** */ function foo() { return true; }").should == ["Boolean"]
    end

    it "returns false" do
      returns("/** */ function foo() { return false; }").should == ["Boolean"]
    end

    it "returns negation" do
      returns("/** */ function foo() { return !foo; }").should == ["Boolean"]
    end

    it "returns > comparison" do
      returns("/** */ function foo() { return x > y; }").should == ["Boolean"]
    end

    it "returns <= comparison" do
      returns("/** */ function foo() { return x <= y; }").should == ["Boolean"]
    end

    it "returns == comparison" do
      returns("/** */ function foo() { return x == y; }").should == ["Boolean"]
    end

    it "returns 'in' expression" do
      returns("/** */ function foo() { return key in object; }").should == ["Boolean"]
    end

    it "returns 'instanceof' expression" do
      returns("/** */ function foo() { return obj instanceof cls; }").should == ["Boolean"]
    end

    it "returns 'delete' expression" do
      returns("/** */ function foo() { return delete foo[bar]; }").should == ["Boolean"]
    end

    it "returns conjunction of boolean expressions" do
      returns("/** */ function foo() { return x > y && y > z; }").should == ["Boolean"]
    end

    it "returns disjunction of boolean expressions" do
      returns("/** */ function foo() { return x == y || y == z; }").should == ["Boolean"]
    end

    it "returns conditional expression evaluating to boolean" do
      returns("/** */ function foo() { return x ? true : a > b; }").should == ["Boolean"]
    end

    it "returns assignment of boolean" do
      returns("/** */ function foo() { return x = true; }").should == ["Boolean"]
    end
  end

end