diff --git a/lib/yard/docstring_parser.rb b/lib/yard/docstring_parser.rb index 40b9cb5ae..fb86426a9 100644 --- a/lib/yard/docstring_parser.rb +++ b/lib/yard/docstring_parser.rb @@ -281,18 +281,33 @@ def self.after_parse_callbacks seen_names = [] infile_info = "\n in file `#{parser.object.file}' " \ "near line #{parser.object.line}" + splat_param = parser.object.parameters.any? { |name, _| name =~ /\A\*/ } + param_tags = [] parser.tags.each do |tag| - next if tag.is_a?(Tags::RefTagList) # we don't handle this yet next unless tag.tag_name == "param" + if tag.is_a?(Tags::RefTagList) + if tag.name + param_tags << tag + elsif !(CodeObjects::Proxy === tag.owner) + param_tags += tag.tags + end + # if the ref tag doesn't have a name and doesn't yet resolve, do nothing + else + param_tags << tag + end + end + param_tags.each do |tag| if seen_names.include?(tag.name) - log.warn "@param tag has duplicate parameter name: " \ + log.warn "#{parser.object} @param tag has duplicate parameter name: " \ "#{tag.name} #{infile_info}" - elsif names.include?(tag.name) - seen_names << tag.name - else - log.warn "@param tag has unknown parameter name: " \ + end + # warn about unknown params. but not delegated params, where the param tag + # is a reference and the object takes *args or **keywords. + if !names.include?(tag.name) && !((tag.is_a?(Tags::RefTag) || tag.is_a?(Tags::RefTagList)) && splat_param) + log.warn "#{parser.object} @param tag has unknown parameter name: " \ "#{tag.name} #{infile_info}" end + seen_names << tag.name end end @@ -337,7 +352,7 @@ def call_after_parse_callbacks next unless "#{tag.name}#{tag.text}" =~ /\A\{.*\}\Z/ infile_info = "\n in file `#{parser.object.file}' " \ "near line #{parser.object.line}" - log.warn "@see tag (##{i + 1}) should not be wrapped in {} " \ + log.warn "#{parser.object} @see tag (##{i + 1}) should not be wrapped in {} " \ "(causes rendering issues): #{infile_info}" end end diff --git a/spec/docstring_parser_spec.rb b/spec/docstring_parser_spec.rb index 1d6a9d33c..3d7ad6853 100644 --- a/spec/docstring_parser_spec.rb +++ b/spec/docstring_parser_spec.rb @@ -208,15 +208,51 @@ class TestLibrary < Tags::Library; end end it "warns about invalid named parameters" do - expect(log).to receive(:warn).with(/@param tag has unknown parameter name: notaparam/) + expect(log).to receive(:warn).with(/#foo @param tag has unknown parameter name: notaparam/) YARD.parse_string <<-eof # @param notaparam foo def foo(a) end eof end - it "warns about invalid named parameters on @!method directives" do + it "does warn about invalid named reference parameters without a splat" do expect(log).to receive(:warn).with(/@param tag has unknown parameter name: notaparam/) + YARD.parse_string <<-eof + # @param notaparam (see #elsewhere) + def foo(a) end + eof + end + + it "does not warn about unknown named parameters that are delegated" do + expect(log).not_to receive(:warn).with(/@param tag has unknown parameter name: notaparam/) + YARD.parse_string <<-eof + # @param notaparam (see #elsewhere) + def foo(*a) end + eof + end + + it "does not warn about unknown named delegated parameters from ref tag list" do + expect(log).not_to receive(:warn).with(/@param tag has unknown parameter name: barparam/) + YARD.parse_string <<-eof + # @param barparam + def bar(barparam) end + # @param (see #bar) + def foo(*a) end + eof + end + + it "does warn about unknown named delegated parameters from ref tag list without splat" do + expect(log).to receive(:warn).with(/@param tag has unknown parameter name: barparam/) + YARD.parse_string <<-eof + # @param barparam + def bar(barparam) end + # @param (see #bar) + def foo(a) end + eof + end + + it "warns about invalid named parameters on @!method directives" do + expect(log).to receive(:warn).with(/#foo @param tag has unknown parameter name: notaparam/) YARD.parse_string <<-eof # @!method foo(a) # @param notaparam foo @@ -225,7 +261,7 @@ def foo(a) end end it "warns about duplicate named parameters" do - expect(log).to receive(:warn).with(/@param tag has duplicate parameter name: a/) + expect(log).to receive(:warn).with(/#foo @param tag has duplicate parameter name: a/) YARD.parse_string <<-eof # @param a foo # @param a foo @@ -252,7 +288,7 @@ def foo(a) end end it "warns on mismatching param with inline method modifier" do - expect(log).to receive(:warn).with(/@param tag has unknown parameter name: notaparam/) + expect(log).to receive(:warn).with(/::foo @param tag has unknown parameter name: notaparam/) YARD.parse_string <<-eof # @param [Numeric] notaparam # @return [Numeric] @@ -268,12 +304,12 @@ def foo(a) end end it "warns if {} wraps single name" do - expect(log).to receive(:warn).with(/@see tag \(#1\) should not be wrapped in \{\}/) + expect(log).to receive(:warn).with(/Foo @see tag \(#1\) should not be wrapped in \{\}/) YARD.parse_string "# @see {invalid}\nclass Foo;end" end it "warns if {} wraps across name and text" do - expect(log).to receive(:warn).with(/@see tag \(#1\) should not be wrapped in \{\}/) + expect(log).to receive(:warn).with(/Foo @see tag \(#1\) should not be wrapped in \{\}/) YARD.parse_string "# @see {invalid tag}\nclass Foo;end" end end