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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +56 -2
- data/config/default.yml +20 -2
- data/lib/rubocop/cop/yard/collection_style.rb +95 -0
- data/lib/rubocop/cop/yard/collection_type.rb +133 -0
- data/lib/rubocop/cop/yard/helper.rb +75 -0
- data/lib/rubocop/cop/yard/meaningless_tag.rb +4 -1
- data/lib/rubocop/cop/yard/mismatch_name.rb +59 -18
- data/lib/rubocop/cop/yard/tag_type_syntax.rb +64 -0
- data/lib/rubocop/cop/yard_cops.rb +4 -1
- data/lib/rubocop/yard/version.rb +1 -1
- data/sig/rubocop/yard.rbs +1 -1
- metadata +6 -3
- data/lib/rubocop/cop/yard/tag_type.rb +0 -126
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5f138fd3b5b0c5eb5550b368164986ba104280ebb40af21d7b8fe843105dad3
|
4
|
+
data.tar.gz: 2a237052e98046b9ac7ffcd72100fd1d96ad90164d8075e8f997981c73a78f98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe3818daae4c2d4597a86ad8f1e1e6785a56e5c34e5e96b0b78be0b52b7cb219ce4d9f390ebac30af9ec844d5f2add8f4c7dfa7d5d4e6496543a0ab6a880980d
|
7
|
+
data.tar.gz: fd20c16153f50987d92fe2c5a6af00ecf1fd274d29205d57e63c8c176a314555de9f9e926ae1bad5b7c5b18bf3d1e7adcca26e8ead35b0f8eacc55744fb4cabb
|
data/CHANGELOG.md
CHANGED
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/
|
9
|
+
### `YARD/TagTypeSyntax`
|
10
10
|
|
11
11
|
Check tag type syntax error.
|
12
12
|
|
13
13
|
```
|
14
14
|
# @param [Symbol|String]
|
15
|
-
^^^^^^^^^^^^^ SyntaxError
|
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/
|
6
|
+
YARD/TagTypeSyntax:
|
7
7
|
Description: 'Check syntax for yard tag type'
|
8
8
|
Enabled: true
|
9
|
-
VersionAdded: '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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
67
|
+
private
|
50
68
|
|
51
|
-
|
52
|
-
|
53
|
-
|
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'
|
data/lib/rubocop/yard/version.rb
CHANGED
data/sig/rubocop/yard.rbs
CHANGED
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
|
+
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-
|
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/
|
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
|