rubocop-yardoc 0.2.0 → 0.2.2

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.
@@ -4,9 +4,6 @@ module RuboCop
4
4
  module Cop
5
5
  # Helper methods to work with node parameters
6
6
  module ParamHelp
7
- # Matches the [<types>] part of a @param line
8
- COMMENT_TYPES_REGEX = /^[^\[]+\[([^\[]*)\]/
9
-
10
7
  # "@param" tag with data from Yard and parsed source code
11
8
  #
12
9
  # @param name [String] Parameter name
@@ -81,22 +78,26 @@ module RuboCop
81
78
  .map do |yard_tag|
82
79
  source_comment = case yard_tag.tag_name
83
80
  when 'param'
84
- comments.find { |comment| comment.text.match?(/^# @param\s+#{yard_tag.name}/) }
81
+ comments.find do |comment|
82
+ comment.text.strip.match?(/^# @param\s+#{yard_tag.name}(\s|$)/)
83
+ end
85
84
  when 'return'
86
- comments.find { |comment| comment.text.match?(/^# @return\s+/) }
85
+ comments.find { |comment| comment.text.strip.match?(/^# @return\s+/) }
87
86
  else
88
87
  next
89
88
  end
90
89
  description_lines = yard_tag.text ? yard_tag.text.split("\n") : []
91
90
 
92
- types_s = source_comment.text.match(COMMENT_TYPES_REGEX)
91
+ types = yard_tag.types || []
92
+ # Build the regexp with types matched by YARD so we don't have to bother with invalid brackets
93
+ types_s = source_comment.text.match(/\[(#{types.map { |t| Regexp.escape t }.join(',\\s*')})\]/)
93
94
  types_s = types_s ? types_s[1] : ''
94
95
 
95
96
  desc_pos = description_lines.any? ? source_comment.text.index(description_lines.first) : nil
96
97
 
97
98
  ParameterTag.new name: yard_tag.name,
98
99
  tag_source_line: source_comment,
99
- types: yard_tag.types || [],
100
+ types: types,
100
101
  types_s: types_s,
101
102
  tag_range: start_tag_position(source_comment, yard_tag.tag_name),
102
103
  type_range: start_type_position(source_comment, types_s),
@@ -9,28 +9,37 @@ module RuboCop
9
9
  #
10
10
  # @example
11
11
  # # bad
12
- # class MyClass
13
- # end
12
+ #
13
+ # class MyClass; end
14
14
  #
15
15
  # # good
16
+ #
16
17
  # # A useful class.
17
- # class MyClass
18
- # end
18
+ # class MyClass; end
19
+ #
20
+ # @example Ignore: `['MyClass', '/Internal/', 'MyModule::MyOtherClass']`
21
+ # # good (ignored with 'MyClass')
19
22
  #
20
- # @example Ignore: ['MyClass', '/Internal/', 'MyModule::MyClass']
21
- # # good (ignored)
22
23
  # class MyClass; end
23
24
  #
24
- # # good (ignored)
25
+ # # good (ignored with /Internal/)
26
+ #
25
27
  # class MyInternalClass; end
26
28
  #
27
29
  # module MyModule
28
- # # good (ignored)
30
+ # # bad
31
+ #
29
32
  # class MyCustomClass; end
30
- # # good (ignored)
33
+ #
34
+ # # good (ignored with 'MyClass')
35
+ #
31
36
  # class MyClass; end
37
+ #
38
+ # # good (ignored with 'MyModule::MyOtherClass)
39
+ #
40
+ # class MyOtherClass; end
32
41
  # end
33
- class ClassDescription < Base
42
+ class ClassDescription < YardocBase
34
43
  include NodeNameHelp
35
44
 
36
45
  MSG = 'Missing YARD documentation for class `%<name>s`.'
@@ -13,17 +13,21 @@ module RuboCop
13
13
  #
14
14
  # @example
15
15
  # # bad
16
+ #
16
17
  # # @param foo [String] a foo
17
18
  # # @param longer_name [Integer] a number
18
19
  # # @param bar [String] A multiline, non aligned
19
20
  # # description
21
+ # def my_method(foo, longer_name, bar); end
20
22
  #
21
23
  # # good
24
+ #
22
25
  # # @param foo [String] a foo
23
26
  # # @param longer_name [Integer] a number
24
27
  # # @param bar [String] A multiline, aligned
25
28
  # # description
26
- class ColumnParams < Base
29
+ # def my_method(foo, longer_name, bar); end
30
+ class ColumnParams < YardocBase
27
31
  extend AutoCorrector
28
32
  include ParamHelp
29
33
  include YardHelp
@@ -9,48 +9,59 @@ module RuboCop
9
9
  #
10
10
  # @example
11
11
  # # bad
12
+ #
12
13
  # MY_CONST = 10
13
14
  #
14
15
  # # good
15
- # # That's 9+1
16
+ #
17
+ # # That's 9 + 1
16
18
  # MY_CONST = 10
17
19
  #
18
- # @example Ignores: ['MY_CONST']
20
+ # @example Ignores: `['MY_CONST']`
19
21
  # # bad
22
+ #
20
23
  # A_CONST = 'something'
21
24
  #
22
25
  # # good (ignored)
26
+ #
23
27
  # MY_CONST = 10
24
28
  #
25
29
  # class MyClass
26
30
  # # good (ignored)
31
+ #
27
32
  # MY_CONST
28
33
  # end
29
34
  #
30
- # @example Ignores: [/^MY_CONST/]
35
+ # @example Ignores: `['/^MY_CONST/']`
31
36
  # # bad
37
+ #
32
38
  # A_CONST = 'something'
33
39
  #
34
40
  # # good (ignored)
41
+ #
35
42
  # MY_CONST = 10
36
43
  #
37
44
  # class MyClass
38
45
  # # bad
46
+ #
39
47
  # MY_CONST
40
48
  # end
41
49
  #
42
- # @example Ignores: ['MyClass::MY_CONST']
50
+ # @example Ignores: `['MyClass::MY_CONST']`
43
51
  # # bad
52
+ #
44
53
  # A_CONST = 'something'
45
54
  #
46
55
  # # bad
56
+ #
47
57
  # MY_CONST = 10
48
58
  #
49
59
  # class MyClass
50
60
  # # good (ignored)
61
+ #
51
62
  # MY_CONST
52
63
  # end
53
- class ConstantDescription < Base
64
+ class ConstantDescription < YardocBase
54
65
  include NodeNameHelp
55
66
 
56
67
  MSG = 'Missing YARD documentation for constant `%<name>s`.'
@@ -3,78 +3,104 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Yardoc
6
- # Ensures all public methods have a description.
6
+ # Ensures all methods have a description.
7
7
  #
8
8
  # This does not ensure parameters and other documentation, only the description
9
9
  #
10
10
  # Private/protected methods are skipped unless `DocumentPrivate: true`.
11
11
  # Methods can be ignored via `Ignore`.
12
12
  #
13
- # @example
13
+ # @example DocumentPrivate: `true` (default)
14
14
  # # bad
15
+ #
15
16
  # def my_method; end
16
17
  #
18
+ # # bad
19
+ #
20
+ # # @param name [String] The name
21
+ # def some_method(name); end
22
+ #
17
23
  # # good
24
+ #
18
25
  # # Does something useful.
19
- # def my_method; end
26
+ # def my_other_method; end
20
27
  #
21
- # @example DocumentPrivate: true
22
- # # bad
23
28
  # private
24
29
  #
30
+ # # good
31
+ #
32
+ # # Does something useful.
33
+ # # def my_other_other_method; end
34
+ #
35
+ # @example DocumentPrivate: `true`
36
+ # private
37
+ #
38
+ # # good
39
+ #
25
40
  # def secret; end
26
41
  #
27
- # @example Ignore: [a_method]
42
+ # @example Ignore: `['a_method']`
28
43
  # # bad
44
+ #
29
45
  # def something; end
30
46
  #
31
47
  # # good (ignored)
48
+ #
32
49
  # def a_method; end
33
50
  #
34
51
  # class MyClass
35
52
  # # good (ignored)
53
+ #
36
54
  # def a_method; end
37
55
  # end
38
56
  #
39
- # @example Ignore: [/^a_m/]
57
+ # @example Ignore: `['/^a_m/']`
40
58
  # # bad
59
+ #
41
60
  # def something; end
42
61
  #
43
62
  # # good (ignored)
63
+ #
44
64
  # def a_method; end
45
65
  #
46
66
  # class MyClass
47
67
  # # good (ignored)
68
+ #
48
69
  # def a_method; end
49
70
  # end
50
71
  #
51
- # @example Ignore: [MyClass::a_method]
72
+ # @example Ignore: `['MyClass::a_method']`
52
73
  # # bad
74
+ #
53
75
  # def something; end
54
76
  #
55
77
  # # bad
78
+ #
56
79
  # def a_method; end
57
80
  #
58
81
  # class MyClass
59
82
  # # good (ignored)
83
+ #
60
84
  # def a_method; end
61
85
  # end
62
- class MethodDescription < Base
86
+ class MethodDescription < YardocBase
63
87
  include VisibilityHelp
64
88
  include NodeNameHelp
89
+ include YardHelp
65
90
 
66
91
  MSG = 'Missing YARD documentation for method `%<name>s`.'
67
92
 
68
93
  # Executed for each method definition
69
94
  #
70
95
  # @param node [RuboCop::AST::Node] The AST node
71
- def on_def(node)
96
+ def on_def(node) # rubocop:disable Metrics/AbcSize
72
97
  name = node.method_name.to_s
73
98
  full_name = fully_qualified_name(node)
74
99
  full_name += "::#{name}"
75
100
  return if ignored?(name, ignore_list) || ignored?(full_name, ignore_list)
76
101
  return if skip_private? && node_visibility(node) == :private
77
- return if documented?(node)
102
+
103
+ return if documented?(node) && !yard_docstring(node).text.strip.empty?
78
104
 
79
105
  add_offense(node, message: format(MSG, name: name))
80
106
  end
@@ -9,28 +9,36 @@ module RuboCop
9
9
  #
10
10
  # @example
11
11
  # # bad
12
- # module MyModule
13
- # end
12
+ #
13
+ # module MyModule; end
14
14
  #
15
15
  # # good
16
- # # A useful module.
17
- # module MyModule
18
- # end
19
16
  #
20
- # @example Ignore: ['MyModule', '/Internal/', 'MyModule::MySubModule']
21
- # # good (ignored)
17
+ # # A useful module.
22
18
  # module MyModule; end
23
19
  #
24
- # # good (ignored)
20
+ # @example Ignore: `['MyModule', '/Internal/', 'MyModule::MySubModule']`
21
+ #
22
+ # # good (ignored with '/Internal/')
23
+ #
25
24
  # module MyInternalModule; end
26
25
  #
27
- # module MyModule
28
- # # good (ignored)
26
+ # # good (ignored with 'MyModule')
27
+ #
28
+ # module MyModule; end
29
+ #
30
+ # # bad
31
+ #
32
+ # module MyOtherModule
33
+ # # good (ignored with 'MyModule::MySubModule')
34
+ #
29
35
  # module MySubModule; end
30
- # # good (ignored)
36
+ #
37
+ # # good (ignored with 'MyModule')
38
+ #
31
39
  # module MyModule; end
32
40
  # end
33
- class ModuleDescription < Base
41
+ class ModuleDescription < YardocBase
34
42
  include NodeNameHelp
35
43
 
36
44
  MSG = 'Missing YARD documentation for module `%<name>s`.'
@@ -9,16 +9,18 @@ module RuboCop
9
9
  #
10
10
  # @example
11
11
  # # bad
12
+ #
12
13
  # # @param x [Integer] desc
13
14
  # # @return [void] desc
14
15
  # def my_method(x); end
15
16
  #
16
17
  # # good
18
+ #
17
19
  # # @param x [Integer] Desc
18
20
  # # @return [void] Desc
19
21
  # def my_method(x); end
20
22
  #
21
- class ParamDescriptionCasing < Base
23
+ class ParamDescriptionCasing < YardocBase
22
24
  extend AutoCorrector
23
25
 
24
26
  MSG = 'First letter of a @param or @return tag should be in uppercase'
@@ -8,20 +8,23 @@ module RuboCop
8
8
  # By default, each @param must include a name, type, and description.
9
9
  # Set `RequireDescription: false` to only require name and type.
10
10
  #
11
- # @example RequireDescription: true (default)
11
+ # @example RequireDescription: `true` (default)
12
12
  # # bad
13
+ #
13
14
  # # @param name [String]
14
15
  # def greet(name); end
15
16
  #
16
17
  # # good
18
+ #
17
19
  # # @param name [String] the name to greet
18
20
  # def greet(name); end
19
21
  #
20
- # @example RequireDescription: false
22
+ # @example RequireDescription: `false`
21
23
  # # good
24
+ #
22
25
  # # @param name [String]
23
26
  # def greet(name); end
24
- class ParamDocumentation < Base
27
+ class ParamDocumentation < YardocBase
25
28
  include ParamHelp
26
29
  include YardHelp
27
30
 
@@ -8,6 +8,7 @@ module RuboCop
8
8
  #
9
9
  # @example
10
10
  # # bad
11
+ #
11
12
  # # Title
12
13
  # # @param x [Integer] desc
13
14
  # # @param y [Integer] desc
@@ -15,6 +16,7 @@ module RuboCop
15
16
  # def my_method(x, y); end
16
17
  #
17
18
  # # good
19
+ #
18
20
  # # Title
19
21
  # #
20
22
  # # @param x [Integer] desc
@@ -23,7 +25,7 @@ module RuboCop
23
25
  # # @return [void]
24
26
  # def my_method(x, y); end
25
27
  #
26
- class SeparateTagsBlocks < Base
28
+ class SeparateTagsBlocks < YardocBase
27
29
  include RangeHelp
28
30
  extend AutoCorrector
29
31
 
@@ -14,20 +14,23 @@ module RuboCop
14
14
  # Meta tags are not supported (`@!<tag>`, used when metaprogramming).
15
15
  #
16
16
  # @example
17
- # # bad
18
- # # @unknown_tag some value
19
- # def foo; end
20
- #
21
17
  # # good
18
+ #
22
19
  # # @param name [String] a name
23
20
  # # @return [void]
24
21
  # def foo(name); end
25
22
  #
26
- # @example AdditionalTags: ['custom_tag']
23
+ # # bad
24
+ #
25
+ # # @unknown_tag some value
26
+ # def foo; end
27
+ #
28
+ # @example AdditionalTags: `['custom_tag']`
27
29
  # # good
30
+ #
28
31
  # # @custom_tag some value
29
32
  # def foo; end
30
- class SupportedTags < Base
33
+ class SupportedTags < YardocBase
31
34
  include ParamHelp
32
35
  include YardHelp
33
36
 
@@ -9,26 +9,30 @@ module RuboCop
9
9
  #
10
10
  # @example
11
11
  # # bad
12
+ #
12
13
  # # @return [void]
13
14
  # # @param name [String] a name
14
15
  # def foo(name); end
15
16
  #
16
17
  # # good
18
+ #
17
19
  # # @param name [String] a name
18
20
  # # @return [void]
19
21
  # def foo(name); end
20
22
  #
21
- # @example Order: [return, param]
23
+ # @example Order: `['return', 'param']`
22
24
  # # bad
25
+ #
23
26
  # # @param name [String] a name
24
27
  # # @return [void]
25
28
  # def foo(name); end
26
29
  #
27
30
  # # good
31
+ #
28
32
  # # @return [void]
29
33
  # # @param name [String] a name
30
34
  # def foo(name); end
31
- class TagOrder < Base
35
+ class TagOrder < YardocBase
32
36
  include YardHelp
33
37
 
34
38
  MSG = 'YARD tags are out of order. Expected order: %<order>s. Found `@%<current>s` after `@%<previous>s`.'
@@ -7,13 +7,15 @@ module RuboCop
7
7
  #
8
8
  # @example
9
9
  # # bad
10
+ #
10
11
  # # @param [String,nil]
11
12
  # def greet(name); end
12
13
  #
13
14
  # # good
14
- # # @param name [String, nil] the name to greet
15
+ #
16
+ # # @param name [String, nil]
15
17
  # def greet(name); end
16
- class TypesFormat < Base
18
+ class TypesFormat < YardocBase
17
19
  include ParamHelp
18
20
  include YardHelp
19
21
 
@@ -3,19 +3,19 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Yardoc
6
- # Ensures all types are separated by a space
7
- #
8
- # Types are parsed with YARD.
6
+ # Ensures all types are parseable by YARD.
9
7
  #
10
8
  # @example
11
9
  # # bad
10
+ #
12
11
  # # @param name [.method, Array[String]] the name to greet
13
12
  # def greet(name); end
14
13
  #
15
14
  # # good
15
+ #
16
16
  # # @param [String, List(item, item2), Array<String>, Hash{String, Integer}, ClassName, #method, nil, true]
17
17
  # def greet(name); end
18
- class ValidTypes < Base
18
+ class ValidTypes < YardocBase
19
19
  include ParamHelp
20
20
  include YardHelp
21
21
 
@@ -8,31 +8,35 @@ module RuboCop
8
8
  # Also checks for @yieldparam when the yield passes arguments,
9
9
  # and @yieldreturn when the yield's return value is used.
10
10
  #
11
- # @example RequireParamDocumentation: true (default)
11
+ # @example RequireParamDocumentation: `true` (default)
12
12
  # # bad
13
+ #
13
14
  # def each
14
15
  # yield item
15
16
  # end
16
17
  #
17
18
  # # good
19
+ #
18
20
  # # @yield [item] iterates over items
19
21
  # # @yieldparam item [Object] the current item
20
22
  # def each
21
23
  # yield item
22
24
  # end
23
25
  #
24
- # @example RequireParamDocumentation: false
26
+ # @example RequireParamDocumentation: `false`
25
27
  # # bad (no yield documentation)
28
+ #
26
29
  # def each
27
30
  # yield item
28
31
  # end
29
32
  #
30
- # # good (ignored missing param documentation)
33
+ # # good (ignored)
34
+ #
31
35
  # # @yield [item] iterates over items
32
36
  # def each
33
37
  # yield item
34
38
  # end
35
- class YieldDocumentation < Base
39
+ class YieldDocumentation < YardocBase
36
40
  include YardHelp
37
41
 
38
42
  MSG_YIELD = 'Method yields a block but is missing a @yield tag.'
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Base class that provides shared YARD parsing utilities for all YARD cops.
6
+ class YardocBase < RuboCop::Cop::Base
7
+ # Represents a comment entry (it may be a multi-line comment)
8
+ #
9
+ # @return [Struct] With :comment, :text, :tag, :line, :blank?
10
+ CommentEntry = Struct.new(:comment, :text, :tag, :line, :blank, keyword_init: true) do
11
+ # Whether the comment is empty
12
+ #
13
+ # @return [Boolean]
14
+ def blank?
15
+ blank
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ # Checks whether a node has any preceding documentation comments.
22
+ #
23
+ # @param node [RuboCop::AST::Node] The AST node
24
+ #
25
+ # @return [Boolean]
26
+ def documented?(node)
27
+ node_comments(node).any?
28
+ end
29
+
30
+ # Checks whether a name matches any entry in an ignore list from cop configuration.
31
+ # Entries may be plain strings or /regexp/ strings.
32
+ #
33
+ # @param name [String] The fully-qualified name to check
34
+ # @param list [Array<String>] List of names or /pattern/ strings
35
+ #
36
+ # @return [Boolean]
37
+ def ignored?(name, list)
38
+ list.any? do |pattern|
39
+ if pattern.start_with?('/') && pattern.end_with?('/')
40
+ Regexp.new(pattern[1..-2]).match?(name)
41
+ else
42
+ name == pattern
43
+ end
44
+ end
45
+ end
46
+
47
+ # List of symbols ignored for the cop
48
+ def ignore_list
49
+ Array(cop_config['Ignore'])
50
+ end
51
+
52
+ # Parsed node comments
53
+ #
54
+ # @param node [RuboCop::AST::Node] The AST node
55
+ #
56
+ # @return [Array<Parser::Source::Comment>]
57
+ def node_comments(node)
58
+ processed_source.ast_with_comments.fetch(node, [])
59
+ end
60
+ end
61
+ end
62
+ end
@@ -4,7 +4,7 @@ require_relative 'mixin/node_name_help'
4
4
  require_relative 'mixin/param_help'
5
5
  require_relative 'mixin/yard_help'
6
6
 
7
- require_relative 'yardoc/base'
7
+ require_relative 'yardoc_base'
8
8
 
9
9
  require_relative 'yardoc/class_description'
10
10
  require_relative 'yardoc/column_params'
@@ -3,6 +3,6 @@
3
3
  module RuboCop
4
4
  module Yardoc
5
5
  # Library version
6
- VERSION = '0.2.0'
6
+ VERSION = '0.2.2'
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-yardoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manuel Tancoigne
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-04 00:00:00.000000000 Z
11
+ date: 2026-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lint_roller
@@ -79,11 +79,11 @@ files:
79
79
  - README.md
80
80
  - config/default.yml
81
81
  - config/obsoletion.yml
82
+ - cops.adoc
82
83
  - lib/rubocop-yardoc.rb
83
84
  - lib/rubocop/cop/mixin/node_name_help.rb
84
85
  - lib/rubocop/cop/mixin/param_help.rb
85
86
  - lib/rubocop/cop/mixin/yard_help.rb
86
- - lib/rubocop/cop/yardoc/base.rb
87
87
  - lib/rubocop/cop/yardoc/class_description.rb
88
88
  - lib/rubocop/cop/yardoc/column_params.rb
89
89
  - lib/rubocop/cop/yardoc/constant_description.rb
@@ -97,6 +97,7 @@ files:
97
97
  - lib/rubocop/cop/yardoc/types_format.rb
98
98
  - lib/rubocop/cop/yardoc/valid_types.rb
99
99
  - lib/rubocop/cop/yardoc/yield_documentation.rb
100
+ - lib/rubocop/cop/yardoc_base.rb
100
101
  - lib/rubocop/cop/yardoc_cops.rb
101
102
  - lib/rubocop/yardoc.rb
102
103
  - lib/rubocop/yardoc/plugin.rb