rubocop-yard 0.3.1 → 0.5.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: 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