rubocop-yard 0.4.0 → 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: 35f55b03315d73095819182351a19278528a8d91f5fb71f9644e39734ef0aa89
4
- data.tar.gz: 731f21ea908307e87b59e36dae70fc9da5ced99157cdc53d0084c077673fd496
3
+ metadata.gz: d4dd27dab805f9450c5043686ad6a530235d31e2a148e1913db76288fa51a39e
4
+ data.tar.gz: 2820aa0509d5ff6ef0adecbc772168cd8f2b64650109feb4bfb38e810625812a
5
5
  SHA512:
6
- metadata.gz: 378e7d10ff7f50c807a1fe0fe6266e7163ebcece3bc6a4cf340cf98baade320a4785d2520c42aa9319bc20e20642dc46b39311088b58e521f57ee0ce15fab48b
7
- data.tar.gz: a33bc93f4cb7712b0c73c259d3ecb7f94409973ec73c897494e723c33f5879b7686deba726e7a1f871da20aae3abd46554641c8d1ea29d5c58124cc66646213e
6
+ metadata.gz: be2c582e52297d345a8d09f413d98e15c7724b4adbd288fa5bfa38b280e9b5c752dd549885e6ec521bcd4c9dd335a71a9210475ef585d472332c2dd30066350c
7
+ data.tar.gz: c239f115e44518f79edb4da03cbf54c4c3e6d66659e1ff51ede40047fb5d7af6944dd4a0e08cc8e9bad53c88ae0d7869a4071655105c40704cab44a7eda10620
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
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
+
3
11
  ## [0.4.0] - 2023-09-19
4
12
 
5
13
  - 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,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
@@ -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
@@ -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,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yard'
4
+ require_relative 'yard/collection_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.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.4.0
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-19 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,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_helper.rb
55
+ - lib/rubocop/cop/yard/collection_style.rb
56
+ - lib/rubocop/cop/yard/collection_type.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