rubocop-yard 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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