yard-lint 1.10.2 → 1.10.3
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 +7 -0
- data/README.md +10 -0
- data/lib/yard/lint/templates/default_config.yml +21 -0
- data/lib/yard/lint/templates/strict_config.yml +21 -0
- data/lib/yard/lint/validators/base.rb +4 -4
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/config.rb +7 -1
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/result.rb +1 -0
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/validator.rb +66 -2
- data/lib/yard/lint/validators/documentation/line_length/validator.rb +1 -1
- data/lib/yard/lint/validators/documentation/line_length.rb +1 -1
- data/lib/yard/lint/validators/documentation/orphaned_doc_comment/validator.rb +6 -0
- data/lib/yard/lint/validators/documentation/text_substitution/parser.rb +1 -1
- data/lib/yard/lint/validators/documentation/text_substitution.rb +6 -6
- data/lib/yard/lint/validators/documentation/undocumented_objects/config.rb +1 -0
- data/lib/yard/lint/validators/documentation/undocumented_objects/parser.rb +48 -4
- data/lib/yard/lint/validators/documentation/undocumented_objects.rb +29 -1
- data/lib/yard/lint/validators/tags/tag_separator/config.rb +22 -0
- data/lib/yard/lint/validators/tags/tag_separator/messages_builder.rb +49 -0
- data/lib/yard/lint/validators/tags/tag_separator/parser.rb +60 -0
- data/lib/yard/lint/validators/tags/tag_separator/result.rb +28 -0
- data/lib/yard/lint/validators/tags/tag_separator/validator.rb +141 -0
- data/lib/yard/lint/validators/tags/tag_separator.rb +67 -0
- data/lib/yard/lint/version.rb +1 -1
- data/lib/yard/lint.rb +1 -1
- metadata +9 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b257fdb7ca6477899e3be3411289568fea84f44fbc3e06ae10e0803e236c4443
|
|
4
|
+
data.tar.gz: 44609a51c40daa3f6b9a0a85ea647448768d4ad34bbd4b99358effee51170f19
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4c98789f3b1bfab2c0cfa1fbdf69801adf0d7b942517fb99f7bb6c75ff3a95f050d7561a543b1d0b6e0ea749d6d63e18b0d6efa179c8d0f4e7d07637d825fed
|
|
7
|
+
data.tar.gz: 9bfe860758ef6de5770acfacc2e821661f883491f923d169c7fa32d942c6979f88d68df8f57fc284b461a0e5e5d7661c58200df9c1a3018d5f8a8d50e66149c2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## 1.10.3 (2026-08-05)
|
|
2
|
+
- **[Feature]** Added `Tags/TagSeparator` (opt-in, disabled by default, severity `convention`) - a stricter sibling of `Tags/TagGroupSeparator`. Where `TagGroupSeparator` only inserts a blank line between different tag *groups* (and therefore can never separate two tags of the same type, such as sibling `@param` tags), `TagSeparator` requires a blank line between *every* pair of consecutive tags. Tags listed under the `Exempt` option may immediately follow the previous tag without a blank line - useful for `@option` tags, which document keys of a preceding `@param` hash and read best when clustered directly beneath it. `RequireAfterDescription` additionally requires a blank line between the description and the first tag.
|
|
3
|
+
- **[Feature]** `Documentation/UndocumentedObjects` gained an `ExcludedObjects` option that matches the fully-qualified name of any object - class, module, method, or constant - via exact names or `/regex/` patterns anchored to the full path. This closes a gap where constants could never be excluded: `ExcludedMethods` matches only the trailing method name and deliberately ignores classes/modules/constants (so a pattern like `/cache/` cannot silently suppress a class such as `Memcached`), which left no way to skip a constant like `ATui::Input::KEY_A_Z` or to target a single object by its full path. `ExcludedObjects` (e.g. `'/^ATui::Input::KEY_/'`, `'MyApp::Config::DEFAULTS'`, or arity notation `'MyApp::Api#call/1'`) now handles those cases; `ExcludedMethods` behavior is unchanged (#299).
|
|
4
|
+
- **[Feature]** `Documentation/BlankLineBeforeDefinition` gained an `IgnoredCommentPatterns` option: comment lines matching any configured pattern are not treated as documentation, so a blank line below them is no longer reported as a detached docstring. This resolves false positives on comments that are not docs - commented-out code in a constant block (`# KEY_F12 = ...` then a blank line then `KEY_C_A = ...`), `# FIXME` notes, or section separators - without the linter having to guess which comments are code. Each entry is a `/regex/` (matched as a regular expression) or a plain string (matched as a literal at word boundaries, so `api` does not match inside `rapid`); invalid regexes are skipped. Genuine detached prose docstrings are still reported. Defaults to empty, so existing behaviour is unchanged (#300).
|
|
5
|
+
- **[Bugfix]** `Documentation/BlankLineBeforeDefinition` offenses now carry their validator name. The result builder overrode the shared offense construction but omitted the `validator` field, so the text output rendered the offense as ` : Blank line ...` with an empty validator slot, leaving no way to tell which validator to disable. The name (`Documentation/BlankLineBeforeDefinition`) is now populated like every other validator (#300).
|
|
6
|
+
- **[Bugfix]** `Documentation/OrphanedDocComment` no longer flags a tagged doc comment above a class variable assignment (`@@name = ...`) as orphaned. YARD's `ClassVariableHandler` registers class variables as documentable code objects and attaches the preceding comment to them (a bare `@private`/`@api` tag included), so the comment is not dropped - it was only the validator's definition matcher that recognized constants (`FOO = ...`) but not class variables. Class variable assignments are now recognized as documentable, so `# @private`/`@@logger = ...` internal-marker pairs are left alone. Single instance-variable assignments (`@cached = ...`), which YARD does not document, are still reported.
|
|
7
|
+
|
|
1
8
|
## 1.10.2 (2026-07-24)
|
|
2
9
|
- **[Bugfix]** `Semantic/AbstractMethods` no longer flags an `@abstract` method whose body is `fail NotImplementedError`. `fail` is a built-in alias of `raise` (`Kernel#fail`), so `fail NotImplementedError` is an identical abstract-method guard to `raise NotImplementedError`, but the `AllowedImplementations` patterns are written with `raise` and were matched literally. A leading `fail` keyword is now normalized to `raise` before matching, so both forms are recognized (and any custom `raise`-based `AllowedImplementations` pattern automatically covers its `fail` alias). Identifiers such as `failure` and non-leading `fail(...)` calls are left untouched.
|
|
3
10
|
|
data/README.md
CHANGED
|
@@ -228,9 +228,19 @@ Documentation/UndocumentedObjects:
|
|
|
228
228
|
Description: 'Checks for classes, modules, and methods without documentation.'
|
|
229
229
|
Enabled: true
|
|
230
230
|
Severity: warning
|
|
231
|
+
# Exclude methods by their (unqualified) name, name/arity, or /regex/
|
|
231
232
|
ExcludedMethods:
|
|
232
233
|
- 'initialize/0'
|
|
233
234
|
- '/^_/'
|
|
235
|
+
# Exclude any object (class, module, method, or constant) by its
|
|
236
|
+
# fully-qualified name, full-path name/arity, or a /regex/ matched against
|
|
237
|
+
# the full path. Use this to skip constants (ExcludedMethods never applies to
|
|
238
|
+
# them) or to target a single object without matching same-named objects
|
|
239
|
+
# elsewhere.
|
|
240
|
+
ExcludedObjects:
|
|
241
|
+
- 'MyApp::Config::DEFAULTS' # exact fully-qualified constant
|
|
242
|
+
- '/^MyApp::Keys::KEY_/' # every KEY_* constant under MyApp::Keys
|
|
243
|
+
- 'MyApp::Api#call/1' # only MyApp::Api#call with exactly 1 param
|
|
234
244
|
# Skip classes inheriting from these base classes (exact full-path match)
|
|
235
245
|
AllowedParentClasses:
|
|
236
246
|
- StandardError
|
|
@@ -36,6 +36,13 @@ Documentation/UndocumentedObjects:
|
|
|
36
36
|
ExcludedMethods:
|
|
37
37
|
- 'initialize/0' # Exclude parameter-less initialize
|
|
38
38
|
- '/^_/' # Exclude private methods (by convention)
|
|
39
|
+
# ExcludedObjects matches the fully-qualified name of ANY object (class,
|
|
40
|
+
# module, method, or constant). Unlike ExcludedMethods it can exclude
|
|
41
|
+
# constants and be matched against a full path (exact name, name/arity, or /regex/).
|
|
42
|
+
# ExcludedObjects:
|
|
43
|
+
# - 'MyApp::Config::DEFAULTS' # exact fully-qualified constant
|
|
44
|
+
# - '/^MyApp::Keys::KEY_/' # every KEY_* constant under MyApp::Keys
|
|
45
|
+
# - 'MyApp::Api#call/1' # only MyApp::Api#call with exactly 1 param
|
|
39
46
|
# AllowedParentClasses:
|
|
40
47
|
# - StandardError # Skip error subclasses
|
|
41
48
|
# - ApplicationRecord # Skip Rails model subclasses
|
|
@@ -107,6 +114,13 @@ Documentation/BlankLineBeforeDefinition:
|
|
|
107
114
|
EnabledPatterns:
|
|
108
115
|
SingleBlankLine: true
|
|
109
116
|
OrphanedDocs: true
|
|
117
|
+
# Comment lines matching any of these patterns are NOT treated as
|
|
118
|
+
# documentation, so a blank line below them is not reported as a detached
|
|
119
|
+
# docstring. Each entry is a /regex/ or a literal matched at word boundaries.
|
|
120
|
+
# Handy for commented-out code in constant blocks, FIXME notes, separators, etc.
|
|
121
|
+
# IgnoredCommentPatterns:
|
|
122
|
+
# - '/\A#\s*\w[\w:]*\s*=/' # commented-out assignments, e.g. "# KEY = 1"
|
|
123
|
+
# - 'FIXME' # a comment with the word FIXME
|
|
110
124
|
|
|
111
125
|
Documentation/LineLength:
|
|
112
126
|
Description: 'Detects documentation comment lines that exceed the configured maximum length.'
|
|
@@ -346,6 +360,13 @@ Tags/TagGroupSeparator:
|
|
|
346
360
|
yield: [yield, yieldparam, yieldreturn]
|
|
347
361
|
RequireAfterDescription: false
|
|
348
362
|
|
|
363
|
+
Tags/TagSeparator:
|
|
364
|
+
Description: 'Enforces a blank line between every consecutive YARD tag.'
|
|
365
|
+
Enabled: false # Opt-in validator
|
|
366
|
+
Severity: convention
|
|
367
|
+
Exempt: []
|
|
368
|
+
RequireAfterDescription: false
|
|
369
|
+
|
|
349
370
|
Tags/ForbiddenTags:
|
|
350
371
|
Description: 'Detects forbidden tag and type combinations.'
|
|
351
372
|
Enabled: false # Opt-in validator
|
|
@@ -40,6 +40,13 @@ Documentation/UndocumentedObjects:
|
|
|
40
40
|
ExcludedMethods:
|
|
41
41
|
- 'initialize/0' # Exclude parameter-less initialize
|
|
42
42
|
- '/^_/' # Exclude private methods (by convention)
|
|
43
|
+
# ExcludedObjects matches the fully-qualified name of ANY object (class,
|
|
44
|
+
# module, method, or constant). Unlike ExcludedMethods it can exclude
|
|
45
|
+
# constants and be matched against a full path (exact name, name/arity, or /regex/).
|
|
46
|
+
# ExcludedObjects:
|
|
47
|
+
# - 'MyApp::Config::DEFAULTS' # exact fully-qualified constant
|
|
48
|
+
# - '/^MyApp::Keys::KEY_/' # every KEY_* constant under MyApp::Keys
|
|
49
|
+
# - 'MyApp::Api#call/1' # only MyApp::Api#call with exactly 1 param
|
|
43
50
|
# AllowedParentClasses:
|
|
44
51
|
# - StandardError # Skip error subclasses
|
|
45
52
|
# - ApplicationRecord # Skip Rails model subclasses
|
|
@@ -110,6 +117,13 @@ Documentation/BlankLineBeforeDefinition:
|
|
|
110
117
|
EnabledPatterns:
|
|
111
118
|
SingleBlankLine: true
|
|
112
119
|
OrphanedDocs: true
|
|
120
|
+
# Comment lines matching any of these patterns are NOT treated as
|
|
121
|
+
# documentation, so a blank line below them is not reported as a detached
|
|
122
|
+
# docstring. Each entry is a /regex/ or a literal matched at word boundaries.
|
|
123
|
+
# Handy for commented-out code in constant blocks, FIXME notes, separators, etc.
|
|
124
|
+
# IgnoredCommentPatterns:
|
|
125
|
+
# - '/\A#\s*\w[\w:]*\s*=/' # commented-out assignments, e.g. "# KEY = 1"
|
|
126
|
+
# - 'FIXME' # a comment with the word FIXME
|
|
113
127
|
|
|
114
128
|
Documentation/LineLength:
|
|
115
129
|
Description: 'Detects documentation comment lines that exceed the configured maximum length.'
|
|
@@ -344,6 +358,13 @@ Tags/TagGroupSeparator:
|
|
|
344
358
|
yield: [yield, yieldparam, yieldreturn]
|
|
345
359
|
RequireAfterDescription: false
|
|
346
360
|
|
|
361
|
+
Tags/TagSeparator:
|
|
362
|
+
Description: 'Enforces a blank line between every consecutive YARD tag.'
|
|
363
|
+
Enabled: false # Opt-in validator
|
|
364
|
+
Severity: error
|
|
365
|
+
Exempt: []
|
|
366
|
+
RequireAfterDescription: false
|
|
367
|
+
|
|
347
368
|
Tags/ForbiddenTags:
|
|
348
369
|
Description: 'Detects forbidden tag and type combinations.'
|
|
349
370
|
Enabled: false # Opt-in validator
|
|
@@ -186,7 +186,7 @@ module Yard
|
|
|
186
186
|
superclass_path = superclass.respond_to?(:path) ? superclass.path.to_s : superclass.to_s
|
|
187
187
|
next false if superclass_path.empty?
|
|
188
188
|
# Every Ruby class without an explicit parent implicitly inherits from Object,
|
|
189
|
-
# so matching it would exempt all classes
|
|
189
|
+
# so matching it would exempt all classes - never the intent.
|
|
190
190
|
# BasicObject is the root of the hierarchy and is guarded for the same reason.
|
|
191
191
|
next false if superclass_path == 'Object' || superclass_path == 'BasicObject'
|
|
192
192
|
|
|
@@ -199,10 +199,10 @@ module Yard
|
|
|
199
199
|
# object without reporting an offense.
|
|
200
200
|
#
|
|
201
201
|
# Three pattern forms are supported (matching ExcludedMethods convention):
|
|
202
|
-
# - Exact name: 'call'
|
|
203
|
-
# - Arity: 'initialize/1'
|
|
202
|
+
# - Exact name: 'call' - matches any arity
|
|
203
|
+
# - Arity: 'initialize/1' - matches only the given parameter count
|
|
204
204
|
# (required + optional, excluding * and &)
|
|
205
|
-
# - Regex: '/^perform/'
|
|
205
|
+
# - Regex: '/^perform/' - matches against the bare method name
|
|
206
206
|
#
|
|
207
207
|
# Invalid regex patterns are silently ignored. The empty regex '//' is always
|
|
208
208
|
# rejected (it would match every method, making the option useless).
|
|
@@ -15,7 +15,13 @@ module Yard
|
|
|
15
15
|
'EnabledPatterns' => {
|
|
16
16
|
'SingleBlankLine' => true,
|
|
17
17
|
'OrphanedDocs' => true
|
|
18
|
-
}
|
|
18
|
+
},
|
|
19
|
+
# Comment lines matching any of these patterns are not treated as
|
|
20
|
+
# documentation, so a blank line below them is not reported as a
|
|
21
|
+
# detached docstring. Each entry is a `/regex/` or a literal matched
|
|
22
|
+
# at word boundaries. Useful for commented-out code, `# FIXME`
|
|
23
|
+
# notes, etc.
|
|
24
|
+
'IgnoredCommentPatterns' => []
|
|
19
25
|
}.freeze
|
|
20
26
|
end
|
|
21
27
|
end
|
|
@@ -30,6 +30,7 @@ module Yard
|
|
|
30
30
|
severity: severity,
|
|
31
31
|
type: self.class.offense_type,
|
|
32
32
|
name: computed_offense_name,
|
|
33
|
+
validator: validator_name,
|
|
33
34
|
message: build_message(offense_data),
|
|
34
35
|
location: offense_data[:location] || offense_data[:file],
|
|
35
36
|
location_line: offense_data[:line] || offense_data[:location_line] || 0
|
|
@@ -136,7 +136,8 @@ module Yard
|
|
|
136
136
|
end
|
|
137
137
|
|
|
138
138
|
# Check if a comment line is not YARD documentation (magic comment,
|
|
139
|
-
# shebang, tool sigil/directive,
|
|
139
|
+
# shebang, tool sigil/directive, a bare `#` separator, or a line the
|
|
140
|
+
# user has excluded via `IgnoredCommentPatterns`).
|
|
140
141
|
# @param line [String] stripped comment line
|
|
141
142
|
# @return [Boolean] true if the line should not count as documentation
|
|
142
143
|
def non_documentation_comment?(line)
|
|
@@ -144,7 +145,70 @@ module Yard
|
|
|
144
145
|
line.start_with?('#!') || # shebang
|
|
145
146
|
line.match?(/\A#\s*(rubocop|standard):/i) || # linter directives
|
|
146
147
|
line.match?(/\A#\s*typed:/i) || # Sorbet sigil
|
|
147
|
-
line.match?(/\A#+\s*\z/)
|
|
148
|
+
line.match?(/\A#+\s*\z/) || # bare # separator
|
|
149
|
+
ignored_comment?(line) # user-configured exclusions
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Whether the comment line matches one of the user-configured
|
|
153
|
+
# `IgnoredCommentPatterns`. Such a line is not treated as
|
|
154
|
+
# documentation, so it never counts as a docstring detached from the
|
|
155
|
+
# definition below it. This lets a project silence the blank-line
|
|
156
|
+
# offense for comments that are not docs - commented-out code in a
|
|
157
|
+
# constant block, `# FIXME` notes, section separators - without the
|
|
158
|
+
# linter having to guess what is and is not code (see issue #300).
|
|
159
|
+
# @param line [String] stripped comment line
|
|
160
|
+
# @return [Boolean] true if the line matches an ignored pattern
|
|
161
|
+
def ignored_comment?(line)
|
|
162
|
+
ignored_comment_patterns.any? { |pattern| pattern.match?(line) }
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Compiled `IgnoredCommentPatterns` from configuration. Each entry is
|
|
166
|
+
# either a `/regex/` (compiled as a regular expression) or a plain
|
|
167
|
+
# string (matched as a whole word). Blank entries, the empty regex
|
|
168
|
+
# `//`, and invalid regexes are dropped (an empty regex would match
|
|
169
|
+
# every comment, making the option useless - consistent with the
|
|
170
|
+
# other pattern-list options). Memoized for the lifetime of the
|
|
171
|
+
# validator.
|
|
172
|
+
# @return [Array<Regexp>] compiled patterns
|
|
173
|
+
def ignored_comment_patterns
|
|
174
|
+
@ignored_comment_patterns ||=
|
|
175
|
+
Array(config_or_default('IgnoredCommentPatterns'))
|
|
176
|
+
.compact
|
|
177
|
+
.map { |pattern| pattern.to_s.strip }
|
|
178
|
+
.reject(&:empty?)
|
|
179
|
+
.reject { |pattern| pattern == '//' }
|
|
180
|
+
.filter_map { |pattern| compile_comment_pattern(pattern) }
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Compile a single `IgnoredCommentPatterns` entry into a Regexp.
|
|
184
|
+
# A `/.../` entry is a regular expression. Anything else is a literal
|
|
185
|
+
# matched at word boundaries, so a fragment like `api` does not match
|
|
186
|
+
# inside `rapid` (which would silently suppress a real docstring); use
|
|
187
|
+
# a `/regex/` entry for substring or case-insensitive matching. An
|
|
188
|
+
# invalid regex is skipped (returns nil) rather than raising; the empty
|
|
189
|
+
# regex `//` is filtered out before it reaches here.
|
|
190
|
+
# @param pattern [String] one configured pattern
|
|
191
|
+
# @return [Regexp, nil]
|
|
192
|
+
def compile_comment_pattern(pattern)
|
|
193
|
+
if (match = pattern.match(%r{\A/(.+)/\z}))
|
|
194
|
+
Regexp.new(match[1])
|
|
195
|
+
else
|
|
196
|
+
Regexp.new(word_bounded_literal(pattern))
|
|
197
|
+
end
|
|
198
|
+
rescue RegexpError
|
|
199
|
+
nil
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Escape a literal pattern and anchor it at word boundaries, but only
|
|
203
|
+
# on a side that ends in a word character so punctuation-only patterns
|
|
204
|
+
# (e.g. `...` section separators) still match.
|
|
205
|
+
# @param pattern [String] the literal pattern
|
|
206
|
+
# @return [String] escaped, boundary-anchored regex source
|
|
207
|
+
def word_bounded_literal(pattern)
|
|
208
|
+
escaped = Regexp.escape(pattern)
|
|
209
|
+
escaped = "\\b#{escaped}" if pattern.match?(/\A\w/)
|
|
210
|
+
escaped = "#{escaped}\\b" if pattern.match?(/\w\z/)
|
|
211
|
+
escaped
|
|
148
212
|
end
|
|
149
213
|
|
|
150
214
|
# Check if the given pattern is enabled in configuration
|
|
@@ -9,7 +9,7 @@ module Yard
|
|
|
9
9
|
#
|
|
10
10
|
# Uses YARD's `docstring.line_range` to locate the exact source lines belonging to
|
|
11
11
|
# each docstring block. This handles block comments, wrapped tag descriptions, and
|
|
12
|
-
# macro expansion correctly
|
|
12
|
+
# macro expansion correctly - no arithmetic reconstruction needed.
|
|
13
13
|
class Validator < Validators::Base
|
|
14
14
|
in_process visibility: :all
|
|
15
15
|
|
|
@@ -11,7 +11,7 @@ module Yard
|
|
|
11
11
|
# above a documentable Ruby construct). YARD's parsed docstring is used to
|
|
12
12
|
# determine which lines belong to a docstring, avoiding fragile backwards-scanning.
|
|
13
13
|
#
|
|
14
|
-
# Disabled by default
|
|
14
|
+
# Disabled by default - enable it and set MaxLength to taste.
|
|
15
15
|
#
|
|
16
16
|
# @example Bad - line exceeds MaxLength (120)
|
|
17
17
|
# # This documentation line is too long and exceeds the configured maximum length.
|
|
@@ -29,12 +29,18 @@ module Yard
|
|
|
29
29
|
# Also matches define_method which YARD handles via a built-in dynamic handler.
|
|
30
30
|
# `attr\b` is matched after the `attr_*` variants so the bare `attr :name` form
|
|
31
31
|
# (handled by YARD's attribute handler) is recognised without swallowing them.
|
|
32
|
+
# Class variable assignments (`@@name = ...`) are matched too: YARD's
|
|
33
|
+
# ClassVariableHandler registers them as documentable objects and attaches the
|
|
34
|
+
# preceding comment (including a bare `@private`/`@api` tag), so such a comment is
|
|
35
|
+
# not orphaned.
|
|
32
36
|
DEFINITION_PATTERN = /
|
|
33
37
|
\A\s*(private\s+|protected\s+|public\s+)?
|
|
34
38
|
(def |class |module |attr_reader|attr_writer|attr_accessor|attr_internal|attr\b|alias_method\b|alias\b|define_method\b)
|
|
35
39
|
|
|
|
36
40
|
\A\s*[A-Z][A-Za-z0-9_:]*\s*=
|
|
37
41
|
|
|
|
42
|
+
\A\s*@@[A-Za-z_]\w*\s*=
|
|
43
|
+
|
|
|
38
44
|
\A\s*\w+\s+def\b
|
|
39
45
|
/x.freeze
|
|
40
46
|
|
|
@@ -22,7 +22,7 @@ module Yard
|
|
|
22
22
|
def call(yard_output, **_kwargs)
|
|
23
23
|
return [] if yard_output.nil? || yard_output.strip.empty?
|
|
24
24
|
|
|
25
|
-
# Do not strip lines
|
|
25
|
+
# Do not strip lines - forbidden/replacement may have significant whitespace
|
|
26
26
|
lines = yard_output.split("\n")
|
|
27
27
|
violations = []
|
|
28
28
|
|
|
@@ -8,17 +8,17 @@ module Yard
|
|
|
8
8
|
#
|
|
9
9
|
# Detects forbidden characters or strings in YARD documentation comments
|
|
10
10
|
# and suggests replacements. The primary use case is detecting AI-generated
|
|
11
|
-
# em-dashes (
|
|
11
|
+
# em-dashes (-, U+2014) and en-dashes (-, U+2013) where a plain hyphen (-)
|
|
12
12
|
# is preferred, but any string-to-string substitution rule can be configured.
|
|
13
13
|
#
|
|
14
|
-
# All substitution rules are checked on every line
|
|
14
|
+
# All substitution rules are checked on every line - multiple violations can
|
|
15
15
|
# be reported for the same line when more than one forbidden string appears.
|
|
16
16
|
# Fenced code blocks (``` ... ```) are skipped.
|
|
17
17
|
#
|
|
18
|
-
# Disabled by default
|
|
18
|
+
# Disabled by default - enable it and configure Substitutions to taste.
|
|
19
19
|
#
|
|
20
20
|
# @example Bad - em-dash used in documentation
|
|
21
|
-
# # Connects the start
|
|
21
|
+
# # Connects the start - and end of the range.
|
|
22
22
|
# def connect(start, finish)
|
|
23
23
|
# end
|
|
24
24
|
#
|
|
@@ -39,8 +39,8 @@ module Yard
|
|
|
39
39
|
# Documentation/TextSubstitution:
|
|
40
40
|
# Enabled: true
|
|
41
41
|
# Substitutions:
|
|
42
|
-
# "
|
|
43
|
-
# "
|
|
42
|
+
# "-": "-" # em-dash (U+2014)
|
|
43
|
+
# "-": "-" # en-dash (U+2013)
|
|
44
44
|
# "…": "..." # ellipsis (U+2026)
|
|
45
45
|
#
|
|
46
46
|
# To disable:
|
|
@@ -24,11 +24,15 @@ module Yard
|
|
|
24
24
|
'ExcludedMethods'
|
|
25
25
|
) || []
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
excluded_objects = config&.validator_config(
|
|
28
|
+
'Documentation/UndocumentedObjects',
|
|
29
|
+
'ExcludedObjects'
|
|
30
|
+
) || []
|
|
29
31
|
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
+
# Ensure patterns are Arrays and sanitize: remove nil, empty,
|
|
33
|
+
# whitespace-only, and normalize
|
|
34
|
+
excluded_methods = sanitize_patterns(Array(excluded_methods))
|
|
35
|
+
excluded_objects = sanitize_patterns(Array(excluded_objects))
|
|
32
36
|
|
|
33
37
|
yard_list_output
|
|
34
38
|
.split("\n")
|
|
@@ -41,6 +45,14 @@ module Yard
|
|
|
41
45
|
element = match[3]
|
|
42
46
|
arity = match[4]&.to_i
|
|
43
47
|
|
|
48
|
+
# Skip if the fully-qualified object name is excluded. Unlike
|
|
49
|
+
# ExcludedMethods (which matches only the trailing method name
|
|
50
|
+
# and never applies to classes/modules/constants),
|
|
51
|
+
# ExcludedObjects matches the whole element - so it can
|
|
52
|
+
# exclude constants (e.g. "Foo::KEY_A") and lets a regex be
|
|
53
|
+
# anchored to the full path.
|
|
54
|
+
next if object_excluded?(element, arity, excluded_objects)
|
|
55
|
+
|
|
44
56
|
# Skip if method is in excluded list
|
|
45
57
|
next if method_excluded?(element, arity, excluded_methods)
|
|
46
58
|
|
|
@@ -88,6 +100,38 @@ module Yard
|
|
|
88
100
|
end
|
|
89
101
|
end
|
|
90
102
|
|
|
103
|
+
# Checks if an object should be excluded based on ExcludedObjects config.
|
|
104
|
+
# Matches against the fully-qualified object name (the whole element),
|
|
105
|
+
# so it applies to every object type - classes, modules, methods, and
|
|
106
|
+
# constants alike. Supports exact full names, arity notation (for
|
|
107
|
+
# methods), and regex patterns matched against the full path.
|
|
108
|
+
# @param element [String] the fully-qualified object name
|
|
109
|
+
# (e.g. "Foo::Bar#baz", "Foo::Bar", "Foo::KEY_A")
|
|
110
|
+
# @param arity [Integer, nil] number of parameters for a method element
|
|
111
|
+
# (required + optional, excluding splat and block); nil for
|
|
112
|
+
# classes, modules, and constants
|
|
113
|
+
# @param excluded_objects [Array<String>] list of exclusion patterns
|
|
114
|
+
# @return [Boolean] true if the object should be excluded
|
|
115
|
+
def object_excluded?(element, arity, excluded_objects)
|
|
116
|
+
excluded_objects.any? do |pattern|
|
|
117
|
+
case pattern
|
|
118
|
+
when %r{^/(.+)/$}
|
|
119
|
+
# Regex pattern matched against the full path: '/^Foo::KEY_/'
|
|
120
|
+
match_regex_pattern(element, Regexp.last_match(1))
|
|
121
|
+
when %r{/\d+$}
|
|
122
|
+
# Arity pattern on a full path: 'Foo::Bar#baz/1' matches the
|
|
123
|
+
# method Foo::Bar#baz only when it takes exactly one parameter.
|
|
124
|
+
# A full path contains a single '/' (the arity delimiter), so
|
|
125
|
+
# match_arity_pattern splits it correctly. Constants and other
|
|
126
|
+
# objects have a nil arity and never match an arity pattern.
|
|
127
|
+
match_arity_pattern(element, arity, pattern)
|
|
128
|
+
else
|
|
129
|
+
# Exact full-name match: 'Foo::Bar::KEY_A'
|
|
130
|
+
element == pattern
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
91
135
|
# Sanitize exclusion patterns
|
|
92
136
|
# @param patterns [Array] raw patterns from config
|
|
93
137
|
# @return [Array<String>] cleaned and validated patterns
|
|
@@ -8,7 +8,35 @@ module Yard
|
|
|
8
8
|
#
|
|
9
9
|
# Checks for missing documentation on classes, modules, and methods.
|
|
10
10
|
# This validator supports flexible method exclusions through the `ExcludedMethods`
|
|
11
|
-
# configuration option
|
|
11
|
+
# configuration option, and full-object exclusions (including constants)
|
|
12
|
+
# through the `ExcludedObjects` option.
|
|
13
|
+
#
|
|
14
|
+
# ## `ExcludedMethods` vs `ExcludedObjects`
|
|
15
|
+
#
|
|
16
|
+
# `ExcludedMethods` matches only the **unqualified method name** (the part
|
|
17
|
+
# after the final `#`/`.`) and never applies to classes, modules, or
|
|
18
|
+
# constants. This keeps a pattern like `/cache/` from silently suppressing
|
|
19
|
+
# a class such as `Memcached`, but it means constants cannot be excluded and
|
|
20
|
+
# a method cannot be targeted by its full path.
|
|
21
|
+
#
|
|
22
|
+
# `ExcludedObjects` matches the **fully-qualified name** of any object -
|
|
23
|
+
# class, module, method, or constant - and supports exact names, arity
|
|
24
|
+
# notation (for methods), and regex patterns matched against the full path:
|
|
25
|
+
#
|
|
26
|
+
# Documentation/UndocumentedObjects:
|
|
27
|
+
# ExcludedObjects:
|
|
28
|
+
# - 'MyApp::Config::DEFAULTS' # exact fully-qualified constant
|
|
29
|
+
# - '/^MyApp::Keys::KEY_/' # every KEY_* constant under MyApp::Keys
|
|
30
|
+
# - '/^MyApp::Internal#/' # every method on MyApp::Internal
|
|
31
|
+
# - 'MyApp::Api#call/1' # only MyApp::Api#call with exactly 1 param
|
|
32
|
+
#
|
|
33
|
+
# Arity notation follows the same rules as `ExcludedMethods` (see below):
|
|
34
|
+
# the count is `required + optional` parameters, excluding splat (`*`) and
|
|
35
|
+
# block (`&`). Classes, modules, and constants have no arity and never match
|
|
36
|
+
# an arity pattern.
|
|
37
|
+
#
|
|
38
|
+
# Use `ExcludedObjects` when you need to skip constants (issue #299) or to
|
|
39
|
+
# exclude one specific object without affecting same-named objects elsewhere.
|
|
12
40
|
#
|
|
13
41
|
# ## Pattern Types
|
|
14
42
|
#
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module TagSeparator
|
|
8
|
+
# Configuration for TagSeparator validator
|
|
9
|
+
class Config < ::Yard::Lint::Validators::Config
|
|
10
|
+
self.id = :tag_separator
|
|
11
|
+
self.defaults = {
|
|
12
|
+
"Enabled" => false,
|
|
13
|
+
"Severity" => "convention",
|
|
14
|
+
"Exempt" => [],
|
|
15
|
+
"RequireAfterDescription" => false
|
|
16
|
+
}.freeze
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module TagSeparator
|
|
8
|
+
# Builds messages for missing tag separator offenses.
|
|
9
|
+
class MessagesBuilder
|
|
10
|
+
class << self
|
|
11
|
+
# Build message for missing tag separator.
|
|
12
|
+
#
|
|
13
|
+
# @param offense [Hash] offense data with :method_name and :separators keys
|
|
14
|
+
#
|
|
15
|
+
# @return [String] formatted message
|
|
16
|
+
def call(offense)
|
|
17
|
+
transitions = parse_transitions(offense[:separators])
|
|
18
|
+
|
|
19
|
+
if transitions.size == 1
|
|
20
|
+
from, to = transitions.first
|
|
21
|
+
"The `#{offense[:method_name]}` is missing a blank line between the " \
|
|
22
|
+
"`#{from}` and `#{to}` tags."
|
|
23
|
+
else
|
|
24
|
+
formatted = transitions.map { |from, to| "`#{from}` -> `#{to}`" }.join(", ")
|
|
25
|
+
"The `#{offense[:method_name]}` is missing blank lines between tags: " \
|
|
26
|
+
"#{formatted}."
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
# Parses transition string into array of [from, to] pairs.
|
|
33
|
+
#
|
|
34
|
+
# @param separators [String] string in format "from->to,from->to"
|
|
35
|
+
#
|
|
36
|
+
# @return [Array<Array<String>>] array of [from, to] pairs
|
|
37
|
+
def parse_transitions(separators)
|
|
38
|
+
separators
|
|
39
|
+
.to_s
|
|
40
|
+
.split(",")
|
|
41
|
+
.map { |transition| transition.split("->") }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module TagSeparator
|
|
8
|
+
# Parser for extracting tag separator violations from raw validator output.
|
|
9
|
+
#
|
|
10
|
+
# @example Output format (skip-lint)
|
|
11
|
+
# /path/to/file.rb:10: ClassName#method_name
|
|
12
|
+
# param->param,param->return
|
|
13
|
+
class Parser < Parsers::Base
|
|
14
|
+
# Parses raw validator output into structured offense data.
|
|
15
|
+
#
|
|
16
|
+
# @param raw_output [String] raw validator output string
|
|
17
|
+
#
|
|
18
|
+
# @return [Array<Hash>] array of hashes with offense details
|
|
19
|
+
def call(raw_output)
|
|
20
|
+
return [] if raw_output.nil? || raw_output.empty?
|
|
21
|
+
|
|
22
|
+
entries_by_location = {}
|
|
23
|
+
|
|
24
|
+
raw_output.split("\n").each_slice(2).each do |location, separators|
|
|
25
|
+
next if location.nil? || separators.nil?
|
|
26
|
+
|
|
27
|
+
# The location line is already a unique identifier for the
|
|
28
|
+
# documented object (file, line and title); using it directly
|
|
29
|
+
# as the key avoids collisions that a normalizing regexp could
|
|
30
|
+
# accidentally introduce between distinct paths/methods.
|
|
31
|
+
key = location.strip
|
|
32
|
+
|
|
33
|
+
if separators == "valid"
|
|
34
|
+
entries_by_location[key] = "valid"
|
|
35
|
+
else
|
|
36
|
+
entries_by_location[key] ||= [location, separators]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
entries_by_location.delete_if { |_key, value| value == "valid" }
|
|
41
|
+
|
|
42
|
+
location_parser = Validators::Documentation::UndocumentedMethodArguments::Parser.new
|
|
43
|
+
|
|
44
|
+
# Parse each location together with its own separators so that an
|
|
45
|
+
# unparseable location line drops only its own offense instead of
|
|
46
|
+
# shifting the separators of all offenses that follow it
|
|
47
|
+
entries_by_location.values.filter_map do |location, separators|
|
|
48
|
+
offense = location_parser.call(location).first
|
|
49
|
+
next unless offense
|
|
50
|
+
|
|
51
|
+
offense[:separators] = separators
|
|
52
|
+
offense
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module TagSeparator
|
|
8
|
+
# Result object for tag separator validation.
|
|
9
|
+
# Transforms parsed separator violations into offense objects.
|
|
10
|
+
class Result < Results::Base
|
|
11
|
+
self.default_severity = "convention"
|
|
12
|
+
self.offense_type = "method"
|
|
13
|
+
self.offense_name = "MissingTagSeparator"
|
|
14
|
+
|
|
15
|
+
# Build human-readable message for tag separator offense.
|
|
16
|
+
#
|
|
17
|
+
# @param offense [Hash] offense data with :method_name and :separators keys
|
|
18
|
+
#
|
|
19
|
+
# @return [String] formatted message
|
|
20
|
+
def build_message(offense)
|
|
21
|
+
MessagesBuilder.call(offense)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module TagSeparator
|
|
8
|
+
# Validates that a blank line separates every pair of consecutive YARD tags.
|
|
9
|
+
#
|
|
10
|
+
# Unlike {TagGroupSeparator}, which only separates different tag groups,
|
|
11
|
+
# this validator requires a blank line between all consecutive tags,
|
|
12
|
+
# including same-type tags such as sibling @param tags. Tags listed in
|
|
13
|
+
# the Exempt option may immediately follow the previous tag.
|
|
14
|
+
class Validator < Base
|
|
15
|
+
# Enable in-process execution with all visibility
|
|
16
|
+
in_process visibility: :all
|
|
17
|
+
|
|
18
|
+
# Execute query for a single object during in-process execution.
|
|
19
|
+
# Checks if consecutive tags are separated by blank lines.
|
|
20
|
+
#
|
|
21
|
+
# @param object [YARD::CodeObjects::Base] the code object to query
|
|
22
|
+
# @param collector [Executor::ResultCollector] collector for output
|
|
23
|
+
#
|
|
24
|
+
# @return [void]
|
|
25
|
+
def in_process_query(object, collector)
|
|
26
|
+
# is_alias? exists only on method objects; on namespace objects
|
|
27
|
+
# YARD's method_missing raises NameError, so guard by type first
|
|
28
|
+
return if object.type == :method && object.is_alias?
|
|
29
|
+
|
|
30
|
+
docstring = object.docstring.all
|
|
31
|
+
return if docstring.nil? || docstring.empty?
|
|
32
|
+
|
|
33
|
+
transitions = offending_transitions(docstring)
|
|
34
|
+
|
|
35
|
+
collector.puts "#{object.file}:#{object.line}: #{object.title}"
|
|
36
|
+
|
|
37
|
+
if transitions.empty?
|
|
38
|
+
collector.puts "valid"
|
|
39
|
+
else
|
|
40
|
+
collector.puts transitions.map { |transition| "#{transition[:from]}->#{transition[:to]}" }.join(",")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# Find all locations where a blank line separator is missing between tags
|
|
47
|
+
#
|
|
48
|
+
# In YARD, a docstring's free-form description can only appear before the
|
|
49
|
+
# first `@tag`. Once the parser hits a tag, everything that follows
|
|
50
|
+
# (until the next tag) is considered part of that tag's content.
|
|
51
|
+
#
|
|
52
|
+
# A description is optional.
|
|
53
|
+
#
|
|
54
|
+
# A segment is a part of the docstring. It is either the free-form
|
|
55
|
+
# description or a YARD tag.
|
|
56
|
+
#
|
|
57
|
+
# The first segment never results in an offense.
|
|
58
|
+
#
|
|
59
|
+
# @param docstring [String] the raw docstring content
|
|
60
|
+
#
|
|
61
|
+
# @return [Array<Hash>] array of hashes with :from and :to tag names
|
|
62
|
+
def offending_transitions(docstring)
|
|
63
|
+
lines = docstring.split("\n")
|
|
64
|
+
transitions = []
|
|
65
|
+
|
|
66
|
+
previous_segment_name = nil # nil means we are processing the first segment
|
|
67
|
+
|
|
68
|
+
had_blank_line = true
|
|
69
|
+
|
|
70
|
+
lines.each do |line|
|
|
71
|
+
if line.strip.empty?
|
|
72
|
+
had_blank_line = true
|
|
73
|
+
next
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# YARD tags begin at column 0 of the docstring. Indented @-leading
|
|
77
|
+
# lines are tag continuation or @example/code content (e.g. an
|
|
78
|
+
# instance variable like `@result`), not new tags.
|
|
79
|
+
tag_name = line[/\A@(\S+)/, 1]
|
|
80
|
+
|
|
81
|
+
if tag_name
|
|
82
|
+
if offending_transition?(previous_segment_name, had_blank_line, tag_name)
|
|
83
|
+
transitions << {from: previous_segment_name, to: tag_name}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
previous_segment_name = tag_name
|
|
87
|
+
elsif previous_segment_name.nil?
|
|
88
|
+
previous_segment_name = "description"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
had_blank_line = false
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
transitions
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Whether a missing separator should be reported between the previous
|
|
98
|
+
# segment and the current tag
|
|
99
|
+
#
|
|
100
|
+
# @param previous_segment_name [String, nil] the previous tag name, 'description', or nil
|
|
101
|
+
# @param had_blank_line [Boolean] whether a blank line preceded the current tag
|
|
102
|
+
# @param tag_name [String] the current tag name
|
|
103
|
+
#
|
|
104
|
+
# @return [Boolean] whether to report a missing separator
|
|
105
|
+
def offending_transition?(previous_segment_name, had_blank_line, tag_name)
|
|
106
|
+
# The first segment never results in an offense
|
|
107
|
+
return false if previous_segment_name.nil?
|
|
108
|
+
|
|
109
|
+
# If a blank line occurs between the previous segment and the current
|
|
110
|
+
# tag there is no offense
|
|
111
|
+
return false if had_blank_line
|
|
112
|
+
|
|
113
|
+
# In a description->tag transition, exempt does not apply. This
|
|
114
|
+
# transition is only an offense if the RequireAfterDescription option
|
|
115
|
+
# is set.
|
|
116
|
+
return require_after_description? if previous_segment_name == "description"
|
|
117
|
+
|
|
118
|
+
return false if exempt.include?(tag_name)
|
|
119
|
+
|
|
120
|
+
true
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# @return [Array<String>] tag names that may follow without a blank line
|
|
124
|
+
def exempt
|
|
125
|
+
@exempt ||= config.validator_config("Tags/TagSeparator", "Exempt") ||
|
|
126
|
+
Config.defaults["Exempt"]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# @return [Boolean] whether to require separator after description
|
|
130
|
+
def require_after_description?
|
|
131
|
+
@require_after_description ||= config.validator_config(
|
|
132
|
+
"Tags/TagSeparator",
|
|
133
|
+
"RequireAfterDescription"
|
|
134
|
+
) || false
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
# TagSeparator validator
|
|
8
|
+
#
|
|
9
|
+
# Enforces a blank line between *every* pair of consecutive YARD tags,
|
|
10
|
+
# including consecutive tags of the same type (e.g. two `@param` tags).
|
|
11
|
+
# This is stricter than {TagGroupSeparator}, which only separates
|
|
12
|
+
# different tag *groups* and therefore can never separate same-type tags
|
|
13
|
+
# such as sibling `@param` tags. This validator is disabled by default.
|
|
14
|
+
#
|
|
15
|
+
# @example Bad - No blank line between consecutive @param tags
|
|
16
|
+
# # @param organization_id [String] the organization ID
|
|
17
|
+
# # @param id [String] the pet ID
|
|
18
|
+
# # @return [Pet] the pet object
|
|
19
|
+
# def call(organization_id, id)
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# @example Good - Blank line separates every tag
|
|
23
|
+
# # @param organization_id [String] the organization ID
|
|
24
|
+
# #
|
|
25
|
+
# # @param id [String] the pet ID
|
|
26
|
+
# #
|
|
27
|
+
# # @return [Pet] the pet object
|
|
28
|
+
# def call(organization_id, id)
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# ## Configuration
|
|
32
|
+
#
|
|
33
|
+
# To enable this validator:
|
|
34
|
+
#
|
|
35
|
+
# Tags/TagSeparator:
|
|
36
|
+
# Enabled: true
|
|
37
|
+
#
|
|
38
|
+
# To allow certain tags to immediately follow the previous tag without a
|
|
39
|
+
# blank line, list them under `Exempt`. This is useful for `@option`
|
|
40
|
+
# tags, which document keys of a preceding `@param` hash and read best
|
|
41
|
+
# when clustered directly beneath it:
|
|
42
|
+
#
|
|
43
|
+
# Tags/TagSeparator:
|
|
44
|
+
# Enabled: true
|
|
45
|
+
# Exempt:
|
|
46
|
+
# - option
|
|
47
|
+
#
|
|
48
|
+
# With the configuration above, this is valid:
|
|
49
|
+
#
|
|
50
|
+
# # @param opts [Hash] the options
|
|
51
|
+
# # @option opts [String] :name the name
|
|
52
|
+
# # @option opts [Integer] :age the age
|
|
53
|
+
# #
|
|
54
|
+
# # @return [void]
|
|
55
|
+
#
|
|
56
|
+
# To also require a blank line between the description and the first tag,
|
|
57
|
+
# set `RequireAfterDescription`:
|
|
58
|
+
#
|
|
59
|
+
# Tags/TagSeparator:
|
|
60
|
+
# Enabled: true
|
|
61
|
+
# RequireAfterDescription: true
|
|
62
|
+
module TagSeparator
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
data/lib/yard/lint/version.rb
CHANGED
data/lib/yard/lint.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Yard
|
|
|
25
25
|
# - :mode [Symbol] one of :ref, :staged, :changed
|
|
26
26
|
# - :base_ref [String, nil] base ref for :ref mode (auto-detects main/master if nil)
|
|
27
27
|
# @param source [String, nil] in-memory source content; when given, the file is not
|
|
28
|
-
# read from disk
|
|
28
|
+
# read from disk - `path` must be a single .rb file path (used for config/exclusion
|
|
29
29
|
# resolution and offense location reporting only)
|
|
30
30
|
# @return [Yard::Lint::Result] result object with offenses
|
|
31
31
|
def run(path:, config: nil, config_file: nil, progress: nil, diff: nil, source: nil)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yard-lint
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.10.
|
|
4
|
+
version: 1.10.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Maciej Mensfeld
|
|
@@ -247,6 +247,12 @@ files:
|
|
|
247
247
|
- lib/yard/lint/validators/tags/tag_group_separator/parser.rb
|
|
248
248
|
- lib/yard/lint/validators/tags/tag_group_separator/result.rb
|
|
249
249
|
- lib/yard/lint/validators/tags/tag_group_separator/validator.rb
|
|
250
|
+
- lib/yard/lint/validators/tags/tag_separator.rb
|
|
251
|
+
- lib/yard/lint/validators/tags/tag_separator/config.rb
|
|
252
|
+
- lib/yard/lint/validators/tags/tag_separator/messages_builder.rb
|
|
253
|
+
- lib/yard/lint/validators/tags/tag_separator/parser.rb
|
|
254
|
+
- lib/yard/lint/validators/tags/tag_separator/result.rb
|
|
255
|
+
- lib/yard/lint/validators/tags/tag_separator/validator.rb
|
|
250
256
|
- lib/yard/lint/validators/tags/tag_type_position.rb
|
|
251
257
|
- lib/yard/lint/validators/tags/tag_type_position/config.rb
|
|
252
258
|
- lib/yard/lint/validators/tags/tag_type_position/messages_builder.rb
|
|
@@ -304,6 +310,8 @@ metadata:
|
|
|
304
310
|
homepage_uri: https://github.com/mensfeld/yard-lint
|
|
305
311
|
source_code_uri: https://github.com/mensfeld/yard-lint
|
|
306
312
|
changelog_uri: https://github.com/mensfeld/yard-lint/blob/master/CHANGELOG.md
|
|
313
|
+
bug_tracker_uri: https://github.com/mensfeld/yard-lint/issues
|
|
314
|
+
documentation_uri: https://github.com/mensfeld/yard-lint
|
|
307
315
|
rubygems_mfa_required: 'true'
|
|
308
316
|
rdoc_options: []
|
|
309
317
|
require_paths:
|