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
@@ -0,0 +1,261 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ # Generates .yard-lint-todo.yml file with exclusions for current violations
6
+ class TodoGenerator
7
+ # Default grouping threshold (15+ files triggers pattern grouping)
8
+ DEFAULT_EXCLUDE_LIMIT = 15
9
+
10
+ class << self
11
+ # Generate .yard-lint-todo.yml file with exclusions for current violations
12
+ # @param path [String] directory or file path containing Ruby files to analyze for violations
13
+ # @param config [Config] yard-lint configuration object with validator settings
14
+ # @param force [Boolean] whether to overwrite existing todo file if present
15
+ # @param exclude_limit [Integer] minimum files in directory before grouping into wildcard patterns
16
+ # @return [Hash] result with :message, :offense_count, :validator_count
17
+ def generate(path:, config:, force: false, exclude_limit: DEFAULT_EXCLUDE_LIMIT)
18
+ new(path: path, config: config, force: force, exclude_limit: exclude_limit).generate
19
+ end
20
+ end
21
+
22
+ # Initialize a new TodoGenerator instance
23
+ # @param path [String] directory or file path containing Ruby files to analyze
24
+ # @param config [Config] yard-lint configuration object
25
+ # @param force [Boolean] whether to overwrite existing todo file
26
+ # @param exclude_limit [Integer] minimum files before grouping into patterns
27
+ def initialize(path:, config:, force:, exclude_limit:)
28
+ @path = path
29
+ @config = config
30
+ @force = force
31
+ @exclude_limit = exclude_limit
32
+ @todo_path = File.join(Dir.pwd, '.yard-lint-todo.yml')
33
+ @config_path = File.join(Dir.pwd, Config::DEFAULT_CONFIG_FILE)
34
+ end
35
+
36
+ # Generate the .yard-lint-todo.yml file with exclusions for current violations
37
+ # @return [Hash] result hash with :message, :offense_count, :validator_count keys
38
+ def generate
39
+ # Step 1: Check if todo file exists
40
+ validate_todo_file_not_exists! unless @force
41
+
42
+ # Step 2: Run linting to collect violations
43
+ lint_result = run_linting
44
+
45
+ # Step 3: Handle clean codebase
46
+ return no_violations_result if lint_result[:violations_by_validator].empty?
47
+
48
+ # Step 4: Group violations by validator
49
+ violations_by_validator = group_violations_by_validator(lint_result)
50
+
51
+ # Step 5: Generate todo YAML content
52
+ todo_content = build_todo_yaml(violations_by_validator)
53
+
54
+ # Step 6: Write todo file
55
+ File.write(@todo_path, todo_content)
56
+
57
+ # Step 7: Update main config to inherit todo file
58
+ update_main_config
59
+
60
+ # Step 8: Build success message
61
+ build_success_result(violations_by_validator, lint_result[:total_offenses])
62
+ end
63
+
64
+ private
65
+
66
+ # Validate that the todo file doesn't already exist
67
+ # @return [void]
68
+ # @raise [Errors::TodoFileExistsError] if todo file exists and force is false
69
+ def validate_todo_file_not_exists!
70
+ return unless File.exist?(@todo_path)
71
+
72
+ raise Errors::TodoFileExistsError,
73
+ '.yard-lint-todo.yml already exists. Use --regenerate-todo to overwrite.'
74
+ end
75
+
76
+ # Run linting and collect violations per validator
77
+ # @return [Hash] hash with :violations_by_validator and :total_offenses keys
78
+ def run_linting
79
+ # Run each validator individually to track violations per validator
80
+ # This is more reliable than trying to infer validator from offense name
81
+ files = Yard::Lint.send(:expand_path, @path, @config)
82
+ runner = Runner.new(files, @config)
83
+
84
+ # Run validators and collect raw results
85
+ raw_results = runner.send(:run_validators)
86
+ result_builder = ResultBuilder.new(@config)
87
+
88
+ violations_by_validator = {}
89
+ total_offenses = 0
90
+
91
+ # Process each validator's results
92
+ ConfigLoader::ALL_VALIDATORS.each do |validator_name|
93
+ next unless @config.validator_enabled?(validator_name)
94
+
95
+ validator_result = result_builder.build(validator_name, raw_results)
96
+ next unless validator_result && validator_result.offenses.any?
97
+
98
+ # Extract file paths from offenses
99
+ file_paths = validator_result.offenses.map do |offense|
100
+ make_relative_path(offense[:location])
101
+ end.uniq.sort
102
+
103
+ violations_by_validator[validator_name] = file_paths
104
+ total_offenses += validator_result.count
105
+ end
106
+
107
+ { violations_by_validator: violations_by_validator, total_offenses: total_offenses }
108
+ end
109
+
110
+ # Build result hash for when no violations are found
111
+ # @return [Hash] result indicating clean codebase
112
+ def no_violations_result
113
+ {
114
+ message: "No offenses found. No .yard-lint-todo.yml needed.\nYour codebase is already compliant!",
115
+ offense_count: 0,
116
+ validator_count: 0
117
+ }
118
+ end
119
+
120
+ # Apply path grouping to each validator's file list
121
+ # @param lint_result [Hash] hash containing violations_by_validator data
122
+ # @return [Hash] hash of validator names to grouped file patterns
123
+ def group_violations_by_validator(lint_result)
124
+ # Apply path grouping to each validator's file list
125
+ lint_result[:violations_by_validator].transform_values do |files|
126
+ PathGrouper.group(files, limit: @exclude_limit)
127
+ end
128
+ end
129
+
130
+ # Convert absolute path to relative path from current directory
131
+ # @param path [String] absolute or relative file path
132
+ # @return [String] relative path from current directory
133
+ def make_relative_path(path)
134
+ pwd = Dir.pwd
135
+ path.start_with?(pwd) ? path.sub("#{pwd}/", '') : path
136
+ end
137
+
138
+ # Build YAML content for the todo file
139
+ # @param violations_by_validator [Hash] hash of validator names to file patterns
140
+ # @return [String] formatted YAML content
141
+ def build_todo_yaml(violations_by_validator)
142
+ lines = []
143
+
144
+ # Header
145
+ lines << "# This file was auto-generated by yard-lint --auto-gen-config on #{Time.now.utc.strftime('%Y-%m-%d %H:%M:%S UTC')}"
146
+ lines << '# It contains exclusions for all current violations to establish a baseline.'
147
+ lines << '#'
148
+ lines << '# To gradually fix violations:'
149
+ lines << '# 1. Remove files/patterns from the Exclude list below'
150
+ lines << '# 2. Run yard-lint to see the violations for those files'
151
+ lines << '# 3. Fix the violations'
152
+ lines << '# 4. Commit the changes'
153
+ lines << '#'
154
+ lines << '# To regenerate this file, run: yard-lint --regenerate-todo'
155
+ lines << ''
156
+
157
+ # Group validators by category
158
+ categories = group_by_category(violations_by_validator)
159
+
160
+ # Write each category
161
+ ConfigUpdater::CATEGORY_ORDER.each do |category|
162
+ validators = categories[category]
163
+ next unless validators&.any?
164
+
165
+ lines << ConfigUpdater::CATEGORY_COMMENTS[category] if ConfigUpdater::CATEGORY_COMMENTS[category]
166
+
167
+ validators.each do |validator_name, file_patterns|
168
+ lines << "#{validator_name}:"
169
+ lines << ' Exclude:'
170
+ file_patterns.each do |pattern|
171
+ lines << " - '#{pattern}'"
172
+ end
173
+ lines << ''
174
+ end
175
+ end
176
+
177
+ lines.join("\n")
178
+ end
179
+
180
+ # Group validators by their category (Documentation, Tags, etc.)
181
+ # @param violations_by_validator [Hash] hash of validator names to patterns
182
+ # @return [Hash] validators grouped by category
183
+ def group_by_category(violations_by_validator)
184
+ categories = Hash.new { |h, k| h[k] = {} }
185
+
186
+ violations_by_validator.each do |validator_name, patterns|
187
+ category = validator_name.split('/').first
188
+ categories[category][validator_name] = patterns
189
+ end
190
+
191
+ categories
192
+ end
193
+
194
+ # Update or create main config file to inherit from todo file
195
+ # @return [void]
196
+ def update_main_config
197
+ if File.exist?(@config_path)
198
+ update_existing_config
199
+ else
200
+ create_minimal_config
201
+ end
202
+ end
203
+
204
+ # Update existing config file to add inherit_from todo file
205
+ # @return [void]
206
+ def update_existing_config
207
+ config_yaml = YAML.load_file(@config_path) || {}
208
+ inherit_from = Array(config_yaml['inherit_from'] || [])
209
+
210
+ # Add todo file to inherit_from if not already present
211
+ unless inherit_from.include?('.yard-lint-todo.yml')
212
+ inherit_from.unshift('.yard-lint-todo.yml')
213
+ config_yaml['inherit_from'] = inherit_from
214
+
215
+ # Write updated config
216
+ File.write(@config_path, config_yaml.to_yaml)
217
+ end
218
+ end
219
+
220
+ # Create a minimal config file that inherits from todo file
221
+ # @return [void]
222
+ def create_minimal_config
223
+ content = <<~YAML
224
+ # YARD-Lint Configuration
225
+ # See https://github.com/mensfeld/yard-lint for documentation
226
+
227
+ inherit_from:
228
+ - .yard-lint-todo.yml
229
+ YAML
230
+
231
+ File.write(@config_path, content)
232
+ end
233
+
234
+ # Build success result hash with summary message
235
+ # @param violations_by_validator [Hash] hash of validator names to file patterns
236
+ # @param total_offenses [Integer] total number of offenses found
237
+ # @return [Hash] result with :message, :offense_count, :validator_count keys
238
+ def build_success_result(violations_by_validator, total_offenses)
239
+ lines = []
240
+ lines << 'Created .yard-lint-todo.yml'
241
+ lines << "Silenced #{total_offenses} offense(s) across #{violations_by_validator.size} validator(s):"
242
+
243
+ violations_by_validator.each do |validator_name, file_patterns|
244
+ lines << " #{validator_name}: #{file_patterns.size} pattern(s)"
245
+ end
246
+
247
+ lines << ''
248
+ lines << 'Updated .yard-lint.yml to inherit from .yard-lint-todo.yml' if File.exist?(@config_path)
249
+ lines << ''
250
+ lines << 'Run yard-lint again to confirm - you should see no offenses.'
251
+ lines << 'To fix violations incrementally, remove entries from .yard-lint-todo.yml'
252
+
253
+ {
254
+ message: lines.join("\n"),
255
+ offense_count: total_offenses,
256
+ validator_count: violations_by_validator.size
257
+ }
258
+ end
259
+ end
260
+ end
261
+ end
@@ -85,7 +85,7 @@ module Yard
85
85
  # @return [Object] the configured value or default value from the validator's Config.defaults
86
86
  # @example Usage in a validator (e.g., Tags::RedundantParamDescription)
87
87
  # def config_articles
88
- # config_or_default('Articles')
88
+ # config_or_default("Articles")
89
89
  # end
90
90
  # @note The validator name is automatically extracted from the class namespace.
91
91
  # For example, Yard::Lint::Validators::Tags::RedundantParamDescription::Validator
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module MissingReturn
8
+ # Configuration for MissingReturn validator
9
+ class Config < ::Yard::Lint::Validators::Config
10
+ self.id = :missing_return
11
+ self.defaults = {
12
+ 'Enabled' => false, # Disabled by default (opt-in validator)
13
+ 'Severity' => 'warning',
14
+ 'ExcludedMethods' => [
15
+ 'initialize' # Exclude all initialize methods by default
16
+ ]
17
+ }.freeze
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module MissingReturn
8
+ # Builds messages for missing return tag offenses
9
+ class MessagesBuilder
10
+ class << self
11
+ # Build message for a method missing @return tag
12
+ # @param offense [Hash] offense data with :element key
13
+ # @return [String] formatted message
14
+ def call(offense)
15
+ "Missing @return tag for `#{offense[:element]}`"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module MissingReturn
8
+ # Class used to extract details about methods missing @return tags from validator output
9
+ # @example Output format (skip-lint)
10
+ # /path/to/file.rb:3: ClassName#method_name|2
11
+ # /path/to/file.rb:10: ModuleName.class_method|0
12
+ class Parser < ::Yard::Lint::Parsers::Base
13
+ # Regex used to parse validator output format
14
+ # Format: file.rb:LINE: ObjectName|ARITY
15
+ LINE_REGEX = /^(.+):(\d+): (.+?)\|(\d+)$/
16
+
17
+ # @param validator_output [String] raw validator results string
18
+ # @param config [Yard::Lint::Config, nil] configuration object (optional)
19
+ # @return [Array<Hash>] Array with methods missing @return tags
20
+ def call(validator_output, config: nil)
21
+ excluded_methods = config&.validator_config(
22
+ 'Documentation/MissingReturn',
23
+ 'ExcludedMethods'
24
+ ) || []
25
+
26
+ # Ensure excluded_methods is an Array
27
+ excluded_methods = Array(excluded_methods)
28
+
29
+ # Sanitize patterns: remove nil, empty, whitespace-only, and normalize
30
+ excluded_methods = sanitize_patterns(excluded_methods)
31
+
32
+ validator_output
33
+ .split("\n")
34
+ .map(&:strip)
35
+ .reject(&:empty?)
36
+ .filter_map do |line|
37
+ match = line.match(LINE_REGEX)
38
+ next unless match
39
+
40
+ element = match[3]
41
+ arity = match[4].to_i
42
+
43
+ # Skip if method is in excluded list
44
+ next if method_excluded?(element, arity, excluded_methods)
45
+
46
+ {
47
+ location: match[1],
48
+ line: match[2].to_i,
49
+ element: element
50
+ }
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ # Checks if a method should be excluded based on ExcludedMethods config
57
+ # Supports: simple names, arity notation, and regex patterns
58
+ # @param element [String] the element name (e.g., "Class#method")
59
+ # @param arity [Integer] number of parameters (required + optional,
60
+ # excluding splat and block)
61
+ # @param excluded_methods [Array<String>] list of exclusion patterns
62
+ # @return [Boolean] true if method should be excluded
63
+ def method_excluded?(element, arity, excluded_methods)
64
+ # Extract method name from element (e.g., "Foo::Bar#baz" -> "baz")
65
+ method_name = element.split(/[#.]/).last
66
+ return false unless method_name
67
+
68
+ excluded_methods.any? do |pattern|
69
+ case pattern
70
+ when %r{^/(.+)/$}
71
+ # Regex pattern: '/^_/' matches methods starting with _
72
+ match_regex_pattern(method_name, Regexp.last_match(1))
73
+ when %r{/\d+$}
74
+ # Arity pattern: 'initialize/0' checks method name and parameter count
75
+ match_arity_pattern(method_name, arity, pattern)
76
+ else
77
+ # Simple name match: 'initialize'
78
+ # Simple names match any arity (use arity notation for specific arity)
79
+ method_name == pattern
80
+ end
81
+ end
82
+ end
83
+
84
+ # Sanitize exclusion patterns
85
+ # @param patterns [Array] raw patterns from config
86
+ # @return [Array<String>] cleaned and validated patterns
87
+ def sanitize_patterns(patterns)
88
+ patterns
89
+ .compact # Remove nil values
90
+ .map { |p| p.to_s.strip } # Convert to strings and trim whitespace
91
+ .reject(&:empty?) # Remove empty strings
92
+ .reject { |p| p == '//' } # Reject empty regex (matches everything)
93
+ end
94
+
95
+ # Match a regex pattern against method name with error handling
96
+ # @param method_name [String] the method name to match
97
+ # @param regex_pattern [String] the regex pattern (without delimiters)
98
+ # @return [Boolean] true if matches, false if invalid regex or no match
99
+ def match_regex_pattern(method_name, regex_pattern)
100
+ return false if regex_pattern.empty? # Empty regex would match everything
101
+
102
+ Regexp.new(regex_pattern).match?(method_name)
103
+ rescue RegexpError
104
+ # Invalid regex - skip this pattern
105
+ false
106
+ end
107
+
108
+ # Match an arity pattern like "initialize/0"
109
+ # @param method_name [String] the method name
110
+ # @param arity [Integer] number of parameters the method accepts
111
+ # @param pattern [String] the full pattern like "initialize/0"
112
+ # @return [Boolean] true if matches
113
+ def match_arity_pattern(method_name, arity, pattern)
114
+ pattern_name, pattern_arity_str = pattern.split('/')
115
+
116
+ # Validate arity is numeric
117
+ return false unless pattern_arity_str.match?(/^\d+$/)
118
+
119
+ pattern_arity = pattern_arity_str.to_i
120
+
121
+ method_name == pattern_name && arity == pattern_arity
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ 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 MissingReturn
8
+ # Result object for missing return tag validation
9
+ class Result < Results::Base
10
+ self.default_severity = 'warning'
11
+ self.offense_type = 'line'
12
+ self.offense_name = 'MissingReturnTag'
13
+
14
+ # Build human-readable message for missing return tag offense
15
+ # @param offense [Hash] offense data
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,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module MissingReturn
8
+ # Runs a query that will pick all methods that do not have a @return tag documented
9
+ class Validator < 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
+ # Finds methods without @return tag.
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?
24
+
25
+ # Check if @return tag is missing
26
+ return_tag = object.tag(:return)
27
+ return unless return_tag.nil?
28
+
29
+ # Calculate arity (exclude splat and block parameters)
30
+ arity = object.parameters.reject { |p| p[0].to_s.start_with?('*', '&') }.size
31
+
32
+ # Output method with arity for parser filtering
33
+ collector.puts "#{object.file}:#{object.line}: #{object.title}|#{arity}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ # MissingReturn validator
8
+ #
9
+ # Ensures that all methods have an explicit `@return` tag documented.
10
+ # This validator helps maintain consistent API documentation by catching
11
+ # methods that are missing return value documentation. This validator is
12
+ # disabled by default and must be explicitly enabled in configuration.
13
+ #
14
+ # @example Bad - Missing @return tag
15
+ # # Calculates the total price
16
+ # def calculate_total
17
+ # @items.sum(&:price)
18
+ # end
19
+ #
20
+ # @example Good - Return value documented
21
+ # # Calculates the total price
22
+ # # @return [Float] the total price of all items
23
+ # def calculate_total
24
+ # @items.sum(&:price)
25
+ # end
26
+ #
27
+ # ## Configuration
28
+ #
29
+ # This validator is disabled by default. To enable it:
30
+ #
31
+ # Documentation/MissingReturn:
32
+ # Enabled: true
33
+ # Severity: warning
34
+ # ExcludedMethods:
35
+ # - initialize
36
+ # - '/^_/' # Exclude private methods starting with underscore
37
+ #
38
+ # ### ExcludedMethods
39
+ #
40
+ # Supports three pattern types:
41
+ # - Simple name: 'initialize' - matches all methods with that name
42
+ # - Arity notation: 'initialize/0' - matches only methods with specific parameter count
43
+ # - Regex pattern: '/^_/' - matches methods using regular expressions
44
+ module MissingReturn
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -7,7 +7,7 @@ module Yard
7
7
  module Documentation
8
8
  module UndocumentedBooleanMethods
9
9
  # Class used to extract details about undocumented boolean methods
10
- # @example
10
+ # @example Output format (skip-lint)
11
11
  # Platform::Analysis::Authors#valid?
12
12
  class Parser < Parsers::Base
13
13
  # Regex to extract location and method name from yard list output
@@ -6,7 +6,7 @@ module Yard
6
6
  module Documentation
7
7
  module UndocumentedMethodArguments
8
8
  # Class used to extract details about methods with undocumented arguments
9
- # @example
9
+ # @example Output format (skip-lint)
10
10
  # /path/to/file.rb:10: Platform::Analysis::Authors#initialize
11
11
  class Parser < Parsers::Base
12
12
  # Regex to extract file, line, and method name from yard list output
@@ -21,6 +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
25
+ # doesn't need explicit @param documentation, matching attr_accessor behavior
26
+ return if object.is_attribute?
24
27
 
25
28
  # Check if parameters count exceeds @param tags count
26
29
  param_count = object.parameters.size
@@ -6,7 +6,7 @@ module Yard
6
6
  module Documentation
7
7
  module UndocumentedObjects
8
8
  # Class used to extract details about undocumented objects from raw yard list output
9
- # @example
9
+ # @example Output format (skip-lint)
10
10
  # /path/to/file.rb:3: UndocumentedClass
11
11
  # /path/to/file.rb:4: UndocumentedClass#method_one|2
12
12
  class Parser < ::Yard::Lint::Parsers::Base
@@ -47,6 +47,12 @@ module Yard
47
47
  if type_string.start_with?('{')
48
48
  # {K => V} -> Hash{K => V}
49
49
  "Hash#{type_string}"
50
+ elsif type_string.start_with?('<')
51
+ # <String> -> Array<String>
52
+ "Array#{type_string}"
53
+ elsif type_string.start_with?('(')
54
+ # (String, Integer) -> Array(String, Integer)
55
+ "Array#{type_string}"
50
56
  else
51
57
  # Hash<K, V> -> Hash{K => V}
52
58
  type_string.gsub(/Hash<(.+?)>/) do
@@ -61,8 +67,8 @@ module Yard
61
67
  # @param type_string [String] the type string
62
68
  # @return [String] the converted type string
63
69
  def convert_to_short(type_string)
64
- # Hash{K => V} -> {K => V}
65
- type_string.sub(/^Hash/, '')
70
+ # Hash{K => V} -> {K => V} or Array<String> -> <String> or Array(S, I) -> (S, I)
71
+ type_string.sub(/^(Hash|Array)/, '')
66
72
  end
67
73
  end
68
74
  end