yard-lint 1.0.0 → 1.1.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +42 -265
  4. data/bin/yard-lint +20 -0
  5. data/lib/yard/lint/config.rb +0 -2
  6. data/lib/yard/lint/config_generator.rb +191 -0
  7. data/lib/yard/lint/validators/base.rb +36 -0
  8. data/lib/yard/lint/validators/documentation/markdown_syntax/config.rb +20 -0
  9. data/lib/yard/lint/validators/documentation/markdown_syntax/messages_builder.rb +44 -0
  10. data/lib/yard/lint/validators/documentation/markdown_syntax/parser.rb +53 -0
  11. data/lib/yard/lint/validators/documentation/markdown_syntax/result.rb +25 -0
  12. data/lib/yard/lint/validators/documentation/markdown_syntax/validator.rb +38 -0
  13. data/lib/yard/lint/validators/documentation/markdown_syntax.rb +37 -0
  14. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods.rb +26 -1
  15. data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +26 -1
  16. data/lib/yard/lint/validators/documentation/undocumented_objects.rb +131 -2
  17. data/lib/yard/lint/validators/documentation/undocumented_options/config.rb +20 -0
  18. data/lib/yard/lint/validators/documentation/undocumented_options/parser.rb +53 -0
  19. data/lib/yard/lint/validators/documentation/undocumented_options/result.rb +29 -0
  20. data/lib/yard/lint/validators/documentation/undocumented_options/validator.rb +38 -0
  21. data/lib/yard/lint/validators/documentation/undocumented_options.rb +40 -0
  22. data/lib/yard/lint/validators/semantic/abstract_methods.rb +31 -1
  23. data/lib/yard/lint/validators/tags/api_tags.rb +34 -1
  24. data/lib/yard/lint/validators/tags/collection_type/config.rb +2 -1
  25. data/lib/yard/lint/validators/tags/collection_type/messages_builder.rb +40 -11
  26. data/lib/yard/lint/validators/tags/collection_type/parser.rb +6 -5
  27. data/lib/yard/lint/validators/tags/collection_type/validator.rb +26 -7
  28. data/lib/yard/lint/validators/tags/collection_type.rb +38 -2
  29. data/lib/yard/lint/validators/tags/example_syntax/config.rb +20 -0
  30. data/lib/yard/lint/validators/tags/example_syntax/messages_builder.rb +28 -0
  31. data/lib/yard/lint/validators/tags/example_syntax/parser.rb +79 -0
  32. data/lib/yard/lint/validators/tags/example_syntax/result.rb +42 -0
  33. data/lib/yard/lint/validators/tags/example_syntax/validator.rb +88 -0
  34. data/lib/yard/lint/validators/tags/example_syntax.rb +42 -0
  35. data/lib/yard/lint/validators/tags/invalid_types/validator.rb +2 -2
  36. data/lib/yard/lint/validators/tags/invalid_types.rb +25 -1
  37. data/lib/yard/lint/validators/tags/meaningless_tag/validator.rb +2 -4
  38. data/lib/yard/lint/validators/tags/meaningless_tag.rb +31 -3
  39. data/lib/yard/lint/validators/tags/option_tags/validator.rb +7 -1
  40. data/lib/yard/lint/validators/tags/option_tags.rb +26 -1
  41. data/lib/yard/lint/validators/tags/order.rb +25 -1
  42. data/lib/yard/lint/validators/tags/redundant_param_description/config.rb +33 -0
  43. data/lib/yard/lint/validators/tags/redundant_param_description/messages_builder.rb +61 -0
  44. data/lib/yard/lint/validators/tags/redundant_param_description/parser.rb +67 -0
  45. data/lib/yard/lint/validators/tags/redundant_param_description/result.rb +25 -0
  46. data/lib/yard/lint/validators/tags/redundant_param_description/validator.rb +148 -0
  47. data/lib/yard/lint/validators/tags/redundant_param_description.rb +168 -0
  48. data/lib/yard/lint/validators/tags/tag_type_position/validator.rb +2 -4
  49. data/lib/yard/lint/validators/tags/tag_type_position.rb +39 -2
  50. data/lib/yard/lint/validators/tags/type_syntax.rb +26 -2
  51. data/lib/yard/lint/validators/warnings/duplicated_parameter_name.rb +26 -1
  52. data/lib/yard/lint/validators/warnings/invalid_directive_format.rb +26 -1
  53. data/lib/yard/lint/validators/warnings/invalid_tag_format.rb +25 -1
  54. data/lib/yard/lint/validators/warnings/unknown_directive.rb +26 -1
  55. data/lib/yard/lint/validators/warnings/unknown_parameter_name.rb +23 -1
  56. data/lib/yard/lint/validators/warnings/unknown_tag.rb +26 -1
  57. data/lib/yard/lint/version.rb +1 -1
  58. metadata +39 -1
@@ -4,9 +4,37 @@ module Yard
4
4
  module Lint
5
5
  module Validators
6
6
  module Tags
7
- # MeaninglessTag validator module
8
- # Detects @param and @option tags on classes, modules, or constants
9
- # (these tags only make sense on methods)
7
+ # MeaninglessTag validator
8
+ #
9
+ # Prevents `@param` and `@option` tags from being used on classes, modules,
10
+ # or constants, where they make no sense. These tags are only valid on methods.
11
+ # This validator is enabled by default.
12
+ #
13
+ # ## Configuration
14
+ #
15
+ # To disable this validator:
16
+ #
17
+ # Tags/MeaninglessTag:
18
+ # Enabled: false
19
+ #
20
+ # @example Bad - @param on a class
21
+ # # @param name [String] this makes no sense on a class
22
+ # class User
23
+ # end
24
+ #
25
+ # @example Bad - @option on a module
26
+ # # @option config [Boolean] :enabled modules don't have parameters
27
+ # module Authentication
28
+ # end
29
+ #
30
+ # @example Good - @param on a method
31
+ # class User
32
+ # # @param name [String] the user's name
33
+ # def initialize(name)
34
+ # @name = name
35
+ # end
36
+ # end
37
+ #
10
38
  module MeaninglessTag
11
39
  end
12
40
  end
@@ -29,13 +29,14 @@ module Yard
29
29
  # @return [String] yard query to find methods with options parameter
30
30
  # but no @option tags
31
31
  def query
32
+ parameter_names = config_parameter_names
32
33
  <<~QUERY
33
34
  '
34
35
  if object.is_a?(YARD::CodeObjects::MethodObject)
35
36
  # Check if method has a parameter named "options" or "opts"
36
37
  has_options_param = object.parameters.any? do |param|
37
38
  param_name = param[0].to_s.gsub(/[*:]/, '')
38
- ['options', 'opts', 'kwargs'].include?(param_name)
39
+ #{parameter_names.inspect}.include?(param_name)
39
40
  end
40
41
 
41
42
  if has_options_param
@@ -52,6 +53,11 @@ module Yard
52
53
  '
53
54
  QUERY
54
55
  end
56
+
57
+ # @return [Array<String>] parameter names that should have @option tags
58
+ def config_parameter_names
59
+ config.validator_config('Tags/OptionTags', 'ParameterNames') || Config.defaults['ParameterNames']
60
+ end
55
61
  end
56
62
  end
57
63
  end
@@ -4,7 +4,32 @@ module Yard
4
4
  module Lint
5
5
  module Validators
6
6
  module Tags
7
- # OptionTags validator module
7
+ # OptionTags validator
8
+ #
9
+ # Ensures that methods with options parameters document them using `@option`
10
+ # tags. This validator is enabled by default.
11
+ #
12
+ # ## Configuration
13
+ #
14
+ # To disable this validator:
15
+ #
16
+ # Tags/OptionTags:
17
+ # Enabled: false
18
+ #
19
+ # @example Good - Options hash is documented
20
+ # # @param name [String] the name
21
+ # # @param options [Hash] configuration options
22
+ # # @option options [Boolean] :enabled Whether to enable the feature
23
+ # # @option options [Integer] :timeout Timeout in seconds
24
+ # def configure(name, options = {})
25
+ # end
26
+ #
27
+ # @example Bad - Missing @option tags
28
+ # # @param name [String] the name
29
+ # # @param options [Hash] configuration options
30
+ # def configure(name, options = {})
31
+ # end
32
+ #
8
33
  module OptionTags
9
34
  end
10
35
  end
@@ -4,7 +4,31 @@ module Yard
4
4
  module Lint
5
5
  module Validators
6
6
  module Tags
7
- # Order validator module
7
+ # Order validator
8
+ #
9
+ # Enforces a consistent order for YARD documentation tags. This validator
10
+ # checks that tags appear in a logical sequence (e.g., `@param` before
11
+ # `@return`, `@option` after `@param`). This validator is enabled by default.
12
+ #
13
+ # @example Bad - Tags in wrong order
14
+ # # @return [String] the result
15
+ # # @param name [String] the name
16
+ # def process(name)
17
+ # end
18
+ #
19
+ # @example Good - Tags in correct order
20
+ # # @param name [String] the name
21
+ # # @return [String] the result
22
+ # def process(name)
23
+ # end
24
+ #
25
+ # ## Configuration
26
+ #
27
+ # To disable this validator:
28
+ #
29
+ # Tags/Order:
30
+ # Enabled: false
31
+ #
8
32
  module Order
9
33
  end
10
34
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module RedundantParamDescription
8
+ # Configuration for RedundantParamDescription validator
9
+ class Config < ::Yard::Lint::Validators::Config
10
+ self.id = :redundant_param_description
11
+ self.defaults = {
12
+ 'Enabled' => true,
13
+ 'Severity' => 'convention',
14
+ 'CheckedTags' => %w[param option],
15
+ 'Articles' => %w[The the A a An an],
16
+ 'MaxRedundantWords' => 6,
17
+ 'GenericTerms' => %w[object instance value data item element],
18
+ 'EnabledPatterns' => {
19
+ 'ArticleParam' => true,
20
+ 'PossessiveParam' => true,
21
+ 'TypeRestatement' => true,
22
+ 'ParamToVerb' => true,
23
+ 'IdPattern' => true,
24
+ 'DirectionalDate' => true,
25
+ 'TypeGeneric' => true
26
+ }
27
+ }.freeze
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module RedundantParamDescription
8
+ # Builds human-readable messages for redundant parameter description violations
9
+ class MessagesBuilder
10
+ # Build message for a violation
11
+ # @param offense [Hash] the offense data
12
+ # @return [String] formatted message
13
+ def self.call(offense)
14
+ tag_name = offense[:tag_name]
15
+ param_name = offense[:param_name]
16
+ description = offense[:description]
17
+ pattern_type = offense[:pattern_type]
18
+
19
+ case pattern_type
20
+ when 'article_param'
21
+ 'The @' + tag_name + ' description \'' + description + '\' is redundant - ' \
22
+ 'it just restates the parameter name. ' \
23
+ 'Consider removing the description: @' + tag_name + ' ' + param_name + ' [Type]'
24
+
25
+ when 'possessive_param'
26
+ 'The @' + tag_name + ' description \'' + description + '\' adds no meaningful information ' \
27
+ 'beyond the parameter name. ' \
28
+ 'Consider removing it or explaining the parameter\'s specific purpose.'
29
+
30
+ when 'type_restatement'
31
+ 'The @' + tag_name + ' description \'' + description + '\' just repeats the type name. ' \
32
+ 'Consider removing the description or explaining what makes this ' + param_name + ' significant.'
33
+
34
+ when 'param_to_verb'
35
+ 'The @' + tag_name + ' description \'' + description + '\' is too generic. ' \
36
+ 'Consider removing it or explaining what the ' + param_name + ' is used for in detail.'
37
+
38
+ when 'id_pattern'
39
+ 'The @' + tag_name + ' description \'' + description + '\' is self-explanatory from the parameter name. ' \
40
+ 'Consider removing the description: @' + tag_name + ' ' + param_name + ' [Type]'
41
+
42
+ when 'directional_date'
43
+ 'The @' + tag_name + ' description \'' + description + '\' is redundant - ' \
44
+ 'the parameter name already indicates this. ' \
45
+ 'Consider removing the description or explaining the date\'s specific meaning.'
46
+
47
+ when 'type_generic'
48
+ 'The @' + tag_name + ' description \'' + description + '\' just combines type and generic terms. ' \
49
+ 'Consider removing it or providing specific details about this ' + param_name + '.'
50
+
51
+ else
52
+ 'The @' + tag_name + ' description \'' + description + '\' appears redundant. ' \
53
+ 'Consider providing a meaningful description or omitting it entirely.'
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module RedundantParamDescription
8
+ # Parses YARD output for redundant parameter description violations
9
+ class Parser < Parsers::Base
10
+ # Parse YARD output into structured violations
11
+ # @param yard_output [String] raw YARD output
12
+ # @return [Array<Hash>] array of violation hashes
13
+ def call(yard_output)
14
+ return [] if yard_output.nil? || yard_output.empty?
15
+
16
+ violations = []
17
+ lines = yard_output.lines.map(&:chomp)
18
+
19
+ i = 0
20
+ while i < lines.length
21
+ line = lines[i]
22
+
23
+ # Match location line: "file:line: object_name"
24
+ location_match = line.match(/^(.+):(\d+): (.+)$/)
25
+ unless location_match
26
+ i += 1
27
+ next
28
+ end
29
+
30
+ file_path = location_match[1]
31
+ line_number = location_match[2].to_i
32
+ object_name = location_match[3]
33
+
34
+ # Next line contains violation data
35
+ i += 1
36
+ next unless i < lines.length
37
+
38
+ data_line = lines[i]
39
+ parts = data_line.split('|')
40
+ next unless parts.length == 6
41
+
42
+ tag_name, param_name, description, type_name, pattern_type, word_count = parts
43
+
44
+ violations << {
45
+ name: 'RedundantParamDescription',
46
+ tag_name: tag_name,
47
+ param_name: param_name,
48
+ description: description,
49
+ type_name: type_name.empty? ? nil : type_name,
50
+ pattern_type: pattern_type,
51
+ word_count: word_count.to_i,
52
+ location: file_path,
53
+ line: line_number,
54
+ object_name: object_name
55
+ }
56
+
57
+ i += 1
58
+ end
59
+
60
+ violations
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module RedundantParamDescription
8
+ # Result builder for redundant parameter description violations
9
+ class Result < Results::Base
10
+ self.default_severity = 'convention'
11
+ self.offense_type = 'tag'
12
+ self.offense_name = 'RedundantParamDescription'
13
+
14
+ # Build human-readable message for redundant param offense
15
+ # @param offense [Hash] offense data with redundancy 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,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module RedundantParamDescription
8
+ # Validates that parameter descriptions are not redundant/meaningless
9
+ class Validator < Validators::Base
10
+ # YARD query to detect redundant parameter descriptions
11
+ # @return [String] YARD Ruby query code
12
+ def query
13
+ articles = config_articles.join('|')
14
+ generic_terms = config_generic_terms.join('|')
15
+ max_words = config_max_redundant_words
16
+ checked_tags = config_checked_tags
17
+ patterns = config_enabled_patterns
18
+
19
+ # Build query as single line for shell compatibility
20
+ query_body = 'if object.is_a?(YARD::CodeObjects::MethodObject); ' \
21
+ "object.docstring.tags.select { |tag| #{checked_tags.inspect}.include?(tag.tag_name) }.each do |tag|; " \
22
+ 'next unless tag.name && tag.text && !tag.text.strip.empty?; ' \
23
+ 'param_name = tag.name; ' \
24
+ 'description = tag.text.strip.gsub(/\\.$/, ""); ' \
25
+ 'word_count = description.split.length; ' \
26
+ 'type_name = tag.types&.first&.gsub(/[<>{}\\[\\],]/, "")&.strip; ' \
27
+ "next if word_count > #{max_words}; " \
28
+ 'pattern_type = nil; ' \
29
+ "if #{patterns['ArticleParam']} && word_count <= 3; " \
30
+ "articles_re = /^(#{articles})/i; " \
31
+ 'desc_parts = description.split; ' \
32
+ 'if desc_parts.length == 2 && desc_parts[0].match?(articles_re) && desc_parts[1].downcase == param_name.downcase; ' \
33
+ 'pattern_type = "article_param"; ' \
34
+ 'end; ' \
35
+ 'end; ' \
36
+ "if pattern_type.nil? && #{patterns['PossessiveParam']} && word_count <= 4; " \
37
+ 'desc_parts = description.split; ' \
38
+ 'if desc_parts.length >= 3; ' \
39
+ "articles_re = /^(#{articles})/i; " \
40
+ 'if desc_parts[0].match?(articles_re) && desc_parts[1].end_with?("s") && desc_parts[1].include?(39.chr) && desc_parts[2].downcase == param_name.downcase; ' \
41
+ 'pattern_type = "possessive_param"; ' \
42
+ 'end; ' \
43
+ 'end; ' \
44
+ 'end; ' \
45
+ "if pattern_type.nil? && #{patterns['TypeRestatement']} && type_name && word_count <= 2; " \
46
+ "generic_terms_arr = [\"#{generic_terms.gsub('|', '\", \"')}\"].map(&:downcase); " \
47
+ 'if description.downcase == type_name.downcase; ' \
48
+ 'pattern_type = "type_restatement"; ' \
49
+ 'elsif word_count == 2; ' \
50
+ 'parts = description.split; ' \
51
+ 'if parts[0].downcase == type_name.downcase && generic_terms_arr.include?(parts[1].downcase); ' \
52
+ 'pattern_type = "type_restatement"; ' \
53
+ 'end; ' \
54
+ 'end; ' \
55
+ 'end; ' \
56
+ "if pattern_type.nil? && #{patterns['ParamToVerb']} && word_count <= 4; " \
57
+ 'parts = description.split; ' \
58
+ 'if parts.length == 3 && parts[0].downcase == param_name.downcase && parts[1].downcase == "to"; ' \
59
+ 'pattern_type = "param_to_verb"; ' \
60
+ 'end; ' \
61
+ 'end; ' \
62
+ "if pattern_type.nil? && #{patterns['IdPattern']} && word_count <= 6; " \
63
+ 'if param_name =~ /_id$|_uuid$|_identifier$/; ' \
64
+ 'if description =~ /^(ID|Unique identifier|Identifier)\\s+(of|for)\\s+/i; ' \
65
+ 'pattern_type = "id_pattern"; ' \
66
+ 'end; ' \
67
+ 'end; ' \
68
+ 'end; ' \
69
+ "if pattern_type.nil? && #{patterns['DirectionalDate']} && word_count <= 4; " \
70
+ 'if param_name =~ /^(from|to|till|until)$/; ' \
71
+ 'parts = description.split; ' \
72
+ 'if parts.length == 3 && parts[0].downcase == param_name.downcase && parts[1].downcase == "this"; ' \
73
+ 'pattern_type = "directional_date"; ' \
74
+ 'end; ' \
75
+ 'end; ' \
76
+ 'end; ' \
77
+ "if pattern_type.nil? && #{patterns['TypeGeneric']} && type_name && word_count <= 5; " \
78
+ "generic_terms_arr = [\"#{generic_terms.gsub('|', '\", \"')}\"].map(&:downcase); " \
79
+ 'parts = description.split; ' \
80
+ 'if parts.length >= 2 && parts[0].downcase == type_name.downcase && generic_terms_arr.include?(parts[1].downcase); ' \
81
+ 'pattern_type = "type_generic"; ' \
82
+ 'end; ' \
83
+ 'end; ' \
84
+ 'if pattern_type; ' \
85
+ 'puts object.file + ":" + object.line.to_s + ": " + object.title; ' \
86
+ 'puts tag.tag_name + "|" + param_name + "|" + tag.text.strip + "|" + (type_name || "") + "|" + pattern_type + "|" + word_count.to_s; ' \
87
+ 'end; ' \
88
+ 'end; ' \
89
+ 'end; ' \
90
+ 'false'
91
+
92
+ # Wrap in single quotes like other validators do
93
+ "'#{query_body}'"
94
+ end
95
+
96
+ # Builds and executes the YARD command
97
+ # @param dir [String] the directory containing the .yardoc database
98
+ # @param file_list_path [String] path to file containing list of files to analyze
99
+ # @return [Hash] command output with stdout, stderr, exit_code
100
+ def yard_cmd(dir, file_list_path)
101
+ # Create a temporary script file to avoid shell escaping issues
102
+ require 'tempfile'
103
+
104
+ script = Tempfile.new(['yard_lint_query', '.sh'])
105
+ script.write("#!/bin/bash\n")
106
+ # Write query to a variable - since query already has outer single quotes, just assign it
107
+ script.write("QUERY=#{query}\n")
108
+ script.write("cat #{Shellwords.escape(file_list_path)} | xargs yard list #{shell_arguments} --query \"$QUERY\" -q -b #{Shellwords.escape(dir)}\n")
109
+ script.chmod(0o755)
110
+ script.close
111
+
112
+ result = shell(script.path)
113
+ script.unlink
114
+ result
115
+ end
116
+
117
+ private
118
+
119
+ # @return [Array<String>] configured articles to check
120
+ def config_articles
121
+ config_or_default('Articles')
122
+ end
123
+
124
+ # @return [Array<String>] configured generic terms to check
125
+ def config_generic_terms
126
+ config_or_default('GenericTerms')
127
+ end
128
+
129
+ # @return [Integer] maximum word count for redundant descriptions
130
+ def config_max_redundant_words
131
+ config_or_default('MaxRedundantWords')
132
+ end
133
+
134
+ # @return [Array<String>] tags to check for redundant descriptions
135
+ def config_checked_tags
136
+ config_or_default('CheckedTags')
137
+ end
138
+
139
+ # @return [Hash] enabled pattern detection flags
140
+ def config_enabled_patterns
141
+ config_or_default('EnabledPatterns')
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ # Tags validators - validate YARD tag quality and consistency
7
+ module Tags
8
+ # RedundantParamDescription validator
9
+ #
10
+ # Detects parameter descriptions that add no meaningful information beyond
11
+ # what's already obvious from the parameter name and type. This validator
12
+ # helps maintain high-quality documentation by flagging descriptions that
13
+ # should either be expanded with meaningful details or removed entirely.
14
+ #
15
+ # ## Why This Matters
16
+ #
17
+ # Documentation is most valuable when AI assistants and human developers
18
+ # can trust it. Redundant descriptions like `@param user [User] The user`
19
+ # create noise without adding value, training both humans and AI to ignore
20
+ # parameter documentation. Better to have no description (letting the type
21
+ # speak for itself) than a redundant one.
22
+ #
23
+ # @example Redundant - Article + param name (will be flagged)
24
+ # # @param appointment [Appointment] The appointment
25
+ # # @param user [User] the user
26
+ # def process(appointment, user)
27
+ # end
28
+ #
29
+ # @example Redundant - Possessive form (will be flagged)
30
+ # # @param appointment [Appointment] The event's appointment
31
+ # def schedule(appointment)
32
+ # end
33
+ #
34
+ # @example Redundant - Type restatement (will be flagged)
35
+ # # @param user [User] User object
36
+ # # @param value [Integer] Integer value
37
+ # def update(user, value)
38
+ # end
39
+ #
40
+ # @example Redundant - Parameter to verb pattern (will be flagged)
41
+ # # @param payments [Array] Payments to count
42
+ # # @param user [User] User to notify
43
+ # def process(payments, user)
44
+ # end
45
+ #
46
+ # @example Redundant - ID pattern (will be flagged)
47
+ # # @param treatment_id [String] ID of the treatment
48
+ # # @param uuid [String] Unique identifier for the list
49
+ # def find(treatment_id, uuid)
50
+ # end
51
+ #
52
+ # @example Redundant - Directional date (will be flagged)
53
+ # # @param from [Date] from this date
54
+ # # @param till [Date] till this date
55
+ # def filter(from, till)
56
+ # end
57
+ #
58
+ # @example Redundant - Type + generic term (will be flagged)
59
+ # # @param payment [Payment] Payment object
60
+ # # @param data [Hash] Hash data
61
+ # def process(payment, data)
62
+ # end
63
+ #
64
+ # @example Good - No description (type is self-explanatory)
65
+ # # @param appointment [Appointment]
66
+ # # @param user [User]
67
+ # def process(appointment, user)
68
+ # end
69
+ #
70
+ # @example Good - Long, meaningful descriptions with context
71
+ # # @param date [Date, nil] the date that can describe the event starting information or nil if event did not yet start
72
+ # # @param user [User] the user who initiated the request and will receive notifications
73
+ # # @param data [Hash] configuration options for the API endpoint including timeout and retry settings
74
+ # def configure(date, user, data)
75
+ # end
76
+ #
77
+ # @example Good - Short but adds value beyond param name
78
+ # # @param count [Integer] maximum number of retries before giving up
79
+ # # @param timeout [Integer] seconds to wait before timing out the connection
80
+ # # @param enabled [Boolean] whether the feature is enabled for this account
81
+ # def setup(count, timeout, enabled)
82
+ # end
83
+ #
84
+ # @example Good - Starts similar but continues with valuable info
85
+ # # @param user [User] the current user, or guest if not authenticated
86
+ # # @param data [Hash] the request payload containing user preferences
87
+ # # @param id [String] unique identifier used for tracking across systems
88
+ # def track(user, data, id)
89
+ # end
90
+ #
91
+ # ## Configuration
92
+ #
93
+ # The validator is highly configurable to match your project's needs:
94
+ #
95
+ # Tags/RedundantParamDescription:
96
+ # Description: 'Detects meaningless parameter descriptions.'
97
+ # Enabled: true
98
+ # Severity: convention
99
+ # CheckedTags:
100
+ # - param
101
+ # - option
102
+ # # Articles that trigger the article_param pattern
103
+ # Articles:
104
+ # - The
105
+ # - the
106
+ # - A
107
+ # - a
108
+ # - An
109
+ # - an
110
+ # # Maximum word count for redundant descriptions (longer descriptions are never flagged)
111
+ # MaxRedundantWords: 6
112
+ # # Generic terms that trigger the type_generic pattern
113
+ # GenericTerms:
114
+ # - object
115
+ # - instance
116
+ # - value
117
+ # - data
118
+ # - item
119
+ # - element
120
+ # # Pattern toggles (enable/disable specific detection patterns)
121
+ # EnabledPatterns:
122
+ # ArticleParam: true # "The user", "the appointment"
123
+ # PossessiveParam: true # "The event's appointment"
124
+ # TypeRestatement: true # "User object", "Appointment"
125
+ # ParamToVerb: true # "Payments to count"
126
+ # IdPattern: true # "ID of the treatment" for *_id params
127
+ # DirectionalDate: true # "from this date" for from/to/till
128
+ # TypeGeneric: true # "Payment object", "Hash data"
129
+ #
130
+ # ## Pattern Types
131
+ #
132
+ # The validator detects 7 different redundancy patterns:
133
+ #
134
+ # 1. **ArticleParam**: `"The user"`, `"the appointment"` - Article + parameter name
135
+ # 2. **PossessiveParam**: `"The event's appointment"` - Possessive form + parameter name
136
+ # 3. **TypeRestatement**: `"User object"`, `"Appointment"` - Just repeats the type
137
+ # 4. **ParamToVerb**: `"Payments to count"` - Parameter name + "to" + verb
138
+ # 5. **IdPattern**: `"ID of the treatment"` - For `_id` or `_uuid` suffixed parameters
139
+ # 6. **DirectionalDate**: `"from this date"` - For `from`, `to`, `till` parameters
140
+ # 7. **TypeGeneric**: `"Payment object"` - Type + generic term like "object", "instance"
141
+ #
142
+ # You can disable individual patterns while keeping others enabled.
143
+ #
144
+ # ## False Positive Prevention
145
+ #
146
+ # The validator uses multiple strategies to prevent false positives:
147
+ #
148
+ # 1. **Word count threshold**: Descriptions longer than `MaxRedundantWords` (default: 6)
149
+ # are never flagged, even if they start with a redundant pattern
150
+ # 2. **EXACT pattern matching**: Only flags complete matches, not partial/prefix matches
151
+ # 3. **Configurable patterns**: Disable patterns that don't work for your codebase
152
+ #
153
+ # This means `"the date that can describe the event starting information"` (9 words)
154
+ # will never be flagged, even though it starts with "the date".
155
+ #
156
+ # ## Disabling
157
+ #
158
+ # To disable this validator:
159
+ #
160
+ # Tags/RedundantParamDescription:
161
+ # Enabled: false
162
+ #
163
+ module RedundantParamDescription
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -94,15 +94,13 @@ module Yard
94
94
 
95
95
  # @return [String] the enforced style ('type_after_name' (standard) or 'type_first')
96
96
  def enforced_style
97
- config.validator_config('Tags/TagTypePosition', 'EnforcedStyle') || 'type_after_name'
97
+ config_or_default('EnforcedStyle')
98
98
  end
99
99
 
100
100
  # Array of tag names to check, formatted for YARD query
101
101
  # @return [String] Ruby array literal string
102
102
  def checked_tags_array
103
- tags = config.validator_config(
104
- 'Tags/TagTypePosition', 'CheckedTags'
105
- ) || %w[param option]
103
+ tags = config_or_default('CheckedTags')
106
104
  "[#{tags.map { |t| "\"#{t}\"" }.join(',')}]"
107
105
  end
108
106
  end