rubocop-yard 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4dd27dab805f9450c5043686ad6a530235d31e2a148e1913db76288fa51a39e
4
- data.tar.gz: 2820aa0509d5ff6ef0adecbc772168cd8f2b64650109feb4bfb38e810625812a
3
+ metadata.gz: 903402913fde5bdf6702b4412bc6f1ab07197f5f993039c440ffd1a736b5277c
4
+ data.tar.gz: 31eeffd0cbd9fd26fe3cf5035784c990bea7e9296e10cbb22a36218c31064db9
5
5
  SHA512:
6
- metadata.gz: be2c582e52297d345a8d09f413d98e15c7724b4adbd288fa5bfa38b280e9b5c752dd549885e6ec521bcd4c9dd335a71a9210475ef585d472332c2dd30066350c
7
- data.tar.gz: c239f115e44518f79edb4da03cbf54c4c3e6d66659e1ff51ede40047fb5d7af6944dd4a0e08cc8e9bad53c88ae0d7869a4071655105c40704cab44a7eda10620
6
+ metadata.gz: 48d2bd70f00ffd7c31e864039b77b9eabaff53e52b25c8825959c25486f024e3f3d63827ba98dc8ea5f09dd6a964e93700eaa1f88ef1ec6746d46804026bd444
7
+ data.tar.gz: 7f4e6010137fd5f1c55b70c9c6744f4ca194e6a44078aca0643020bd0ba1c9b43b7cec3cea1790eff025f79990cedb443e75b737d292095fba0896f5b0f17b68
data/CHANGELOG.md CHANGED
@@ -1,8 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.5.0] - 2023-10-9
3
+ ## [0.7.0] - 2023-10-14
4
4
 
5
- - Add new cop `YARD/CollectionStyle`
5
+ - New feature
6
+ - `YARD/MismatchName`: Check undocumented argument.
7
+
8
+ ## [0.6.0] - 2023-10-10
6
9
 
7
10
  - Split cop from `YARD/TagType` to
8
11
  - `YARD/TagTypeSyntax`
data/README.md CHANGED
@@ -86,6 +86,14 @@ Check `@param` and `@option` name with method definition.
86
86
  def foo(strings, opts = {})
87
87
  ```
88
88
 
89
+ Check undocumented argument.
90
+
91
+ ```
92
+ # @param [String] strings
93
+ ^^^^^^^^^^^^^^^^^^^^^^^^^ This method has argument `opts`, But not documented
94
+ def foo(strings, opts = {})
95
+ ```
96
+
89
97
  ### `YARD/MeaninglessTag`
90
98
 
91
99
  Check `@param` and `@option` with class/module or casgn
@@ -42,7 +42,7 @@ module RuboCop
42
42
  # # good
43
43
  # # @param [Array<String>]
44
44
  class CollectionStyle < Base
45
- include YARD::CollectionHelper
45
+ include YARD::Helper
46
46
  include RangeHelp
47
47
  include ConfigurableEnforcedStyle
48
48
  extend AutoCorrector
@@ -62,7 +62,7 @@ module RuboCop
62
62
  docstring = ::YARD::DocstringParser.new.parse(comment.text.gsub(/\A#\s*/, ''))
63
63
  each_types_explainer(docstring) do |type, types_explainer|
64
64
  correct_type = styled_string(types_explainer)
65
- unless type == correct_type
65
+ unless ignore_whitespace(type) == ignore_whitespace(correct_type)
66
66
  add_offense(comment, message: "`#{type}` is using #{bad_style} style syntax") do |corrector|
67
67
  corrector.replace(comment, comment.source.sub(/\[(.*)\]/) { "[#{correct_type}]" })
68
68
  end
@@ -70,6 +70,10 @@ module RuboCop
70
70
  end
71
71
  end
72
72
 
73
+ def ignore_whitespace(str)
74
+ str.tr(' ', '')
75
+ end
76
+
73
77
  def bad_style
74
78
  if style == :long
75
79
  :short
@@ -22,7 +22,7 @@ module RuboCop
22
22
  # # good
23
23
  # # @param [Hash{Symbol => String}]
24
24
  class CollectionType < Base
25
- include YARD::CollectionHelper
25
+ include YARD::Helper
26
26
  include RangeHelp
27
27
  include ConfigurableEnforcedStyle
28
28
  extend AutoCorrector
@@ -86,8 +86,7 @@ module RuboCop
86
86
  case types_explainer.name
87
87
  when 'Hash'
88
88
  if types_explainer.types.length == 2
89
- # `Hash<Key, Value>` pattern is the documented hash specific syntax.
90
- message = "`Hash<Key, Value>` is the documented hash specific syntax"
89
+ message = "`Hash<Key, Value>` is ambiguous syntax"
91
90
  add_offense(tag_range_for_comment(comment), message: message) do |corrector|
92
91
  hash_type = ::YARD::Tags::TypesExplainer::HashCollectionType.new(
93
92
  'Hash',
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module YARD
6
- module CollectionHelper
6
+ module Helper
7
7
  def extract_tag_types(tag)
8
8
  case tag
9
9
  when ::YARD::Tags::OptionTag
@@ -13,12 +13,16 @@ module RuboCop
13
13
  end
14
14
  end
15
15
 
16
+ def parse_type(type)
17
+ ::YARD::Tags::TypesExplainer::Parser.parse(type)
18
+ end
19
+
16
20
  def each_types_explainer(docstring, &block)
17
21
  docstring.tags.each do |tag|
18
22
  types = extract_tag_types(tag)
19
23
 
20
24
  begin
21
- types_explainers = ::YARD::Tags::TypesExplainer::Parser.parse(types.join(', '))
25
+ types_explainers = parse_type(types.join(', '))
22
26
  types.zip(types_explainers).each do |type, types_explainer|
23
27
  block.call(type, types_explainer)
24
28
  end
@@ -17,8 +17,10 @@ module RuboCop
17
17
  # def foo(bar, opts = {}, *arg)
18
18
  # end
19
19
  class MismatchName < Base
20
+ include YARD::Helper
20
21
  include RangeHelp
21
22
  include DocumentationComment
23
+ extend AutoCorrector
22
24
 
23
25
  def on_def(node)
24
26
  return unless node.arguments?
@@ -27,32 +29,93 @@ module RuboCop
27
29
  return false unless preceding_comment?(node, preceding_lines.last)
28
30
 
29
31
  yard_docstring = preceding_lines.map { |line| line.text.gsub(/\A#\s*/, '') }.join("\n")
30
- docstring = ::YARD::DocstringParser.new.parse(yard_docstring)
31
- docstring.tags.each_with_index do |tag, i|
32
- next unless tag.tag_name == 'param' || tag.tag_name == 'option'
33
-
34
- comment = find_by_tag(preceding_lines, tag, i)
35
- next unless comment
36
-
37
- unless tag.name && tag.types
38
- if tag.name.nil?
39
- add_offense(comment, message: "No tag name is supplied in `@#{tag.tag_name}`")
40
- elsif tag.types.nil?
41
- add_offense(comment, message: "No types are associated with the tag in `@#{tag.tag_name}`")
32
+ docstring = begin
33
+ ::YARD::DocstringParser.new.parse(yard_docstring)
34
+ rescue
35
+ return false
36
+ end
37
+
38
+ return false if include_overload_tag?(docstring)
39
+
40
+ each_tags_by_docstring(['param', 'option'], docstring) do |tags|
41
+ tags.each_with_index do |tag, i|
42
+ comment = find_by_tag(preceding_lines, tag, i)
43
+ next unless comment
44
+
45
+ # YARD::Tags::RefTagList is not has name and types
46
+ next if tag.instance_of?(::YARD::Tags::RefTagList)
47
+
48
+ types = extract_tag_types(tag)
49
+ unless tag.name && types
50
+ if tag.name.nil?
51
+ add_offense(comment, message: "No tag name is supplied in `@#{tag.tag_name}`")
52
+ elsif types.nil?
53
+ add_offense(comment, message: "No types are associated with the tag in `@#{tag.tag_name}`")
54
+ end
55
+
56
+ next
42
57
  end
43
58
 
44
- next
59
+ next unless node.arguments.none? { |arg_node| tag.name.to_sym == arg_node.name }
60
+
61
+ begin
62
+ parse_type(types.join(', '))
63
+ rescue SyntaxError
64
+ next
65
+ end if types
66
+
67
+ add_offense_to_tag(node, comment, tag)
45
68
  end
69
+ end
46
70
 
47
- next unless node.arguments.none? { |arg_node| tag.name.to_sym == arg_node.name }
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
48
84
 
49
- add_offense_to_tag(comment, tag)
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
94
+ end
50
95
  end
51
96
  end
52
97
  alias on_defs on_def
53
98
 
54
99
  private
55
100
 
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
+
113
+ def each_tags_by_docstring(tag_names, docstring)
114
+ tag_names.each do |tag_name|
115
+ yield docstring.tags.select { |tag| tag.tag_name == tag_name }
116
+ end
117
+ end
118
+
56
119
  def find_by_tag(preceding_lines, tag, i)
57
120
  count = -1
58
121
  preceding_lines.find do |line|
@@ -61,13 +124,25 @@ module RuboCop
61
124
  end
62
125
  end
63
126
 
64
- def add_offense_to_tag(comment, tag)
127
+ def add_offense_to_tag(node, comment, tag)
65
128
  tag_name_regexp = Regexp.new("\\b#{Regexp.escape(tag.name)}\\b")
66
129
  start_column = comment.source.index(tag_name_regexp)
67
130
  offense_start = comment.location.column + start_column
68
131
  offense_end = offense_start + tag.name.length - 1
69
132
  range = source_range(processed_source.buffer, comment.location.line, offense_start..offense_end)
70
- 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)
142
+ end
143
+
144
+ def include_overload_tag?(docstring)
145
+ docstring.tags.any? { |tag| tag.tag_name == "overload" }
71
146
  end
72
147
  end
73
148
  end
@@ -10,6 +10,7 @@ module RuboCop
10
10
  # # good
11
11
  # # @param [Integer, String]
12
12
  class TagTypeSyntax < Base
13
+ include YARD::Helper
13
14
  include RangeHelp
14
15
 
15
16
  def on_new_investigation
@@ -26,10 +27,10 @@ module RuboCop
26
27
  def check(comment)
27
28
  docstring = comment.text.gsub(/\A#\s*/, '')
28
29
  ::YARD::DocstringParser.new.parse(docstring).tags.each do |tag|
29
- types = extract_tag_type(tag)
30
+ types = extract_tag_types(tag)
30
31
 
31
32
  check_syntax_error(comment) do
32
- ::YARD::Tags::TypesExplainer::Parser.parse(types.join(', '))
33
+ parse_type(types.join(', '))
33
34
  end
34
35
  end
35
36
  end
@@ -42,15 +43,6 @@ module RuboCop
42
43
  end
43
44
  end
44
45
 
45
- def extract_tag_type(tag)
46
- case tag
47
- when ::YARD::Tags::OptionTag
48
- tag.pair.types
49
- else
50
- tag.types
51
- end
52
- end
53
-
54
46
  def inline_comment?(comment)
55
47
  !comment_line?(comment.source_range.source_line)
56
48
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yard'
4
- require_relative 'yard/collection_helper'
4
+ require_relative 'yard/helper'
5
5
  require_relative 'yard/collection_style'
6
6
  require_relative 'yard/collection_type'
7
7
  require_relative 'yard/meaningless_tag'
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module YARD
5
- VERSION = "0.5.0"
5
+ VERSION = "0.7.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-yard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ksss
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-10-08 00:00:00.000000000 Z
11
+ date: 2023-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -51,9 +51,9 @@ files:
51
51
  - README.md
52
52
  - config/default.yml
53
53
  - lib/rubocop-yard.rb
54
- - lib/rubocop/cop/yard/collection_helper.rb
55
54
  - lib/rubocop/cop/yard/collection_style.rb
56
55
  - lib/rubocop/cop/yard/collection_type.rb
56
+ - lib/rubocop/cop/yard/helper.rb
57
57
  - lib/rubocop/cop/yard/meaningless_tag.rb
58
58
  - lib/rubocop/cop/yard/mismatch_name.rb
59
59
  - lib/rubocop/cop/yard/tag_type_syntax.rb