yard-lint 0.2.1 → 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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -1
  3. data/README.md +121 -89
  4. data/bin/yard-lint +44 -0
  5. data/lib/yard/lint/config.rb +35 -3
  6. data/lib/yard/lint/config_generator.rb +191 -0
  7. data/lib/yard/lint/config_loader.rb +1 -1
  8. data/lib/yard/lint/result_builder.rb +10 -1
  9. data/lib/yard/lint/validators/base.rb +77 -12
  10. data/lib/yard/lint/validators/documentation/markdown_syntax/config.rb +20 -0
  11. data/lib/yard/lint/validators/documentation/markdown_syntax/messages_builder.rb +44 -0
  12. data/lib/yard/lint/validators/documentation/markdown_syntax/parser.rb +53 -0
  13. data/lib/yard/lint/validators/documentation/markdown_syntax/result.rb +25 -0
  14. data/lib/yard/lint/validators/documentation/markdown_syntax/validator.rb +38 -0
  15. data/lib/yard/lint/validators/documentation/markdown_syntax.rb +37 -0
  16. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/validator.rb +7 -6
  17. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods.rb +26 -1
  18. data/lib/yard/lint/validators/documentation/undocumented_method_arguments/validator.rb +4 -5
  19. data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +26 -1
  20. data/lib/yard/lint/validators/documentation/undocumented_objects/config.rb +2 -1
  21. data/lib/yard/lint/validators/documentation/undocumented_objects/parser.rb +95 -5
  22. data/lib/yard/lint/validators/documentation/undocumented_objects/validator.rb +15 -11
  23. data/lib/yard/lint/validators/documentation/undocumented_objects.rb +131 -2
  24. data/lib/yard/lint/validators/documentation/undocumented_options/config.rb +20 -0
  25. data/lib/yard/lint/validators/documentation/undocumented_options/parser.rb +53 -0
  26. data/lib/yard/lint/validators/documentation/undocumented_options/result.rb +29 -0
  27. data/lib/yard/lint/validators/documentation/undocumented_options/validator.rb +38 -0
  28. data/lib/yard/lint/validators/documentation/undocumented_options.rb +40 -0
  29. data/lib/yard/lint/validators/semantic/abstract_methods/validator.rb +4 -5
  30. data/lib/yard/lint/validators/semantic/abstract_methods.rb +31 -1
  31. data/lib/yard/lint/validators/tags/api_tags/validator.rb +4 -5
  32. data/lib/yard/lint/validators/tags/api_tags.rb +34 -1
  33. data/lib/yard/lint/validators/tags/collection_type/config.rb +22 -0
  34. data/lib/yard/lint/validators/tags/collection_type/messages_builder.rb +73 -0
  35. data/lib/yard/lint/validators/tags/collection_type/parser.rb +50 -0
  36. data/lib/yard/lint/validators/tags/collection_type/result.rb +25 -0
  37. data/lib/yard/lint/validators/tags/collection_type/validator.rb +92 -0
  38. data/lib/yard/lint/validators/tags/collection_type.rb +50 -0
  39. data/lib/yard/lint/validators/tags/example_syntax/config.rb +20 -0
  40. data/lib/yard/lint/validators/tags/example_syntax/messages_builder.rb +28 -0
  41. data/lib/yard/lint/validators/tags/example_syntax/parser.rb +79 -0
  42. data/lib/yard/lint/validators/tags/example_syntax/result.rb +42 -0
  43. data/lib/yard/lint/validators/tags/example_syntax/validator.rb +88 -0
  44. data/lib/yard/lint/validators/tags/example_syntax.rb +42 -0
  45. data/lib/yard/lint/validators/tags/invalid_types/validator.rb +8 -8
  46. data/lib/yard/lint/validators/tags/invalid_types.rb +25 -1
  47. data/lib/yard/lint/validators/tags/meaningless_tag/config.rb +22 -0
  48. data/lib/yard/lint/validators/tags/meaningless_tag/messages_builder.rb +28 -0
  49. data/lib/yard/lint/validators/tags/meaningless_tag/parser.rb +53 -0
  50. data/lib/yard/lint/validators/tags/meaningless_tag/result.rb +26 -0
  51. data/lib/yard/lint/validators/tags/meaningless_tag/validator.rb +82 -0
  52. data/lib/yard/lint/validators/tags/meaningless_tag.rb +43 -0
  53. data/lib/yard/lint/validators/tags/option_tags/validator.rb +11 -6
  54. data/lib/yard/lint/validators/tags/option_tags.rb +26 -1
  55. data/lib/yard/lint/validators/tags/order/validator.rb +4 -5
  56. data/lib/yard/lint/validators/tags/order.rb +25 -1
  57. data/lib/yard/lint/validators/tags/redundant_param_description/config.rb +33 -0
  58. data/lib/yard/lint/validators/tags/redundant_param_description/messages_builder.rb +61 -0
  59. data/lib/yard/lint/validators/tags/redundant_param_description/parser.rb +67 -0
  60. data/lib/yard/lint/validators/tags/redundant_param_description/result.rb +25 -0
  61. data/lib/yard/lint/validators/tags/redundant_param_description/validator.rb +148 -0
  62. data/lib/yard/lint/validators/tags/redundant_param_description.rb +168 -0
  63. data/lib/yard/lint/validators/tags/tag_type_position/config.rb +22 -0
  64. data/lib/yard/lint/validators/tags/tag_type_position/messages_builder.rb +38 -0
  65. data/lib/yard/lint/validators/tags/tag_type_position/parser.rb +51 -0
  66. data/lib/yard/lint/validators/tags/tag_type_position/result.rb +25 -0
  67. data/lib/yard/lint/validators/tags/tag_type_position/validator.rb +111 -0
  68. data/lib/yard/lint/validators/tags/tag_type_position.rb +51 -0
  69. data/lib/yard/lint/validators/tags/type_syntax/config.rb +21 -0
  70. data/lib/yard/lint/validators/tags/type_syntax/messages_builder.rb +27 -0
  71. data/lib/yard/lint/validators/tags/type_syntax/parser.rb +54 -0
  72. data/lib/yard/lint/validators/tags/type_syntax/result.rb +25 -0
  73. data/lib/yard/lint/validators/tags/type_syntax/validator.rb +76 -0
  74. data/lib/yard/lint/validators/tags/type_syntax.rb +38 -0
  75. data/lib/yard/lint/validators/warnings/duplicated_parameter_name/validator.rb +4 -5
  76. data/lib/yard/lint/validators/warnings/duplicated_parameter_name.rb +26 -1
  77. data/lib/yard/lint/validators/warnings/invalid_directive_format/validator.rb +4 -5
  78. data/lib/yard/lint/validators/warnings/invalid_directive_format.rb +26 -1
  79. data/lib/yard/lint/validators/warnings/invalid_tag_format/validator.rb +4 -5
  80. data/lib/yard/lint/validators/warnings/invalid_tag_format.rb +25 -1
  81. data/lib/yard/lint/validators/warnings/unknown_directive/validator.rb +4 -5
  82. data/lib/yard/lint/validators/warnings/unknown_directive.rb +26 -1
  83. data/lib/yard/lint/validators/warnings/unknown_parameter_name/parser.rb +4 -1
  84. data/lib/yard/lint/validators/warnings/unknown_parameter_name/validator.rb +4 -5
  85. data/lib/yard/lint/validators/warnings/unknown_parameter_name.rb +23 -1
  86. data/lib/yard/lint/validators/warnings/unknown_tag/validator.rb +4 -5
  87. data/lib/yard/lint/validators/warnings/unknown_tag.rb +26 -1
  88. data/lib/yard/lint/version.rb +1 -1
  89. data/lib/yard/lint.rb +1 -0
  90. data/misc/logo.png +0 -0
  91. metadata +64 -1
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module CollectionType
8
+ # Validates Hash collection type syntax in YARD tags
9
+ class Validator < Base
10
+ private
11
+
12
+ # Runs YARD query to find incorrect Hash<K, V> syntax
13
+ # @param dir [String] directory where YARD database is stored
14
+ # @param file_list_path [String] path to temp file containing file paths (one per line)
15
+ # @return [Hash] shell command execution results
16
+ def yard_cmd(dir, file_list_path)
17
+ # Write query to a temporary file to avoid shell escaping issues
18
+ cmd = "cat #{Shellwords.escape(file_list_path)} | xargs yard list --query #{query} "
19
+
20
+ Tempfile.create(['yard_query', '.sh']) do |f|
21
+ f.write("#!/bin/bash\n")
22
+ f.write(cmd)
23
+ f.write("#{shell_arguments} -b #{Shellwords.escape(dir)}\n")
24
+ f.flush
25
+ f.chmod(0o755)
26
+
27
+ shell("bash #{Shellwords.escape(f.path)}")
28
+ end
29
+ end
30
+
31
+ # YARD query that finds incorrect collection syntax based on EnforcedStyle
32
+ # Format output as two lines per violation:
33
+ # Line 1: file.rb:LINE: ClassName#method_name
34
+ # Line 2: tag_name|type_string|detected_style
35
+ # @return [String] YARD query string
36
+ def query
37
+ style = enforced_style
38
+
39
+ <<~QUERY.strip
40
+ '
41
+ docstring
42
+ .tags
43
+ .select { |tag| #{validated_tags_array}.include?(tag.tag_name) }
44
+ .each do |tag|
45
+ next unless tag.types
46
+
47
+ tag.types.each do |type_str|
48
+ detected_style = nil
49
+
50
+ # Check for Hash<...> syntax (angle brackets)
51
+ if type_str =~ /Hash<.*>/
52
+ detected_style = "short"
53
+ # Check for Hash{...} syntax (curly braces)
54
+ elsif type_str =~ /Hash\\{.*\\}/
55
+ detected_style = "long"
56
+ # Check for {...} syntax without Hash prefix
57
+ elsif type_str =~ /^\\{.*\\}$/
58
+ detected_style = "short"
59
+ end
60
+
61
+ # Report violations based on enforced style
62
+ if detected_style && detected_style != "#{style}"
63
+ puts object.file + ":" + object.line.to_s + ": " + object.title
64
+ puts tag.tag_name + "|" + type_str + "|" + detected_style
65
+ break
66
+ end
67
+ end
68
+ end
69
+
70
+ false
71
+ '
72
+ QUERY
73
+ end
74
+
75
+ # Gets the enforced collection style from configuration
76
+ # @return [String] 'long' or 'short'
77
+ def enforced_style
78
+ config_or_default('EnforcedStyle')
79
+ end
80
+
81
+ # Array of tag names to validate, formatted for YARD query
82
+ # @return [String] Ruby array literal string
83
+ def validated_tags_array
84
+ tags = config_or_default('ValidatedTags')
85
+ "[#{tags.map { |t| "\"#{t}\"" }.join(',')}]"
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ # CollectionType validator
8
+ #
9
+ # Enforces correct YARD collection type syntax for Hash types. YARD uses
10
+ # `Hash{K => V}` syntax with curly braces and hash rockets, not the generic
11
+ # `Hash<K, V>` syntax used in other languages and documentation tools.
12
+ # This validator is enabled by default.
13
+ #
14
+ # ## Why This Matters
15
+ #
16
+ # YARD has specific syntax conventions that differ from other documentation tools.
17
+ # Using the correct syntax ensures that YARD can properly parse and display your
18
+ # type annotations in generated documentation.
19
+ #
20
+ # @example Bad - Generic syntax with angle brackets
21
+ # # @param options [Hash<Symbol, String>] configuration options
22
+ # # @param mapping [Hash<String, Integer>] key to value mapping
23
+ # def configure(options, mapping)
24
+ # end
25
+ #
26
+ # @example Good - YARD syntax with curly braces
27
+ # # @param options [Hash{Symbol => String}] configuration options
28
+ # # @param mapping [Hash{String => Integer}] key to value mapping
29
+ # def configure(options, mapping)
30
+ # end
31
+ #
32
+ # @example Good - Arrays use angle brackets (correct for YARD)
33
+ # # @param items [Array<String>] list of items
34
+ # # @param numbers [Array<Integer>] list of numbers
35
+ # def process(items, numbers)
36
+ # end
37
+ #
38
+ # ## Configuration
39
+ #
40
+ # To disable this validator:
41
+ #
42
+ # Tags/CollectionType:
43
+ # Enabled: false
44
+ #
45
+ module CollectionType
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module ExampleSyntax
8
+ # Configuration for ExampleSyntax validator
9
+ class Config < ::Yard::Lint::Validators::Config
10
+ self.id = :example_syntax
11
+ self.defaults = {
12
+ 'Enabled' => true,
13
+ 'Severity' => 'warning'
14
+ }.freeze
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module ExampleSyntax
8
+ # Builds messages for example syntax offenses
9
+ class MessagesBuilder
10
+ class << self
11
+ # Build message for example syntax offense
12
+ # @param offense [Hash] offense data with :example_name and :error_message keys
13
+ # @return [String] formatted message
14
+ def call(offense)
15
+ example_name = offense[:example_name]
16
+ error_msg = offense[:error_message]
17
+ object_name = offense[:object_name]
18
+
19
+ "Object `#{object_name}` has syntax error in @example " \
20
+ "'#{example_name}': #{error_msg}"
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module ExampleSyntax
8
+ # Parser for @example syntax validation results
9
+ class Parser < Parsers::Base
10
+ # @param yard_output [String] raw yard output with example syntax issues
11
+ # @return [Array<Hash>] array with example syntax violation details
12
+ def call(yard_output)
13
+ return [] if yard_output.nil? || yard_output.empty?
14
+
15
+ lines = yard_output.split("\n").reject(&:empty?)
16
+ results = []
17
+
18
+ # Output format is variable lines per error:
19
+ # 1. file.rb:10: ClassName#method_name
20
+ # 2. syntax_error
21
+ # 3. Example name
22
+ # 4+. Error message (can be multiple lines)
23
+ # Next error starts with another file.rb:line: pattern
24
+
25
+ i = 0
26
+ while i < lines.length
27
+ location_line = lines[i]
28
+
29
+ # Parse location line: "file.rb:10: ClassName#method_name"
30
+ # File paths typically start with a letter or . or / or ~
31
+ match = location_line.match(%r{^([a-zA-Z./~].+):(\d+): (.+)$})
32
+ unless match
33
+ i += 1
34
+ next
35
+ end
36
+
37
+ file = match[1]
38
+ line = match[2].to_i
39
+ object_name = match[3]
40
+
41
+ # Next line should be status
42
+ i += 1
43
+ status_line = lines[i]
44
+ next unless status_line == 'syntax_error'
45
+
46
+ # Next line is example name
47
+ i += 1
48
+ example_name = lines[i]
49
+
50
+ # Collect all remaining lines until we hit the next location line or end
51
+ error_message_lines = []
52
+ i += 1
53
+ while i < lines.length
54
+ # Check if this line starts a new error (matches file:line: pattern)
55
+ # File paths typically start with a letter or . or / or ~
56
+ break if lines[i].match?(%r{^[a-zA-Z./~].+:\d+: .+$})
57
+
58
+ error_message_lines << lines[i]
59
+ i += 1
60
+ end
61
+
62
+ results << {
63
+ name: 'ExampleSyntax',
64
+ object_name: object_name,
65
+ example_name: example_name,
66
+ error_message: error_message_lines.join("\n"),
67
+ location: file,
68
+ line: line
69
+ }
70
+ end
71
+
72
+ results
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module ExampleSyntax
8
+ # Result object for example syntax validation
9
+ class Result < Results::Base
10
+ self.default_severity = 'warning'
11
+ self.offense_type = 'line'
12
+ self.offense_name = 'ExampleSyntaxError'
13
+
14
+ # Build human-readable message for example syntax offense
15
+ # @param offense [Hash] offense data with :example_name and :error_message keys
16
+ # @return [String] formatted message
17
+ def build_message(offense)
18
+ MessagesBuilder.call(offense)
19
+ end
20
+
21
+ private
22
+
23
+ # Override to build offenses with dynamic names from parsed data
24
+ # @return [Array<Hash>] array of offense hashes
25
+ def build_offenses
26
+ @parsed_data.map do |offense_data|
27
+ {
28
+ severity: configured_severity,
29
+ type: self.class.offense_type,
30
+ name: offense_data[:name] || self.class.offense_name,
31
+ message: build_message(offense_data),
32
+ location: offense_data[:location] || offense_data[:file],
33
+ location_line: offense_data[:line] || offense_data[:location_line] || 0
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module ExampleSyntax
8
+ # Validator to check syntax of code in @example tags
9
+ class Validator < Base
10
+ private
11
+
12
+ # Runs yard list query to find objects with invalid syntax in @example tags
13
+ # @param dir [String] dir where the yard db is (or where it should be generated)
14
+ # @param file_list_path [String] path to temp file containing file paths (one per line)
15
+ # @return [Hash] shell command execution hash results
16
+ def yard_cmd(dir, file_list_path)
17
+ cmd = <<~CMD
18
+ cat #{Shellwords.escape(file_list_path)} | xargs yard list \
19
+ --private \
20
+ --protected \
21
+ -b #{Shellwords.escape(dir)}
22
+ CMD
23
+ cmd = cmd.tr("\n", ' ')
24
+ cmd = cmd.gsub('yard list', "yard list --query #{query}")
25
+
26
+ shell(cmd)
27
+ end
28
+
29
+ # @return [String] yard query to find @example tags with syntax errors
30
+ def query
31
+ <<~QUERY
32
+ '
33
+ if object.has_tag?(:example)
34
+ example_tags = object.tags(:example)
35
+
36
+ example_tags.each_with_index do |example, index|
37
+ code = example.text
38
+ next if code.nil? || code.empty?
39
+
40
+ # Clean the code: strip output indicators (#=>) and everything after it
41
+ code_lines = code.split("\\n").map do |line|
42
+ line.sub(/\\s*#\\s*=>.*$/, "")
43
+ end
44
+
45
+ cleaned_code = code_lines.join("\\n").strip
46
+ next if cleaned_code.empty?
47
+
48
+ # Check if code looks incomplete (single expression without context)
49
+ # Skip validation for these cases
50
+ lines = cleaned_code.split("\\n").reject { |l| l.strip.empty? || l.strip.start_with?("#") }
51
+
52
+ # Skip if it is a single line that looks like an incomplete expression
53
+ if lines.size == 1
54
+ line = lines.first.strip
55
+ # Skip method calls, variable references, or simple expressions
56
+ # These are likely incomplete snippets showing usage
57
+ if line.match?(/^[a-z_][a-z0-9_]*(\\.| |$)/) || line.match?(/^[A-Z]/) && !line.match?(/^(class|module|def)\\s/)
58
+ next
59
+ end
60
+ end
61
+
62
+ # Try to parse the code
63
+ begin
64
+ RubyVM::InstructionSequence.compile(cleaned_code)
65
+ rescue SyntaxError => e
66
+ # Report syntax errors
67
+ example_name = example.name || "Example " + (index + 1).to_s
68
+ puts object.file + ":" + object.line.to_s + ": " + object.title
69
+ puts "syntax_error"
70
+ puts example_name
71
+ puts e.message
72
+ rescue => e
73
+ # Other errors (like NameError, ArgumentError) are fine
74
+ # We only check syntax, not semantics
75
+ next
76
+ end
77
+ end
78
+ end
79
+ false
80
+ '
81
+ QUERY
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,42 @@
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
+ # ExampleSyntax validator
9
+ #
10
+ # Validates Ruby syntax in `@example` tags using RubyVM::InstructionSequence.compile().
11
+ # This validator ensures that code examples in documentation are syntactically valid.
12
+ # It automatically strips output indicators (`#=>`) and skips incomplete single-line
13
+ # snippets. This validator is enabled by default.
14
+ #
15
+ # @example Bad - Invalid Ruby syntax in example
16
+ # # @example
17
+ # # def broken(
18
+ # # # missing closing
19
+ # def my_method
20
+ # end
21
+ #
22
+ # @example Good - Valid Ruby syntax in example
23
+ # # @example
24
+ # # def my_method(name)
25
+ # # puts name
26
+ # # end
27
+ # def my_method(name)
28
+ # end
29
+ #
30
+ # ## Configuration
31
+ #
32
+ # To disable this validator:
33
+ #
34
+ # Tags/ExampleSyntax:
35
+ # Enabled: false
36
+ #
37
+ module ExampleSyntax
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -24,17 +24,17 @@ module Yard
24
24
 
25
25
  # Runs yard list query with proper settings on a given dir and files
26
26
  # @param dir [String] dir where the yard db is (or where it should be generated)
27
- # @param escaped_file_names [String] files for which we want to get the stats
27
+ # @param file_list_path [String] path to temp file containing file paths (one per line)
28
28
  # @return [Hash] shell command execution hash results
29
- def yard_cmd(dir, escaped_file_names)
29
+ def yard_cmd(dir, file_list_path)
30
30
  # Write query to a temporary file to avoid shell escaping issues
31
- require 'tempfile'
31
+ squery = Shellwords.escape(query)
32
+ cmd = "cat #{Shellwords.escape(file_list_path)} | xargs yard list --query #{squery} "
32
33
 
33
34
  Tempfile.create(['yard_query', '.sh']) do |f|
34
35
  f.write("#!/bin/bash\n")
35
- f.write("yard list --query #{Shellwords.escape(query)} ")
36
- f.write("--private --protected -b #{Shellwords.escape(dir)} ")
37
- f.write("#{escaped_file_names}\n")
36
+ f.write(cmd)
37
+ f.write("--private --protected -b #{Shellwords.escape(dir)}\n")
38
38
  f.flush
39
39
  f.chmod(0o755)
40
40
 
@@ -77,14 +77,14 @@ module Yard
77
77
  # @return [String] tags names for which we want to check the invalid tags
78
78
  # types definitions
79
79
  def checked_tags_names
80
- validated_tags = config.validator_config('Tags/InvalidTypes', 'ValidatedTags')
80
+ validated_tags = config_or_default('ValidatedTags')
81
81
  query_array(validated_tags)
82
82
  end
83
83
 
84
84
  # @return [String] extra names that we allow for types definitions in a yard
85
85
  # query acceptable form
86
86
  def allowed_types_code
87
- extra_types = config.validator_config('Tags/InvalidTypes', 'ExtraTypes') || []
87
+ extra_types = config_or_default('ExtraTypes')
88
88
  query_array(ALLOWED_DEFAULTS + extra_types)
89
89
  end
90
90
 
@@ -4,7 +4,31 @@ module Yard
4
4
  module Lint
5
5
  module Validators
6
6
  module Tags
7
- # InvalidTypes validator module
7
+ # InvalidTypes validator
8
+ #
9
+ # Detects invalid or malformed type annotations in YARD tags. This validator
10
+ # checks that type specifications follow YARD's type syntax rules and that
11
+ # type names are valid. This validator is enabled by default.
12
+ #
13
+ # @example Bad - Invalid type syntax
14
+ # # @param name [String | Integer] the name (wrong pipe syntax)
15
+ # # @return [Array[String]] invalid nested syntax
16
+ # def process(name)
17
+ # end
18
+ #
19
+ # @example Good - Valid type syntax
20
+ # # @param name [String, Integer] the name (comma-separated union)
21
+ # # @return [Array<String>] valid nested syntax
22
+ # def process(name)
23
+ # end
24
+ #
25
+ # ## Configuration
26
+ #
27
+ # To disable this validator:
28
+ #
29
+ # Tags/InvalidTypes:
30
+ # Enabled: false
31
+ #
8
32
  module InvalidTypes
9
33
  end
10
34
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module MeaninglessTag
8
+ # Configuration for MeaninglessTag validator
9
+ class Config < ::Yard::Lint::Validators::Config
10
+ self.id = :meaningless_tag
11
+ self.defaults = {
12
+ 'Enabled' => true,
13
+ 'Severity' => 'warning',
14
+ 'CheckedTags' => %w[param option],
15
+ 'InvalidObjectTypes' => %w[class module constant]
16
+ }.freeze
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module MeaninglessTag
8
+ # Builds human-readable messages for MeaninglessTag violations
9
+ class MessagesBuilder
10
+ class << self
11
+ # Formats a meaningless tag violation message
12
+ # @param offense [Hash] offense details with :object_type, :tag_name, :object_name
13
+ # @return [String] formatted message
14
+ def call(offense)
15
+ object_type = offense[:object_type]
16
+ tag_name = offense[:tag_name]
17
+ object_name = offense[:object_name]
18
+
19
+ "The @#{tag_name} tag is meaningless on a #{object_type} " \
20
+ "(#{object_name}). This tag only makes sense on methods."
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module MeaninglessTag
8
+ # Parser for MeaninglessTag validator output
9
+ # Parses YARD query output that reports meaningless tags on non-methods
10
+ class Parser < ::Yard::Lint::Parsers::Base
11
+ # Parses YARD output and extracts meaningless tag violations
12
+ # Expected format (two lines per violation):
13
+ # file.rb:LINE: ClassName
14
+ # object_type|tag_name
15
+ # @param yard_output [String] raw YARD query results
16
+ # @param _kwargs [Hash] unused keyword arguments (for compatibility)
17
+ # @return [Array<Hash>] array with violation details
18
+ def call(yard_output, **_kwargs)
19
+ return [] if yard_output.nil? || yard_output.strip.empty?
20
+
21
+ lines = yard_output.split("\n").map(&:strip).reject(&:empty?)
22
+ violations = []
23
+
24
+ lines.each_slice(2) do |location_line, details_line|
25
+ next unless location_line && details_line
26
+
27
+ # Parse location: "file.rb:10: ClassName"
28
+ location_match = location_line.match(/^(.+):(\d+): (.+)$/)
29
+ next unless location_match
30
+
31
+ # Parse details: "object_type|tag_name"
32
+ details = details_line.split('|', 2)
33
+ next unless details.size == 2
34
+
35
+ object_type, tag_name = details
36
+
37
+ violations << {
38
+ location: location_match[1],
39
+ line: location_match[2].to_i,
40
+ object_name: location_match[3],
41
+ object_type: object_type,
42
+ tag_name: tag_name
43
+ }
44
+ end
45
+
46
+ violations
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module MeaninglessTag
8
+ # Result wrapper for MeaninglessTag validator
9
+ # Formats parsed violations into offense objects
10
+ class Result < Results::Base
11
+ self.default_severity = 'warning'
12
+ self.offense_type = 'class'
13
+ self.offense_name = 'MeaninglessTag'
14
+
15
+ # Builds a human-readable message for the offense
16
+ # @param offense [Hash] offense details
17
+ # @return [String] formatted message
18
+ def build_message(offense)
19
+ MessagesBuilder.call(offense)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end