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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -2
  3. data/README.md +165 -7
  4. data/bin/yard-lint +45 -11
  5. data/lib/yard/lint/executor/in_process_registry.rb +38 -12
  6. data/lib/yard/lint/results/base.rb +3 -0
  7. data/lib/yard/lint/runner.rb +4 -2
  8. data/lib/yard/lint/templates/default_config.yml +40 -0
  9. data/lib/yard/lint/templates/strict_config.yml +39 -0
  10. data/lib/yard/lint/validators/base.rb +107 -1
  11. data/lib/yard/lint/validators/documentation/line_length/config.rb +21 -0
  12. data/lib/yard/lint/validators/documentation/line_length/messages_builder.rb +26 -0
  13. data/lib/yard/lint/validators/documentation/line_length/parser.rb +65 -0
  14. data/lib/yard/lint/validators/documentation/line_length/result.rb +26 -0
  15. data/lib/yard/lint/validators/documentation/line_length/validator.rb +59 -0
  16. data/lib/yard/lint/validators/documentation/line_length.rb +43 -0
  17. data/lib/yard/lint/validators/documentation/missing_return/config.rb +2 -1
  18. data/lib/yard/lint/validators/documentation/missing_return/parser.rb +0 -1
  19. data/lib/yard/lint/validators/documentation/missing_return/validator.rb +1 -0
  20. data/lib/yard/lint/validators/documentation/orphaned_doc_comment/config.rb +20 -0
  21. data/lib/yard/lint/validators/documentation/orphaned_doc_comment/messages_builder.rb +23 -0
  22. data/lib/yard/lint/validators/documentation/orphaned_doc_comment/parser.rb +38 -0
  23. data/lib/yard/lint/validators/documentation/orphaned_doc_comment/result.rb +24 -0
  24. data/lib/yard/lint/validators/documentation/orphaned_doc_comment/validator.rb +121 -0
  25. data/lib/yard/lint/validators/documentation/orphaned_doc_comment.rb +53 -0
  26. data/lib/yard/lint/validators/documentation/text_substitution/config.rb +24 -0
  27. data/lib/yard/lint/validators/documentation/text_substitution/messages_builder.rb +33 -0
  28. data/lib/yard/lint/validators/documentation/text_substitution/parser.rb +57 -0
  29. data/lib/yard/lint/validators/documentation/text_substitution/result.rb +24 -0
  30. data/lib/yard/lint/validators/documentation/text_substitution/validator.rb +72 -0
  31. data/lib/yard/lint/validators/documentation/text_substitution.rb +55 -0
  32. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/config.rb +2 -1
  33. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/validator.rb +1 -0
  34. data/lib/yard/lint/validators/documentation/undocumented_method_arguments/config.rb +3 -1
  35. data/lib/yard/lint/validators/documentation/undocumented_method_arguments/validator.rb +3 -1
  36. data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +1 -2
  37. data/lib/yard/lint/validators/documentation/undocumented_objects/config.rb +2 -1
  38. data/lib/yard/lint/validators/documentation/undocumented_objects/validator.rb +1 -0
  39. data/lib/yard/lint/validators/documentation/undocumented_options/config.rb +2 -1
  40. data/lib/yard/lint/validators/documentation/undocumented_options/validator.rb +1 -0
  41. data/lib/yard/lint/validators/documentation/undocumented_options.rb +1 -2
  42. data/lib/yard/lint/validators/tags/api_tags/result.rb +1 -0
  43. data/lib/yard/lint/validators/tags/api_tags/validator.rb +1 -1
  44. data/lib/yard/lint/validators/tags/example_style/result.rb +1 -0
  45. data/lib/yard/lint/validators/tags/example_syntax/result.rb +1 -0
  46. data/lib/yard/lint/validators/tags/invalid_types/validator.rb +1 -1
  47. data/lib/yard/lint/validators/tags/meaningless_tag/validator.rb +1 -1
  48. data/lib/yard/lint/validators/tags/missing_yield/config.rb +20 -0
  49. data/lib/yard/lint/validators/tags/missing_yield/messages_builder.rb +22 -0
  50. data/lib/yard/lint/validators/tags/missing_yield/parser.rb +39 -0
  51. data/lib/yard/lint/validators/tags/missing_yield/result.rb +24 -0
  52. data/lib/yard/lint/validators/tags/missing_yield/validator.rb +76 -0
  53. data/lib/yard/lint/validators/tags/missing_yield.rb +56 -0
  54. data/lib/yard/lint/validators/tags/redundant_param_description/validator.rb +4 -2
  55. data/lib/yard/lint/validators/tags/redundant_param_description.rb +2 -1
  56. data/lib/yard/lint/validators/tags/type_syntax.rb +1 -2
  57. data/lib/yard/lint/validators/warnings/duplicated_parameter_name.rb +1 -2
  58. data/lib/yard/lint/validators/warnings/invalid_directive_format.rb +1 -2
  59. data/lib/yard/lint/version.rb +1 -1
  60. data/lib/yard/lint.rb +14 -4
  61. metadata +25 -1
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module OrphanedDocComment
8
+ # Scans source files for YARD-tagged comment blocks that YARD will silently drop.
9
+ # A comment block is orphaned when it has YARD tags but is immediately followed
10
+ # by a non-documentable statement or sits at end-of-file.
11
+ class Validator < Base
12
+ in_process visibility: :public
13
+
14
+ # Derive known tag names from YARD's own registry so we stay in sync with
15
+ # any tags added by YARD plugins. This avoids false positives from instance
16
+ # variable mentions in comments (e.g. `# @body.each` or `# @result is nil`).
17
+ YARD_TAG_PATTERN = begin
18
+ tags = ::YARD::Tags::Library.labels.keys.map(&:to_s).sort_by(&:length).reverse.join('|')
19
+ /\A\s*#\s*@(#{tags})\b/
20
+ end.freeze
21
+ # @return [Regexp] matches YARD directive lines (@!macro, @!method, etc.)
22
+ YARD_DIRECTIVE_PATTERN = /\A\s*#\s*@!/.freeze
23
+ # Matches method/class/module/attribute/alias definitions (with optional visibility prefix)
24
+ # and constant assignments (uppercase-leading identifier followed by =), both of which
25
+ # YARD tracks and attaches preceding doc comments to.
26
+ # Also matches define_method which YARD handles via a built-in dynamic handler.
27
+ DEFINITION_PATTERN = /
28
+ \A\s*(private\s+|protected\s+|public\s+)?
29
+ (def |class |module |attr_reader|attr_writer|attr_accessor|attr_internal|alias_method\b|alias\b|define_method\b)
30
+ |
31
+ \A\s*[A-Z][A-Za-z0-9_:]*\s*=
32
+ /x.freeze
33
+
34
+ # @param object [YARD::CodeObjects::Base] the code object to query
35
+ # @param collector [Executor::ResultCollector] collector for output
36
+ # @return [void]
37
+ def in_process_query(object, collector)
38
+ return unless object.file && File.exist?(object.file)
39
+
40
+ @scanned_files ||= {}
41
+ return if @scanned_files[object.file]
42
+
43
+ @scanned_files[object.file] = true
44
+ scan_file(object.file, collector)
45
+ end
46
+
47
+ private
48
+
49
+ # @param file [String] absolute path to the source file to scan
50
+ # @param collector [Executor::ResultCollector] collector for output lines
51
+ # @return [void]
52
+ def scan_file(file, collector)
53
+ lines = File.readlines(file, chomp: true)
54
+ i = 0
55
+
56
+ while i < lines.length
57
+ if comment_line?(lines[i])
58
+ block_start = i
59
+ tags = []
60
+
61
+ has_directive = false
62
+ while i < lines.length && comment_line?(lines[i])
63
+ has_directive = true if directive_line?(lines[i])
64
+ tag = extract_yard_tag(lines[i])
65
+ tags << tag if tag
66
+ i += 1
67
+ end
68
+
69
+ # Skip blocks that contain @! directives - they are macro/method definitions,
70
+ # not orphaned doc comments.
71
+ next if has_directive || tags.empty?
72
+
73
+ # Skip trailing blank lines after the comment block
74
+ i += 1 while i < lines.length && lines[i].strip.empty?
75
+
76
+ if i >= lines.length || !definition_line?(lines[i])
77
+ collector.puts "#{file}:#{block_start + 1}: #{tags.uniq.join(',')}"
78
+ end
79
+ else
80
+ i += 1
81
+ end
82
+ end
83
+ end
84
+
85
+ # @param line [String] a raw source line
86
+ # @return [Boolean] true if the line is a Ruby comment (excluding magic comments)
87
+ def comment_line?(line)
88
+ stripped = line.strip
89
+ stripped.start_with?('#') && !magic_comment?(stripped)
90
+ end
91
+
92
+ # @param stripped_line [String] a comment line with leading/trailing whitespace removed
93
+ # @return [Boolean] true if the line is a Ruby magic comment (frozen_string_literal, encoding, etc.)
94
+ def magic_comment?(stripped_line)
95
+ stripped_line.match?(/\A#\s*(frozen[_-]string[_-]literal|encoding|warn[_-]indent|shareable[_-]constant[_-]value)\s*:/i)
96
+ end
97
+
98
+ # @param line [String] a raw source line
99
+ # @return [Boolean] true if the line contains a YARD directive (@!macro, @!method, etc.)
100
+ def directive_line?(line)
101
+ line.match?(YARD_DIRECTIVE_PATTERN)
102
+ end
103
+
104
+ # @param line [String] a raw source line
105
+ # @return [String, nil] the tag string (e.g. "@param") or nil if no known YARD tag found
106
+ def extract_yard_tag(line)
107
+ match = line.match(YARD_TAG_PATTERN)
108
+ "@#{match[1]}" if match
109
+ end
110
+
111
+ # @param line [String] a raw source line
112
+ # @return [Boolean] true if the line starts a YARD-documentable definition
113
+ def definition_line?(line)
114
+ line.match?(DEFINITION_PATTERN)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ # OrphanedDocComment validator
8
+ #
9
+ # Detects YARD documentation comment blocks that contain tags (`@param`,
10
+ # `@return`, etc.) but are not attached to any documentable Ruby construct.
11
+ # YARD silently drops these comments - they never appear in the registry and
12
+ # the documentation is permanently lost.
13
+ #
14
+ # This happens when a YARD-tagged comment is immediately followed by a
15
+ # non-documentable statement (variable assignment, `require`, `include`, etc.)
16
+ # or sits at the end of a file.
17
+ #
18
+ # @note This validator is complementary to `Documentation/BlankLineBeforeDefinition`,
19
+ # which catches doc blocks separated from a `def` by blank lines.
20
+ # `OrphanedDocComment` catches doc blocks that lead to non-definition code entirely.
21
+ #
22
+ # @example Bad - comment before variable assignment
23
+ # # @param name [String] the name
24
+ # # @return [void]
25
+ # MY_CONSTANT = "value"
26
+ #
27
+ # @example Bad - comment before require
28
+ # # @param name [String] the name
29
+ # require "some_gem"
30
+ #
31
+ # @example Bad - comment at end of file
32
+ # # @param name [String] the name
33
+ # # @return [void]
34
+ # # (EOF)
35
+ #
36
+ # @example Good - comment before def
37
+ # # @param name [String] the name
38
+ # # @return [void]
39
+ # def process(name)
40
+ # end
41
+ #
42
+ # ## Configuration
43
+ #
44
+ # To disable this validator:
45
+ #
46
+ # Documentation/OrphanedDocComment:
47
+ # Enabled: false
48
+ module OrphanedDocComment
49
+ end
50
+ end
51
+ end
52
+ end
53
+ 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 TextSubstitution
8
+ # Configuration for the TextSubstitution validator
9
+ class Config < ::Yard::Lint::Validators::Config
10
+ self.id = :text_substitution
11
+ self.defaults = {
12
+ 'Enabled' => false,
13
+ 'Severity' => 'warning',
14
+ 'Substitutions' => {
15
+ "—" => '-', # em-dash (—)
16
+ "–" => '-' # en-dash (–)
17
+ }
18
+ }.freeze
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module TextSubstitution
8
+ # Builds human-readable messages for TextSubstitution violations.
9
+ class MessagesBuilder
10
+ class << self
11
+ # @param offense [Hash] offense details with :forbidden, :replacement, :line_text keys
12
+ # @return [String] formatted message
13
+ def call(offense)
14
+ forbidden = offense[:forbidden]
15
+ replacement = offense[:replacement]
16
+ line_text = offense[:line_text]
17
+
18
+ message = "Replace '#{forbidden}' with '#{replacement}' in documentation"
19
+
20
+ if line_text && !line_text.empty?
21
+ truncated = line_text.length > 60 ? "#{line_text[0, 57]}..." : line_text
22
+ message += ". Found: \"#{truncated}\""
23
+ end
24
+
25
+ message
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module TextSubstitution
8
+ # Parses TextSubstitution validator output into structured violation hashes.
9
+ #
10
+ # Wire format (four lines per violation):
11
+ # file.rb:LINE: ObjectTitle
12
+ # forbidden
13
+ # replacement
14
+ # line_offset|line_text
15
+ #
16
+ # forbidden and replacement occupy their own lines so they may contain
17
+ # any character, including the pipe delimiter used in the last field.
18
+ class Parser < ::Yard::Lint::Parsers::Base
19
+ # @param yard_output [String] raw collector output from the validator
20
+ # @option _kwargs [Object] :unused accepts no options (reserved for future use)
21
+ # @return [Array<Hash>] array with violation details
22
+ def call(yard_output, **_kwargs)
23
+ return [] if yard_output.nil? || yard_output.strip.empty?
24
+
25
+ # Do not strip lines — forbidden/replacement may have significant whitespace
26
+ lines = yard_output.split("\n")
27
+ violations = []
28
+
29
+ lines.each_slice(4) do |location_line, forbidden_line, replacement_line, details_line|
30
+ next unless location_line && forbidden_line && replacement_line && details_line
31
+
32
+ location_match = location_line.strip.match(/^(.+):(\d+): (.+)$/)
33
+ next unless location_match
34
+
35
+ # line_offset is always numeric; split on first pipe only so line_text may contain pipes
36
+ line_offset_str, line_text = details_line.split('|', 2)
37
+ next unless line_offset_str
38
+
39
+ violations << {
40
+ location: location_match[1],
41
+ line: location_match[2].to_i,
42
+ object_name: location_match[3],
43
+ forbidden: forbidden_line,
44
+ replacement: replacement_line,
45
+ line_offset: line_offset_str.to_i,
46
+ line_text: line_text || ''
47
+ }
48
+ end
49
+
50
+ violations
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ 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 TextSubstitution
8
+ # Result wrapper for TextSubstitution violations
9
+ class Result < Results::Base
10
+ self.default_severity = 'warning'
11
+ self.offense_type = 'line'
12
+ self.offense_name = 'TextSubstitution'
13
+
14
+ # @param offense [Hash]
15
+ # @return [String]
16
+ def build_message(offense)
17
+ MessagesBuilder.call(offense)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module TextSubstitution
8
+ # Validates that documentation does not contain forbidden strings.
9
+ # Reports every matching substitution rule on every line independently.
10
+ class Validator < Base
11
+ in_process visibility: :public
12
+
13
+ # @param object [YARD::CodeObjects::Base]
14
+ # @param collector [Executor::ResultCollector]
15
+ # @return [void]
16
+ def in_process_query(object, collector)
17
+ docstring_text = object.docstring.to_s
18
+ return if docstring_text.empty?
19
+
20
+ substitutions = config_or_default('Substitutions')
21
+ return if substitutions.nil? || substitutions.empty?
22
+
23
+ violations = find_violations(docstring_text, substitutions)
24
+ return if violations.empty?
25
+
26
+ violations.each do |violation|
27
+ collector.puts "#{object.file}:#{object.line}: #{object.title}"
28
+ collector.puts violation[:forbidden]
29
+ collector.puts violation[:replacement]
30
+ collector.puts "#{violation[:line_offset]}|#{violation[:line_text]}"
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ # Scans each line of a docstring for forbidden strings, skipping fenced code blocks.
37
+ # @param docstring_text [String] full docstring text to scan
38
+ # @param substitutions [Hash{String => String}] map of forbidden string to replacement
39
+ # @return [Array<Hash>] list of violations with forbidden, replacement, line_offset, line_text
40
+ def find_violations(docstring_text, substitutions)
41
+ violations = []
42
+ in_code_block = false
43
+
44
+ docstring_text.lines.each_with_index do |line, line_offset|
45
+ if line.strip.start_with?('```')
46
+ in_code_block = !in_code_block
47
+ next
48
+ end
49
+ next if in_code_block
50
+
51
+ substitutions.each do |forbidden, replacement|
52
+ next if forbidden.nil? || forbidden.empty?
53
+ next if replacement.nil? || replacement.empty?
54
+ next unless line.include?(forbidden)
55
+
56
+ violations << {
57
+ forbidden: forbidden,
58
+ replacement: replacement,
59
+ line_offset: line_offset,
60
+ line_text: line.strip
61
+ }
62
+ end
63
+ end
64
+
65
+ violations
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ # TextSubstitution validator
8
+ #
9
+ # Detects forbidden characters or strings in YARD documentation comments
10
+ # and suggests replacements. The primary use case is detecting AI-generated
11
+ # em-dashes (—, U+2014) and en-dashes (–, U+2013) where a plain hyphen (-)
12
+ # is preferred, but any string-to-string substitution rule can be configured.
13
+ #
14
+ # All substitution rules are checked on every line — multiple violations can
15
+ # be reported for the same line when more than one forbidden string appears.
16
+ # Fenced code blocks (``` ... ```) are skipped.
17
+ #
18
+ # Disabled by default — enable it and configure Substitutions to taste.
19
+ #
20
+ # @example Bad - em-dash used in documentation
21
+ # # Connects the start — and end of the range.
22
+ # def connect(start, finish)
23
+ # end
24
+ #
25
+ # @example Good - plain hyphen
26
+ # # Connects the start - and end of the range.
27
+ # def connect(start, finish)
28
+ # end
29
+ #
30
+ # ## Configuration
31
+ #
32
+ # Enable with built-in defaults (em-dash and en-dash):
33
+ #
34
+ # Documentation/TextSubstitution:
35
+ # Enabled: true
36
+ #
37
+ # Enable with explicit substitution rules:
38
+ #
39
+ # Documentation/TextSubstitution:
40
+ # Enabled: true
41
+ # Substitutions:
42
+ # "—": "-" # em-dash (U+2014)
43
+ # "–": "-" # en-dash (U+2013)
44
+ # "…": "..." # ellipsis (U+2026)
45
+ #
46
+ # To disable:
47
+ #
48
+ # Documentation/TextSubstitution:
49
+ # Enabled: false
50
+ module TextSubstitution
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -10,7 +10,8 @@ module Yard
10
10
  self.id = :undocumented_boolean_methods
11
11
  self.defaults = {
12
12
  'Enabled' => true,
13
- 'Severity' => 'warning'
13
+ 'Severity' => 'warning',
14
+ 'AllowedParentClasses' => []
14
15
  }.freeze
15
16
  end
16
17
  end
@@ -22,6 +22,7 @@ module Yard
22
22
  # Skip aliases and implicit methods
23
23
  return if object.is_alias?
24
24
  return unless object.is_explicit?
25
+ return if parent_class_allowed?(object)
25
26
  # Only check boolean methods (ending with ?)
26
27
  return unless object.name.to_s.end_with?('?')
27
28
 
@@ -10,7 +10,9 @@ module Yard
10
10
  self.id = :undocumented_method_arguments
11
11
  self.defaults = {
12
12
  'Enabled' => true,
13
- 'Severity' => 'warning'
13
+ 'Severity' => 'warning',
14
+ 'AllowedParentClasses' => [],
15
+ 'AllowedMethods' => []
14
16
  }.freeze
15
17
  end
16
18
  end
@@ -21,7 +21,9 @@ module Yard
21
21
  # Skip aliases and implicit methods
22
22
  return if object.is_alias?
23
23
  return unless object.is_explicit?
24
- # Skip attribute methods (@!attribute directive) — their setter parameter
24
+ return if parent_class_allowed?(object)
25
+ return if method_allowed?(object)
26
+ # Skip attribute methods (@!attribute directive) - their setter parameter
25
27
  # doesn't need explicit @param documentation, matching attr_accessor behavior
26
28
  return if object.is_attribute?
27
29
 
@@ -8,8 +8,7 @@ module Yard
8
8
  #
9
9
  # Ensures that all method parameters are documented with `@param` tags.
10
10
  # This validator checks that every parameter in a method signature has
11
- # a corresponding `@param` documentation tag. This validator is enabled
12
- # by default.
11
+ # a corresponding `@param` documentation tag. This validator is enabled by default.
13
12
  #
14
13
  # @example Bad - Missing @param tags
15
14
  # # Does something with data
@@ -11,7 +11,8 @@ module Yard
11
11
  self.defaults = {
12
12
  'Enabled' => true,
13
13
  'Severity' => 'warning',
14
- 'ExcludedMethods' => ['initialize/0']
14
+ 'ExcludedMethods' => ['initialize/0'],
15
+ 'AllowedParentClasses' => []
15
16
  }.freeze
16
17
  self.combines_with = ['Documentation/UndocumentedBooleanMethods'].freeze
17
18
  end
@@ -16,6 +16,7 @@ module Yard
16
16
  # @param collector [Executor::ResultCollector] collector for output
17
17
  # @return [void]
18
18
  def in_process_query(object, collector)
19
+ return if parent_class_allowed?(object)
19
20
  # Check if docstring is empty
20
21
  return unless object.docstring.all.empty?
21
22
 
@@ -10,7 +10,8 @@ module Yard
10
10
  self.id = :undocumented_options
11
11
  self.defaults = {
12
12
  'Enabled' => true,
13
- 'Severity' => 'warning'
13
+ 'Severity' => 'warning',
14
+ 'AllowedParentClasses' => []
14
15
  }.freeze
15
16
  end
16
17
  end
@@ -18,6 +18,7 @@ module Yard
18
18
  def in_process_query(object, collector)
19
19
  # Only check method objects
20
20
  return unless object.is_a?(YARD::CodeObjects::MethodObject)
21
+ return if parent_class_allowed?(object)
21
22
 
22
23
  params = object.parameters || []
23
24
 
@@ -8,8 +8,7 @@ module Yard
8
8
  #
9
9
  # Checks that options hashes have detailed documentation about their keys.
10
10
  # When a method accepts an options hash parameter, the individual option
11
- # keys should be documented using `@option` tags. This validator is enabled
12
- # by default.
11
+ # keys should be documented using `@option` tags. This validator is enabled by default.
13
12
  #
14
13
  # @example Bad - Options parameter without @option tags
15
14
  # # Configures the service
@@ -28,6 +28,7 @@ module Yard
28
28
  severity: configured_severity,
29
29
  type: self.class.offense_type,
30
30
  name: offense_data[:name] || self.class.offense_name,
31
+ validator: validator_name,
31
32
  message: build_message(offense_data),
32
33
  location: offense_data[:location] || offense_data[:file],
33
34
  location_line: offense_data[:line] || offense_data[:location_line] || 0
@@ -29,7 +29,7 @@ module Yard
29
29
  # @!attribute directives, Struct.new and Data.define generated readers/writers).
30
30
  # These are auto-generated accessors where per-method @api tags either flow
31
31
  # through the declaration's docstring automatically or cannot be attached at
32
- # all for example, YARD's Data.define handler creates getters with only a
32
+ # all - for example, YARD's Data.define handler creates getters with only a
33
33
  # @return tag (hard-replacing their docstring and stripping any inherited @api
34
34
  # from the enclosing class), and @!attribute directives written above a
35
35
  # Data.define constant attach to the enclosing namespace, not the Data class
@@ -28,6 +28,7 @@ module Yard
28
28
  severity: configured_severity,
29
29
  type: self.class.offense_type,
30
30
  name: offense_data[:name] || self.class.offense_name,
31
+ validator: validator_name,
31
32
  message: build_message(offense_data),
32
33
  location: offense_data[:location] || offense_data[:file],
33
34
  location_line: offense_data[:line] || offense_data[:location_line] || 0
@@ -28,6 +28,7 @@ module Yard
28
28
  severity: configured_severity,
29
29
  type: self.class.offense_type,
30
30
  name: offense_data[:name] || self.class.offense_name,
31
+ validator: validator_name,
31
32
  message: build_message(offense_data),
32
33
  location: offense_data[:location] || offense_data[:file],
33
34
  location_line: offense_data[:line] || offense_data[:location_line] || 0
@@ -67,7 +67,7 @@ module Yard
67
67
  # @param type_str [String] the raw type string (e.g., "Array<self>", "Hash{Symbol => String}")
68
68
  # @return [Array<String>] individual type names (e.g., ["Array", "self"], ["Hash", "Symbol", "String"])
69
69
  def extract_type_names(type_str)
70
- type_str.split(/[=><,{}\s()]+/).reject(&:empty?)
70
+ type_str.split(/[=><,{}\s();]+/).reject(&:empty?)
71
71
  end
72
72
 
73
73
  # Check if a type is defined in Ruby runtime or YARD registry
@@ -22,7 +22,7 @@ module Yard
22
22
 
23
23
  return unless invalid_types.include?(object_type)
24
24
 
25
- # @param is meaningful on Struct.new / Data.define constants because
25
+ # The `@param` tag is meaningful on Struct.new / Data.define constants because
26
26
  # Solargraph uses those annotations to type the synthesized accessors.
27
27
  effective_tags = struct_or_data_class?(object) ? tags_to_check - ['param'] : tags_to_check
28
28
  return if effective_tags.empty?
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module MissingYield
8
+ # Configuration for MissingYield validator
9
+ class Config < ::Yard::Lint::Validators::Config
10
+ self.id = :missing_yield
11
+ self.defaults = {
12
+ 'Enabled' => false,
13
+ 'Severity' => 'warning'
14
+ }.freeze
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module MissingYield
8
+ # Builds messages for missing yield tag offenses
9
+ class MessagesBuilder
10
+ class << self
11
+ # @param offense [Hash] offense data with :element key
12
+ # @return [String] formatted message
13
+ def call(offense)
14
+ "Method `#{offense[:element]}` yields to a block but is missing a @yield, @yieldparam, or @yieldreturn tag"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end