@@ -20,6 +20,7 @@ class MismatchName < Base
2020 include YARD ::Helper
2121 include RangeHelp
2222 include DocumentationComment
23+ extend AutoCorrector
2324
2425 def on_def ( node )
2526 return unless node . arguments?
@@ -28,7 +29,12 @@ def on_def(node)
2829 return false unless preceding_comment? ( node , preceding_lines . last )
2930
3031 yard_docstring = preceding_lines . map { |line | line . text . gsub ( /\A #\s */ , '' ) } . join ( "\n " )
31- docstring = ::YARD ::DocstringParser . new . parse ( yard_docstring )
32+ docstring = begin
33+ ::YARD ::DocstringParser . new . parse ( yard_docstring )
34+ rescue
35+ return false
36+ end
37+
3238 return false if include_overload_tag? ( docstring )
3339
3440 each_tags_by_docstring ( [ 'param' , 'option' ] , docstring ) do |tags |
@@ -56,16 +62,54 @@ def on_def(node)
5662 parse_type ( types . join ( ', ' ) )
5763 rescue SyntaxError
5864 next
59- end
65+ end if types
6066
61- add_offense_to_tag ( comment , tag )
67+ add_offense_to_tag ( node , comment , tag )
68+ end
69+ end
70+
71+ # Documentation only or just `@return` is a common form of documentation.
72+ # The subsequent features will be limited to cases where both `@param` and `@option` are present.
73+ unless docstring . tags . find { |tag | ( tag . tag_name == 'param' && !tag . instance_of? ( ::YARD ::Tags ::RefTagList ) ) || tag . tag_name == 'option' }
74+ return false
75+ end
76+ node . arguments . each do |argument |
77+ next if argument . type == :blockarg
78+ next if argument . name . nil?
79+
80+ found = docstring . tags . find do |tag |
81+ next unless tag . tag_name == 'param' || tag . tag_name == 'option'
82+ tag . name &.to_sym == argument . name
83+ end
84+
85+ unless found
86+ comment = preceding_lines . last
87+ return if part_of_ignored_node? ( comment )
88+ add_offense ( comment , message : "This method has argument `#{ argument . name } `, But not documented" ) do |corrector |
89+ corrector . replace (
90+ comment . source_range . end ,
91+ "#{ comment . source_range . end . join ( node . source_range . begin ) . source } # #{ tag_prototype ( argument ) } "
92+ )
93+ end
6294 end
6395 end
6496 end
6597 alias on_defs on_def
6698
6799 private
68100
101+ # @param [RuboCop::AST::ArgNode] argument
102+ def tag_prototype ( argument )
103+ case argument . type
104+ when :kwrestarg
105+ "@param [Hash{Symbol => Object}] #{ argument . name } "
106+ when :restarg
107+ "@param [Array<Object>] #{ argument . name } "
108+ else
109+ "@param [Object] #{ argument . name } "
110+ end
111+ end
112+
69113 def each_tags_by_docstring ( tag_names , docstring )
70114 tag_names . each do |tag_name |
71115 yield docstring . tags . select { |tag | tag . tag_name == tag_name }
@@ -80,13 +124,21 @@ def find_by_tag(preceding_lines, tag, i)
80124 end
81125 end
82126
83- def add_offense_to_tag ( comment , tag )
127+ def add_offense_to_tag ( node , comment , tag )
84128 tag_name_regexp = Regexp . new ( "\\ b#{ Regexp . escape ( tag . name ) } \\ b" )
85129 start_column = comment . source . index ( tag_name_regexp )
86130 offense_start = comment . location . column + start_column
87131 offense_end = offense_start + tag . name . length - 1
88132 range = source_range ( processed_source . buffer , comment . location . line , offense_start ..offense_end )
89- add_offense ( range , message : "`#{ tag . name } ` is not found in method arguments" )
133+ argument_names = node . arguments . map ( &:name ) . compact
134+ argument_name =
135+ if argument_names . empty?
136+ ''
137+ else
138+ " of [#{ argument_names . join ( ', ' ) } ]"
139+ end
140+ add_offense ( range , message : "`#{ tag . name } ` is not found in method arguments#{ argument_name } " )
141+ ignore_node ( comment )
90142 end
91143
92144 def include_overload_tag? ( docstring )
0 commit comments