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,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ # ExampleStyle validator
8
+ #
9
+ # Validates code style in `@example` tags using RuboCop or StandardRB.
10
+ # This validator ensures that code examples follow the same style guidelines
11
+ # as the project codebase for consistency.
12
+ #
13
+ # ## Requirements
14
+ #
15
+ # - RuboCop or StandardRB gem must be installed
16
+ # - Validator auto-detects which linter to use based on project setup
17
+ #
18
+ # ## Configuration
19
+ #
20
+ # Basic usage:
21
+ #
22
+ # Tags/ExampleStyle:
23
+ # Enabled: true
24
+ #
25
+ # Advanced configuration:
26
+ #
27
+ # Tags/ExampleStyle:
28
+ # Enabled: true
29
+ # Linter: auto # 'auto', 'rubocop', 'standard', or 'none'
30
+ # SkipPatterns:
31
+ # - '/skip-lint/i'
32
+ # - '/bad code/i'
33
+ # DisabledCops:
34
+ # - 'Metrics/MethodLength'
35
+ #
36
+ # ## Skipping Examples
37
+ #
38
+ # Skip linting for specific examples (negative examples):
39
+ #
40
+ # # @example Bad code (skip-lint)
41
+ # # user = User.new("invalid")
42
+ #
43
+ # Or use inline RuboCop directives:
44
+ #
45
+ # # @example
46
+ # # # rubocop:disable all
47
+ # # user = User.new("invalid")
48
+ # # # rubocop:enable all
49
+ module ExampleStyle
50
+ autoload :Validator, 'yard/lint/validators/tags/example_style/validator'
51
+ autoload :Config, 'yard/lint/validators/tags/example_style/config'
52
+ autoload :Parser, 'yard/lint/validators/tags/example_style/parser'
53
+ autoload :Result, 'yard/lint/validators/tags/example_style/result'
54
+ autoload :MessagesBuilder, 'yard/lint/validators/tags/example_style/messages_builder'
55
+ autoload :LinterDetector, 'yard/lint/validators/tags/example_style/linter_detector'
56
+ autoload :RubocopRunner, 'yard/lint/validators/tags/example_style/rubocop_runner'
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module ForbiddenTags
8
+ # Configuration for ForbiddenTags validator
9
+ class Config < ::Yard::Lint::Validators::Config
10
+ self.id = :forbidden_tags
11
+ self.defaults = {
12
+ 'Enabled' => false,
13
+ 'Severity' => 'convention',
14
+ 'ForbiddenPatterns' => []
15
+ }.freeze
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module ForbiddenTags
8
+ # Builds human-readable messages for ForbiddenTags violations
9
+ class MessagesBuilder
10
+ class << self
11
+ # Formats a forbidden tag violation message
12
+ # @param offense [Hash] offense details with :tag_name, :types_text, :pattern_types
13
+ # @return [String] formatted message
14
+ def call(offense)
15
+ tag_name = offense[:tag_name]
16
+ types_text = offense[:types_text]
17
+ pattern_types = offense[:pattern_types]
18
+
19
+ if pattern_types.nil? || pattern_types.empty?
20
+ "Forbidden tag detected: @#{tag_name}. " \
21
+ 'This tag is not allowed by project configuration.'
22
+ else
23
+ type_display = types_text.empty? ? '' : " [#{types_text}]"
24
+ "Forbidden tag pattern detected: @#{tag_name}#{type_display}. " \
25
+ "Type(s) '#{pattern_types}' are not allowed for @#{tag_name}."
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module ForbiddenTags
8
+ # Parser for ForbiddenTags validator output
9
+ # Parses in-process output that reports forbidden tag patterns
10
+ class Parser < ::Yard::Lint::Parsers::Base
11
+ # Parses validator output and extracts forbidden tag violations
12
+ # Expected format (two lines per violation):
13
+ # file.rb:LINE: ObjectName
14
+ # tag_name|types_text|pattern_types
15
+ # @param yard_output [String] raw validator output
16
+ # @return [Array<Hash>] array with violation details
17
+ def call(yard_output, **)
18
+ return [] if yard_output.nil? || yard_output.strip.empty?
19
+
20
+ lines = yard_output.split("\n").map(&:strip).reject(&:empty?)
21
+ violations = []
22
+
23
+ lines.each_slice(2) do |location_line, details_line|
24
+ next unless location_line && details_line
25
+
26
+ location_match = location_line.match(/^(.+):(\d+): (.+)$/)
27
+ next unless location_match
28
+
29
+ details = details_line.split('|', 3)
30
+ next if details.empty?
31
+
32
+ tag_name, types_text, pattern_types = details
33
+
34
+ violations << {
35
+ location: location_match[1],
36
+ line: location_match[2].to_i,
37
+ object_name: location_match[3],
38
+ tag_name: tag_name,
39
+ types_text: types_text || '',
40
+ pattern_types: pattern_types || ''
41
+ }
42
+ end
43
+
44
+ violations
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ 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 ForbiddenTags
8
+ # Result wrapper for ForbiddenTags validator
9
+ # Formats parsed violations into offense objects
10
+ class Result < Results::Base
11
+ self.default_severity = 'convention'
12
+ self.offense_type = 'tag'
13
+ self.offense_name = 'ForbiddenTags'
14
+
15
+ private
16
+
17
+ # Builds a human-readable message for the offense
18
+ # @param offense [Hash] offense details
19
+ # @return [String] formatted message
20
+ def build_message(offense)
21
+ MessagesBuilder.call(offense)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module ForbiddenTags
8
+ # Validates that forbidden tag/type combinations are not used
9
+ class Validator < Base
10
+ # Enable in-process execution with all visibility
11
+ in_process visibility: :all
12
+
13
+ # Execute query for a single object during in-process execution.
14
+ # Checks for forbidden tag/type combinations in docstrings.
15
+ # @param object [YARD::CodeObjects::Base] the code object to query
16
+ # @param collector [Executor::ResultCollector] collector for output
17
+ def in_process_query(object, collector)
18
+ patterns = forbidden_patterns
19
+ return if patterns.empty?
20
+
21
+ object.docstring.tags.each do |tag|
22
+ patterns.each do |pattern|
23
+ next unless matches_pattern?(tag, pattern)
24
+
25
+ collector.puts "#{object.file}:#{object.line}: #{object.title}"
26
+ collector.puts build_details(tag, pattern)
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ # @return [Array<Hash>] configured forbidden patterns
34
+ def forbidden_patterns
35
+ config_or_default('ForbiddenPatterns')
36
+ end
37
+
38
+ # Check if a tag matches a forbidden pattern
39
+ # @param tag [YARD::Tags::Tag] the tag to check
40
+ # @param pattern [Hash] the forbidden pattern with 'Tag' and optional 'Types'
41
+ # @return [Boolean] true if tag matches the pattern
42
+ def matches_pattern?(tag, pattern)
43
+ return false unless tag.tag_name == pattern['Tag']
44
+
45
+ pattern_types = pattern['Types']
46
+ return true if pattern_types.nil? || pattern_types.empty?
47
+
48
+ tag_types = tag.types || []
49
+ (tag_types & pattern_types).any?
50
+ end
51
+
52
+ # Build details string for output
53
+ # @param tag [YARD::Tags::Tag] the matched tag
54
+ # @param pattern [Hash] the matched pattern
55
+ # @return [String] details line for parser
56
+ def build_details(tag, pattern)
57
+ types_text = (tag.types || []).join(',')
58
+ pattern_types = (pattern['Types'] || []).join(',')
59
+ "#{tag.tag_name}|#{types_text}|#{pattern_types}"
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ # ForbiddenTags validator
8
+ #
9
+ # Detects forbidden YARD tag and type combinations. This is useful for
10
+ # enforcing project-specific documentation conventions, such as:
11
+ # - Disallowing `@return [void]` (prefer documenting side effects)
12
+ # - Forbidding overly generic types like `@param [Object]`
13
+ # - Preventing use of certain tags entirely
14
+ #
15
+ # This validator is disabled by default (opt-in).
16
+ #
17
+ # ## Configuration
18
+ #
19
+ # To enable and configure forbidden patterns:
20
+ #
21
+ # Tags/ForbiddenTags:
22
+ # Enabled: true
23
+ # Severity: convention
24
+ # ForbiddenPatterns:
25
+ # # Forbid @return [void]
26
+ # - Tag: return
27
+ # Types:
28
+ # - void
29
+ # # Forbid @param [Object]
30
+ # - Tag: param
31
+ # Types:
32
+ # - Object
33
+ # # Forbid @api tag entirely
34
+ # - Tag: api
35
+ #
36
+ # @example Bad - @return [void] (when configured as forbidden)
37
+ # # Does something
38
+ # # @return [void]
39
+ # def do_something
40
+ # puts "done"
41
+ # end
42
+ #
43
+ # @example Good - Document side effects instead
44
+ # # Prints 'done' to stdout
45
+ # # @return [nil] always returns nil after printing
46
+ # def do_something
47
+ # puts "done"
48
+ # end
49
+ #
50
+ # @example Bad - @param [Object] (when configured as forbidden)
51
+ # # Process data
52
+ # # @param data [Object] the data
53
+ # def process(data)
54
+ # data.to_s
55
+ # end
56
+ #
57
+ # @example Good - Use specific type
58
+ # # Process data
59
+ # # @param data [String, Integer] the data to process
60
+ # def process(data)
61
+ # data.to_s
62
+ # end
63
+ module ForbiddenTags
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -34,7 +34,7 @@ module Yard
34
34
  allowed_types = ALLOWED_DEFAULTS + extra_types
35
35
 
36
36
  # Sanitize type string (remove type syntax characters)
37
- sanitize = ->(type) { type.tr('=><>,{} ', '') }
37
+ sanitize = ->(type) { type.tr('=><>,{} ()', '') }
38
38
 
39
39
  # Check for invalid types
40
40
  invalid_types = object.docstring.tags
@@ -7,7 +7,7 @@ module Yard
7
7
  module TagGroupSeparator
8
8
  # Parser for extracting tag group separator violations from raw validator output.
9
9
  #
10
- # @example Output format
10
+ # @example Output format (skip-lint)
11
11
  # /path/to/file.rb:10: ClassName#method_name
12
12
  # param->return,return->error
13
13
  class Parser < Parsers::Base
@@ -7,6 +7,18 @@ module Yard
7
7
  module TypeSyntax
8
8
  # Runs YARD to validate type syntax using TypesExplainer::Parser
9
9
  class Validator < Base
10
+ # Matches valid Ruby symbol literals: :foo, :foo?, :foo!, :foo=
11
+ SYMBOL_LITERAL = /\A:[a-zA-Z_]\w*[?!=]?\z/
12
+ # Matches valid quoted symbol literals: :"foo", :'foo' (quotes must match)
13
+ QUOTED_SYMBOL_LITERAL = /\A:("[^"]*"|'[^']*')\z/
14
+ # Matches string literals: "foo", 'foo' (quotes must match)
15
+ STRING_LITERAL = /\A("[^"]*"|'[^']*')\z/
16
+ # Matches numeric literals: 1, -1, 1.0, -2.5
17
+ NUMERIC_LITERAL = /\A-?\d+(\.\d+)?\z/
18
+
19
+ private_constant :SYMBOL_LITERAL, :QUOTED_SYMBOL_LITERAL, :STRING_LITERAL,
20
+ :NUMERIC_LITERAL
21
+
10
22
  # Enable in-process execution
11
23
  in_process visibility: :public
12
24
 
@@ -25,6 +37,13 @@ module Yard
25
37
  next unless tag.types
26
38
 
27
39
  tag.types.each do |type_str|
40
+ # Skip literal types that YARD accepts but TypesExplainer::Parser doesn't
41
+ # See: https://github.com/mensfeld/yard-lint/issues/109
42
+ next if type_str.match?(SYMBOL_LITERAL)
43
+ next if type_str.match?(QUOTED_SYMBOL_LITERAL)
44
+ next if type_str.match?(STRING_LITERAL)
45
+ next if type_str.match?(NUMERIC_LITERAL)
46
+
28
47
  begin
29
48
  YARD::Tags::TypesExplainer::Parser.parse(type_str)
30
49
  rescue SyntaxError => e
@@ -6,7 +6,7 @@ module Yard
6
6
  module Warnings
7
7
  module UnknownTag
8
8
  # Parser used to extract warnings details that are related to yard unknown tags
9
- # @example
9
+ # @example Output format (skip-lint)
10
10
  # [warn]: Unknown tag @example1 in file `/builds/path/engine.rb` near line 32
11
11
  class Parser < ::Yard::Lint::Parsers::OneLineBase
12
12
  # Set of regexps for detecting warnings reported by YARD stats
@@ -3,6 +3,6 @@
3
3
  module Yard
4
4
  module Lint
5
5
  # @return [String] version of the YARD Lint gem
6
- VERSION = '1.3.0'
6
+ VERSION = '1.5.0'
7
7
  end
8
8
  end
data/mise.toml ADDED
@@ -0,0 +1,2 @@
1
+ [tools]
2
+ ruby = "3.3.0"