rubocop-yard 0.3.1 → 0.5.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: b62c88ebfb27a008a19460e87ced5cde1352bd778fd7432052606b1220cd82aa
4
- data.tar.gz: 11799a04ee4e9944c1303d5daedab3e09eca7ad01262c155ee9022689acb1d73
3
+ metadata.gz: d4dd27dab805f9450c5043686ad6a530235d31e2a148e1913db76288fa51a39e
4
+ data.tar.gz: 2820aa0509d5ff6ef0adecbc772168cd8f2b64650109feb4bfb38e810625812a
5
5
  SHA512:
6
- metadata.gz: 4743f4c79a957b78f28af9639bd78fd06f37c935d52e9123e6a48d2c9280db8132153c2f0f299dba401d0df351858517d5a13b62d4d446042a0ef1419b6aa343
7
- data.tar.gz: 5ea7b97be5355401201a0c50692dd9079211c42b5e99b19c4120d2873708e3a5b464747b75f5fa2e1822779169d3d762cd5c5339bade0bb54b6fef9ec6edb886
6
+ metadata.gz: be2c582e52297d345a8d09f413d98e15c7724b4adbd288fa5bfa38b280e9b5c752dd549885e6ec521bcd4c9dd335a71a9210475ef585d472332c2dd30066350c
7
+ data.tar.gz: c239f115e44518f79edb4da03cbf54c4c3e6d66659e1ff51ede40047fb5d7af6944dd4a0e08cc8e9bad53c88ae0d7869a4071655105c40704cab44a7eda10620
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2023-10-9
4
+
5
+ - Add new cop `YARD/CollectionStyle`
6
+
7
+ - Split cop from `YARD/TagType` to
8
+ - `YARD/TagTypeSyntax`
9
+ - `YARD/CollectionType`
10
+
11
+ ## [0.4.0] - 2023-09-19
12
+
13
+ - Add new cop `YARD/MeaninglessTag`
14
+
3
15
  ## [0.3.1] - 2023-09-16
4
16
 
5
17
  Fix config/default.yml
data/README.md CHANGED
@@ -6,18 +6,66 @@ You can check YARD format in Ruby code comment by RuboCop.
6
6
 
7
7
  ## Features
8
8
 
9
- ### `YARD/TagType`
9
+ ### `YARD/TagTypeSyntax`
10
10
 
11
11
  Check tag type syntax error.
12
12
 
13
13
  ```
14
14
  # @param [Symbol|String]
15
- # ^^^^^^^^^^^^^ SyntaxError as YARD tag type
15
+ ^^^^^^^^^^^^^ (SyntaxError) invalid character at |
16
16
  ```
17
17
 
18
+ ### `YARD/CollectionStyle`
19
+
20
+ `EnforcedStyle long (default)`
21
+
22
+ ```
23
+ # bad
24
+ # @param [{KeyType => ValueType}]
25
+
26
+ # bad
27
+ # @param [(String)]
28
+
29
+ # bad
30
+ # @param [<String>]
31
+
32
+ # good
33
+ # @param [Hash{KeyType => ValueType}]
34
+
35
+ # good
36
+ # @param [Array(String)]
37
+
38
+ # good
39
+ # @param [Array<String>]
40
+ ```
41
+
42
+ `EnforcedStyle short`
43
+
44
+ ```
45
+ # bad
46
+ # @param [Hash{KeyType => ValueType}]
47
+
48
+ # bad
49
+ # @param [Array(String)]
50
+
51
+ # bad
52
+ # @param [Array<String>]
53
+
54
+ # good
55
+ # @param [{KeyType => ValueType}]
56
+
57
+ # good
58
+ # @param [(String)]
59
+
60
+ # good
61
+ # @param [<String>]
62
+ ```
63
+
64
+ ### `YARD/CollectionType`
65
+
18
66
  ```
19
67
  # @param [Hash<Symbol, String>]
20
- # ^^^^^^^^^^^^^^^^^^^^ `<Type>` is the collection type syntax. Did you mean `{KeyType => ValueType}` or `Hash{KeyType => ValueType}`
68
+ ^^^^^^^^^^^^^^^^^^^^ `<Type>` is the collection type syntax. Did you mean `{KeyType => ValueType}` or `Hash{KeyType => ValueType}`
21
69
  ```
22
70
 
23
71
  ### `YARD/MismatchName`
@@ -25,13 +73,32 @@ Check tag type syntax error.
25
73
  Check `@param` and `@option` name with method definition.
26
74
 
27
75
  ```rb
76
+ # @param [String]
77
+ ^^^^^^^^^^^^^^^^^ No tag name is supplied in `@param`
78
+
79
+ # @param string
80
+ ^^^^^^^^^^^^^^^ No types are associated with the tag in `@param`
81
+
28
82
  # @param [String] string
29
- # ^^^^^^ `string` is not found in method arguments
83
+ ^^^^^^ `string` is not found in method arguments
30
84
  # @option opt bar [String]
31
- # ^^^ `opt` is not found in method arguments
85
+ ^^^ `opt` is not found in method arguments
32
86
  def foo(strings, opts = {})
33
87
  ```
34
88
 
89
+ ### `YARD/MeaninglessTag`
90
+
91
+ Check `@param` and `@option` with class/module or casgn
92
+
93
+ ```rb
94
+ # @param [String] foo
95
+ ^^^^^^^^^^^^^^^^^^^^^ `@param` is meaningless tag on module
96
+ module Foo
97
+ # @option foo bar [String]
98
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^ `@option` is meaningless tag on casgn
99
+ CONST = 1
100
+ ```
101
+
35
102
  ## Installation
36
103
 
37
104
  Install the gem and add to the application's Gemfile by executing:
data/config/default.yml CHANGED
@@ -1,7 +1,30 @@
1
- YARD/TagType:
1
+ YARD/MeaninglessTag:
2
+ Description: 'Check meaningless tag'
3
+ Enabled: true
4
+ VersionAdded: '0.4.0'
5
+
6
+ YARD/TagTypeSyntax:
2
7
  Description: 'Check syntax for yard tag type'
3
8
  Enabled: true
4
- VersionAdded: '0.2.0'
9
+ VersionAdded: '0.5.0'
10
+
11
+ YARD/CollectionStyle:
12
+ Description: 'Check collection type style'
13
+ Enabled: true
14
+ VersionAdded: '0.5.0'
15
+ EnforcedStyle: long
16
+ SupportedStyles:
17
+ - long
18
+ - short
19
+
20
+ YARD/CollectionType:
21
+ Description: 'Check collection type syntax'
22
+ Enabled: true
23
+ VersionAdded: '0.5.0'
24
+ EnforcedStyle: long
25
+ SupportedStyles:
26
+ - long
27
+ - short
5
28
 
6
29
  YARD/MismatchName:
7
30
  Description: 'Check @param and @option name and method parameters'
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module YARD
6
+ module CollectionHelper
7
+ def extract_tag_types(tag)
8
+ case tag
9
+ when ::YARD::Tags::OptionTag
10
+ tag.pair.types
11
+ else
12
+ tag.types
13
+ end
14
+ end
15
+
16
+ def each_types_explainer(docstring, &block)
17
+ docstring.tags.each do |tag|
18
+ types = extract_tag_types(tag)
19
+
20
+ begin
21
+ types_explainers = ::YARD::Tags::TypesExplainer::Parser.parse(types.join(', '))
22
+ types.zip(types_explainers).each do |type, types_explainer|
23
+ block.call(type, types_explainer)
24
+ end
25
+ rescue SyntaxError
26
+ end
27
+ end
28
+ end
29
+
30
+ def styled_string(types_explainer)
31
+ case types_explainer
32
+ when ::YARD::Tags::TypesExplainer::HashCollectionType
33
+ tname = case [style, types_explainer.name]
34
+ when [:short, 'Hash']
35
+ ''
36
+ when [:long, 'Hash']
37
+ 'Hash'
38
+ else
39
+ types_explainer.name
40
+ end
41
+ "#{tname}{#{types_explainer.key_types.map { styled_string(_1) }.join(', ')} => #{types_explainer.value_types.map { styled_string(_1) }.join(', ')}}"
42
+ when ::YARD::Tags::TypesExplainer::FixedCollectionType
43
+ tname = case [style, types_explainer.name]
44
+ when [:short, 'Array']
45
+ ''
46
+ when [:long, 'Array']
47
+ 'Array'
48
+ else
49
+ types_explainer.name
50
+ end
51
+ "#{tname}(#{types_explainer.types.map { styled_string(_1) }.join(', ')})"
52
+ when ::YARD::Tags::TypesExplainer::CollectionType
53
+ tname = case [style, types_explainer.name]
54
+ when [:short, 'Array']
55
+ ''
56
+ when [:long, 'Array']
57
+ 'Array'
58
+ else
59
+ types_explainer.name
60
+ end
61
+ "#{tname}<#{types_explainer.types.map { styled_string(_1) }.join(', ')}>"
62
+ when ::YARD::Tags::TypesExplainer::Type
63
+ types_explainer.name
64
+ else
65
+ raise "#{types_explainer.class} is not supported"
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module YARD
6
+ # @example EnforcedStyle short
7
+ #
8
+ # # bad
9
+ # # @param [Hash{KeyType => ValueType}]
10
+ #
11
+ # # bad
12
+ # # @param [Array(String)]
13
+ #
14
+ # # bad
15
+ # # @param [Array<String>]
16
+ #
17
+ # # good
18
+ # # @param [{KeyType => ValueType}]
19
+ #
20
+ # # good
21
+ # # @param [(String)]
22
+ #
23
+ # # good
24
+ # # @param [<String>]
25
+ #
26
+ # @example EnforcedStyle long (default)
27
+ # # bad
28
+ # # @param [{KeyType => ValueType}]
29
+ #
30
+ # # bad
31
+ # # @param [(String)]
32
+ #
33
+ # # bad
34
+ # # @param [<String>]
35
+ #
36
+ # # good
37
+ # # @param [Hash{KeyType => ValueType}]
38
+ #
39
+ # # good
40
+ # # @param [Array(String)]
41
+ #
42
+ # # good
43
+ # # @param [Array<String>]
44
+ class CollectionStyle < Base
45
+ include YARD::CollectionHelper
46
+ include RangeHelp
47
+ include ConfigurableEnforcedStyle
48
+ extend AutoCorrector
49
+
50
+ def on_new_investigation
51
+ processed_source.comments.each do |comment|
52
+ next if inline_comment?(comment)
53
+ next unless include_yard_tag?(comment)
54
+
55
+ check(comment)
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def check(comment)
62
+ docstring = ::YARD::DocstringParser.new.parse(comment.text.gsub(/\A#\s*/, ''))
63
+ each_types_explainer(docstring) do |type, types_explainer|
64
+ correct_type = styled_string(types_explainer)
65
+ unless type == correct_type
66
+ add_offense(comment, message: "`#{type}` is using #{bad_style} style syntax") do |corrector|
67
+ corrector.replace(comment, comment.source.sub(/\[(.*)\]/) { "[#{correct_type}]" })
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def bad_style
74
+ if style == :long
75
+ :short
76
+ else
77
+ :long
78
+ end
79
+ end
80
+
81
+ def inline_comment?(comment)
82
+ !comment_line?(comment.source_range.source_line)
83
+ end
84
+
85
+ def include_yard_tag?(comment)
86
+ comment.source.match?(/@(?:param|return|option|raise|yieldparam|yieldreturn)\s+.*\[.*\]/)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module YARD
6
+ # @example common
7
+ # # bad
8
+ # # @param [Hash<Symbol, String>]
9
+ #
10
+ # # bad
11
+ # # @param [Hash(String)]
12
+ #
13
+ # # bad
14
+ # # @param [Array{Symbol => String}]
15
+ #
16
+ # # good
17
+ # # @param [Hash{Symbol => String}]
18
+ #
19
+ # # good
20
+ # # @param [Array(String)]
21
+ #
22
+ # # good
23
+ # # @param [Hash{Symbol => String}]
24
+ class CollectionType < Base
25
+ include YARD::CollectionHelper
26
+ include RangeHelp
27
+ include ConfigurableEnforcedStyle
28
+ extend AutoCorrector
29
+
30
+ def on_new_investigation
31
+ processed_source.comments.each do |comment|
32
+ next if inline_comment?(comment)
33
+ next unless include_yard_tag?(comment)
34
+
35
+ docstring = ::YARD::DocstringParser.new.parse(comment.text.gsub(/\A#\s*/, ''))
36
+ check_mismatch_collection_type(comment, docstring)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def check_mismatch_collection_type(comment, docstring)
43
+ each_types_explainer(docstring) do |_type, types_explainer|
44
+ check_mismatch_collection_type_one(comment, types_explainer)
45
+ end
46
+ end
47
+
48
+ def check_mismatch_collection_type_one(comment, types_explainer)
49
+ case types_explainer
50
+ when ::YARD::Tags::TypesExplainer::HashCollectionType
51
+ case types_explainer.name
52
+ when 'Hash'
53
+ types_explainer.key_types.each { |t| check_mismatch_collection_type_one(comment, t) }
54
+ types_explainer.value_types.each { |t| check_mismatch_collection_type_one(comment, t) }
55
+ when 'Array'
56
+ message = "`{KeyType => ValueType}` is the Hash collection type syntax."
57
+ add_offense(tag_range_for_comment(comment), message: message) do |corrector|
58
+ types_explainer.name = "Hash"
59
+ correct_tag_type(corrector, comment, types_explainer)
60
+ end
61
+ end
62
+ when ::YARD::Tags::TypesExplainer::FixedCollectionType
63
+ case types_explainer.name
64
+ when 'Hash'
65
+ if types_explainer.types.length == 2
66
+ message = "`Hash(Key, Value)` is miswritten of `Hash<Key, Value>` in perhaps"
67
+ add_offense(tag_range_for_comment(comment), message: message) do |corrector|
68
+ hash_type = ::YARD::Tags::TypesExplainer::HashCollectionType.new(
69
+ 'Hash',
70
+ [types_explainer.types[0]],
71
+ [types_explainer.types[1]]
72
+ )
73
+ correct_tag_type(corrector, comment, hash_type)
74
+ end
75
+ else
76
+ message = "`(Type)` is the fixed collection type syntax."
77
+ add_offense(tag_range_for_comment(comment), message: message) do |corrector|
78
+ types_explainer.name = "Array"
79
+ correct_tag_type(corrector, comment, types_explainer)
80
+ end
81
+ end
82
+ when 'Array'
83
+ types_explainer.types.each { |t| check_mismatch_collection_type_one(comment, t) }
84
+ end
85
+ when ::YARD::Tags::TypesExplainer::CollectionType
86
+ case types_explainer.name
87
+ when 'Hash'
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"
91
+ add_offense(tag_range_for_comment(comment), message: message) do |corrector|
92
+ hash_type = ::YARD::Tags::TypesExplainer::HashCollectionType.new(
93
+ 'Hash',
94
+ [types_explainer.types[0]],
95
+ [types_explainer.types[1]]
96
+ )
97
+ correct_tag_type(corrector, comment, hash_type)
98
+ end
99
+ else
100
+ message = "`<Type>` is the collection type syntax."
101
+ add_offense(tag_range_for_comment(comment), message: message) do |corrector|
102
+ types_explainer.name = "Array"
103
+ correct_tag_type(corrector, comment, types_explainer)
104
+ end
105
+ end
106
+ when 'Array'
107
+ types_explainer.types.each { |t| check_mismatch_collection_type_one(comment, t) }
108
+ end
109
+ end
110
+ end
111
+
112
+ def correct_tag_type(corrector, comment, types_explainer)
113
+ corrector.replace(comment, comment.source.sub(/\[(.*)\]/) { "[#{styled_string(types_explainer)}]" })
114
+ end
115
+
116
+ def inline_comment?(comment)
117
+ !comment_line?(comment.source_range.source_line)
118
+ end
119
+
120
+ def include_yard_tag?(comment)
121
+ comment.source.match?(/@(?:param|return|option|raise|yieldparam|yieldreturn)\s+.*\[.*\]/)
122
+ end
123
+
124
+ def tag_range_for_comment(comment)
125
+ start_column = comment.source.index(/\[/) + 1
126
+ end_column = comment.source.index(/\]/)
127
+ offense_start = comment.location.column + start_column
128
+ offense_end = comment.location.column + end_column
129
+ source_range(processed_source.buffer, comment.location.line, offense_start..offense_end)
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module YARD
6
+ # @example meaningless tag
7
+ # # bad
8
+ # # @param [String] foo
9
+ # # @option bar baz [String]
10
+ # class Foo
11
+ #
12
+ # # bad
13
+ # # @param [String] foo
14
+ # # @option bar baz [String]
15
+ # CONST = 1
16
+ #
17
+ # # good
18
+ # class Foo
19
+ #
20
+ # # good
21
+ # CONST = 1
22
+ class MeaninglessTag < Base
23
+ include RangeHelp
24
+ include DocumentationComment
25
+ extend AutoCorrector
26
+
27
+ def on_class(node)
28
+ check(node)
29
+ end
30
+ alias on_module on_class
31
+ alias on_casgn on_class
32
+
33
+ def check(node)
34
+ preceding_lines = preceding_lines(node)
35
+ return false unless preceding_comment?(node, preceding_lines.last)
36
+
37
+ yard_docstring = preceding_lines.map { |line| line.text.gsub(/\A#\s*/, '') }.join("\n")
38
+ docstring = ::YARD::DocstringParser.new.parse(yard_docstring)
39
+ docstring.tags.each do |tag|
40
+ next unless tag.tag_name == 'param' || tag.tag_name == 'option'
41
+
42
+ comment = preceding_lines.find { |line| line.text.include?("@#{tag.tag_name}") }
43
+ next unless comment
44
+
45
+ add_offense(comment, message: "`@#{tag.tag_name}` is meaningless tag on #{node.type}") do |corrector|
46
+ corrector.replace(comment, comment.text.gsub("@#{tag.tag_name}", tag.tag_name))
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module YARD
6
- # @example
6
+ # @example mismatch name
7
7
  # # bad
8
8
  # # @param [void] baz
9
9
  # # @option opt aaa [void]
@@ -12,7 +12,7 @@ module RuboCop
12
12
  #
13
13
  # # good
14
14
  # # @param [void] bar
15
- # # @param [Array] argsa
15
+ # # @param [Array] arg
16
16
  # # @option opts aaa [void]
17
17
  # def foo(bar, opts = {}, *arg)
18
18
  # end
@@ -28,29 +28,47 @@ module RuboCop
28
28
 
29
29
  yard_docstring = preceding_lines.map { |line| line.text.gsub(/\A#\s*/, '') }.join("\n")
30
30
  docstring = ::YARD::DocstringParser.new.parse(yard_docstring)
31
- docstring.tags.each do |tag|
31
+ docstring.tags.each_with_index do |tag, i|
32
32
  next unless tag.tag_name == 'param' || tag.tag_name == 'option'
33
- next unless node.arguments.none? { |arg_node| tag.name.to_sym == arg_node.name }
34
33
 
35
- tag_name_regexp = Regexp.new("\\b#{Regexp.escape(tag.name)}\\b")
36
- comment = preceding_lines.find { |line| line.text.match?(tag_name_regexp) && line.text.include?("@#{tag.tag_name}") }
34
+ comment = find_by_tag(preceding_lines, tag, i)
37
35
  next unless comment
38
36
 
39
- start_column = comment.source.index(tag_name_regexp)
40
- offense_start = comment.location.column + start_column
41
- offense_end = offense_start + tag.name.length - 1
42
- range = source_range(processed_source.buffer, comment.location.line, offense_start..offense_end)
43
- add_offense(range, message: "`#{tag.name}` is not found in method arguments")
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}`")
42
+ end
43
+
44
+ next
45
+ end
46
+
47
+ next unless node.arguments.none? { |arg_node| tag.name.to_sym == arg_node.name }
48
+
49
+ add_offense_to_tag(comment, tag)
44
50
  end
45
51
  end
46
52
  alias on_defs on_def
47
- end
48
53
 
49
- private
54
+ private
50
55
 
51
- # @param [void] aaa
52
- # @option opts bbb [void]
53
- def dummy(aaa, opts = {}, *)
56
+ def find_by_tag(preceding_lines, tag, i)
57
+ count = -1
58
+ preceding_lines.find do |line|
59
+ count += 1 if line.text.include?("@#{tag.tag_name}")
60
+ count == i
61
+ end
62
+ end
63
+
64
+ def add_offense_to_tag(comment, tag)
65
+ tag_name_regexp = Regexp.new("\\b#{Regexp.escape(tag.name)}\\b")
66
+ start_column = comment.source.index(tag_name_regexp)
67
+ offense_start = comment.location.column + start_column
68
+ offense_end = offense_start + tag.name.length - 1
69
+ 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")
71
+ end
54
72
  end
55
73
  end
56
74
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module YARD
6
+ # @example tag type
7
+ # # bad
8
+ # # @param [Integer String]
9
+ #
10
+ # # good
11
+ # # @param [Integer, String]
12
+ class TagTypeSyntax < Base
13
+ include RangeHelp
14
+
15
+ def on_new_investigation
16
+ processed_source.comments.each do |comment|
17
+ next if inline_comment?(comment)
18
+ next unless include_yard_tag?(comment)
19
+
20
+ check(comment)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def check(comment)
27
+ docstring = comment.text.gsub(/\A#\s*/, '')
28
+ ::YARD::DocstringParser.new.parse(docstring).tags.each do |tag|
29
+ types = extract_tag_type(tag)
30
+
31
+ check_syntax_error(comment) do
32
+ ::YARD::Tags::TypesExplainer::Parser.parse(types.join(', '))
33
+ end
34
+ end
35
+ end
36
+
37
+ def check_syntax_error(comment)
38
+ begin
39
+ yield
40
+ rescue SyntaxError => e
41
+ add_offense(tag_range_for_comment(comment), message: "(#{e.class}) #{e.message}")
42
+ end
43
+ end
44
+
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
+ def inline_comment?(comment)
55
+ !comment_line?(comment.source_range.source_line)
56
+ end
57
+
58
+ def include_yard_tag?(comment)
59
+ comment.source.match?(/@(?:param|return|option|raise|yieldparam|yieldreturn)\s+.*\[.*\]/)
60
+ end
61
+
62
+ def tag_range_for_comment(comment)
63
+ start_column = comment.source.index(/\[/) + 1
64
+ end_column = comment.source.index(/\]/)
65
+ offense_start = comment.location.column + start_column
66
+ offense_end = comment.location.column + end_column
67
+ source_range(processed_source.buffer, comment.location.line, offense_start..offense_end)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yard'
4
- require_relative 'yard/tag_type'
4
+ require_relative 'yard/collection_helper'
5
+ require_relative 'yard/collection_style'
6
+ require_relative 'yard/collection_type'
7
+ require_relative 'yard/meaningless_tag'
5
8
  require_relative 'yard/mismatch_name'
9
+ require_relative 'yard/tag_type_syntax'
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module YARD
5
- VERSION = "0.3.1"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
data/sig/rubocop/yard.rbs CHANGED
@@ -5,7 +5,7 @@ module RuboCop
5
5
  end
6
6
  module Cop
7
7
  module YARD
8
- class TagType
8
+ class CollectionType
9
9
  type t = YARD::Tags::TypesExplainer::Type
10
10
  | ::YARD::Tags::TypesExplainer::CollectionType
11
11
  | ::YARD::Tags::TypesExplainer::FixedCollectionType
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.3.1
4
+ version: 0.5.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-09-16 00:00:00.000000000 Z
11
+ date: 2023-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -51,8 +51,12 @@ 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
+ - lib/rubocop/cop/yard/collection_style.rb
56
+ - lib/rubocop/cop/yard/collection_type.rb
57
+ - lib/rubocop/cop/yard/meaningless_tag.rb
54
58
  - lib/rubocop/cop/yard/mismatch_name.rb
55
- - lib/rubocop/cop/yard/tag_type.rb
59
+ - lib/rubocop/cop/yard/tag_type_syntax.rb
56
60
  - lib/rubocop/cop/yard_cops.rb
57
61
  - lib/rubocop/yard.rb
58
62
  - lib/rubocop/yard/inject.rb
@@ -1,109 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module YARD
6
- # @example
7
- # # bad
8
- # @param [Integer String]
9
- #
10
- # # bad
11
- # @param [Hash<Symbol, String>]
12
- #
13
- # # bad
14
- # @param [Hash(String)]
15
- #
16
- # # bad
17
- # @param [Array{Symbol => String}]
18
- #
19
- # # good
20
- # @param [Integer, String]
21
- #
22
- # # good
23
- # @param [<String>]
24
- # @param [Array<String>]
25
- # @param [List<String>]
26
- # @param [Array<(String, Fixnum, Hash)>]
27
- #
28
- # # good
29
- # @param [(String)]
30
- # @param [Array(String)]
31
- #
32
- # # good
33
- # @param [{KeyType => ValueType}]
34
- # @param [Hash{KeyType => ValueType}]
35
- class TagType < Base
36
- MSG = ''
37
- include RangeHelp # @return [void,]
38
-
39
- def on_new_investigation
40
- processed_source.comments.each do |comment|
41
- next if inline_comment?(comment)
42
- next unless include_yard_tag?(comment)
43
-
44
- check(comment)
45
- end
46
- end
47
-
48
- private
49
-
50
- def check(comment)
51
- docstring = comment.text.gsub(/\A#\s*/, '')
52
- ::YARD::DocstringParser.new.parse(docstring).tags.each do |tag|
53
- next unless tag.types
54
-
55
- ::YARD::Tags::TypesExplainer::Parser.parse(tag.types.join(', ')).each do |types_explainer|
56
- check_mismatch_collection_type(comment, types_explainer)
57
- end
58
- rescue SyntaxError
59
- add_offense(tag_range_for_comment(comment), message: 'SyntaxError as YARD tag type')
60
- end
61
- end
62
-
63
- def check_mismatch_collection_type(comment, types_explainer)
64
- case types_explainer
65
- when ::YARD::Tags::TypesExplainer::HashCollectionType
66
- if types_explainer.name == 'Hash'
67
- types_explainer.key_types.each { |t| check_mismatch_collection_type(comment, t) }
68
- types_explainer.value_types.each { |t| check_mismatch_collection_type(comment, t) }
69
- else
70
- did_you_mean = types_explainer.name == 'Array' ? 'Did you mean `<Type>` or `Array<Type>`' : ''
71
- message = "`{KeyType => ValueType}` is the hash collection type syntax. #{did_you_mean}"
72
- add_offense(tag_range_for_comment(comment), message: message)
73
- end
74
- when ::YARD::Tags::TypesExplainer::FixedCollectionType
75
- if types_explainer.name == 'Hash'
76
- message = "`(Type)` is the fixed collection type syntax. Did you mean `{KeyType => ValueType}` or `Hash{KeyType => ValueType}`"
77
- add_offense(tag_range_for_comment(comment), message: message)
78
- else
79
- types_explainer.types.each { |t| check_mismatch_collection_type(comment, t) }
80
- end
81
- when ::YARD::Tags::TypesExplainer::CollectionType
82
- if types_explainer.name == 'Hash'
83
- message = "`<Type>` is the collection type syntax. `{KeyType => ValueType}` or `Hash{KeyType => ValueType}` is more good"
84
- add_offense(tag_range_for_comment(comment), message: message)
85
- else
86
- types_explainer.types.each { |t| check_mismatch_collection_type(comment, t) }
87
- end
88
- end
89
- end
90
-
91
- def inline_comment?(comment)
92
- !comment_line?(comment.source_range.source_line)
93
- end
94
-
95
- def include_yard_tag?(comment)
96
- comment.source.match?(/@(?:param|return|option|raise|yieldparam|yieldreturn)\s+\[.*\]/)
97
- end
98
-
99
- def tag_range_for_comment(comment)
100
- start_column = comment.source.index(/\[/) + 1
101
- end_column = comment.source.index(/\]/)
102
- offense_start = comment.location.column + start_column
103
- offense_end = comment.location.column + end_column
104
- source_range(processed_source.buffer, comment.location.line, offense_start..offense_end)
105
- end
106
- end
107
- end
108
- end
109
- end