yard-lint 1.3.0.rc1 → 1.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb5e9ce0ee619596a8e5d35f7d46a9494ac6e0682215ad0f0bf48a3a87ee8f04
4
- data.tar.gz: 5eff9bd848c484de1c752d60577f9d3ce2910a8d4f61825b02d1552a9209c598
3
+ metadata.gz: 776d4e0a37ddbafe6b61e0df58c8a663432a04397c0d6cd9b36527484cde5675
4
+ data.tar.gz: afe2b858507d15d1b264cd8ac1a15dd0285eac638ea227643f23fb41977e6521
5
5
  SHA512:
6
- metadata.gz: 1e435ab938578b0c84ce189def96cfe2c0ab3e0d5881ab3a7d8e1c89b4e082fa21b0388175685ccce08df1a95bcacca8d5c61a703f0aaf81f9950b6bd95119a8
7
- data.tar.gz: 1de8b9658868b6bda3504151486ffa25fc86ee379d3fd41e19b05b5bf93024bd6f244bfaf79e97d45b08e4a3862db890dda9a4f8d8d9576e616021c945a43bf9
6
+ metadata.gz: d2076e759985b33a992394ebba9fc769aa70b3a16fc7914db8e4fb16bb3aac2d1d8bb5b9e6532d65bb545767321d59e1cf2c1ece89588bef12898cf2238dd05d
7
+ data.tar.gz: '08c2a18358bc5ee1fef76cd5f5d627c9859b99e1ebd7789b29babc1d1317c295e8d688583e7a456d744ff87677e9ac2f3abb925a1f8f176e1bff96a0622c00a9'
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 4.0
data/CHANGELOG.md CHANGED
@@ -1,6 +1,22 @@
1
1
  # YARD-Lint Changelog
2
2
 
3
- ## 1.3.0 (Unreleased)
3
+ ## 1.4.0 (2026-01-19)
4
+ - **[Fix]** Handle directive definitions depending on file load order (#65, @zaben903)
5
+ - **[CI]** Update Ruby 4.0 from preview2 to stable release as the default version
6
+ - Ruby 4.0 is now the default for yard-lint dogfooding and gem release workflows
7
+ - Code coverage is now tracked only on Ruby 4.0
8
+ - Added `.ruby-version` file specifying Ruby 4.0
9
+ - Continues to support Ruby 3.2, 3.3, and 3.4
10
+ - **[Feature]** Add `Tags/ForbiddenTags` validator to detect forbidden tag and type combinations (#59)
11
+ - Allows projects to disallow specific tag patterns like `@return [void]`
12
+ - Supports tag-only patterns (forbid entire tag) and tag+type patterns
13
+ - Configurable `ForbiddenPatterns` list with `Tag` and optional `Types` keys
14
+ - Use case: Enforce documentation of side effects instead of `@return [void]`
15
+ - Use case: Forbid overly generic types like `@param [Object]`
16
+ - Use case: Forbid specific tags entirely (e.g., `@api`)
17
+ - Disabled by default (opt-in validator) with 'convention' severity
18
+
19
+ ## 1.3.0 (2025-12-10)
4
20
  - **[Fix]** Expand `Tags/Order` default `EnforcedOrder` to include all standard YARD tags
5
21
  - Previous config only included: `param`, `option`, `return`, `raise`, `example`
6
22
  - Now includes full order: `param`, `option`, `yield`, `yieldparam`, `yieldreturn`, `return`, `raise`, `see`, `example`, `note`, `todo`
data/README.md CHANGED
@@ -38,6 +38,7 @@ YARD-Lint validates your YARD documentation for:
38
38
  - **Blank lines before definitions**: Detects blank lines between YARD documentation and method/class/module definitions (single blank line = convention violation, 2+ blank lines = orphaned documentation that YARD ignores)
39
39
  - **Informal notation patterns**: Detects `Note:`, `TODO:`, `See:` etc. and suggests proper YARD tags (`@note`, `@todo`, `@see`)
40
40
  - **Tag group separation**: Enforces blank lines between different YARD tag groups (e.g., between `@param` and `@return` tags) for improved readability
41
+ - **Forbidden tag patterns**: Configurable patterns to disallow specific tags or tag/type combinations (e.g., `@return [void]`, `@param [Object]`)
41
42
  - **YARD warnings**: Unknown tags, invalid directives, duplicated parameter names, and more
42
43
  - **Smart suggestions**: Provides "did you mean" suggestions for typos in parameter names using Ruby's `did_you_mean` gem with Levenshtein distance fallback
43
44
 
@@ -555,6 +556,7 @@ Supported glob patterns:
555
556
  | `Tags/RedundantParamDescription` | Detects meaningless parameter descriptions that add no value beyond the parameter name | Enabled (convention) | `Enabled`, `Severity`, `Exclude`, `CheckedTags`, `Articles`, `MaxRedundantWords`, `MinMeaningfulLength`, `GenericTerms`, `EnabledPatterns` |
556
557
  | `Tags/InformalNotation` | Detects informal notation patterns (`Note:`, `TODO:`, etc.) and suggests proper YARD tags | Enabled (warning) | `Enabled`, `Severity`, `Exclude`, `CaseSensitive`, `RequireStartOfLine`, `Patterns` |
557
558
  | `Tags/TagGroupSeparator` | Enforces blank line separators between different YARD tag groups (e.g., between `@param` and `@return` tags) | Disabled (opt-in) | `Enabled`, `Severity`, `Exclude`, `TagGroups`, `RequireAfterDescription` |
559
+ | `Tags/ForbiddenTags` | Detects forbidden tag and type combinations (e.g., `@return [void]`, `@param [Object]`) | Disabled (opt-in) | `Enabled`, `Severity`, `Exclude`, `ForbiddenPatterns` |
558
560
  | **Warnings Validators** |
559
561
  | `Warnings/UnknownTag` | Detects unknown YARD tags with "did you mean" suggestions | Enabled (error) | `Enabled`, `Severity`, `Exclude` |
560
562
  | `Warnings/UnknownDirective` | Detects unknown YARD directives | Enabled (error) | `Enabled`, `Severity`, `Exclude` |
@@ -33,6 +33,15 @@ module Yard
33
33
  original_level = YARD::Logger.instance.level
34
34
  YARD::Logger.instance.level = 4 # Only show fatal errors
35
35
 
36
+ # First pass: parse all files to process directive definitions
37
+ YARD.parse(files)
38
+
39
+ # Clear checksums to force reparsing without clearing the registry.
40
+ # This allows macro definitions from the first pass to be available
41
+ # during the second pass, enabling proper directive expansion regardless of parse order.
42
+ YARD::Registry.checksums.clear
43
+
44
+ # Second pass: reparse files now that all directive definitions are available
36
45
  @warnings = capture_warnings { YARD.parse(files) }
37
46
  @parsed = true
38
47
 
@@ -1,16 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Shim for IRB::Notifier to avoid IRB dependency in Ruby 3.5+
3
+ # Shim for IRB::Notifier to avoid IRB dependency in Ruby 3.5+/4.0+
4
4
  #
5
- # YARD's legacy parser vendors old IRB code that depends on IRB::Notifier.
5
+ # WHY THIS SHIM IS NEEDED:
6
6
  # In Ruby 3.5+, IRB is no longer part of the default gems and must be explicitly installed.
7
- # This shim provides just enough functionality to keep YARD's legacy parser working
8
- # without requiring the full IRB gem as a dependency.
7
+ # YARD's codebase has a dependency chain that triggers `require "irb/notifier"` even when
8
+ # using the modern Ripper-based parser:
9
9
  #
10
- # The notifier is only used for debug output in YARD's legacy parser, which we don't need.
10
+ # @!attribute directive parsing
11
+ # → YARD::Tags::OverloadTag#parse_signature
12
+ # → YARD::Parser::Ruby::Legacy::TokenList
13
+ # → ruby_lex.rb
14
+ # → irb/slex.rb
15
+ # → require "irb/notifier" ← FAILS without IRB gem or this shim
16
+ #
17
+ # This happens because YARD uses its legacy TokenList for parsing attribute signatures,
18
+ # regardless of which main parser is selected. Until YARD removes this dependency,
19
+ # this shim is required for Ruby 3.5+/4.0+ compatibility.
20
+ #
21
+ # WHAT THIS SHIM DOES:
22
+ # Provides a minimal no-op implementation of IRB::Notifier that satisfies YARD's
23
+ # requirements. The notifier is only used for debug output which we don't need.
11
24
  #
12
25
  # IMPORTANT: This shim only loads if IRB::Notifier is not already defined.
13
- # If IRB gem is present, we use the real implementation instead.
26
+ # If the IRB gem is present, we use the real implementation instead.
14
27
 
15
28
  # Only load the shim if IRB::Notifier is not already defined
16
29
  unless defined?(IRB::Notifier)
@@ -241,6 +241,20 @@ Tags/TagGroupSeparator:
241
241
  yield: [yield, yieldparam, yieldreturn]
242
242
  RequireAfterDescription: false
243
243
 
244
+ Tags/ForbiddenTags:
245
+ Description: 'Detects forbidden tag and type combinations.'
246
+ Enabled: false # Opt-in validator
247
+ Severity: convention
248
+ ForbiddenPatterns: []
249
+ # Example patterns:
250
+ # - Tag: return
251
+ # Types:
252
+ # - void
253
+ # - Tag: param
254
+ # Types:
255
+ # - Object
256
+ # - Tag: api # Forbids @api tag entirely (no Types = any occurrence)
257
+
244
258
  # Warnings validators - catches YARD parser errors
245
259
  Warnings/UnknownTag:
246
260
  Description: 'Detects unknown YARD tags.'
@@ -245,6 +245,20 @@ Tags/TagGroupSeparator:
245
245
  yield: [yield, yieldparam, yieldreturn]
246
246
  RequireAfterDescription: false
247
247
 
248
+ Tags/ForbiddenTags:
249
+ Description: 'Detects forbidden tag and type combinations.'
250
+ Enabled: false # Opt-in validator
251
+ Severity: error
252
+ ForbiddenPatterns: []
253
+ # Example patterns:
254
+ # - Tag: return
255
+ # Types:
256
+ # - void
257
+ # - Tag: param
258
+ # Types:
259
+ # - Object
260
+ # - Tag: api # Forbids @api tag entirely (no Types = any occurrence)
261
+
248
262
  # Warnings validators - catches YARD parser errors
249
263
  Warnings/UnknownTag:
250
264
  Description: 'Detects unknown YARD tags.'
@@ -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
@@ -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.rc1'
6
+ VERSION = '1.4.0'
7
7
  end
8
8
  end
data/lib/yard-lint.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Load IRB notifier shim before YARD to avoid IRB dependency in Ruby 3.5+
3
+ # Load IRB notifier shim before YARD to avoid IRB dependency in Ruby 3.5+/4.0+
4
4
  # This must be loaded before any YARD code is required
5
5
  require_relative 'yard/lint/ext/irb_notifier_shim'
6
6
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yard-lint
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0.rc1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -47,6 +47,7 @@ extensions: []
47
47
  extra_rdoc_files: []
48
48
  files:
49
49
  - ".coditsu/ci.yml"
50
+ - ".ruby-version"
50
51
  - CHANGELOG.md
51
52
  - LICENSE.txt
52
53
  - README.md
@@ -142,6 +143,12 @@ files:
142
143
  - lib/yard/lint/validators/tags/example_syntax/parser.rb
143
144
  - lib/yard/lint/validators/tags/example_syntax/result.rb
144
145
  - lib/yard/lint/validators/tags/example_syntax/validator.rb
146
+ - lib/yard/lint/validators/tags/forbidden_tags.rb
147
+ - lib/yard/lint/validators/tags/forbidden_tags/config.rb
148
+ - lib/yard/lint/validators/tags/forbidden_tags/messages_builder.rb
149
+ - lib/yard/lint/validators/tags/forbidden_tags/parser.rb
150
+ - lib/yard/lint/validators/tags/forbidden_tags/result.rb
151
+ - lib/yard/lint/validators/tags/forbidden_tags/validator.rb
145
152
  - lib/yard/lint/validators/tags/informal_notation.rb
146
153
  - lib/yard/lint/validators/tags/informal_notation/config.rb
147
154
  - lib/yard/lint/validators/tags/informal_notation/messages_builder.rb
@@ -259,7 +266,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
259
266
  - !ruby/object:Gem::Version
260
267
  version: '0'
261
268
  requirements: []
262
- rubygems_version: 3.6.9
269
+ rubygems_version: 4.0.0.dev
263
270
  specification_version: 4
264
271
  summary: YARD documentation linter and validator
265
272
  test_files: []