yard-lint 1.2.2 → 1.3.0.rc1

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +174 -1
  3. data/README.md +118 -3
  4. data/Rakefile +20 -0
  5. data/bin/yard-lint +80 -37
  6. data/lib/yard/lint/config.rb +5 -0
  7. data/lib/yard/lint/config_generator.rb +8 -179
  8. data/lib/yard/lint/config_updater.rb +222 -0
  9. data/lib/yard/lint/errors.rb +6 -0
  10. data/lib/yard/lint/executor/in_process_registry.rb +130 -0
  11. data/lib/yard/lint/executor/query_executor.rb +109 -0
  12. data/lib/yard/lint/executor/result_collector.rb +55 -0
  13. data/lib/yard/lint/executor/warning_dispatcher.rb +79 -0
  14. data/lib/yard/lint/results/base.rb +2 -1
  15. data/lib/yard/lint/runner.rb +88 -35
  16. data/lib/yard/lint/stats_calculator.rb +1 -1
  17. data/lib/yard/lint/templates/default_config.yml +279 -0
  18. data/lib/yard/lint/templates/strict_config.yml +283 -0
  19. data/lib/yard/lint/validators/base.rb +52 -118
  20. data/lib/yard/lint/validators/documentation/blank_line_before_definition/config.rb +25 -0
  21. data/lib/yard/lint/validators/documentation/blank_line_before_definition/messages_builder.rb +39 -0
  22. data/lib/yard/lint/validators/documentation/blank_line_before_definition/parser.rb +59 -0
  23. data/lib/yard/lint/validators/documentation/blank_line_before_definition/result.rb +61 -0
  24. data/lib/yard/lint/validators/documentation/blank_line_before_definition/validator.rb +94 -0
  25. data/lib/yard/lint/validators/documentation/blank_line_before_definition.rb +63 -0
  26. data/lib/yard/lint/validators/documentation/empty_comment_line/config.rb +24 -0
  27. data/lib/yard/lint/validators/documentation/empty_comment_line/messages_builder.rb +34 -0
  28. data/lib/yard/lint/validators/documentation/empty_comment_line/parser.rb +60 -0
  29. data/lib/yard/lint/validators/documentation/empty_comment_line/result.rb +25 -0
  30. data/lib/yard/lint/validators/documentation/empty_comment_line/validator.rb +109 -0
  31. data/lib/yard/lint/validators/documentation/empty_comment_line.rb +58 -0
  32. data/lib/yard/lint/validators/documentation/markdown_syntax/validator.rb +36 -21
  33. data/lib/yard/lint/validators/documentation/markdown_syntax.rb +0 -1
  34. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/validator.rb +19 -29
  35. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods.rb +0 -1
  36. data/lib/yard/lint/validators/documentation/undocumented_method_arguments/validator.rb +18 -34
  37. data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +0 -1
  38. data/lib/yard/lint/validators/documentation/undocumented_objects/parser.rb +2 -2
  39. data/lib/yard/lint/validators/documentation/undocumented_objects/validator.rb +17 -25
  40. data/lib/yard/lint/validators/documentation/undocumented_objects.rb +4 -5
  41. data/lib/yard/lint/validators/documentation/undocumented_options/validator.rb +30 -21
  42. data/lib/yard/lint/validators/documentation/undocumented_options.rb +0 -1
  43. data/lib/yard/lint/validators/semantic/abstract_methods/result.rb +2 -2
  44. data/lib/yard/lint/validators/semantic/abstract_methods/validator.rb +31 -43
  45. data/lib/yard/lint/validators/semantic/abstract_methods.rb +0 -1
  46. data/lib/yard/lint/validators/tags/api_tags/validator.rb +24 -39
  47. data/lib/yard/lint/validators/tags/api_tags.rb +0 -1
  48. data/lib/yard/lint/validators/tags/collection_type/parser.rb +1 -1
  49. data/lib/yard/lint/validators/tags/collection_type/validator.rb +37 -66
  50. data/lib/yard/lint/validators/tags/collection_type.rb +0 -1
  51. data/lib/yard/lint/validators/tags/example_syntax/validator.rb +51 -64
  52. data/lib/yard/lint/validators/tags/example_syntax.rb +0 -1
  53. data/lib/yard/lint/validators/tags/informal_notation/config.rb +40 -0
  54. data/lib/yard/lint/validators/tags/informal_notation/messages_builder.rb +35 -0
  55. data/lib/yard/lint/validators/tags/informal_notation/parser.rb +55 -0
  56. data/lib/yard/lint/validators/tags/informal_notation/result.rb +26 -0
  57. data/lib/yard/lint/validators/tags/informal_notation/validator.rb +133 -0
  58. data/lib/yard/lint/validators/tags/informal_notation.rb +45 -0
  59. data/lib/yard/lint/validators/tags/invalid_types/validator.rb +57 -70
  60. data/lib/yard/lint/validators/tags/invalid_types.rb +0 -1
  61. data/lib/yard/lint/validators/tags/meaningless_tag/parser.rb +1 -1
  62. data/lib/yard/lint/validators/tags/meaningless_tag/validator.rb +22 -54
  63. data/lib/yard/lint/validators/tags/meaningless_tag.rb +0 -1
  64. data/lib/yard/lint/validators/tags/non_ascii_type/config.rb +21 -0
  65. data/lib/yard/lint/validators/tags/non_ascii_type/messages_builder.rb +29 -0
  66. data/lib/yard/lint/validators/tags/non_ascii_type/parser.rb +59 -0
  67. data/lib/yard/lint/validators/tags/non_ascii_type/result.rb +25 -0
  68. data/lib/yard/lint/validators/tags/non_ascii_type/validator.rb +50 -0
  69. data/lib/yard/lint/validators/tags/non_ascii_type.rb +39 -0
  70. data/lib/yard/lint/validators/tags/option_tags/result.rb +2 -2
  71. data/lib/yard/lint/validators/tags/option_tags/validator.rb +25 -40
  72. data/lib/yard/lint/validators/tags/option_tags.rb +0 -1
  73. data/lib/yard/lint/validators/tags/order/validator.rb +28 -55
  74. data/lib/yard/lint/validators/tags/order.rb +0 -1
  75. data/lib/yard/lint/validators/tags/redundant_param_description/config.rb +15 -1
  76. data/lib/yard/lint/validators/tags/redundant_param_description/messages_builder.rb +5 -0
  77. data/lib/yard/lint/validators/tags/redundant_param_description/validator.rb +134 -100
  78. data/lib/yard/lint/validators/tags/redundant_param_description.rb +0 -1
  79. data/lib/yard/lint/validators/tags/tag_group_separator/config.rb +29 -0
  80. data/lib/yard/lint/validators/tags/tag_group_separator/messages_builder.rb +49 -0
  81. data/lib/yard/lint/validators/tags/tag_group_separator/parser.rb +67 -0
  82. data/lib/yard/lint/validators/tags/tag_group_separator/result.rb +28 -0
  83. data/lib/yard/lint/validators/tags/tag_group_separator/validator.rb +117 -0
  84. data/lib/yard/lint/validators/tags/tag_group_separator.rb +49 -0
  85. data/lib/yard/lint/validators/tags/tag_type_position/parser.rb +1 -1
  86. data/lib/yard/lint/validators/tags/tag_type_position/validator.rb +53 -84
  87. data/lib/yard/lint/validators/tags/tag_type_position.rb +4 -5
  88. data/lib/yard/lint/validators/tags/type_syntax/parser.rb +8 -3
  89. data/lib/yard/lint/validators/tags/type_syntax/validator.rb +29 -59
  90. data/lib/yard/lint/validators/tags/type_syntax.rb +0 -1
  91. data/lib/yard/lint/validators/warnings/duplicated_parameter_name/validator.rb +1 -18
  92. data/lib/yard/lint/validators/warnings/invalid_directive_format/validator.rb +1 -18
  93. data/lib/yard/lint/validators/warnings/invalid_tag_format/validator.rb +1 -18
  94. data/lib/yard/lint/validators/warnings/unknown_directive/validator.rb +1 -18
  95. data/lib/yard/lint/validators/warnings/unknown_parameter_name/messages_builder.rb +243 -0
  96. data/lib/yard/lint/validators/warnings/unknown_parameter_name/result.rb +4 -3
  97. data/lib/yard/lint/validators/warnings/unknown_parameter_name/validator.rb +1 -18
  98. data/lib/yard/lint/validators/warnings/unknown_tag/messages_builder.rb +144 -0
  99. data/lib/yard/lint/validators/warnings/unknown_tag/result.rb +4 -3
  100. data/lib/yard/lint/validators/warnings/unknown_tag/validator.rb +1 -18
  101. data/lib/yard/lint/validators/warnings/unknown_tag.rb +10 -0
  102. data/lib/yard/lint/version.rb +1 -1
  103. data/lib/yard/lint.rb +81 -13
  104. data/renovate.json +1 -8
  105. metadata +40 -6
  106. data/bin/console +0 -11
  107. data/bin/setup +0 -8
  108. data/lib/yard/lint/command_cache.rb +0 -93
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module BlankLineBeforeDefinition
8
+ # Validates blank lines between documentation and definitions
9
+ class Validator < Validators::Base
10
+ # Enable in-process execution for this validator
11
+ in_process visibility: :public
12
+
13
+ # Execute query for a single object during in-process execution.
14
+ # Checks for blank lines between documentation blocks and definitions.
15
+ # @param object [YARD::CodeObjects::Base] the code object to query
16
+ # @param collector [Executor::ResultCollector] collector for output
17
+ # @return [void]
18
+ def in_process_query(object, collector)
19
+ return unless object.file && File.exist?(object.file) && object.line.to_i > 1
20
+
21
+ source_lines = File.readlines(object.file)
22
+ definition_line = object.line - 1
23
+
24
+ blank_count, has_doc_block = analyze_spacing(source_lines, definition_line)
25
+
26
+ return if blank_count.zero? || !has_doc_block
27
+
28
+ violation_type = blank_count >= 2 ? 'orphaned' : 'single'
29
+
30
+ return unless pattern_enabled?(violation_type)
31
+
32
+ collector.puts "#{object.file}:#{object.line}: #{object.title}"
33
+ collector.puts "#{violation_type}:#{blank_count}"
34
+ end
35
+
36
+ private
37
+
38
+ # Analyze spacing between documentation and definition
39
+ # @param source_lines [Array<String>] lines of source file
40
+ # @param definition_line [Integer] 0-indexed line of definition
41
+ # @return [Array<Integer, Boolean>] blank count and whether doc block exists
42
+ def analyze_spacing(source_lines, definition_line)
43
+ blank_count = 0
44
+ has_doc_block = false
45
+
46
+ (definition_line - 1).downto(0) do |i|
47
+ line = source_lines[i].to_s.rstrip
48
+ stripped = line.strip
49
+
50
+ if stripped.empty?
51
+ blank_count += 1
52
+ elsif stripped.start_with?('#')
53
+ # Skip Ruby magic comments - they're not YARD documentation
54
+ next if magic_comment?(stripped)
55
+
56
+ has_doc_block = true
57
+ break
58
+ else
59
+ # Non-comment, non-blank line - no documentation above
60
+ break
61
+ end
62
+ end
63
+
64
+ [blank_count, has_doc_block]
65
+ end
66
+
67
+ # Check if a comment line is a Ruby magic comment
68
+ # @param line [String] stripped comment line
69
+ # @return [Boolean] true if line is a magic comment
70
+ def magic_comment?(line)
71
+ # Ruby magic comments: frozen_string_literal, encoding, warn_indent, shareable_constant_value
72
+ line.match?(/^#\s*(frozen[_-]string[_-]literal|encoding|warn[_-]indent|shareable[_-]constant[_-]value)\s*:/i)
73
+ end
74
+
75
+ # Check if the given pattern is enabled in configuration
76
+ # @param violation_type [String] 'single' or 'orphaned'
77
+ # @return [Boolean] whether the pattern is enabled
78
+ def pattern_enabled?(violation_type)
79
+ patterns = config_or_default('EnabledPatterns')
80
+ case violation_type
81
+ when 'single'
82
+ patterns['SingleBlankLine']
83
+ when 'orphaned'
84
+ patterns['OrphanedDocs']
85
+ else
86
+ true
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ # BlankLineBeforeDefinition validator
8
+ #
9
+ # Detects blank lines between YARD documentation and method/class/module definitions.
10
+ # YARD requires documentation to be immediately adjacent to the definition it documents.
11
+ #
12
+ # @example Bad - Single blank line (convention violation)
13
+ # # Description of the method
14
+ # # @param value [String] the value
15
+ #
16
+ # def process(value)
17
+ # end
18
+ #
19
+ # @example Bad - Multiple blank lines (orphaned documentation)
20
+ # # Description of the method
21
+ # # @param value [String] the value
22
+ #
23
+ #
24
+ # def process(value)
25
+ # end
26
+ #
27
+ # @example Good - No blank lines
28
+ # # Description of the method
29
+ # # @param value [String] the value
30
+ # def process(value)
31
+ # end
32
+ #
33
+ # ## Severity Levels
34
+ #
35
+ # - **1 blank line**: Convention violation - YARD associates the doc but this
36
+ # violates formatting conventions
37
+ # - **2+ blank lines**: Orphaned documentation - YARD ignores the documentation entirely
38
+ #
39
+ # ## Configuration
40
+ #
41
+ # To customize severity levels:
42
+ #
43
+ # Documentation/BlankLineBeforeDefinition:
44
+ # Severity: warning # For single blank line
45
+ # OrphanedSeverity: error # For 2+ blank lines
46
+ #
47
+ # To check only single blank lines:
48
+ #
49
+ # Documentation/BlankLineBeforeDefinition:
50
+ # EnabledPatterns:
51
+ # SingleBlankLine: true
52
+ # OrphanedDocs: false
53
+ #
54
+ # To disable this validator:
55
+ #
56
+ # Documentation/BlankLineBeforeDefinition:
57
+ # Enabled: false
58
+ module BlankLineBeforeDefinition
59
+ end
60
+ end
61
+ end
62
+ end
63
+ 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 EmptyCommentLine
8
+ # Configuration for EmptyCommentLine validator
9
+ class Config < ::Yard::Lint::Validators::Config
10
+ self.id = :empty_comment_line
11
+ self.defaults = {
12
+ 'Enabled' => true,
13
+ 'Severity' => 'convention',
14
+ 'EnabledPatterns' => {
15
+ 'Leading' => true,
16
+ 'Trailing' => true
17
+ }
18
+ }.freeze
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module EmptyCommentLine
8
+ # Builds human-readable messages for empty comment line violations
9
+ class MessagesBuilder
10
+ # Maps violation types to human-readable descriptions
11
+ ERROR_DESCRIPTIONS = {
12
+ 'leading' => 'Empty leading comment line in documentation',
13
+ 'trailing' => 'Empty trailing comment line in documentation'
14
+ }.freeze
15
+
16
+ class << self
17
+ # Formats a violation message
18
+ # @param offense [Hash] the offense details
19
+ # @return [String] formatted message
20
+ def call(offense)
21
+ type = offense[:violation_type]
22
+ object_name = offense[:object_name]
23
+
24
+ description = ERROR_DESCRIPTIONS[type] || 'Empty comment line in documentation'
25
+
26
+ "#{description} for '#{object_name}'"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module EmptyCommentLine
8
+ # Parses YARD output for empty comment line violations
9
+ class Parser < Parsers::Base
10
+ # Parse YARD output into structured violations
11
+ # @param output [String] raw YARD output
12
+ # @return [Array<Hash>] array of violation hashes
13
+ def call(output)
14
+ return [] if output.nil? || output.empty?
15
+
16
+ violations = []
17
+ lines = output.lines.map(&:chomp)
18
+
19
+ i = 0
20
+ while i < lines.size
21
+ line = lines[i]
22
+
23
+ # Match location line: "file:line: object_name"
24
+ if (location_match = line.match(/^(.+):(\d+): (.+)$/))
25
+ file_path = location_match[1]
26
+ object_line = location_match[2].to_i
27
+ object_name = location_match[3]
28
+
29
+ # Next line contains violation details
30
+ i += 1
31
+ next unless i < lines.size
32
+
33
+ # Parse violations: "leading:5|trailing:10"
34
+ violation_parts = lines[i].split('|')
35
+
36
+ violation_parts.each do |part|
37
+ type, line_num = part.split(':', 2)
38
+ next unless type && line_num
39
+
40
+ violations << {
41
+ location: file_path,
42
+ line: line_num.to_i,
43
+ object_line: object_line,
44
+ object_name: object_name,
45
+ violation_type: type
46
+ }
47
+ end
48
+ end
49
+
50
+ i += 1
51
+ end
52
+
53
+ violations
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module EmptyCommentLine
8
+ # Result builder for empty comment line violations
9
+ class Result < Results::Base
10
+ self.default_severity = 'convention'
11
+ self.offense_type = 'line'
12
+ self.offense_name = 'EmptyCommentLine'
13
+
14
+ # Build human-readable message for empty comment line offense
15
+ # @param offense [Hash] offense data with violation details
16
+ # @return [String] formatted message
17
+ def build_message(offense)
18
+ MessagesBuilder.call(offense)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module EmptyCommentLine
8
+ # Validates empty comment lines at the start/end of documentation blocks
9
+ class Validator < Validators::Base
10
+ # Enable in-process execution for this validator
11
+ in_process visibility: :public
12
+
13
+ # Execute query for a single object during in-process execution.
14
+ # Checks for empty leading/trailing comment lines in documentation blocks.
15
+ # @param object [YARD::CodeObjects::Base] the code object to query
16
+ # @param collector [Executor::ResultCollector] collector for output
17
+ # @return [void]
18
+ def in_process_query(object, collector)
19
+ return unless object.file && File.exist?(object.file) && object.line.to_i > 1
20
+
21
+ check_leading = check_leading?
22
+ check_trailing = check_trailing?
23
+
24
+ source_lines = File.readlines(object.file)
25
+ definition_line = object.line - 1
26
+
27
+ # Find comment block boundaries
28
+ comment_end = nil
29
+ comment_start = nil
30
+
31
+ (definition_line - 1).downto(0) do |i|
32
+ line = source_lines[i].to_s.rstrip
33
+ stripped = line.strip
34
+
35
+ if stripped.empty? && comment_end.nil?
36
+ # Skip empty lines before finding comment block
37
+ next
38
+ elsif stripped.start_with?('#')
39
+ comment_end ||= i
40
+ comment_start = i
41
+ else
42
+ break
43
+ end
44
+ end
45
+
46
+ return unless comment_start && comment_end
47
+
48
+ comment_block = source_lines[comment_start..comment_end]
49
+
50
+ # Find first and last content lines
51
+ first_content_idx = nil
52
+ last_content_idx = nil
53
+
54
+ comment_block.each_with_index do |line, idx|
55
+ stripped = line.strip
56
+ has_content = stripped.match?(/^#.+\S/)
57
+ if has_content
58
+ first_content_idx ||= idx
59
+ last_content_idx = idx
60
+ end
61
+ end
62
+
63
+ return unless first_content_idx && last_content_idx
64
+
65
+ violations = []
66
+
67
+ # Check for leading empty comment lines
68
+ if check_leading
69
+ (0...first_content_idx).each do |i|
70
+ if comment_block[i].strip.match?(/^#\s*$/)
71
+ violations << "leading:#{comment_start + i + 1}"
72
+ end
73
+ end
74
+ end
75
+
76
+ # Check for trailing empty comment lines
77
+ if check_trailing
78
+ ((last_content_idx + 1)...comment_block.length).each do |i|
79
+ if comment_block[i].strip.match?(/^#\s*$/)
80
+ violations << "trailing:#{comment_start + i + 1}"
81
+ end
82
+ end
83
+ end
84
+
85
+ return if violations.empty?
86
+
87
+ collector.puts "#{object.file}:#{object.line}: #{object.title}"
88
+ collector.puts violations.join('|')
89
+ end
90
+
91
+ private
92
+
93
+ # @return [Boolean] whether to check for leading empty lines
94
+ def check_leading?
95
+ patterns = config_or_default('EnabledPatterns')
96
+ patterns['Leading']
97
+ end
98
+
99
+ # @return [Boolean] whether to check for trailing empty lines
100
+ def check_trailing?
101
+ patterns = config_or_default('EnabledPatterns')
102
+ patterns['Trailing']
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ # EmptyCommentLine validator
8
+ #
9
+ # Detects empty comment lines at the start or end of YARD documentation blocks.
10
+ # Empty lines BETWEEN tag groups are allowed for readability.
11
+ #
12
+ # @example Bad - Empty leading comment line
13
+ # #
14
+ # # Description of the method
15
+ # # @param value [String] the value
16
+ # def process(value)
17
+ # end
18
+ #
19
+ # @example Bad - Empty trailing comment line
20
+ # # Description of the method
21
+ # # @param value [String] the value
22
+ # #
23
+ # def process(value)
24
+ # end
25
+ #
26
+ # @example Good - No leading or trailing empty lines
27
+ # # Description of the method
28
+ # # @param value [String] the value
29
+ # def process(value)
30
+ # end
31
+ #
32
+ # @example Good - Empty line between sections (allowed)
33
+ # # Description of the method
34
+ # #
35
+ # # @param value [String] the value
36
+ # # @return [Boolean] success
37
+ # def process(value)
38
+ # end
39
+ #
40
+ # ## Configuration
41
+ #
42
+ # To check only leading empty lines:
43
+ #
44
+ # Documentation/EmptyCommentLine:
45
+ # EnabledPatterns:
46
+ # Leading: true
47
+ # Trailing: false
48
+ #
49
+ # To disable this validator:
50
+ #
51
+ # Documentation/EmptyCommentLine:
52
+ # Enabled: false
53
+ module EmptyCommentLine
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -7,28 +7,43 @@ module Yard
7
7
  module MarkdownSyntax
8
8
  # Validates markdown syntax in documentation
9
9
  class Validator < Validators::Base
10
- # YARD query to extract docstrings and check for markdown errors
11
- # @return [String] YARD Ruby query code
12
- def query
13
- <<~QUERY.strip
14
- 'docstring_text = object.docstring.to_s; unless docstring_text.empty?; errors = []; backtick_count = docstring_text.scan(/\\x60/).count; errors << "unclosed_backtick" if backtick_count.odd?; code_block_count = docstring_text.scan(/^```/).count; errors << "unclosed_code_block" if code_block_count.odd?; non_code_text = docstring_text.gsub(/\\x60[^\\x60]*\\x60/, ""); bold_count = non_code_text.scan(/\\*\\*/).count; errors << "unclosed_bold" if bold_count.odd?; lines = docstring_text.lines; lines.each_with_index do |line, line_idx|; stripped = line.strip; errors << "invalid_list_marker:" + (line_idx + 1).to_s if stripped =~ /^[•·]/; end; unless errors.empty?; puts object.file + ":" + object.line.to_s + ": " + object.title; puts errors.join("|"); end; end; false'
15
- QUERY
16
- end
10
+ # Enable in-process execution for this validator
11
+ in_process visibility: :public
12
+
13
+ # Execute query for a single object during in-process execution.
14
+ # Checks for markdown syntax errors in docstrings.
15
+ # @param object [YARD::CodeObjects::Base] the code object to query
16
+ # @param collector [Executor::ResultCollector] collector for output
17
+ # @return [void]
18
+ def in_process_query(object, collector)
19
+ docstring_text = object.docstring.to_s
20
+ return if docstring_text.empty?
21
+
22
+ errors = []
23
+
24
+ # Check for unclosed backticks
25
+ backtick_count = docstring_text.scan(/`/).count
26
+ errors << 'unclosed_backtick' if backtick_count.odd?
27
+
28
+ # Check for unclosed code blocks
29
+ code_block_count = docstring_text.scan(/^```/).count
30
+ errors << 'unclosed_code_block' if code_block_count.odd?
31
+
32
+ # Check for unclosed bold markers (excluding code sections)
33
+ non_code_text = docstring_text.gsub(/`[^`]*`/, '')
34
+ bold_count = non_code_text.scan(/\*\*/).count
35
+ errors << 'unclosed_bold' if bold_count.odd?
36
+
37
+ # Check for invalid list markers
38
+ docstring_text.lines.each_with_index do |line, line_idx|
39
+ stripped = line.strip
40
+ errors << "invalid_list_marker:#{line_idx + 1}" if stripped.match?(/^[•·]/)
41
+ end
42
+
43
+ return if errors.empty?
17
44
 
18
- # Builds and executes the YARD command to detect markdown syntax errors
19
- # @param dir [String] the directory containing the .yardoc database
20
- # @param file_list_path [String] path to file containing list of files to analyze
21
- # @return [String] command output
22
- def yard_cmd(dir, file_list_path)
23
- cmd = <<~CMD
24
- cat #{Shellwords.escape(file_list_path)} | xargs yard list \
25
- #{shell_arguments} \
26
- --query #{query} \
27
- -q \
28
- -b #{Shellwords.escape(dir)}
29
- CMD
30
- cmd = cmd.tr("\n", ' ')
31
- shell(cmd)
45
+ collector.puts "#{object.file}:#{object.line}: #{object.title}"
46
+ collector.puts errors.join('|')
32
47
  end
33
48
  end
34
49
  end
@@ -28,7 +28,6 @@ module Yard
28
28
  #
29
29
  # Documentation/MarkdownSyntax:
30
30
  # Enabled: false
31
- #
32
31
  module MarkdownSyntax
33
32
  end
34
33
  end
@@ -8,38 +8,28 @@ module Yard
8
8
  # Runs a query that will pick all the boolean methods (ending with ?) that
9
9
  # do not have a return type or return description documented
10
10
  class Validator < Base
11
- # Query to find all the boolean methods without proper return documentation
12
- # Requires either: no @return tag, OR @return tag with no types specified
13
- # Accepts @return [Boolean] without description text as valid documentation
14
- QUERY = <<~QUERY.tr("\n", ' ')
15
- '
16
- type == :method &&
17
- !is_alias? &&
18
- is_explicit? &&
19
- name.to_s.end_with?("?") &&
20
- (tag("return").nil? || tag("return").types.to_a.empty?)
21
- '
22
- QUERY
11
+ # Enable in-process execution for this validator
12
+ in_process visibility: :public
23
13
 
24
- private_constant :QUERY
14
+ # Execute query for a single object during in-process execution.
15
+ # Finds boolean methods (ending with ?) without @return tag or return types.
16
+ # @param object [YARD::CodeObjects::Base] the code object to query
17
+ # @param collector [Executor::ResultCollector] collector for output
18
+ # @return [void]
19
+ def in_process_query(object, collector)
20
+ # Only check methods
21
+ return unless object.type == :method
22
+ # Skip aliases and implicit methods
23
+ return if object.is_alias?
24
+ return unless object.is_explicit?
25
+ # Only check boolean methods (ending with ?)
26
+ return unless object.name.to_s.end_with?('?')
25
27
 
26
- private
28
+ # Check if @return tag is missing or has no types
29
+ return_tag = object.tag(:return)
30
+ return unless return_tag.nil? || return_tag.types.to_a.empty?
27
31
 
28
- # Runs yard list query with proper settings on a given dir and files
29
- # @param dir [String] dir where we should generate the temp docs
30
- # @param file_list_path [String] path to temp file containing file paths (one per line)
31
- # @return [Hash] shell command execution hash results
32
- def yard_cmd(dir, file_list_path)
33
- cmd = <<~CMD
34
- cat #{Shellwords.escape(file_list_path)} | xargs yard list \
35
- #{shell_arguments} \
36
- --query #{QUERY} \
37
- -q \
38
- -b #{Shellwords.escape(dir)}
39
- CMD
40
- cmd = cmd.tr("\n", ' ')
41
-
42
- shell(cmd)
32
+ collector.puts "#{object.file}:#{object.line}: #{object.title}"
43
33
  end
44
34
  end
45
35
  end
@@ -29,7 +29,6 @@ module Yard
29
29
  #
30
30
  # Documentation/UndocumentedBooleanMethods:
31
31
  # Enabled: false
32
- #
33
32
  module UndocumentedBooleanMethods
34
33
  end
35
34
  end
@@ -7,44 +7,28 @@ module Yard
7
7
  module UndocumentedMethodArguments
8
8
  # Runs yard list to check for missing args docs on methods that were documented
9
9
  class Validator < Base
10
- # Options that stats supports but not list
11
- UNWANTED_OPTIONS = %w[
12
- --list-undoc
13
- ].freeze
10
+ # Enable in-process execution for this validator
11
+ in_process visibility: :public
14
12
 
15
- # Query to find all the documented methods that have some undocumented
16
- # arguments
17
- QUERY = <<~QUERY.tr("\n", ' ')
18
- '
19
- type == :method &&
20
- !is_alias? &&
21
- is_explicit? &&
22
- (parameters.size > @@param.size)
23
- '
24
- QUERY
13
+ # Execute query for a single object during in-process execution.
14
+ # Finds methods where parameters.size > @param tags count.
15
+ # @param object [YARD::CodeObjects::Base] the code object to query
16
+ # @param collector [Executor::ResultCollector] collector for output
17
+ # @return [void]
18
+ def in_process_query(object, collector)
19
+ # Only check methods
20
+ return unless object.type == :method
21
+ # Skip aliases and implicit methods
22
+ return if object.is_alias?
23
+ return unless object.is_explicit?
25
24
 
26
- private_constant :UNWANTED_OPTIONS, :QUERY
25
+ # Check if parameters count exceeds @param tags count
26
+ param_count = object.parameters.size
27
+ param_tags_count = object.tags(:param).size
27
28
 
28
- private
29
+ return unless param_count > param_tags_count
29
30
 
30
- # Runs yard list query with proper settings on a given dir and files
31
- # @param dir [String] dir where we should generate the temp docs
32
- # @param file_list_path [String] path to temp file containing file paths (one per line)
33
- # @return [Hash] shell command execution hash results
34
- def yard_cmd(dir, file_list_path)
35
- shell_args = shell_arguments
36
- UNWANTED_OPTIONS.each { |opt| shell_args.gsub!(opt, '') }
37
-
38
- cmd = <<~CMD
39
- cat #{Shellwords.escape(file_list_path)} | xargs yard list \
40
- #{shell_args} \
41
- --query #{QUERY} \
42
- -q \
43
- -b #{Shellwords.escape(dir)}
44
- CMD
45
- cmd = cmd.tr("\n", ' ')
46
-
47
- shell(cmd)
31
+ collector.puts "#{object.file}:#{object.line}: #{object.title}"
48
32
  end
49
33
  end
50
34
  end