Commit 44e480f2 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Validating type information of params/cfgs/returns.

According to the agreed-on spec for type-definitions-format with
the VJET Eclipse Plugin team.

Added bunch of tests to make it real solid.

Running it on SDK now produces some hords of warnings.
parent 0596796e
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
require 'jsduck/logger'
require 'jsduck/type_parser'

module JsDuck

@@ -16,6 +17,7 @@ module JsDuck
      warn_unnamed
      warn_optional_params
      warn_duplicate_params
      warn_types
    end

    # print warning for each global member
@@ -69,6 +71,24 @@ module JsDuck
      end
    end

    # Check parameter types
    def warn_types
      parser = TypeParser.new
      each_member do |member|
        (member[:params] || []).each do |p|
          if !parser.parse(p[:type])
            warn("Incorrect parameter type syntax #{p[:type]}", member)
          end
        end
        if member[:return] && !parser.parse(member[:return][:type])
          warn("Incorrect return type syntax #{member[:return][:type]}", member)
        end
        if member[:type] && !parser.parse(member[:type])
          warn("Incorrect type syntax #{member[:type]}", member)
        end
      end
    end

    # Loops through all members of all classes
    def each_member(&block)
      @relations.each {|cls| cls.each_member(&block) }
+49 −0
Original line number Diff line number Diff line
require 'strscan'

module JsDuck

  # Validates the syntax of type definitions
  #
  # Quick summary of supported types:
  #
  # - SomeType
  # - Name.spaced.Type
  # - Number[]
  # - String/RegExp
  # - Type...
  #
  # Details are covered in spec.
  #
  class TypeParser
    def parse(str)
      @input = StringScanner.new(str)

      # Return immediately if base type doesn't match
      return false unless base_type

      # Go through enumeration of types, separated with "/"
      while @input.check(/\//)
        @input.scan(/\//)
        # Fail if there's no base type after "/"
        return false unless base_type
      end

      # The definition might end with an ellipsis
      @input.scan(/\.\.\./)

      # Success if we have reached the end of input
      return @input.eos?
    end

    # The basic type
    #
    #     <ident> [ "." <ident> ]* [ "[]" ]
    #
    # dot-separated identifiers followed by optional "[]"
    def base_type
      @input.scan(/[a-zA-Z_]+(\.[a-zA-Z_]+)*(\[\])?/)
    end

  end

end
+88 −0
Original line number Diff line number Diff line
require "jsduck/type_parser"

describe JsDuck::TypeParser do

  def parse(str)
    JsDuck::TypeParser.new.parse(str)
  end

  it "matches simple type" do
    parse("String").should == true
  end

  it "matches namespaced type" do
    parse("Ext.form.Panel").should == true
  end

  it "matches array of simple types" do
    parse("Number[]").should == true
  end

  it "matches array of namespaced types" do
    parse("Ext.form.Panel[]").should == true
  end

  describe "matches alteration of" do
    it "simple types" do
      parse("Number/String").should == true
    end

    it "simple- and namespaced- and array types" do
      parse("Number/Ext.form.Panel/String[]/RegExp/Ext.Element").should == true
    end
  end

  describe "matches varargs of" do
    it "simple type" do
      parse("Number...").should == true
    end

    it "namespaced type" do
      parse("Ext.form.Panel...").should == true
    end

    it "array of simple types" do
      parse("String[]...").should == true
    end

    it "array of namespaced types" do
      parse("Ext.form.Panel[]...").should == true
    end

    it "complex alteration" do
      parse("Ext.form.Panel[]/Number/Ext.Element...").should == true
    end
  end

  describe "doesn't match" do
    it "empty string" do
      parse("").should == false
    end

    it "type ending with dot" do
      parse("Ext.").should == false
    end

    it "type beginning with dot" do
      parse(".Ext").should == false
    end

    it "the [old] array notation" do
      parse("[Number]").should == false
    end

    it "/ at the beginning" do
      parse("/Number").should == false
    end

    it "/ at the end" do
      parse("Number/").should == false
    end

    it "... in the middle" do
      parse("Number.../String").should == false
    end
  end

end