yard-lint 1.5.1 → 1.6.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -0
  3. data/README.md +207 -8
  4. data/bin/yard-lint +45 -11
  5. data/lib/yard/lint/executor/in_process_registry.rb +38 -12
  6. data/lib/yard/lint/results/base.rb +3 -0
  7. data/lib/yard/lint/runner.rb +11 -3
  8. data/lib/yard/lint/templates/default_config.yml +40 -0
  9. data/lib/yard/lint/templates/strict_config.yml +39 -0
  10. data/lib/yard/lint/todo_generator.rb +6 -4
  11. data/lib/yard/lint/validators/base.rb +125 -0
  12. data/lib/yard/lint/validators/documentation/line_length/config.rb +21 -0
  13. data/lib/yard/lint/validators/documentation/line_length/messages_builder.rb +26 -0
  14. data/lib/yard/lint/validators/documentation/line_length/parser.rb +65 -0
  15. data/lib/yard/lint/validators/documentation/line_length/result.rb +26 -0
  16. data/lib/yard/lint/validators/documentation/line_length/validator.rb +59 -0
  17. data/lib/yard/lint/validators/documentation/line_length.rb +43 -0
  18. data/lib/yard/lint/validators/documentation/missing_return/config.rb +2 -1
  19. data/lib/yard/lint/validators/documentation/missing_return/parser.rb +0 -1
  20. data/lib/yard/lint/validators/documentation/missing_return/validator.rb +1 -0
  21. data/lib/yard/lint/validators/documentation/orphaned_doc_comment/config.rb +20 -0
  22. data/lib/yard/lint/validators/documentation/orphaned_doc_comment/messages_builder.rb +23 -0
  23. data/lib/yard/lint/validators/documentation/orphaned_doc_comment/parser.rb +38 -0
  24. data/lib/yard/lint/validators/documentation/orphaned_doc_comment/result.rb +24 -0
  25. data/lib/yard/lint/validators/documentation/orphaned_doc_comment/validator.rb +121 -0
  26. data/lib/yard/lint/validators/documentation/orphaned_doc_comment.rb +53 -0
  27. data/lib/yard/lint/validators/documentation/text_substitution/config.rb +24 -0
  28. data/lib/yard/lint/validators/documentation/text_substitution/messages_builder.rb +33 -0
  29. data/lib/yard/lint/validators/documentation/text_substitution/parser.rb +57 -0
  30. data/lib/yard/lint/validators/documentation/text_substitution/result.rb +24 -0
  31. data/lib/yard/lint/validators/documentation/text_substitution/validator.rb +72 -0
  32. data/lib/yard/lint/validators/documentation/text_substitution.rb +55 -0
  33. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/config.rb +2 -1
  34. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/validator.rb +1 -0
  35. data/lib/yard/lint/validators/documentation/undocumented_method_arguments/config.rb +3 -1
  36. data/lib/yard/lint/validators/documentation/undocumented_method_arguments/validator.rb +3 -1
  37. data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +1 -2
  38. data/lib/yard/lint/validators/documentation/undocumented_objects/config.rb +2 -1
  39. data/lib/yard/lint/validators/documentation/undocumented_objects/validator.rb +1 -0
  40. data/lib/yard/lint/validators/documentation/undocumented_options/config.rb +2 -1
  41. data/lib/yard/lint/validators/documentation/undocumented_options/validator.rb +1 -0
  42. data/lib/yard/lint/validators/documentation/undocumented_options.rb +1 -2
  43. data/lib/yard/lint/validators/tags/api_tags/result.rb +1 -0
  44. data/lib/yard/lint/validators/tags/api_tags/validator.rb +12 -0
  45. data/lib/yard/lint/validators/tags/collection_type/config.rb +1 -1
  46. data/lib/yard/lint/validators/tags/collection_type/validator.rb +1 -3
  47. data/lib/yard/lint/validators/tags/example_style/result.rb +1 -0
  48. data/lib/yard/lint/validators/tags/example_syntax/result.rb +1 -0
  49. data/lib/yard/lint/validators/tags/invalid_types/config.rb +1 -1
  50. data/lib/yard/lint/validators/tags/invalid_types/messages_builder.rb +14 -3
  51. data/lib/yard/lint/validators/tags/invalid_types/parser.rb +63 -3
  52. data/lib/yard/lint/validators/tags/invalid_types/validator.rb +31 -16
  53. data/lib/yard/lint/validators/tags/meaningless_tag/validator.rb +16 -1
  54. data/lib/yard/lint/validators/tags/missing_yield/config.rb +20 -0
  55. data/lib/yard/lint/validators/tags/missing_yield/messages_builder.rb +22 -0
  56. data/lib/yard/lint/validators/tags/missing_yield/parser.rb +39 -0
  57. data/lib/yard/lint/validators/tags/missing_yield/result.rb +24 -0
  58. data/lib/yard/lint/validators/tags/missing_yield/validator.rb +76 -0
  59. data/lib/yard/lint/validators/tags/missing_yield.rb +56 -0
  60. data/lib/yard/lint/validators/tags/non_ascii_type/config.rb +1 -1
  61. data/lib/yard/lint/validators/tags/non_ascii_type/validator.rb +1 -3
  62. data/lib/yard/lint/validators/tags/redundant_param_description/validator.rb +4 -2
  63. data/lib/yard/lint/validators/tags/redundant_param_description.rb +2 -1
  64. data/lib/yard/lint/validators/tags/type_syntax/config.rb +1 -1
  65. data/lib/yard/lint/validators/tags/type_syntax/validator.rb +1 -3
  66. data/lib/yard/lint/validators/tags/type_syntax.rb +1 -2
  67. data/lib/yard/lint/validators/warnings/duplicated_parameter_name/parser.rb +4 -4
  68. data/lib/yard/lint/validators/warnings/duplicated_parameter_name.rb +1 -2
  69. data/lib/yard/lint/validators/warnings/invalid_directive_format.rb +1 -2
  70. data/lib/yard/lint/version.rb +1 -1
  71. data/lib/yard/lint.rb +14 -4
  72. metadata +26 -2
@@ -28,6 +28,7 @@ module Yard
28
28
  severity: configured_severity,
29
29
  type: self.class.offense_type,
30
30
  name: offense_data[:name] || self.class.offense_name,
31
+ validator: validator_name,
31
32
  message: build_message(offense_data),
32
33
  location: offense_data[:location] || offense_data[:file],
33
34
  location_line: offense_data[:line] || offense_data[:location_line] || 0
@@ -28,6 +28,7 @@ module Yard
28
28
  severity: configured_severity,
29
29
  type: self.class.offense_type,
30
30
  name: offense_data[:name] || self.class.offense_name,
31
+ validator: validator_name,
31
32
  message: build_message(offense_data),
32
33
  location: offense_data[:location] || offense_data[:file],
33
34
  location_line: offense_data[:line] || offense_data[:location_line] || 0
@@ -11,7 +11,7 @@ module Yard
11
11
  self.defaults = {
12
12
  'Enabled' => true,
13
13
  'Severity' => 'warning',
14
- 'ValidatedTags' => %w[param option return yieldreturn],
14
+ 'ValidatedTags' => %w[param option return yieldreturn yieldparam raise],
15
15
  'ExtraTypes' => []
16
16
  }.freeze
17
17
  end
@@ -9,11 +9,22 @@ module Yard
9
9
  class MessagesBuilder
10
10
  class << self
11
11
  # Build message for invalid tag types
12
- # @param offense [Hash] offense data with :method_name key
12
+ # @param offense [Hash] offense data with :method_name and :tag_violations keys
13
13
  # @return [String] formatted message
14
14
  def call(offense)
15
- "The `#{offense[:method_name]}` has at least one tag " \
16
- 'with an invalid type definition.'
15
+ violations = offense[:tag_violations]
16
+
17
+ if violations&.any?
18
+ details = violations.map do |v|
19
+ label = v[:param] ? "#{v[:tag]} #{v[:param]}" : v[:tag]
20
+ types = v[:types].map { |t| "`#{t}`" }.join(', ')
21
+ "#{label}: #{types}"
22
+ end.join('; ')
23
+
24
+ "The `#{offense[:method_name]}` has invalid type(s): #{details}"
25
+ else
26
+ "The `#{offense[:method_name]}` has at least one tag with an invalid type definition."
27
+ end
17
28
  end
18
29
  end
19
30
  end
@@ -5,9 +5,69 @@ module Yard
5
5
  module Validators
6
6
  module Tags
7
7
  module InvalidTypes
8
- # Parser for invalid tags types output
9
- # Reuses location parsing logic from undocumented method arguments
10
- class Parser < Validators::Documentation::UndocumentedMethodArguments::Parser
8
+ # Parser for invalid tag types output.
9
+ # The validator emits two lines per offense:
10
+ # /path/to/file.rb:10: ClassName#method_name
11
+ # @tagname param_name:BadType1,BadType2|@tagname2:BadType3
12
+ class Parser < Parsers::Base
13
+ # @return [Regexp] matches "file:line: ClassName#method"
14
+ LOCATION_REGEX = /^(.+):(\d+):\s+(.+)[#.](.+)$/
15
+ # @return [Regexp] matches the tag violations line (starts with @)
16
+ TAG_VIOLATIONS_REGEX = /^@/
17
+
18
+ # @param yard_output [String] raw validator output
19
+ # @return [Array<Hash>] parsed offenses
20
+ def call(yard_output)
21
+ lines = yard_output.split("\n").reject(&:empty?)
22
+ results = []
23
+ i = 0
24
+
25
+ while i < lines.size
26
+ match = lines[i].match(LOCATION_REGEX)
27
+
28
+ unless match
29
+ i += 1
30
+ next
31
+ end
32
+
33
+ offense = {
34
+ location: match[1],
35
+ line: match[2].to_i,
36
+ class_name: match[3],
37
+ method_name: match[4],
38
+ tag_violations: []
39
+ }
40
+
41
+ next_line = lines[i + 1]
42
+ if next_line&.match?(TAG_VIOLATIONS_REGEX)
43
+ offense[:tag_violations] = parse_tag_violations(next_line)
44
+ i += 2
45
+ else
46
+ i += 1
47
+ end
48
+
49
+ results << offense
50
+ end
51
+
52
+ results
53
+ end
54
+
55
+ private
56
+
57
+ # Parse "tagname param:Type1,Type2|tagname2:Type3" into structured data
58
+ # @param line [String] the violations line
59
+ # @return [Array<Hash>] each entry has :tag, :param (may be nil), :types
60
+ def parse_tag_violations(line)
61
+ line.split('|').map do |entry|
62
+ label, types_str = entry.split(':', 2)
63
+ parts = label.split(' ', 2)
64
+ {
65
+ tag: parts[0],
66
+ param: parts[1],
67
+ types: (types_str || '').split(',').reject(&:empty?)
68
+ }
69
+ end
70
+ end
11
71
  end
12
72
  end
13
73
  end
@@ -19,6 +19,9 @@ module Yard
19
19
  nil
20
20
  self
21
21
  void
22
+ undefined
23
+ unspecified
24
+ unknown
22
25
  ].freeze
23
26
 
24
27
  private_constant :ALLOWED_DEFAULTS
@@ -33,35 +36,47 @@ module Yard
33
36
  extra_types = config_or_default('ExtraTypes')
34
37
  allowed_types = ALLOWED_DEFAULTS + extra_types
35
38
 
36
- # Sanitize type string (remove type syntax characters)
37
- sanitize = ->(type) { type.tr('=><>,{} ()', '') }
39
+ # Collect per-tag violations to surface in the offense message.
40
+ # Each entry is "tagname param_name:Type1,Type2" (param_name omitted when nil).
41
+ tag_violations = all_typed_tags(object.docstring, checked_tags).filter_map do |tag|
42
+ bad = (tag.types || [])
43
+ .compact
44
+ .flat_map { |type| extract_type_names(type) }
45
+ .uniq
46
+ .reject { |type| allowed_types.include?(type) }
47
+ .reject { |type| type_defined?(type) }
48
+ .reject { |type| type.include?('#') }
49
+ next if bad.empty?
38
50
 
39
- # Check for invalid types
40
- invalid_types = object.docstring.tags
41
- .select { |tag| checked_tags.include?(tag.tag_name) }
42
- .flat_map(&:types)
43
- .compact
44
- .uniq
45
- .map(&sanitize)
46
- .reject { |type| allowed_types.include?(type) }
47
- .reject { |type| type_defined?(type) }
48
- .reject { |type| type.include?('#') }
51
+ label = tag.name ? "@#{tag.tag_name} #{tag.name}" : "@#{tag.tag_name}"
52
+ "#{label}:#{bad.join(',')}"
53
+ end
49
54
 
50
- return if invalid_types.empty?
55
+ return if tag_violations.empty?
51
56
 
52
57
  collector.puts "#{object.file}:#{object.line}: #{object.title}"
58
+ collector.puts tag_violations.join('|')
53
59
  end
54
60
 
55
61
  private
56
62
 
63
+ # Extract individual type names from a compound type string.
64
+ # Instead of stripping all syntax characters and concatenating (which
65
+ # turns "Array<self>" into "Arrayself"), this splits on syntax boundaries
66
+ # and returns each type name individually.
67
+ # @param type_str [String] the raw type string (e.g., "Array<self>", "Hash{Symbol => String}")
68
+ # @return [Array<String>] individual type names (e.g., ["Array", "self"], ["Hash", "Symbol", "String"])
69
+ def extract_type_names(type_str)
70
+ type_str.split(/[=><,{}\s();]+/).reject(&:empty?)
71
+ end
72
+
57
73
  # Check if a type is defined in Ruby runtime or YARD registry
58
74
  # In in-process mode, parsed classes are in YARD registry but not loaded into Ruby
59
75
  # @param type [String] type name to check
60
76
  # @return [Boolean] true if type is defined (or at least recognized as a valid type)
61
77
  def type_defined?(type)
62
- # Symbol types like :foo are valid YARD documentation notations
63
- # They document that a method accepts specific symbol values
64
- return true if type.start_with?(':')
78
+ # Symbol and string literal types (:foo, "bar") are valid hash key notations
79
+ return true if type.start_with?(':', '"', "'")
65
80
 
66
81
  # Check Ruby runtime first
67
82
  # The shell query uses: !(Kernel.const_defined?(type) rescue nil).nil?
@@ -22,8 +22,13 @@ module Yard
22
22
 
23
23
  return unless invalid_types.include?(object_type)
24
24
 
25
+ # The `@param` tag is meaningful on Struct.new / Data.define constants because
26
+ # Solargraph uses those annotations to type the synthesized accessors.
27
+ effective_tags = struct_or_data_class?(object) ? tags_to_check - ['param'] : tags_to_check
28
+ return if effective_tags.empty?
29
+
25
30
  object.docstring.tags.each do |tag|
26
- next unless tags_to_check.include?(tag.tag_name)
31
+ next unless effective_tags.include?(tag.tag_name)
27
32
 
28
33
  collector.puts "#{object.file}:#{object.line}: #{object.title}"
29
34
  collector.puts "#{object_type}|#{tag.tag_name}"
@@ -42,6 +47,16 @@ module Yard
42
47
  def invalid_object_types
43
48
  config_or_default('InvalidObjectTypes')
44
49
  end
50
+
51
+ # @param object [YARD::CodeObjects::Base] the code object to inspect
52
+ # @return [Boolean] true when the object is a class synthesised by Struct.new
53
+ # or Data.define, where @param documents the generated accessors
54
+ def struct_or_data_class?(object)
55
+ return false unless object.type == :class
56
+
57
+ sc_path = object.superclass&.path
58
+ sc_path == 'Struct' || sc_path == 'Data'
59
+ end
45
60
  end
46
61
  end
47
62
  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 MissingYield
8
+ # Configuration for MissingYield validator
9
+ class Config < ::Yard::Lint::Validators::Config
10
+ self.id = :missing_yield
11
+ self.defaults = {
12
+ 'Enabled' => false,
13
+ 'Severity' => 'warning'
14
+ }.freeze
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ 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 MissingYield
8
+ # Builds messages for missing yield tag offenses
9
+ class MessagesBuilder
10
+ class << self
11
+ # @param offense [Hash] offense data with :element key
12
+ # @return [String] formatted message
13
+ def call(offense)
14
+ "Method `#{offense[:element]}` yields to a block but is missing a @yield, @yieldparam, or @yieldreturn tag"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module MissingYield
8
+ # Parses validator output into structured offense hashes.
9
+ # @example Output format (one offense per line)
10
+ # # "path/to/file.rb:10: ClassName#method_name"
11
+ class Parser < ::Yard::Lint::Parsers::Base
12
+ # @return [Regexp] parses "file:line: element" collector output lines
13
+ LINE_REGEX = /^(.+):(\d+): (.+)$/.freeze
14
+
15
+ # @param validator_output [String] raw collector output
16
+ # @param config [Yard::Lint::Config, nil] configuration (unused)
17
+ # @return [Array<Hash>] parsed offense hashes
18
+ def call(validator_output, config: nil)
19
+ validator_output
20
+ .split("\n")
21
+ .map(&:strip)
22
+ .reject(&:empty?)
23
+ .filter_map do |line|
24
+ match = line.match(LINE_REGEX)
25
+ next unless match
26
+
27
+ {
28
+ location: match[1],
29
+ line: match[2].to_i,
30
+ element: match[3]
31
+ }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module MissingYield
8
+ # Result object for missing yield tag validation
9
+ class Result < Results::Base
10
+ self.default_severity = 'warning'
11
+ self.offense_type = 'line'
12
+ self.offense_name = 'MissingYield'
13
+
14
+ # @param offense [Hash] offense data
15
+ # @return [String] formatted message
16
+ def build_message(offense)
17
+ MessagesBuilder.call(offense)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ module MissingYield
8
+ # Detects methods that call `yield` but lack @yield/@yieldparam/@yieldreturn documentation
9
+ class Validator < Base
10
+ # Enable in-process execution with all visibility (private methods can yield too)
11
+ in_process visibility: :all
12
+
13
+ # Matches the `yield` keyword. The negative lookbehind `(?<![:.])`
14
+ # prevents matching method calls like `Fiber.yield` or `yielder.yield`
15
+ # and symbol literals like `:yield`. Word boundaries ensure `yield_self`
16
+ # and similar identifiers are not matched.
17
+ # Known limitation: `yield` inside regex literals (e.g. /yield/) is
18
+ # not stripped before scanning; it is rare enough to be acceptable.
19
+ YIELD_PATTERN = /(?<![:.])\byield\b/.freeze
20
+
21
+ # @return [Regexp] matches full-line Ruby comments
22
+ COMMENT_LINE_PATTERN = /\A\s*#/.freeze
23
+
24
+ # Matches simple single- and double-quoted string literals to strip before
25
+ # scanning for the yield keyword, reducing false positives from strings
26
+ # that contain the word "yield".
27
+ STRING_LITERAL_PATTERN = /("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/.freeze
28
+
29
+ # Matches user-written @yield, @yieldparam, or @yieldreturn tags in raw
30
+ # docstring text. YARD infers @yield automatically for bare `yield expr`
31
+ # statements and adds it to object.tags(), so we check docstring.all (the
32
+ # raw source text) rather than the parsed tag list to avoid counting
33
+ # YARD-inferred tags as explicit documentation.
34
+ YIELD_TAG_PATTERN = /^\s*@yield(?:param|return)?(?:\s|$)/.freeze
35
+
36
+ # Execute query for a single object during in-process execution.
37
+ # Flags methods that yield without a @yield, @yieldparam, or @yieldreturn tag.
38
+ # @param object [YARD::CodeObjects::Base] the code object to query
39
+ # @param collector [Executor::ResultCollector] collector for output
40
+ # @return [void]
41
+ def in_process_query(object, collector)
42
+ return unless object.type == :method
43
+ return if object.is_alias?
44
+ return unless object.is_explicit?
45
+ return unless object.source
46
+ return if yield_documented?(object)
47
+ return unless source_contains_yield?(object.source)
48
+
49
+ collector.puts "#{object.file}:#{object.line}: #{object.title}"
50
+ end
51
+
52
+ private
53
+
54
+ # @param object [YARD::CodeObjects::MethodObject] the method to check
55
+ # @return [Boolean] true if the user explicitly wrote a yield-related tag
56
+ def yield_documented?(object)
57
+ object.docstring.all.match?(YIELD_TAG_PATTERN)
58
+ end
59
+
60
+ # @param source [String] raw method source code
61
+ # @return [Boolean] true if the source contains the `yield` keyword
62
+ def source_contains_yield?(source)
63
+ source.each_line.any? do |line|
64
+ next false if line.match?(COMMENT_LINE_PATTERN)
65
+
66
+ sanitized = line.gsub(STRING_LITERAL_PATTERN, '""')
67
+ sanitized = sanitized.sub(/#.*$/, '')
68
+ sanitized.match?(YIELD_PATTERN)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Tags
7
+ # MissingYield validator
8
+ #
9
+ # Detects methods that call `yield` in their body but do not document
10
+ # the block with a `@yield`, `@yieldparam`, or `@yieldreturn` tag.
11
+ # Callers need to know a method yields so they can pass a block;
12
+ # undocumented yield is a silent API contract that only source readers discover.
13
+ #
14
+ # This validator is disabled by default (opt-in).
15
+ #
16
+ # @note Does not flag the inverse case (has `@yield` tag but no actual
17
+ # `yield` in source) - that is intentional for abstract/overridable methods.
18
+ #
19
+ # @note Known limitation: `yield` appearing inside heredoc bodies or
20
+ # multi-line string literals may produce false positives. These cases
21
+ # are rare enough in practice that the validator does not attempt to
22
+ # handle them.
23
+ #
24
+ # @example Bad - method yields but block is undocumented
25
+ # # Iterates over items
26
+ # # @param items [Array] the items
27
+ # def each(items)
28
+ # items.each { |item| yield item }
29
+ # end
30
+ #
31
+ # @example Good - block documented with @yield
32
+ # # Iterates over items
33
+ # # @param items [Array] the items
34
+ # # @yield [item] each item in the collection
35
+ # def each(items)
36
+ # items.each { |item| yield item }
37
+ # end
38
+ #
39
+ # @example Good - block documented with @yieldparam
40
+ # # @param items [Array] the items
41
+ # # @yieldparam item [Object] each item
42
+ # def each(items)
43
+ # items.each { |item| yield item }
44
+ # end
45
+ #
46
+ # ## Configuration
47
+ #
48
+ # Tags/MissingYield:
49
+ # Enabled: true
50
+ # Severity: warning
51
+ module MissingYield
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -11,7 +11,7 @@ module Yard
11
11
  self.defaults = {
12
12
  'Enabled' => true,
13
13
  'Severity' => 'warning',
14
- 'ValidatedTags' => %w[param option return yieldreturn yieldparam]
14
+ 'ValidatedTags' => %w[param option return yieldreturn yieldparam raise]
15
15
  }.freeze
16
16
  end
17
17
  end
@@ -23,9 +23,7 @@ module Yard
23
23
  validated_tags = config.validator_config('Tags/NonAsciiType', 'ValidatedTags') ||
24
24
  %w[param option return yieldreturn yieldparam]
25
25
 
26
- object.docstring.tags
27
- .select { |tag| validated_tags.include?(tag.tag_name) }
28
- .each do |tag|
26
+ all_typed_tags(object.docstring, validated_tags).each do |tag|
29
27
  next unless tag.types
30
28
 
31
29
  tag.types.each do |type_str|
@@ -64,7 +64,8 @@ module Yard
64
64
  # @param low_value_verbs [Array<String>] low-value verbs
65
65
  # @param patterns [Hash] enabled pattern flags
66
66
  # @return [String, nil] pattern type or nil
67
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Metrics/ParameterLists
67
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
68
+ # rubocop:disable Metrics/AbcSize, Metrics/ParameterLists
68
69
  def detect_pattern(param_name, description, type_name, word_count, articles, generic_terms, connectors, low_value_verbs, patterns)
69
70
  desc_parts = description.split
70
71
  articles_re = /^(#{articles.join('|')})/i
@@ -136,7 +137,8 @@ module Yard
136
137
 
137
138
  nil
138
139
  end
139
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Metrics/ParameterLists
140
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
141
+ # rubocop:enable Metrics/AbcSize, Metrics/ParameterLists
140
142
 
141
143
  private
142
144
 
@@ -68,7 +68,8 @@ module Yard
68
68
  # end
69
69
  #
70
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
71
+ # # @param date [Date, nil] the date describing when the event starts,
72
+ # # or nil if the event has not yet started
72
73
  # # @param user [User] the user who initiated the request and will receive notifications
73
74
  # # @param data [Hash] configuration options for the API endpoint including timeout and retry settings
74
75
  # def configure(date, user, data)
@@ -11,7 +11,7 @@ module Yard
11
11
  self.defaults = {
12
12
  'Enabled' => true,
13
13
  'Severity' => 'warning',
14
- 'ValidatedTags' => %w[param option return yieldreturn]
14
+ 'ValidatedTags' => %w[param option return yieldreturn yieldparam raise]
15
15
  }.freeze
16
16
  end
17
17
  end
@@ -31,9 +31,7 @@ module Yard
31
31
  validated_tags = config.validator_config('Tags/TypeSyntax', 'ValidatedTags') ||
32
32
  %w[param option return yieldreturn]
33
33
 
34
- object.docstring.tags
35
- .select { |tag| validated_tags.include?(tag.tag_name) }
36
- .each do |tag|
34
+ all_typed_tags(object.docstring, validated_tags).each do |tag|
37
35
  next unless tag.types
38
36
 
39
37
  tag.types.each do |type_str|
@@ -8,8 +8,7 @@ module Yard
8
8
  #
9
9
  # Validates YARD type syntax using YARD's TypesExplainer::Parser. This
10
10
  # validator ensures that type annotations can be properly parsed by YARD
11
- # and follow YARD's type specification format. This validator is enabled
12
- # by default.
11
+ # and follow YARD's type specification format. This validator is enabled by default.
13
12
  #
14
13
  # @example Bad - Invalid type syntax that YARD cannot parse
15
14
  # # @param data [{String, Integer}] invalid hash syntax
@@ -6,13 +6,13 @@ module Yard
6
6
  module Warnings
7
7
  module DuplicatedParameterName
8
8
  # Parser for DuplicatedParameterName warnings
9
- class Parser < ::Yard::Lint::Parsers::OneLineBase
9
+ class Parser < ::Yard::Lint::Parsers::TwoLineBase
10
10
  # Set of regexps for detecting warnings reported by YARD stats
11
11
  self.regexps = {
12
12
  general: /^\[warn\]: @param tag has duplicate parameter name/,
13
- message: /\[warn\]: (.*) in file/,
14
- location: /in file `(.*)`/,
15
- line: /line (\d*)/
13
+ message: /\[warn\]: (.*)$/,
14
+ location: /in file `(.*?)'\s*near/,
15
+ line: /near line (\d+)/
16
16
  }.freeze
17
17
  end
18
18
  end
@@ -9,8 +9,7 @@ module Yard
9
9
  #
10
10
  # Detects duplicate `@param` tags for the same parameter name. If a parameter
11
11
  # is documented multiple times, it's unclear which documentation is correct
12
- # and can confuse both readers and YARD's parser. This validator is enabled
13
- # by default.
12
+ # and can confuse both readers and YARD's parser. This validator is enabled by default.
14
13
  #
15
14
  # @example Bad - Duplicate @param tags
16
15
  # # @param name [String] the name
@@ -9,8 +9,7 @@ module Yard
9
9
  #
10
10
  # Detects malformed YARD directive syntax. Directives have specific format
11
11
  # requirements (like `@!attribute [r] name` for read-only attributes), and
12
- # this validator ensures those requirements are met. This validator is
13
- # enabled by default.
12
+ # this validator ensures those requirements are met. This validator is enabled by default.
14
13
  #
15
14
  # @example Bad - Malformed directive syntax
16
15
  # # @!attribute name missing brackets
@@ -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.5.1'
6
+ VERSION = '1.6.0'
7
7
  end
8
8
  end
data/lib/yard/lint.rb CHANGED
@@ -23,18 +23,28 @@ module Yard
23
23
  # @param diff [Hash, nil] diff mode options
24
24
  # - :mode [Symbol] one of :ref, :staged, :changed
25
25
  # - :base_ref [String, nil] base ref for :ref mode (auto-detects main/master if nil)
26
+ # @param source [String, nil] in-memory source content; when given, the file is not
27
+ # read from disk — `path` must be a single .rb file path (used for config/exclusion
28
+ # resolution and offense location reporting only)
26
29
  # @return [Yard::Lint::Result] result object with offenses
27
- def run(path:, config: nil, config_file: nil, progress: nil, diff: nil)
30
+ def run(path:, config: nil, config_file: nil, progress: nil, diff: nil, source: nil)
31
+ if source
32
+ raise ArgumentError, '`source:` requires `path:` to be a single .rb file, not a directory or glob' \
33
+ if path.is_a?(Array) || path.to_s.include?('*') || File.directory?(path.to_s)
34
+ end
35
+
28
36
  config ||= load_config(config_file)
29
37
 
30
- # Determine files to lint based on diff mode or normal path expansion
31
- files = if diff
38
+ # Determine files to lint based on source, diff mode, or normal path expansion
39
+ files = if source
40
+ [File.expand_path(path)]
41
+ elsif diff
32
42
  get_diff_files(diff, path, config)
33
43
  else
34
44
  expand_path(path, config)
35
45
  end
36
46
 
37
- runner = Runner.new(files, config)
47
+ runner = Runner.new(files, config, source: source)
38
48
 
39
49
  # Enable progress by default if output is a TTY
40
50
  show_progress = progress.nil? ? $stdout.tty? : progress