yard-lint 1.5.2 → 1.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 +18 -2
- data/README.md +165 -7
- data/bin/yard-lint +45 -11
- data/lib/yard/lint/executor/in_process_registry.rb +38 -12
- data/lib/yard/lint/results/base.rb +3 -0
- data/lib/yard/lint/runner.rb +4 -2
- data/lib/yard/lint/templates/default_config.yml +40 -0
- data/lib/yard/lint/templates/strict_config.yml +39 -0
- data/lib/yard/lint/validators/base.rb +107 -1
- data/lib/yard/lint/validators/documentation/line_length/config.rb +21 -0
- data/lib/yard/lint/validators/documentation/line_length/messages_builder.rb +26 -0
- data/lib/yard/lint/validators/documentation/line_length/parser.rb +65 -0
- data/lib/yard/lint/validators/documentation/line_length/result.rb +26 -0
- data/lib/yard/lint/validators/documentation/line_length/validator.rb +59 -0
- data/lib/yard/lint/validators/documentation/line_length.rb +43 -0
- data/lib/yard/lint/validators/documentation/missing_return/config.rb +2 -1
- data/lib/yard/lint/validators/documentation/missing_return/parser.rb +0 -1
- data/lib/yard/lint/validators/documentation/missing_return/validator.rb +1 -0
- data/lib/yard/lint/validators/documentation/orphaned_doc_comment/config.rb +20 -0
- data/lib/yard/lint/validators/documentation/orphaned_doc_comment/messages_builder.rb +23 -0
- data/lib/yard/lint/validators/documentation/orphaned_doc_comment/parser.rb +38 -0
- data/lib/yard/lint/validators/documentation/orphaned_doc_comment/result.rb +24 -0
- data/lib/yard/lint/validators/documentation/orphaned_doc_comment/validator.rb +121 -0
- data/lib/yard/lint/validators/documentation/orphaned_doc_comment.rb +53 -0
- data/lib/yard/lint/validators/documentation/text_substitution/config.rb +24 -0
- data/lib/yard/lint/validators/documentation/text_substitution/messages_builder.rb +33 -0
- data/lib/yard/lint/validators/documentation/text_substitution/parser.rb +57 -0
- data/lib/yard/lint/validators/documentation/text_substitution/result.rb +24 -0
- data/lib/yard/lint/validators/documentation/text_substitution/validator.rb +72 -0
- data/lib/yard/lint/validators/documentation/text_substitution.rb +55 -0
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/config.rb +2 -1
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/validator.rb +1 -0
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments/config.rb +3 -1
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments/validator.rb +3 -1
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +1 -2
- data/lib/yard/lint/validators/documentation/undocumented_objects/config.rb +2 -1
- data/lib/yard/lint/validators/documentation/undocumented_objects/validator.rb +1 -0
- data/lib/yard/lint/validators/documentation/undocumented_options/config.rb +2 -1
- data/lib/yard/lint/validators/documentation/undocumented_options/validator.rb +1 -0
- data/lib/yard/lint/validators/documentation/undocumented_options.rb +1 -2
- data/lib/yard/lint/validators/tags/api_tags/result.rb +1 -0
- data/lib/yard/lint/validators/tags/api_tags/validator.rb +1 -1
- data/lib/yard/lint/validators/tags/example_style/result.rb +1 -0
- data/lib/yard/lint/validators/tags/example_syntax/result.rb +1 -0
- data/lib/yard/lint/validators/tags/invalid_types/validator.rb +1 -1
- data/lib/yard/lint/validators/tags/meaningless_tag/validator.rb +1 -1
- data/lib/yard/lint/validators/tags/missing_yield/config.rb +20 -0
- data/lib/yard/lint/validators/tags/missing_yield/messages_builder.rb +22 -0
- data/lib/yard/lint/validators/tags/missing_yield/parser.rb +39 -0
- data/lib/yard/lint/validators/tags/missing_yield/result.rb +24 -0
- data/lib/yard/lint/validators/tags/missing_yield/validator.rb +76 -0
- data/lib/yard/lint/validators/tags/missing_yield.rb +56 -0
- data/lib/yard/lint/validators/tags/redundant_param_description/validator.rb +4 -2
- data/lib/yard/lint/validators/tags/redundant_param_description.rb +2 -1
- data/lib/yard/lint/validators/tags/type_syntax.rb +1 -2
- data/lib/yard/lint/validators/warnings/duplicated_parameter_name.rb +1 -2
- data/lib/yard/lint/validators/warnings/invalid_directive_format.rb +1 -2
- data/lib/yard/lint/version.rb +1 -1
- data/lib/yard/lint.rb +14 -4
- metadata +25 -1
|
@@ -40,21 +40,34 @@ Documentation/UndocumentedObjects:
|
|
|
40
40
|
ExcludedMethods:
|
|
41
41
|
- 'initialize/0' # Exclude parameter-less initialize
|
|
42
42
|
- '/^_/' # Exclude private methods (by convention)
|
|
43
|
+
# AllowedParentClasses:
|
|
44
|
+
# - StandardError # Skip error subclasses
|
|
45
|
+
# - ApplicationRecord # Skip Rails model subclasses
|
|
43
46
|
|
|
44
47
|
Documentation/UndocumentedMethodArguments:
|
|
45
48
|
Description: 'Checks for method parameters without @param tags.'
|
|
46
49
|
Enabled: true
|
|
47
50
|
Severity: error
|
|
51
|
+
# AllowedParentClasses:
|
|
52
|
+
# - StandardError
|
|
53
|
+
# AllowedMethods: skip @param checks for specific methods (exact name, name/arity, /regex/)
|
|
54
|
+
# - call
|
|
55
|
+
# - perform
|
|
56
|
+
# - initialize/1
|
|
48
57
|
|
|
49
58
|
Documentation/UndocumentedBooleanMethods:
|
|
50
59
|
Description: 'Checks that question mark methods document their boolean return.'
|
|
51
60
|
Enabled: true
|
|
52
61
|
Severity: error
|
|
62
|
+
# AllowedParentClasses:
|
|
63
|
+
# - StandardError
|
|
53
64
|
|
|
54
65
|
Documentation/UndocumentedOptions:
|
|
55
66
|
Description: 'Detects methods with options hash parameters but no @option tags.'
|
|
56
67
|
Enabled: true
|
|
57
68
|
Severity: error
|
|
69
|
+
# AllowedParentClasses:
|
|
70
|
+
# - StandardError
|
|
58
71
|
|
|
59
72
|
Documentation/MissingReturn:
|
|
60
73
|
Description: 'Requires @return tags on all methods (opt-in for strict documentation).'
|
|
@@ -63,6 +76,13 @@ Documentation/MissingReturn:
|
|
|
63
76
|
ExcludedMethods:
|
|
64
77
|
- 'initialize' # Exclude all initialize methods
|
|
65
78
|
# - '/^_/' # Uncomment to exclude private methods (by convention)
|
|
79
|
+
# AllowedParentClasses:
|
|
80
|
+
# - StandardError
|
|
81
|
+
|
|
82
|
+
Documentation/OrphanedDocComment:
|
|
83
|
+
Description: 'Detects YARD comment blocks with tags not attached to any documentable construct.'
|
|
84
|
+
Enabled: true # Enabled in strict mode
|
|
85
|
+
Severity: error
|
|
66
86
|
|
|
67
87
|
Documentation/MarkdownSyntax:
|
|
68
88
|
Description: 'Detects common markdown syntax errors in documentation.'
|
|
@@ -86,6 +106,20 @@ Documentation/BlankLineBeforeDefinition:
|
|
|
86
106
|
SingleBlankLine: true
|
|
87
107
|
OrphanedDocs: true
|
|
88
108
|
|
|
109
|
+
Documentation/LineLength:
|
|
110
|
+
Description: 'Detects documentation comment lines that exceed the configured maximum length.'
|
|
111
|
+
Enabled: false # Opt-in validator
|
|
112
|
+
Severity: error
|
|
113
|
+
MaxLength: 120
|
|
114
|
+
|
|
115
|
+
Documentation/TextSubstitution:
|
|
116
|
+
Description: 'Detects forbidden characters or strings in documentation and suggests replacements.'
|
|
117
|
+
Enabled: true
|
|
118
|
+
Severity: warning
|
|
119
|
+
Substitutions:
|
|
120
|
+
"—": "-" # em-dash (U+2014)
|
|
121
|
+
"–": "-" # en-dash (U+2013)
|
|
122
|
+
|
|
89
123
|
# Tags validators
|
|
90
124
|
Tags/Order:
|
|
91
125
|
Description: 'Enforces consistent ordering of YARD tags.'
|
|
@@ -135,6 +169,11 @@ Tags/MeaninglessTag:
|
|
|
135
169
|
- module
|
|
136
170
|
- constant
|
|
137
171
|
|
|
172
|
+
Tags/MissingYield:
|
|
173
|
+
Description: 'Detects methods that yield to a block without a @yield, @yieldparam, or @yieldreturn tag.'
|
|
174
|
+
Enabled: true # Enabled in strict mode
|
|
175
|
+
Severity: error
|
|
176
|
+
|
|
138
177
|
Tags/CollectionType:
|
|
139
178
|
Description: 'Validates Hash collection syntax consistency.'
|
|
140
179
|
Enabled: true
|
|
@@ -81,7 +81,7 @@ module Yard
|
|
|
81
81
|
# Collect tags matching the given tag names from a docstring, including
|
|
82
82
|
# tags nested inside @overload blocks. YARD stores @overload inner tags
|
|
83
83
|
# on the overload's own docstring, so they are invisible to
|
|
84
|
-
# `docstring.tags`
|
|
84
|
+
# `docstring.tags` - this helper traverses them.
|
|
85
85
|
# @param docstring [YARD::Docstring] the docstring to search
|
|
86
86
|
# @param tag_names [Array<String>] tag names to collect (e.g., %w[param return])
|
|
87
87
|
# @return [Array<YARD::Tags::Tag>] matching tags from the docstring and any overloads
|
|
@@ -97,6 +97,112 @@ module Yard
|
|
|
97
97
|
tags
|
|
98
98
|
end
|
|
99
99
|
|
|
100
|
+
# Checks whether the object's enclosing class (or the object itself if it is
|
|
101
|
+
# a class) has a superclass that appears in the validator's AllowedParentClasses
|
|
102
|
+
# configuration list. When true, validators skip the object so that classes
|
|
103
|
+
# inheriting from common base classes (e.g. StandardError, ApplicationRecord)
|
|
104
|
+
# are not flagged.
|
|
105
|
+
#
|
|
106
|
+
# Matching is done on YARD's resolved superclass path, which is always the
|
|
107
|
+
# fully-qualified name (e.g. "ActiveRecord::Base"). Entries in
|
|
108
|
+
# AllowedParentClasses are matched exactly, so callers must use the same form
|
|
109
|
+
# (e.g. "ActiveRecord::Base", not "Base").
|
|
110
|
+
#
|
|
111
|
+
# For method objects the enclosing namespace's superclass is checked. For
|
|
112
|
+
# class objects the class itself is checked. Other object types always return
|
|
113
|
+
# false so that modules and constants are unaffected.
|
|
114
|
+
#
|
|
115
|
+
# @param object [YARD::CodeObjects::Base] the code object to check
|
|
116
|
+
# @return [Boolean] true if the object should be skipped
|
|
117
|
+
def parent_class_allowed?(object)
|
|
118
|
+
allowed = Array(config_or_default('AllowedParentClasses'))
|
|
119
|
+
return false if allowed.empty?
|
|
120
|
+
|
|
121
|
+
klasses = case object.type
|
|
122
|
+
when :class
|
|
123
|
+
# Check the class's own superclass; also check the enclosing class's
|
|
124
|
+
# superclass so that nested classes and constants inside an allowed
|
|
125
|
+
# parent class are exempted as well.
|
|
126
|
+
[object, object.namespace].select { |k| k.respond_to?(:superclass) }
|
|
127
|
+
when :method, :constant
|
|
128
|
+
ns = object.namespace
|
|
129
|
+
ns.respond_to?(:superclass) ? [ns] : []
|
|
130
|
+
else
|
|
131
|
+
[]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
return false if klasses.empty?
|
|
135
|
+
|
|
136
|
+
klasses.any? do |klass|
|
|
137
|
+
superclass = klass.superclass
|
|
138
|
+
next false if superclass.nil?
|
|
139
|
+
|
|
140
|
+
superclass_path = superclass.respond_to?(:path) ? superclass.path.to_s : superclass.to_s
|
|
141
|
+
next false if superclass_path.empty?
|
|
142
|
+
# Every Ruby class without an explicit parent implicitly inherits from Object,
|
|
143
|
+
# so matching it would exempt all classes — never the intent.
|
|
144
|
+
# BasicObject is the root of the hierarchy and is guarded for the same reason.
|
|
145
|
+
next false if superclass_path == 'Object' || superclass_path == 'BasicObject'
|
|
146
|
+
|
|
147
|
+
allowed.any? { |a| superclass_path == a.to_s }
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Checks whether the method object's name matches any entry in the validator's
|
|
152
|
+
# AllowedMethods configuration list. When true, the validator should skip the
|
|
153
|
+
# object without reporting an offense.
|
|
154
|
+
#
|
|
155
|
+
# Three pattern forms are supported (matching ExcludedMethods convention):
|
|
156
|
+
# - Exact name: 'call' — matches any arity
|
|
157
|
+
# - Arity: 'initialize/1' — matches only the given parameter count
|
|
158
|
+
# (required + optional, excluding * and &)
|
|
159
|
+
# - Regex: '/^perform/' — matches against the bare method name
|
|
160
|
+
#
|
|
161
|
+
# Invalid regex patterns are silently ignored. The empty regex '//' is always
|
|
162
|
+
# rejected (it would match every method, making the option useless).
|
|
163
|
+
#
|
|
164
|
+
# @param object [YARD::CodeObjects::Base] the code object to check
|
|
165
|
+
# @return [Boolean] true if the method should be skipped
|
|
166
|
+
def method_allowed?(object)
|
|
167
|
+
return false unless object.type == :method
|
|
168
|
+
|
|
169
|
+
allowed = Array(config_or_default('AllowedMethods'))
|
|
170
|
+
.compact
|
|
171
|
+
.map { |p| p.to_s.strip }
|
|
172
|
+
.reject(&:empty?)
|
|
173
|
+
.reject { |p| p == '//' }
|
|
174
|
+
return false if allowed.empty?
|
|
175
|
+
|
|
176
|
+
method_name = object.name.to_s
|
|
177
|
+
arity = object.parameters.reject { |p| p[0].to_s.start_with?('*', '&') }.size
|
|
178
|
+
|
|
179
|
+
allowed.any? { |pattern| matches_method_pattern?(method_name, arity, pattern) }
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Matches a single AllowedMethods/ExcludedMethods pattern against a method.
|
|
183
|
+
# @param method_name [String] bare method name (e.g. "call")
|
|
184
|
+
# @param arity [Integer] parameter count (required + optional, no * or &)
|
|
185
|
+
# @param pattern [String] one entry from the AllowedMethods list
|
|
186
|
+
# @return [Boolean]
|
|
187
|
+
def matches_method_pattern?(method_name, arity, pattern)
|
|
188
|
+
case pattern
|
|
189
|
+
when %r{^/(.+)/$}
|
|
190
|
+
regex_str = Regexp.last_match(1)
|
|
191
|
+
return false if regex_str.empty?
|
|
192
|
+
|
|
193
|
+
begin
|
|
194
|
+
Regexp.new(regex_str).match?(method_name)
|
|
195
|
+
rescue RegexpError
|
|
196
|
+
false
|
|
197
|
+
end
|
|
198
|
+
when %r{^[^/]+/\d+$}
|
|
199
|
+
pattern_name, pattern_arity_str = pattern.split('/', 2)
|
|
200
|
+
method_name == pattern_name && arity == pattern_arity_str.to_i
|
|
201
|
+
else
|
|
202
|
+
method_name == pattern
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
100
206
|
# Retrieves configuration value with fallback to default
|
|
101
207
|
# Automatically determines the validator name from the class namespace
|
|
102
208
|
#
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module LineLength
|
|
8
|
+
# Configuration for LineLength validator
|
|
9
|
+
class Config < ::Yard::Lint::Validators::Config
|
|
10
|
+
self.id = :line_length
|
|
11
|
+
self.defaults = {
|
|
12
|
+
'Enabled' => false,
|
|
13
|
+
'Severity' => 'convention',
|
|
14
|
+
'MaxLength' => 120
|
|
15
|
+
}.freeze
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module LineLength
|
|
8
|
+
# Builds human-readable messages for LineLength violations.
|
|
9
|
+
class MessagesBuilder
|
|
10
|
+
class << self
|
|
11
|
+
# @param offense [Hash] offense details with :length, :object_name, and config :max_length
|
|
12
|
+
# @return [String] formatted message
|
|
13
|
+
def call(offense)
|
|
14
|
+
length = offense[:length]
|
|
15
|
+
object_name = offense[:object_name]
|
|
16
|
+
max_length = offense[:max_length]
|
|
17
|
+
|
|
18
|
+
"Documentation line is too long [#{length} > #{max_length}] for '#{object_name}'"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module LineLength
|
|
8
|
+
# Parses LineLength validator output into structured violation hashes.
|
|
9
|
+
#
|
|
10
|
+
# Expected format (two lines per object with violations):
|
|
11
|
+
# file.rb:OBJECT_LINE: ObjectName
|
|
12
|
+
# MAX_LENGTH|LINE_NO:LENGTH|LINE_NO:LENGTH|...
|
|
13
|
+
class Parser < Parsers::Base
|
|
14
|
+
# @param output [String] raw validator output
|
|
15
|
+
# @return [Array<Hash>] array of violation hashes
|
|
16
|
+
def call(output, **)
|
|
17
|
+
return [] if output.nil? || output.empty?
|
|
18
|
+
|
|
19
|
+
violations = []
|
|
20
|
+
lines = output.lines.map(&:chomp)
|
|
21
|
+
|
|
22
|
+
i = 0
|
|
23
|
+
while i < lines.size
|
|
24
|
+
line = lines[i]
|
|
25
|
+
|
|
26
|
+
if (location_match = line.match(/^(.+):(\d+): (.+)$/))
|
|
27
|
+
file_path = location_match[1]
|
|
28
|
+
object_line = location_match[2].to_i
|
|
29
|
+
object_name = location_match[3]
|
|
30
|
+
|
|
31
|
+
i += 1
|
|
32
|
+
next unless i < lines.size
|
|
33
|
+
|
|
34
|
+
parts = lines[i].split('|')
|
|
35
|
+
next if parts.empty?
|
|
36
|
+
|
|
37
|
+
# First token is max_length, remainder are line_no:length pairs
|
|
38
|
+
max_length = parts.shift.to_i
|
|
39
|
+
|
|
40
|
+
parts.each do |part|
|
|
41
|
+
line_no, length = part.split(':', 2)
|
|
42
|
+
next unless line_no && length
|
|
43
|
+
|
|
44
|
+
violations << {
|
|
45
|
+
location: file_path,
|
|
46
|
+
line: line_no.to_i,
|
|
47
|
+
object_line: object_line,
|
|
48
|
+
object_name: object_name,
|
|
49
|
+
length: length.to_i,
|
|
50
|
+
max_length: max_length
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
i += 1
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
violations
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module LineLength
|
|
8
|
+
# Result wrapper for LineLength validator.
|
|
9
|
+
class Result < Results::Base
|
|
10
|
+
self.default_severity = 'convention'
|
|
11
|
+
self.offense_type = 'line'
|
|
12
|
+
self.offense_name = 'LineLength'
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
# @param offense [Hash] offense details including :max_length from the validator
|
|
17
|
+
# @return [String] formatted message
|
|
18
|
+
def build_message(offense)
|
|
19
|
+
MessagesBuilder.call(offense)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module LineLength
|
|
8
|
+
# Validates that documentation comment lines do not exceed the configured maximum length.
|
|
9
|
+
#
|
|
10
|
+
# Uses YARD's `docstring.line_range` to locate the exact source lines belonging to
|
|
11
|
+
# each docstring block. This handles block comments, wrapped tag descriptions, and
|
|
12
|
+
# macro expansion correctly — no arithmetic reconstruction needed.
|
|
13
|
+
class Validator < Validators::Base
|
|
14
|
+
in_process visibility: :all
|
|
15
|
+
|
|
16
|
+
# Execute query for a single object during in-process execution.
|
|
17
|
+
# @param object [YARD::CodeObjects::Base] the code object to query
|
|
18
|
+
# @param collector [Executor::ResultCollector] collector for output
|
|
19
|
+
# @return [void]
|
|
20
|
+
def in_process_query(object, collector)
|
|
21
|
+
return unless object.file && File.exist?(object.file)
|
|
22
|
+
return if object.docstring.all.empty?
|
|
23
|
+
|
|
24
|
+
line_range = object.docstring.line_range
|
|
25
|
+
return unless line_range
|
|
26
|
+
|
|
27
|
+
max_length = config_or_default('MaxLength').to_i
|
|
28
|
+
source_lines = cached_lines(object.file)
|
|
29
|
+
|
|
30
|
+
violations = []
|
|
31
|
+
line_range.each do |line_no|
|
|
32
|
+
raw_line = source_lines[line_no - 1].to_s.rstrip
|
|
33
|
+
next if raw_line.length <= max_length
|
|
34
|
+
|
|
35
|
+
violations << "#{line_no}:#{raw_line.length}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
return if violations.empty?
|
|
39
|
+
|
|
40
|
+
collector.puts "#{object.file}:#{object.line}: #{object.title}"
|
|
41
|
+
collector.puts "#{max_length}|#{violations.join('|')}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# Returns the lines of a source file, reading from disk only on the first call
|
|
47
|
+
# for each unique path.
|
|
48
|
+
# @param file [String] absolute path to the source file
|
|
49
|
+
# @return [Array<String>] lines of the file, memoized per path
|
|
50
|
+
def cached_lines(file)
|
|
51
|
+
@file_cache ||= {}
|
|
52
|
+
@file_cache[file] ||= File.readlines(file)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
# LineLength validator
|
|
8
|
+
#
|
|
9
|
+
# Detects documentation comment lines that exceed a configured maximum length.
|
|
10
|
+
# Only checks lines belonging to YARD docstring blocks (comment lines directly
|
|
11
|
+
# above a documentable Ruby construct). YARD's parsed docstring is used to
|
|
12
|
+
# determine which lines belong to a docstring, avoiding fragile backwards-scanning.
|
|
13
|
+
#
|
|
14
|
+
# Disabled by default — enable it and set MaxLength to taste.
|
|
15
|
+
#
|
|
16
|
+
# @example Bad - line exceeds MaxLength (120)
|
|
17
|
+
# # This documentation line is too long and exceeds the configured maximum length.
|
|
18
|
+
# def process(value)
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# @example Good - line within MaxLength
|
|
22
|
+
# # Process the given value.
|
|
23
|
+
# def process(value)
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# ## Configuration
|
|
27
|
+
#
|
|
28
|
+
# To enable with a custom limit:
|
|
29
|
+
#
|
|
30
|
+
# Documentation/LineLength:
|
|
31
|
+
# Enabled: true
|
|
32
|
+
# MaxLength: 100
|
|
33
|
+
#
|
|
34
|
+
# To disable:
|
|
35
|
+
#
|
|
36
|
+
# Documentation/LineLength:
|
|
37
|
+
# Enabled: false
|
|
38
|
+
module LineLength
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module OrphanedDocComment
|
|
8
|
+
# Configuration for OrphanedDocComment validator
|
|
9
|
+
class Config < ::Yard::Lint::Validators::Config
|
|
10
|
+
self.id = :orphaned_doc_comment
|
|
11
|
+
self.defaults = {
|
|
12
|
+
'Enabled' => true,
|
|
13
|
+
'Severity' => 'warning'
|
|
14
|
+
}.freeze
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module OrphanedDocComment
|
|
8
|
+
# Builds messages for orphaned documentation comment offenses
|
|
9
|
+
class MessagesBuilder
|
|
10
|
+
class << self
|
|
11
|
+
# @param offense [Hash] offense data with :tags key
|
|
12
|
+
# @return [String] formatted message
|
|
13
|
+
def call(offense)
|
|
14
|
+
tags = Array(offense[:tags]).join(', ')
|
|
15
|
+
"Documentation comment with #{tags} is orphaned - YARD will ignore it"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module OrphanedDocComment
|
|
8
|
+
# Parses validator output into structured offense hashes.
|
|
9
|
+
# @example Output format (one line per orphaned block)
|
|
10
|
+
# # "path/to/file.rb:10: @param,@return"
|
|
11
|
+
class Parser < ::Yard::Lint::Parsers::Base
|
|
12
|
+
# @return [Regexp] parses "file:line: @tag1,@tag2" collector output lines
|
|
13
|
+
LINE_REGEX = /^(.+):(\d+): ([@a-z,_]+)$/.freeze
|
|
14
|
+
|
|
15
|
+
# @param validator_output [String] raw validator results string
|
|
16
|
+
# @return [Array<Hash>] array of orphaned comment offense hashes
|
|
17
|
+
def call(validator_output)
|
|
18
|
+
validator_output
|
|
19
|
+
.split("\n")
|
|
20
|
+
.map(&:strip)
|
|
21
|
+
.reject(&:empty?)
|
|
22
|
+
.filter_map do |line|
|
|
23
|
+
match = line.match(LINE_REGEX)
|
|
24
|
+
next unless match
|
|
25
|
+
|
|
26
|
+
{
|
|
27
|
+
location: match[1],
|
|
28
|
+
line: match[2].to_i,
|
|
29
|
+
tags: match[3].split(',')
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module OrphanedDocComment
|
|
8
|
+
# Result object for orphaned documentation comment offenses
|
|
9
|
+
class Result < Results::Base
|
|
10
|
+
self.default_severity = 'warning'
|
|
11
|
+
self.offense_type = 'line'
|
|
12
|
+
self.offense_name = 'OrphanedDocComment'
|
|
13
|
+
|
|
14
|
+
# @param offense [Hash] offense data
|
|
15
|
+
# @return [String] formatted message
|
|
16
|
+
def build_message(offense)
|
|
17
|
+
MessagesBuilder.call(offense)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|