diff --git a/lib/jsduck/lint.rb b/lib/jsduck/lint.rb index bcc93c279ce4357f6edecbed41163b2c1175b170..fbdb85e8fd7719baf08f119996c1c559b0474261 100644 --- a/lib/jsduck/lint.rb +++ b/lib/jsduck/lint.rb @@ -1,4 +1,5 @@ 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) } diff --git a/lib/jsduck/type_parser.rb b/lib/jsduck/type_parser.rb new file mode 100644 index 0000000000000000000000000000000000000000..5c6b344be0c69721b98f91dddd4ec835110cc934 --- /dev/null +++ b/lib/jsduck/type_parser.rb @@ -0,0 +1,49 @@ +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 + # + # [ "." ]* [ "[]" ] + # + # dot-separated identifiers followed by optional "[]" + def base_type + @input.scan(/[a-zA-Z_]+(\.[a-zA-Z_]+)*(\[\])?/) + end + + end + +end diff --git a/spec/type_parser_spec.rb b/spec/type_parser_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..9640f7bc8d130c535e0cad2d605307d8ea1750b3 --- /dev/null +++ b/spec/type_parser_spec.rb @@ -0,0 +1,88 @@ +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 +