rubocop-yard 0.4.0 → 0.6.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: 35f55b03315d73095819182351a19278528a8d91f5fb71f9644e39734ef0aa89
4
- data.tar.gz: 731f21ea908307e87b59e36dae70fc9da5ced99157cdc53d0084c077673fd496
3
+ metadata.gz: e5f138fd3b5b0c5eb5550b368164986ba104280ebb40af21d7b8fe843105dad3
4
+ data.tar.gz: 2a237052e98046b9ac7ffcd72100fd1d96ad90164d8075e8f997981c73a78f98
5
5
  SHA512:
6
- metadata.gz: 378e7d10ff7f50c807a1fe0fe6266e7163ebcece3bc6a4cf340cf98baade320a4785d2520c42aa9319bc20e20642dc46b39311088b58e521f57ee0ce15fab48b
7
- data.tar.gz: a33bc93f4cb7712b0c73c259d3ecb7f94409973ec73c897494e723c33f5879b7686deba726e7a1f871da20aae3abd46554641c8d1ea29d5c58124cc66646213e
6
+ metadata.gz: fe3818daae4c2d4597a86ad8f1e1e6785a56e5c34e5e96b0b78be0b52b7cb219ce4d9f390ebac30af9ec844d5f2add8f4c7dfa7d5d4e6496543a0ab6a880980d
7
+ data.tar.gz: fd20c16153f50987d92fe2c5a6af00ecf1fd274d29205d57e63c8c176a314555de9f9e926ae1bad5b7c5b18bf3d1e7adcca26e8ead35b0f8eacc55744fb4cabb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.0] - 2023-10-10
4
+
5
+ - Split cop from `YARD/TagType` to
6
+ - `YARD/TagTypeSyntax`
7
+ - `YARD/CollectionType`
8
+
3
9
  ## [0.4.0] - 2023-09-19
4
10
 
5
11
  - Add new cop `YARD/MeaninglessTag`
data/README.md CHANGED
@@ -6,15 +6,63 @@ 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
68
  ^^^^^^^^^^^^^^^^^^^^ `<Type>` is the collection type syntax. Did you mean `{KeyType => ValueType}` or `Hash{KeyType => ValueType}`
@@ -25,6 +73,12 @@ 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
83
  ^^^^^^ `string` is not found in method arguments
30
84
  # @option opt bar [String]
data/config/default.yml CHANGED
@@ -3,10 +3,28 @@ YARD/MeaninglessTag:
3
3
  Enabled: true
4
4
  VersionAdded: '0.4.0'
5
5
 
6
- YARD/TagType:
6
+ YARD/TagTypeSyntax:
7
7
  Description: 'Check syntax for yard tag type'
8
8
  Enabled: true
9
- 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
10
28
 
11
29
  YARD/MismatchName:
12
30
  Description: 'Check @param and @option name and method parameters'
@@ -0,0 +1,95 @@
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::Helper
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 ignore_whitespace(type) == ignore_whitespace(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 ignore_whitespace(str)
74
+ str.tr(' ', '')
75
+ end
76
+
77
+ def bad_style
78
+ if style == :long
79
+ :short
80
+ else
81
+ :long
82
+ end
83
+ end
84
+
85
+ def inline_comment?(comment)
86
+ !comment_line?(comment.source_range.source_line)
87
+ end
88
+
89
+ def include_yard_tag?(comment)
90
+ comment.source.match?(/@(?:param|return|option|raise|yieldparam|yieldreturn)\s+.*\[.*\]/)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,133 @@
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::Helper
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
+ message = "`Hash<Key, Value>` is ambiguous syntax"
90
+ add_offense(tag_range_for_comment(comment), message: message) do |corrector|
91
+ hash_type = ::YARD::Tags::TypesExplainer::HashCollectionType.new(
92
+ 'Hash',
93
+ [types_explainer.types[0]],
94
+ [types_explainer.types[1]]
95
+ )
96
+ correct_tag_type(corrector, comment, hash_type)
97
+ end
98
+ else
99
+ message = "`<Type>` is the collection type syntax."
100
+ add_offense(tag_range_for_comment(comment), message: message) do |corrector|
101
+ types_explainer.name = "Array"
102
+ correct_tag_type(corrector, comment, types_explainer)
103
+ end
104
+ end
105
+ when 'Array'
106
+ types_explainer.types.each { |t| check_mismatch_collection_type_one(comment, t) }
107
+ end
108
+ end
109
+ end
110
+
111
+ def correct_tag_type(corrector, comment, types_explainer)
112
+ corrector.replace(comment, comment.source.sub(/\[(.*)\]/) { "[#{styled_string(types_explainer)}]" })
113
+ end
114
+
115
+ def inline_comment?(comment)
116
+ !comment_line?(comment.source_range.source_line)
117
+ end
118
+
119
+ def include_yard_tag?(comment)
120
+ comment.source.match?(/@(?:param|return|option|raise|yieldparam|yieldreturn)\s+.*\[.*\]/)
121
+ end
122
+
123
+ def tag_range_for_comment(comment)
124
+ start_column = comment.source.index(/\[/) + 1
125
+ end_column = comment.source.index(/\]/)
126
+ offense_start = comment.location.column + start_column
127
+ offense_end = comment.location.column + end_column
128
+ source_range(processed_source.buffer, comment.location.line, offense_start..offense_end)
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module YARD
6
+ module Helper
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 parse_type(type)
17
+ ::YARD::Tags::TypesExplainer::Parser.parse(type)
18
+ end
19
+
20
+ def each_types_explainer(docstring, &block)
21
+ docstring.tags.each do |tag|
22
+ types = extract_tag_types(tag)
23
+
24
+ begin
25
+ types_explainers = parse_type(types.join(', '))
26
+ types.zip(types_explainers).each do |type, types_explainer|
27
+ block.call(type, types_explainer)
28
+ end
29
+ rescue SyntaxError
30
+ end
31
+ end
32
+ end
33
+
34
+ def styled_string(types_explainer)
35
+ case types_explainer
36
+ when ::YARD::Tags::TypesExplainer::HashCollectionType
37
+ tname = case [style, types_explainer.name]
38
+ when [:short, 'Hash']
39
+ ''
40
+ when [:long, 'Hash']
41
+ 'Hash'
42
+ else
43
+ types_explainer.name
44
+ end
45
+ "#{tname}{#{types_explainer.key_types.map { styled_string(_1) }.join(', ')} => #{types_explainer.value_types.map { styled_string(_1) }.join(', ')}}"
46
+ when ::YARD::Tags::TypesExplainer::FixedCollectionType
47
+ tname = case [style, types_explainer.name]
48
+ when [:short, 'Array']
49
+ ''
50
+ when [:long, 'Array']
51
+ 'Array'
52
+ else
53
+ types_explainer.name
54
+ end
55
+ "#{tname}(#{types_explainer.types.map { styled_string(_1) }.join(', ')})"
56
+ when ::YARD::Tags::TypesExplainer::CollectionType
57
+ tname = case [style, types_explainer.name]
58
+ when [:short, 'Array']
59
+ ''
60
+ when [:long, 'Array']
61
+ 'Array'
62
+ else
63
+ types_explainer.name
64
+ end
65
+ "#{tname}<#{types_explainer.types.map { styled_string(_1) }.join(', ')}>"
66
+ when ::YARD::Tags::TypesExplainer::Type
67
+ types_explainer.name
68
+ else
69
+ raise "#{types_explainer.class} is not supported"
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -22,6 +22,7 @@ module RuboCop
22
22
  class MeaninglessTag < Base
23
23
  include RangeHelp
24
24
  include DocumentationComment
25
+ extend AutoCorrector
25
26
 
26
27
  def on_class(node)
27
28
  check(node)
@@ -41,7 +42,9 @@ module RuboCop
41
42
  comment = preceding_lines.find { |line| line.text.include?("@#{tag.tag_name}") }
42
43
  next unless comment
43
44
 
44
- add_offense(comment, message: "`@#{tag.tag_name}` is meaningless tag on #{node.type}")
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
45
48
  end
46
49
  end
47
50
  end
@@ -17,6 +17,7 @@ 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
22
23
 
@@ -28,29 +29,69 @@ module RuboCop
28
29
 
29
30
  yard_docstring = preceding_lines.map { |line| line.text.gsub(/\A#\s*/, '') }.join("\n")
30
31
  docstring = ::YARD::DocstringParser.new.parse(yard_docstring)
31
- docstring.tags.each do |tag|
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
-
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}") }
37
- next unless comment
38
-
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")
32
+ return false if include_overload_tag?(docstring)
33
+
34
+ each_tags_by_docstring(['param', 'option'], docstring) do |tags|
35
+ tags.each_with_index do |tag, i|
36
+ comment = find_by_tag(preceding_lines, tag, i)
37
+ next unless comment
38
+
39
+ # YARD::Tags::RefTagList is not has name and types
40
+ next if tag.instance_of?(::YARD::Tags::RefTagList)
41
+
42
+ types = extract_tag_types(tag)
43
+ unless tag.name && types
44
+ if tag.name.nil?
45
+ add_offense(comment, message: "No tag name is supplied in `@#{tag.tag_name}`")
46
+ elsif types.nil?
47
+ add_offense(comment, message: "No types are associated with the tag in `@#{tag.tag_name}`")
48
+ end
49
+
50
+ next
51
+ end
52
+
53
+ next unless node.arguments.none? { |arg_node| tag.name.to_sym == arg_node.name }
54
+
55
+ begin
56
+ parse_type(types.join(', '))
57
+ rescue SyntaxError
58
+ next
59
+ end
60
+
61
+ add_offense_to_tag(comment, tag)
62
+ end
44
63
  end
45
64
  end
46
65
  alias on_defs on_def
47
- end
48
66
 
49
- private
67
+ private
50
68
 
51
- # @param [void] aaa
52
- # @option opts bbb [void]
53
- def dummy(aaa, opts = {}, *)
69
+ def each_tags_by_docstring(tag_names, docstring)
70
+ tag_names.each do |tag_name|
71
+ yield docstring.tags.select { |tag| tag.tag_name == tag_name }
72
+ end
73
+ end
74
+
75
+ def find_by_tag(preceding_lines, tag, i)
76
+ count = -1
77
+ preceding_lines.find do |line|
78
+ count += 1 if line.text.include?("@#{tag.tag_name}")
79
+ count == i
80
+ end
81
+ end
82
+
83
+ def add_offense_to_tag(comment, tag)
84
+ tag_name_regexp = Regexp.new("\\b#{Regexp.escape(tag.name)}\\b")
85
+ start_column = comment.source.index(tag_name_regexp)
86
+ offense_start = comment.location.column + start_column
87
+ offense_end = offense_start + tag.name.length - 1
88
+ 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")
90
+ end
91
+
92
+ def include_overload_tag?(docstring)
93
+ docstring.tags.any? { |tag| tag.tag_name == "overload" }
94
+ end
54
95
  end
55
96
  end
56
97
  end
@@ -0,0 +1,64 @@
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 YARD::Helper
14
+ include RangeHelp
15
+
16
+ def on_new_investigation
17
+ processed_source.comments.each do |comment|
18
+ next if inline_comment?(comment)
19
+ next unless include_yard_tag?(comment)
20
+
21
+ check(comment)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def check(comment)
28
+ docstring = comment.text.gsub(/\A#\s*/, '')
29
+ ::YARD::DocstringParser.new.parse(docstring).tags.each do |tag|
30
+ types = extract_tag_types(tag)
31
+
32
+ check_syntax_error(comment) do
33
+ parse_type(types.join(', '))
34
+ end
35
+ end
36
+ end
37
+
38
+ def check_syntax_error(comment)
39
+ begin
40
+ yield
41
+ rescue SyntaxError => e
42
+ add_offense(tag_range_for_comment(comment), message: "(#{e.class}) #{e.message}")
43
+ end
44
+ end
45
+
46
+ def inline_comment?(comment)
47
+ !comment_line?(comment.source_range.source_line)
48
+ end
49
+
50
+ def include_yard_tag?(comment)
51
+ comment.source.match?(/@(?:param|return|option|raise|yieldparam|yieldreturn)\s+.*\[.*\]/)
52
+ end
53
+
54
+ def tag_range_for_comment(comment)
55
+ start_column = comment.source.index(/\[/) + 1
56
+ end_column = comment.source.index(/\]/)
57
+ offense_start = comment.location.column + start_column
58
+ offense_end = comment.location.column + end_column
59
+ source_range(processed_source.buffer, comment.location.line, offense_start..offense_end)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yard'
4
+ require_relative 'yard/helper'
5
+ require_relative 'yard/collection_style'
6
+ require_relative 'yard/collection_type'
4
7
  require_relative 'yard/meaningless_tag'
5
- require_relative 'yard/tag_type'
6
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.4.0"
5
+ VERSION = "0.6.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.4.0
4
+ version: 0.6.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-19 00:00:00.000000000 Z
11
+ date: 2023-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -51,9 +51,12 @@ files:
51
51
  - README.md
52
52
  - config/default.yml
53
53
  - lib/rubocop-yard.rb
54
+ - lib/rubocop/cop/yard/collection_style.rb
55
+ - lib/rubocop/cop/yard/collection_type.rb
56
+ - lib/rubocop/cop/yard/helper.rb
54
57
  - lib/rubocop/cop/yard/meaningless_tag.rb
55
58
  - lib/rubocop/cop/yard/mismatch_name.rb
56
- - lib/rubocop/cop/yard/tag_type.rb
59
+ - lib/rubocop/cop/yard/tag_type_syntax.rb
57
60
  - lib/rubocop/cop/yard_cops.rb
58
61
  - lib/rubocop/yard.rb
59
62
  - lib/rubocop/yard/inject.rb
@@ -1,126 +0,0 @@
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
- # # 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
- include RangeHelp # @return [void,]
37
-
38
- def on_new_investigation
39
- processed_source.comments.each do |comment|
40
- next if inline_comment?(comment)
41
- next unless include_yard_tag?(comment)
42
-
43
- check(comment)
44
- end
45
- end
46
-
47
- private
48
-
49
- def check(comment)
50
- docstring = comment.text.gsub(/\A#\s*/, '')
51
- ::YARD::DocstringParser.new.parse(docstring).tags.each do |tag|
52
- types = extract_tag_type(tag)
53
-
54
- check_syntax_error(comment) do
55
- types_explainers = ::YARD::Tags::TypesExplainer::Parser.parse(types.join(', '))
56
- types_explainers.each do |types_explainer|
57
- check_mismatch_collection_type(comment, types_explainer)
58
- end
59
- end
60
- end
61
- end
62
-
63
- def check_syntax_error(comment)
64
- begin
65
- yield
66
- rescue SyntaxError => e
67
- add_offense(tag_range_for_comment(comment), message: "(#{e.class}) #{e.message}")
68
- end
69
- end
70
-
71
- def check_mismatch_collection_type(comment, types_explainer)
72
- case types_explainer
73
- when ::YARD::Tags::TypesExplainer::HashCollectionType
74
- if types_explainer.name == 'Hash'
75
- types_explainer.key_types.each { |t| check_mismatch_collection_type(comment, t) }
76
- types_explainer.value_types.each { |t| check_mismatch_collection_type(comment, t) }
77
- else
78
- did_you_mean = types_explainer.name == 'Array' ? 'Did you mean `<Type>` or `Array<Type>`' : ''
79
- message = "`{KeyType => ValueType}` is the hash collection type syntax. #{did_you_mean}"
80
- add_offense(tag_range_for_comment(comment), message: message)
81
- end
82
- when ::YARD::Tags::TypesExplainer::FixedCollectionType
83
- if types_explainer.name == 'Hash'
84
- message = "`(Type)` is the fixed collection type syntax. Did you mean `{KeyType => ValueType}` or `Hash{KeyType => ValueType}`"
85
- add_offense(tag_range_for_comment(comment), message: message)
86
- else
87
- types_explainer.types.each { |t| check_mismatch_collection_type(comment, t) }
88
- end
89
- when ::YARD::Tags::TypesExplainer::CollectionType
90
- if types_explainer.name == 'Hash'
91
- message = "`<Type>` is the collection type syntax. `{KeyType => ValueType}` or `Hash{KeyType => ValueType}` is more good"
92
- add_offense(tag_range_for_comment(comment), message: message)
93
- else
94
- types_explainer.types.each { |t| check_mismatch_collection_type(comment, t) }
95
- end
96
- end
97
- end
98
-
99
- def extract_tag_type(tag)
100
- case tag
101
- when ::YARD::Tags::OptionTag
102
- tag.pair.types
103
- else
104
- tag.types
105
- end
106
- end
107
-
108
- def inline_comment?(comment)
109
- !comment_line?(comment.source_range.source_line)
110
- end
111
-
112
- def include_yard_tag?(comment)
113
- comment.source.match?(/@(?:param|return|option|raise|yieldparam|yieldreturn)\s+.*\[.*\]/)
114
- end
115
-
116
- def tag_range_for_comment(comment)
117
- start_column = comment.source.index(/\[/) + 1
118
- end_column = comment.source.index(/\]/)
119
- offense_start = comment.location.column + start_column
120
- offense_end = comment.location.column + end_column
121
- source_range(processed_source.buffer, comment.location.line, offense_start..offense_end)
122
- end
123
- end
124
- end
125
- end
126
- end