yard-lint 1.3.0 → 1.5.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +73 -3
  4. data/README.md +154 -527
  5. data/Rakefile +11 -8
  6. data/bin/yard-lint +64 -5
  7. data/lib/yard/lint/config.rb +4 -0
  8. data/lib/yard/lint/config_validator.rb +230 -0
  9. data/lib/yard/lint/errors.rb +6 -0
  10. data/lib/yard/lint/executor/in_process_registry.rb +9 -0
  11. data/lib/yard/lint/path_grouper.rb +70 -0
  12. data/lib/yard/lint/result_builder.rb +19 -5
  13. data/lib/yard/lint/results/base.rb +3 -3
  14. data/lib/yard/lint/templates/default_config.yml +31 -0
  15. data/lib/yard/lint/templates/strict_config.yml +31 -0
  16. data/lib/yard/lint/todo_generator.rb +261 -0
  17. data/lib/yard/lint/validators/base.rb +1 -1
  18. data/lib/yard/lint/validators/documentation/missing_return/config.rb +23 -0
  19. data/lib/yard/lint/validators/documentation/missing_return/messages_builder.rb +23 -0
  20. data/lib/yard/lint/validators/documentation/missing_return/parser.rb +128 -0
  21. data/lib/yard/lint/validators/documentation/missing_return/result.rb +25 -0
  22. data/lib/yard/lint/validators/documentation/missing_return/validator.rb +40 -0
  23. data/lib/yard/lint/validators/documentation/missing_return.rb +49 -0
  24. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/parser.rb +1 -1
  25. data/lib/yard/lint/validators/documentation/undocumented_method_arguments/parser.rb +1 -1
  26. data/lib/yard/lint/validators/documentation/undocumented_method_arguments/validator.rb +3 -0
  27. data/lib/yard/lint/validators/documentation/undocumented_objects/parser.rb +1 -1
  28. data/lib/yard/lint/validators/tags/collection_type/messages_builder.rb +8 -2
  29. data/lib/yard/lint/validators/tags/collection_type/validator.rb +33 -14
  30. data/lib/yard/lint/validators/tags/example_style/config.rb +33 -0
  31. data/lib/yard/lint/validators/tags/example_style/linter_detector.rb +71 -0
  32. data/lib/yard/lint/validators/tags/example_style/messages_builder.rb +29 -0
  33. data/lib/yard/lint/validators/tags/example_style/parser.rb +88 -0
  34. data/lib/yard/lint/validators/tags/example_style/result.rb +42 -0
  35. data/lib/yard/lint/validators/tags/example_style/rubocop_runner.rb +210 -0
  36. data/lib/yard/lint/validators/tags/example_style/validator.rb +87 -0
  37. data/lib/yard/lint/validators/tags/example_style.rb +61 -0
  38. data/lib/yard/lint/validators/tags/forbidden_tags/config.rb +21 -0
  39. data/lib/yard/lint/validators/tags/forbidden_tags/messages_builder.rb +34 -0
  40. data/lib/yard/lint/validators/tags/forbidden_tags/parser.rb +51 -0
  41. data/lib/yard/lint/validators/tags/forbidden_tags/result.rb +28 -0
  42. data/lib/yard/lint/validators/tags/forbidden_tags/validator.rb +66 -0
  43. data/lib/yard/lint/validators/tags/forbidden_tags.rb +68 -0
  44. data/lib/yard/lint/validators/tags/invalid_types/validator.rb +1 -1
  45. data/lib/yard/lint/validators/tags/tag_group_separator/parser.rb +1 -1
  46. data/lib/yard/lint/validators/tags/type_syntax/validator.rb +19 -0
  47. data/lib/yard/lint/validators/warnings/unknown_tag/parser.rb +1 -1
  48. data/lib/yard/lint/version.rb +1 -1
  49. data/mise.toml +2 -0
  50. data/package-lock.json +329 -0
  51. data/package.json +7 -0
  52. data/proxy_types +0 -0
  53. data/renovate.json +18 -1
  54. metadata +30 -3
  55. data/.coditsu/ci.yml +0 -3
data/Rakefile CHANGED
@@ -3,8 +3,15 @@
3
3
  require 'bundler/setup'
4
4
  require 'bundler/gem_tasks'
5
5
  require 'etc'
6
+ require 'rake/testtask'
6
7
 
7
- namespace :spec do
8
+ Rake::TestTask.new(:test) do |t|
9
+ t.libs << 'test'
10
+ t.ruby_opts << '-r test_helper'
11
+ t.test_files = FileList['test/**/*_test.rb']
12
+ end
13
+
14
+ namespace :test do
8
15
  # Determine optimal number of parallel processes
9
16
  # Use all CPUs if less than 8, otherwise cap at 8
10
17
  def parallel_process_count
@@ -12,13 +19,9 @@ namespace :spec do
12
19
  [cpus, 8].min
13
20
  end
14
21
 
15
- desc 'Run integration specs in parallel'
16
- task :integrations do
17
- sh "bundle exec parallel_rspec -n #{parallel_process_count} spec/integrations/"
18
- end
19
-
20
- desc 'Run all specs in parallel'
22
+ desc 'Run all tests in parallel'
21
23
  task :parallel do
22
- sh "bundle exec parallel_rspec -n #{parallel_process_count} spec/"
24
+ executable = 'ruby -Itest -r test_helper'
25
+ sh "PARALLEL_TESTS_EXECUTABLE='#{executable}' bundle exec parallel_test -n #{parallel_process_count} test/"
23
26
  end
24
27
  end
data/bin/yard-lint CHANGED
@@ -78,6 +78,22 @@ OptionParser.new do |opts|
78
78
  options[:force] = true
79
79
  end
80
80
 
81
+ opts.separator ''
82
+ opts.separator 'Todo file generation:'
83
+
84
+ opts.on('--auto-gen-config', 'Generate .yard-lint-todo.yml to silence existing violations') do
85
+ options[:auto_gen_config] = true
86
+ end
87
+
88
+ opts.on('--regenerate-todo', 'Regenerate .yard-lint-todo.yml (overwrites existing)') do
89
+ options[:auto_gen_config] = true
90
+ options[:force] = true
91
+ end
92
+
93
+ opts.on('--exclude-limit COUNT', Integer, 'Min files before grouping into pattern (default: 15)') do |count|
94
+ options[:exclude_limit] = count
95
+ end
96
+
81
97
  opts.on('-v', '--version', 'Show version') do
82
98
  puts "yard-lint #{Yard::Lint::VERSION}"
83
99
  exit
@@ -98,6 +114,9 @@ OptionParser.new do |opts|
98
114
  puts ' yard-lint --init # Generate default config'
99
115
  puts ' yard-lint --init --strict # Generate strict config'
100
116
  puts ' yard-lint --update # Update config with new validators'
117
+ puts ' yard-lint --auto-gen-config # Generate todo file for existing violations'
118
+ puts ' yard-lint --regenerate-todo # Regenerate todo file'
119
+ puts ' yard-lint --auto-gen-config --exclude-limit 10 # Custom grouping threshold'
101
120
  exit
102
121
  end
103
122
  end.parse!
@@ -142,6 +161,41 @@ if options[:update]
142
161
  end
143
162
  end
144
163
 
164
+ # Handle --auto-gen-config flag
165
+ if options[:auto_gen_config]
166
+ begin
167
+ # Load config
168
+ config = if config_file
169
+ Yard::Lint::Config.from_file(config_file)
170
+ else
171
+ Yard::Lint::Config.load || Yard::Lint::Config.new
172
+ end
173
+
174
+ # Get path argument
175
+ path = ARGV[0] || '.'
176
+
177
+ # Generate todo file
178
+ result = Yard::Lint::TodoGenerator.generate(
179
+ path: path,
180
+ config: config,
181
+ force: options[:force] || false,
182
+ exclude_limit: options[:exclude_limit] || 15
183
+ )
184
+
185
+ puts result[:message]
186
+ exit 0
187
+ rescue Yard::Lint::Errors::InvalidConfigError => e
188
+ puts "Configuration Error:\n#{e.message}"
189
+ exit 1
190
+ rescue Yard::Lint::Errors::TodoFileExistsError => e
191
+ puts "Error: #{e.message}"
192
+ exit 1
193
+ rescue Yard::Lint::Git::Error, Yard::Lint::Errors::FileNotFoundError => e
194
+ puts "Error: #{e.message}"
195
+ exit 1
196
+ end
197
+ end
198
+
145
199
  # Get path argument (defaults to current directory)
146
200
  path = ARGV[0] || '.'
147
201
 
@@ -149,11 +203,16 @@ path = ARGV[0] || '.'
149
203
  YARD::Registry.clear
150
204
 
151
205
  # Load config and apply CLI overrides
152
- config = if config_file
153
- Yard::Lint::Config.from_file(config_file)
154
- else
155
- Yard::Lint::Config.load || Yard::Lint::Config.new
156
- end
206
+ begin
207
+ config = if config_file
208
+ Yard::Lint::Config.from_file(config_file)
209
+ else
210
+ Yard::Lint::Config.load || Yard::Lint::Config.new
211
+ end
212
+ rescue Yard::Lint::Errors::InvalidConfigError => e
213
+ puts "Configuration Error:\n#{e.message}"
214
+ exit 1
215
+ end
157
216
 
158
217
  # Apply CLI min_coverage override if provided
159
218
  config.min_coverage = options[:min_coverage] if options[:min_coverage]
@@ -20,6 +20,10 @@ module Yard
20
20
  # @param raw_config [Hash] raw configuration hash (new hierarchical format)
21
21
  def initialize(raw_config = {})
22
22
  @raw_config = raw_config
23
+
24
+ # Validate configuration structure and values
25
+ ConfigValidator.validate!(@raw_config) unless raw_config.empty?
26
+
23
27
  @validators = build_validators_config
24
28
  @only_validators = []
25
29
 
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ # YARD Lint - comprehensive linter for YARD documentation
4
+ module Yard
5
+ # YARD Lint module providing linting functionality for YARD documentation
6
+ module Lint
7
+ # Validates configuration structure and values to catch typos and invalid settings
8
+ class ConfigValidator
9
+ # Valid category names (from ConfigUpdater::CATEGORY_ORDER)
10
+ VALID_CATEGORIES = %w[Documentation Tags Warnings Semantic].freeze
11
+
12
+ # Keys that are valid at the root level but are not validators
13
+ SPECIAL_KEYS = %w[
14
+ AllValidators
15
+ inherit_from
16
+ inherit_gem
17
+ ].freeze
18
+
19
+ # Valid boolean values
20
+ BOOLEAN_VALUES = [true, false].freeze
21
+
22
+ # @param raw_config [Hash] raw configuration hash to validate
23
+ def initialize(raw_config)
24
+ @raw_config = raw_config
25
+ @errors = []
26
+ end
27
+
28
+ # Validate configuration and raise error if invalid
29
+ # @param raw_config [Hash] raw configuration hash to validate
30
+ # @return [void]
31
+ # @raise [Errors::InvalidConfigError] if configuration is invalid
32
+ def self.validate!(raw_config)
33
+ validator = new(raw_config)
34
+ validator.validate!
35
+ end
36
+
37
+ # Perform validation
38
+ # @return [void]
39
+ # @raise [Errors::InvalidConfigError] if configuration is invalid
40
+ def validate!
41
+ validate_root_keys!
42
+ validate_global_settings!
43
+ validate_validators!
44
+
45
+ return if @errors.empty?
46
+
47
+ raise Errors::InvalidConfigError, build_error_message
48
+ end
49
+
50
+ private
51
+
52
+ # Validate root-level keys in configuration
53
+ def validate_root_keys!
54
+ @raw_config.each_key do |key|
55
+ # Skip special keys
56
+ next if SPECIAL_KEYS.include?(key)
57
+
58
+ # Skip category-level configs (e.g., 'Documentation', 'Tags')
59
+ next if VALID_CATEGORIES.include?(key)
60
+
61
+ # Validator names must contain a '/' (category/name format)
62
+ next if key.include?('/')
63
+
64
+ # If we get here, it's an unknown validator (missing category prefix)
65
+ @errors << "Unknown validator: '#{key}'"
66
+ suggest_validator_name(key)
67
+ end
68
+ end
69
+
70
+ # Validate AllValidators section
71
+ def validate_global_settings!
72
+ all_validators = @raw_config['AllValidators']
73
+ return unless all_validators
74
+
75
+ unless all_validators.is_a?(Hash)
76
+ @errors << "Invalid AllValidators: must be a Hash, got #{all_validators.class}"
77
+ return
78
+ end
79
+
80
+ # Only validate specific known settings, allow unknown keys to pass through
81
+ # This allows users to put custom data in AllValidators
82
+
83
+ # Validate FailOnSeverity value
84
+ fail_on = all_validators['FailOnSeverity']
85
+ if fail_on && !Config::VALID_SEVERITIES.include?(fail_on.to_s)
86
+ @errors << "Invalid FailOnSeverity: '#{fail_on}'"
87
+ @errors << " Valid values: #{Config::VALID_SEVERITIES.join(', ')}"
88
+ end
89
+
90
+ # Validate MinCoverage value
91
+ min_cov = all_validators['MinCoverage']
92
+ if min_cov && (!min_cov.is_a?(Numeric) || min_cov < 0 || min_cov > 100)
93
+ @errors << "Invalid MinCoverage: '#{min_cov}'"
94
+ @errors << ' Must be a number between 0 and 100'
95
+ end
96
+
97
+ # Validate Exclude is an array
98
+ exclude = all_validators['Exclude']
99
+ if exclude && !exclude.is_a?(Array)
100
+ @errors << "Invalid Exclude in AllValidators: must be an array, got #{exclude.class}"
101
+ end
102
+
103
+ # Validate YardOptions is an array
104
+ yard_options = all_validators['YardOptions']
105
+ if yard_options && !yard_options.is_a?(Array)
106
+ @errors << "Invalid YardOptions in AllValidators: must be an array, got #{yard_options.class}"
107
+ end
108
+ end
109
+
110
+ # Validate validator-specific configurations
111
+ def validate_validators!
112
+ @raw_config.each do |key, value|
113
+ # Only process validator keys (containing '/')
114
+ next unless key.include?('/')
115
+
116
+ unless value.is_a?(Hash)
117
+ @errors << "Invalid configuration for validator '#{key}': expected a Hash, got #{value.class}"
118
+ next
119
+ end
120
+
121
+ validate_validator_exists!(key)
122
+ validate_validator_config!(key, value)
123
+ end
124
+ end
125
+
126
+ # Check if validator name exists
127
+ # @param validator_name [String] validator name to check
128
+ # @return [void]
129
+ def validate_validator_exists!(validator_name)
130
+ return if ConfigLoader::ALL_VALIDATORS.include?(validator_name)
131
+
132
+ @errors << "Unknown validator: '#{validator_name}'"
133
+ suggest_validator_name(validator_name)
134
+ end
135
+
136
+ # Validate individual validator configuration
137
+ # @param validator_name [String] validator name
138
+ # @param config [Hash] validator configuration hash
139
+ # @return [void]
140
+ def validate_validator_config!(validator_name, config)
141
+ # Validate Enabled value
142
+ enabled = config['Enabled']
143
+ if enabled && !BOOLEAN_VALUES.include?(enabled)
144
+ @errors << "Invalid Enabled value for #{validator_name}: '#{enabled}'"
145
+ @errors << ' Must be true or false'
146
+ end
147
+
148
+ # Validate Severity value
149
+ severity = config['Severity']
150
+ if severity && !Config::VALID_SEVERITIES.include?(severity.to_s)
151
+ @errors << "Invalid Severity for #{validator_name}: '#{severity}'"
152
+ @errors << " Valid values: #{Config::VALID_SEVERITIES.join(', ')}"
153
+ suggest_similar_severity(severity.to_s)
154
+ end
155
+
156
+ # Validate Exclude is an array
157
+ exclude = config['Exclude']
158
+ if exclude && !exclude.is_a?(Array)
159
+ @errors << "Invalid Exclude for #{validator_name}: must be an array, got #{exclude.class}"
160
+ end
161
+
162
+ # Validate YardOptions is an array
163
+ yard_options = config['YardOptions']
164
+ if yard_options && !yard_options.is_a?(Array)
165
+ @errors << "Invalid YardOptions for #{validator_name}: must be an array, got #{yard_options.class}"
166
+ end
167
+
168
+ # Check for unknown validator-specific keys
169
+ validate_validator_specific_keys!(validator_name, config)
170
+ end
171
+
172
+ # Validate validator-specific configuration keys
173
+ # @param validator_name [String] validator name
174
+ # @param config [Hash] validator configuration hash
175
+ # @return [void]
176
+ def validate_validator_specific_keys!(validator_name, config)
177
+ # Skip if validator doesn't exist (already reported)
178
+ return unless ConfigLoader::ALL_VALIDATORS.include?(validator_name)
179
+
180
+ validator_config_class = ConfigLoader.validator_config(validator_name)
181
+ return unless validator_config_class
182
+
183
+ # Base config keys that are valid for all validators
184
+ base_keys = %w[Enabled Severity Exclude YardOptions]
185
+ valid_keys = validator_config_class.defaults.keys + Config::METADATA_KEYS + base_keys
186
+
187
+ config.each_key do |key|
188
+ next if valid_keys.include?(key)
189
+
190
+ @errors << "Unknown configuration key for #{validator_name}: '#{key}'"
191
+ @errors << " Valid keys: #{valid_keys.uniq.sort.join(', ')}"
192
+ end
193
+ end
194
+
195
+ # Suggest similar validator names using did_you_mean
196
+ # @param invalid_name [String] invalid validator name
197
+ # @return [void]
198
+ def suggest_validator_name(invalid_name)
199
+ checker = DidYouMean::SpellChecker.new(dictionary: ConfigLoader::ALL_VALIDATORS)
200
+ suggestions = checker.correct(invalid_name)
201
+
202
+ if suggestions.any?
203
+ @errors << " Did you mean: #{suggestions.first}?"
204
+ else
205
+ @errors << ' Run `yard-lint --list-validators` to see all available validators'
206
+ end
207
+ end
208
+
209
+ # Suggest similar severity values
210
+ # @param invalid_severity [String] invalid severity value
211
+ # @return [void]
212
+ def suggest_similar_severity(invalid_severity)
213
+ checker = DidYouMean::SpellChecker.new(dictionary: Config::VALID_SEVERITIES)
214
+ suggestions = checker.correct(invalid_severity)
215
+
216
+ if suggestions.any?
217
+ @errors << " Did you mean: #{suggestions.first}?"
218
+ end
219
+ end
220
+
221
+ # Build comprehensive error message
222
+ def build_error_message
223
+ header = 'Invalid configuration detected:'
224
+ errors_text = @errors.map { |e| " #{e}" }.join("\n")
225
+
226
+ "#{header}\n#{errors_text}"
227
+ end
228
+ end
229
+ end
230
+ end
@@ -18,6 +18,12 @@ module Yard
18
18
 
19
19
  # Raised when a specified file or directory does not exist
20
20
  class FileNotFoundError < BaseError; end
21
+
22
+ # Raised when .yard-lint-todo.yml already exists without force flag
23
+ class TodoFileExistsError < BaseError; end
24
+
25
+ # Raised when configuration is invalid
26
+ class InvalidConfigError < BaseError; end
21
27
  end
22
28
  end
23
29
  end
@@ -33,6 +33,15 @@ module Yard
33
33
  original_level = YARD::Logger.instance.level
34
34
  YARD::Logger.instance.level = 4 # Only show fatal errors
35
35
 
36
+ # First pass: parse all files to process directive definitions
37
+ YARD.parse(files)
38
+
39
+ # Clear checksums to force reparsing without clearing the registry.
40
+ # This allows macro definitions from the first pass to be available
41
+ # during the second pass, enabling proper directive expansion regardless of parse order.
42
+ YARD::Registry.checksums.clear
43
+
44
+ # Second pass: reparse files now that all directive definitions are available
36
45
  @warnings = capture_warnings { YARD.parse(files) }
37
46
  @parsed = true
38
47
 
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ # Groups file paths into patterns when beneficial
6
+ class PathGrouper
7
+ # Coverage threshold for directory-level grouping (80%)
8
+ DIRECTORY_COVERAGE_THRESHOLD = 0.8
9
+
10
+ class << self
11
+ # Group file paths into patterns
12
+ # @param files [Array<String>] array of file paths
13
+ # @param limit [Integer] minimum files to trigger grouping (default: 15)
14
+ # @return [Array<String>] array of paths or patterns
15
+ def group(files, limit: 15)
16
+ return files.uniq.sort if files.size < limit
17
+
18
+ grouped = find_common_directories(files, limit)
19
+ grouped.sort
20
+ end
21
+
22
+ private
23
+
24
+ # Find directories where files can be grouped into patterns
25
+ # @param files [Array<String>] unique file paths to analyze
26
+ # @param limit [Integer] minimum file count threshold for grouping
27
+ # @return [Array<String>] array of file paths or directory patterns
28
+ def find_common_directories(files, limit)
29
+ # Deduplicate files first
30
+ unique_files = files.uniq
31
+ by_dir = unique_files.group_by { |f| File.dirname(f) }
32
+ result = []
33
+
34
+ by_dir.each do |dir, dir_files|
35
+ if should_group_directory?(dir, dir_files.uniq, limit)
36
+ result << "#{dir}/**/*"
37
+ else
38
+ result.concat(dir_files.uniq)
39
+ end
40
+ end
41
+
42
+ result.uniq
43
+ end
44
+
45
+ # Determine if a directory should be grouped
46
+ # @param dir [String] directory path to evaluate
47
+ # @param dir_files [Array<String>] files in this directory
48
+ # @param limit [Integer] minimum file count threshold
49
+ # @return [Boolean] true if directory should be grouped into pattern
50
+ def should_group_directory?(dir, dir_files, limit)
51
+ # Must have enough files and more than just one file
52
+ return false if dir_files.size < limit || dir_files.size == 1
53
+
54
+ # Check coverage: do we have most Ruby files in this directory?
55
+ begin
56
+ all_ruby_files = Dir.glob("#{dir}/**/*.rb")
57
+ # Don't group if directory doesn't exist or is empty
58
+ return false if all_ruby_files.empty?
59
+
60
+ coverage = dir_files.size.to_f / all_ruby_files.size
61
+ coverage >= DIRECTORY_COVERAGE_THRESHOLD
62
+ rescue StandardError
63
+ # If glob fails (directory doesn't exist), don't group
64
+ false
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -91,7 +91,15 @@ module Yard
91
91
  return nil if parsers.empty?
92
92
 
93
93
  # Parse output with all parsers (single or multiple)
94
- parsed = parsers.flat_map { |parser| parser.new.call(stdout) }
94
+ # Check if parser accepts config keyword argument and pass it if supported
95
+ parsed = parsers.flat_map do |parser|
96
+ parser_instance = parser.new
97
+ if parser_accepts_config?(parser_instance)
98
+ parser_instance.call(stdout, config: config)
99
+ else
100
+ parser_instance.call(stdout)
101
+ end
102
+ end
95
103
  return nil if parsed.nil? || parsed.empty?
96
104
 
97
105
  validator_module::Result.new(parsed, config)
@@ -111,16 +119,22 @@ module Yard
111
119
  parsers = discover_parsers(validator_module)
112
120
  parsers.flat_map do |parser|
113
121
  parser_instance = parser.new
114
- # Try passing config to parser if it accepts it (for filtering)
115
- # Otherwise, call without config for backwards compatibility
116
- begin
122
+ if parser_accepts_config?(parser_instance)
117
123
  parser_instance.call(stdout, config: config)
118
- rescue ArgumentError
124
+ else
119
125
  parser_instance.call(stdout)
120
126
  end
121
127
  end
122
128
  end
123
129
 
130
+ # Check if a parser instance accepts a config keyword argument
131
+ # @param parser_instance [Object] parser instance to check
132
+ # @return [Boolean] true if parser accepts config keyword
133
+ def parser_accepts_config?(parser_instance)
134
+ params = parser_instance.method(:call).parameters
135
+ params.any? { |type, name| [:key, :keyreq].include?(type) && name == :config }
136
+ end
137
+
124
138
  # Auto-discover parser classes in a validator module
125
139
  # Finds all classes that inherit from Parsers::Base
126
140
  # @param validator_module [Module] validator module to search
@@ -8,9 +8,9 @@ module Yard
8
8
  #
9
9
  # @example Creating a validator result class
10
10
  # class MyValidator::Result < Results::Base
11
- # self.default_severity = 'warning'
12
- # self.offense_type = 'method'
13
- # self.offense_name = 'MyOffense'
11
+ # self.default_severity = "warning"
12
+ # self.offense_type = "method"
13
+ # self.offense_name = "MyOffense"
14
14
  #
15
15
  # def build_message(offense)
16
16
  # "Found issue in #{offense[:location]}"
@@ -52,6 +52,14 @@ Documentation/UndocumentedOptions:
52
52
  Enabled: true
53
53
  Severity: warning
54
54
 
55
+ Documentation/MissingReturn:
56
+ Description: 'Requires @return tags on all methods (opt-in for strict documentation).'
57
+ Enabled: false # Opt-in validator
58
+ Severity: warning
59
+ ExcludedMethods:
60
+ - 'initialize' # Exclude all initialize methods
61
+ # - '/^_/' # Uncomment to exclude private methods (by convention)
62
+
55
63
  Documentation/MarkdownSyntax:
56
64
  Description: 'Detects common markdown syntax errors in documentation.'
57
65
  Enabled: true
@@ -164,6 +172,15 @@ Tags/ExampleSyntax:
164
172
  Enabled: true
165
173
  Severity: warning
166
174
 
175
+ Tags/ExampleStyle:
176
+ Description: 'Validates code style in @example tags using RuboCop/StandardRB.'
177
+ Enabled: false # Opt-in validator (requires RuboCop or StandardRB)
178
+ Severity: convention
179
+ # Linter: auto # Uncomment to explicitly configure: 'auto', 'rubocop', 'standard', 'none'
180
+ # SkipPatterns: # Uncomment to skip examples matching patterns
181
+ # - '/skip-lint/i'
182
+ # - '/bad code/i'
183
+
167
184
  Tags/RedundantParamDescription:
168
185
  Description: 'Detects meaningless parameter descriptions that add no value.'
169
186
  Enabled: true
@@ -241,6 +258,20 @@ Tags/TagGroupSeparator:
241
258
  yield: [yield, yieldparam, yieldreturn]
242
259
  RequireAfterDescription: false
243
260
 
261
+ Tags/ForbiddenTags:
262
+ Description: 'Detects forbidden tag and type combinations.'
263
+ Enabled: false # Opt-in validator
264
+ Severity: convention
265
+ ForbiddenPatterns: []
266
+ # Example patterns:
267
+ # - Tag: return
268
+ # Types:
269
+ # - void
270
+ # - Tag: param
271
+ # Types:
272
+ # - Object
273
+ # - Tag: api # Forbids @api tag entirely (no Types = any occurrence)
274
+
244
275
  # Warnings validators - catches YARD parser errors
245
276
  Warnings/UnknownTag:
246
277
  Description: 'Detects unknown YARD tags.'
@@ -56,6 +56,14 @@ Documentation/UndocumentedOptions:
56
56
  Enabled: true
57
57
  Severity: error
58
58
 
59
+ Documentation/MissingReturn:
60
+ Description: 'Requires @return tags on all methods (opt-in for strict documentation).'
61
+ Enabled: true # Enabled in strict mode
62
+ Severity: error
63
+ ExcludedMethods:
64
+ - 'initialize' # Exclude all initialize methods
65
+ # - '/^_/' # Uncomment to exclude private methods (by convention)
66
+
59
67
  Documentation/MarkdownSyntax:
60
68
  Description: 'Detects common markdown syntax errors in documentation.'
61
69
  Enabled: true
@@ -168,6 +176,15 @@ Tags/ExampleSyntax:
168
176
  Enabled: true
169
177
  Severity: error
170
178
 
179
+ Tags/ExampleStyle:
180
+ Description: 'Validates code style in @example tags using RuboCop/StandardRB.'
181
+ Enabled: false # Opt-in validator (requires RuboCop or StandardRB)
182
+ Severity: convention
183
+ # Linter: auto # Uncomment to explicitly configure: 'auto', 'rubocop', 'standard', 'none'
184
+ # SkipPatterns: # Uncomment to skip examples matching patterns
185
+ # - '/skip-lint/i'
186
+ # - '/bad code/i'
187
+
171
188
  Tags/RedundantParamDescription:
172
189
  Description: 'Detects meaningless parameter descriptions that add no value.'
173
190
  Enabled: true
@@ -245,6 +262,20 @@ Tags/TagGroupSeparator:
245
262
  yield: [yield, yieldparam, yieldreturn]
246
263
  RequireAfterDescription: false
247
264
 
265
+ Tags/ForbiddenTags:
266
+ Description: 'Detects forbidden tag and type combinations.'
267
+ Enabled: false # Opt-in validator
268
+ Severity: error
269
+ ForbiddenPatterns: []
270
+ # Example patterns:
271
+ # - Tag: return
272
+ # Types:
273
+ # - void
274
+ # - Tag: param
275
+ # Types:
276
+ # - Object
277
+ # - Tag: api # Forbids @api tag entirely (no Types = any occurrence)
278
+
248
279
  # Warnings validators - catches YARD parser errors
249
280
  Warnings/UnknownTag:
250
281
  Description: 'Detects unknown YARD tags.'