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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +73 -1
- data/README.md +121 -89
- data/bin/yard-lint +44 -0
- data/lib/yard/lint/config.rb +35 -3
- data/lib/yard/lint/config_generator.rb +191 -0
- data/lib/yard/lint/config_loader.rb +1 -1
- data/lib/yard/lint/result_builder.rb +10 -1
- data/lib/yard/lint/validators/base.rb +77 -12
- data/lib/yard/lint/validators/documentation/markdown_syntax/config.rb +20 -0
- data/lib/yard/lint/validators/documentation/markdown_syntax/messages_builder.rb +44 -0
- data/lib/yard/lint/validators/documentation/markdown_syntax/parser.rb +53 -0
- data/lib/yard/lint/validators/documentation/markdown_syntax/result.rb +25 -0
- data/lib/yard/lint/validators/documentation/markdown_syntax/validator.rb +38 -0
- data/lib/yard/lint/validators/documentation/markdown_syntax.rb +37 -0
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/validator.rb +7 -6
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods.rb +26 -1
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments/validator.rb +4 -5
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +26 -1
- data/lib/yard/lint/validators/documentation/undocumented_objects/config.rb +2 -1
- data/lib/yard/lint/validators/documentation/undocumented_objects/parser.rb +95 -5
- data/lib/yard/lint/validators/documentation/undocumented_objects/validator.rb +15 -11
- data/lib/yard/lint/validators/documentation/undocumented_objects.rb +131 -2
- data/lib/yard/lint/validators/documentation/undocumented_options/config.rb +20 -0
- data/lib/yard/lint/validators/documentation/undocumented_options/parser.rb +53 -0
- data/lib/yard/lint/validators/documentation/undocumented_options/result.rb +29 -0
- data/lib/yard/lint/validators/documentation/undocumented_options/validator.rb +38 -0
- data/lib/yard/lint/validators/documentation/undocumented_options.rb +40 -0
- data/lib/yard/lint/validators/semantic/abstract_methods/validator.rb +4 -5
- data/lib/yard/lint/validators/semantic/abstract_methods.rb +31 -1
- data/lib/yard/lint/validators/tags/api_tags/validator.rb +4 -5
- data/lib/yard/lint/validators/tags/api_tags.rb +34 -1
- data/lib/yard/lint/validators/tags/collection_type/config.rb +22 -0
- data/lib/yard/lint/validators/tags/collection_type/messages_builder.rb +73 -0
- data/lib/yard/lint/validators/tags/collection_type/parser.rb +50 -0
- data/lib/yard/lint/validators/tags/collection_type/result.rb +25 -0
- data/lib/yard/lint/validators/tags/collection_type/validator.rb +92 -0
- data/lib/yard/lint/validators/tags/collection_type.rb +50 -0
- data/lib/yard/lint/validators/tags/example_syntax/config.rb +20 -0
- data/lib/yard/lint/validators/tags/example_syntax/messages_builder.rb +28 -0
- data/lib/yard/lint/validators/tags/example_syntax/parser.rb +79 -0
- data/lib/yard/lint/validators/tags/example_syntax/result.rb +42 -0
- data/lib/yard/lint/validators/tags/example_syntax/validator.rb +88 -0
- data/lib/yard/lint/validators/tags/example_syntax.rb +42 -0
- data/lib/yard/lint/validators/tags/invalid_types/validator.rb +8 -8
- data/lib/yard/lint/validators/tags/invalid_types.rb +25 -1
- data/lib/yard/lint/validators/tags/meaningless_tag/config.rb +22 -0
- data/lib/yard/lint/validators/tags/meaningless_tag/messages_builder.rb +28 -0
- data/lib/yard/lint/validators/tags/meaningless_tag/parser.rb +53 -0
- data/lib/yard/lint/validators/tags/meaningless_tag/result.rb +26 -0
- data/lib/yard/lint/validators/tags/meaningless_tag/validator.rb +82 -0
- data/lib/yard/lint/validators/tags/meaningless_tag.rb +43 -0
- data/lib/yard/lint/validators/tags/option_tags/validator.rb +11 -6
- data/lib/yard/lint/validators/tags/option_tags.rb +26 -1
- data/lib/yard/lint/validators/tags/order/validator.rb +4 -5
- data/lib/yard/lint/validators/tags/order.rb +25 -1
- data/lib/yard/lint/validators/tags/redundant_param_description/config.rb +33 -0
- data/lib/yard/lint/validators/tags/redundant_param_description/messages_builder.rb +61 -0
- data/lib/yard/lint/validators/tags/redundant_param_description/parser.rb +67 -0
- data/lib/yard/lint/validators/tags/redundant_param_description/result.rb +25 -0
- data/lib/yard/lint/validators/tags/redundant_param_description/validator.rb +148 -0
- data/lib/yard/lint/validators/tags/redundant_param_description.rb +168 -0
- data/lib/yard/lint/validators/tags/tag_type_position/config.rb +22 -0
- data/lib/yard/lint/validators/tags/tag_type_position/messages_builder.rb +38 -0
- data/lib/yard/lint/validators/tags/tag_type_position/parser.rb +51 -0
- data/lib/yard/lint/validators/tags/tag_type_position/result.rb +25 -0
- data/lib/yard/lint/validators/tags/tag_type_position/validator.rb +111 -0
- data/lib/yard/lint/validators/tags/tag_type_position.rb +51 -0
- data/lib/yard/lint/validators/tags/type_syntax/config.rb +21 -0
- data/lib/yard/lint/validators/tags/type_syntax/messages_builder.rb +27 -0
- data/lib/yard/lint/validators/tags/type_syntax/parser.rb +54 -0
- data/lib/yard/lint/validators/tags/type_syntax/result.rb +25 -0
- data/lib/yard/lint/validators/tags/type_syntax/validator.rb +76 -0
- data/lib/yard/lint/validators/tags/type_syntax.rb +38 -0
- data/lib/yard/lint/validators/warnings/duplicated_parameter_name/validator.rb +4 -5
- data/lib/yard/lint/validators/warnings/duplicated_parameter_name.rb +26 -1
- data/lib/yard/lint/validators/warnings/invalid_directive_format/validator.rb +4 -5
- data/lib/yard/lint/validators/warnings/invalid_directive_format.rb +26 -1
- data/lib/yard/lint/validators/warnings/invalid_tag_format/validator.rb +4 -5
- data/lib/yard/lint/validators/warnings/invalid_tag_format.rb +25 -1
- data/lib/yard/lint/validators/warnings/unknown_directive/validator.rb +4 -5
- data/lib/yard/lint/validators/warnings/unknown_directive.rb +26 -1
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/parser.rb +4 -1
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/validator.rb +4 -5
- data/lib/yard/lint/validators/warnings/unknown_parameter_name.rb +23 -1
- data/lib/yard/lint/validators/warnings/unknown_tag/validator.rb +4 -5
- data/lib/yard/lint/validators/warnings/unknown_tag.rb +26 -1
- data/lib/yard/lint/version.rb +1 -1
- data/lib/yard/lint.rb +1 -0
- data/misc/logo.png +0 -0
- metadata +64 -1
|
@@ -7,30 +7,34 @@ module Yard
|
|
|
7
7
|
module UndocumentedObjects
|
|
8
8
|
# Runs yard list to check for undocumented objects
|
|
9
9
|
class Validator < Base
|
|
10
|
-
# Query to find all objects without documentation
|
|
11
|
-
QUERY = "'docstring.blank?'"
|
|
12
|
-
|
|
13
|
-
private_constant :QUERY
|
|
14
|
-
|
|
15
10
|
private
|
|
16
11
|
|
|
17
12
|
# Runs yard list query with proper settings on a given dir and files
|
|
18
13
|
# @param dir [String] dir where we should generate the temp docs
|
|
19
|
-
# @param
|
|
14
|
+
# @param file_list_path [String] path to temp file containing file paths (one per line)
|
|
20
15
|
# @return [Hash] shell command execution hash results
|
|
21
|
-
def yard_cmd(dir,
|
|
16
|
+
def yard_cmd(dir, file_list_path)
|
|
22
17
|
cmd = <<~CMD
|
|
23
|
-
yard list \
|
|
18
|
+
cat #{Shellwords.escape(file_list_path)} | xargs yard list \
|
|
24
19
|
#{shell_arguments} \
|
|
25
|
-
--query #{
|
|
20
|
+
--query #{query} \
|
|
26
21
|
-q \
|
|
27
|
-
-b #{Shellwords.escape(dir)}
|
|
28
|
-
#{escaped_file_names}
|
|
22
|
+
-b #{Shellwords.escape(dir)}
|
|
29
23
|
CMD
|
|
30
24
|
cmd = cmd.tr("\n", ' ')
|
|
31
25
|
|
|
32
26
|
shell(cmd)
|
|
33
27
|
end
|
|
28
|
+
|
|
29
|
+
# Custom query that outputs parameter count for methods
|
|
30
|
+
# Format: file.rb:LINE: ElementName|ARITY
|
|
31
|
+
# Arity counts all parameters (required + optional) excluding splat and block
|
|
32
|
+
# @return [String] YARD query string
|
|
33
|
+
def query
|
|
34
|
+
<<~QUERY.chomp
|
|
35
|
+
"if docstring.all.empty? then if object.is_a?(YARD::CodeObjects::MethodObject) then arity = object.parameters.reject { |p| p[0].start_with?('*', '&') }.size; puts object.file + ':' + object.line.to_s + ': ' + object.title + '|' + arity.to_s; else puts object.file + ':' + object.line.to_s + ': ' + object.title; end; false; end"
|
|
36
|
+
QUERY
|
|
37
|
+
end
|
|
34
38
|
end
|
|
35
39
|
end
|
|
36
40
|
end
|
|
@@ -4,8 +4,137 @@ module Yard
|
|
|
4
4
|
module Lint
|
|
5
5
|
module Validators
|
|
6
6
|
module Documentation
|
|
7
|
-
# UndocumentedObjects validator
|
|
8
|
-
#
|
|
7
|
+
# UndocumentedObjects validator
|
|
8
|
+
#
|
|
9
|
+
# Checks for missing documentation on classes, modules, and methods.
|
|
10
|
+
# This validator supports flexible method exclusions through the `ExcludedMethods`
|
|
11
|
+
# configuration option.
|
|
12
|
+
#
|
|
13
|
+
# ## Pattern Types
|
|
14
|
+
#
|
|
15
|
+
# The `ExcludedMethods` feature supports three pattern types for maximum flexibility:
|
|
16
|
+
#
|
|
17
|
+
# ### 1. Exact Name Matching
|
|
18
|
+
#
|
|
19
|
+
# Excludes methods with the specified name, regardless of arity:
|
|
20
|
+
#
|
|
21
|
+
# ExcludedMethods:
|
|
22
|
+
# - 'to_s' # Excludes ALL to_s methods regardless of parameters
|
|
23
|
+
# - 'inspect' # Excludes ALL inspect methods
|
|
24
|
+
#
|
|
25
|
+
# Note: Exact name matching excludes the method with **any arity**. If you need
|
|
26
|
+
# arity-specific exclusions, use arity notation instead.
|
|
27
|
+
#
|
|
28
|
+
# ### 2. Arity Notation (method_name/N)
|
|
29
|
+
#
|
|
30
|
+
# Excludes methods with specific parameter counts:
|
|
31
|
+
#
|
|
32
|
+
# ExcludedMethods:
|
|
33
|
+
# - 'initialize/0' # Only excludes initialize with NO parameters (default)
|
|
34
|
+
# - 'call/1' # Only excludes call methods with exactly 1 parameter
|
|
35
|
+
# - 'initialize/2' # Only excludes initialize with exactly 2 parameters
|
|
36
|
+
#
|
|
37
|
+
# Note: Arity counts total parameters (required + optional) excluding splat (*)
|
|
38
|
+
# and block (&) parameters.
|
|
39
|
+
#
|
|
40
|
+
# ### 3. Regex Patterns
|
|
41
|
+
#
|
|
42
|
+
# Excludes methods matching a regular expression:
|
|
43
|
+
#
|
|
44
|
+
# ExcludedMethods:
|
|
45
|
+
# - '/^_/' # Excludes all methods starting with underscore (private convention)
|
|
46
|
+
# - '/^test_/' # Excludes all test methods
|
|
47
|
+
# - '/_(helper|util)$/' # Excludes methods ending with _helper or _util
|
|
48
|
+
#
|
|
49
|
+
# ## Configuration Examples
|
|
50
|
+
#
|
|
51
|
+
# ### Minimal setup - Only exclude parameter-less initialize
|
|
52
|
+
#
|
|
53
|
+
# Documentation/UndocumentedObjects:
|
|
54
|
+
# ExcludedMethods:
|
|
55
|
+
# - 'initialize/0'
|
|
56
|
+
#
|
|
57
|
+
# ### Common Rails/Ruby patterns
|
|
58
|
+
#
|
|
59
|
+
# Documentation/UndocumentedObjects:
|
|
60
|
+
# ExcludedMethods:
|
|
61
|
+
# - 'initialize/0' # Parameter-less constructors
|
|
62
|
+
# - '/^_/' # Private methods (by convention)
|
|
63
|
+
# - 'to_s' # String conversion
|
|
64
|
+
# - 'inspect' # Object inspection
|
|
65
|
+
# - 'hash' # Hash code generation
|
|
66
|
+
# - 'eql?' # Equality comparison
|
|
67
|
+
# - '==' # Binary equality operator
|
|
68
|
+
# - '<=>' # Spaceship operator (comparison)
|
|
69
|
+
# - '+' # Addition operator
|
|
70
|
+
# - '-' # Subtraction operator
|
|
71
|
+
# - '+@' # Unary plus operator
|
|
72
|
+
# - '-@' # Unary minus operator
|
|
73
|
+
#
|
|
74
|
+
# ### Test framework exclusions
|
|
75
|
+
#
|
|
76
|
+
# Documentation/UndocumentedObjects:
|
|
77
|
+
# ExcludedMethods:
|
|
78
|
+
# - '/^test_/' # Minitest methods
|
|
79
|
+
# - '/^should_/' # Shoulda methods
|
|
80
|
+
# - 'setup/0' # Setup with no params
|
|
81
|
+
# - 'teardown/0' # Teardown with no params
|
|
82
|
+
#
|
|
83
|
+
# ## Pattern Validation & Edge Cases
|
|
84
|
+
#
|
|
85
|
+
# The `ExcludedMethods` feature includes robust validation and error handling:
|
|
86
|
+
#
|
|
87
|
+
# **Automatic Pattern Sanitization:**
|
|
88
|
+
# - **Nil values** are automatically removed
|
|
89
|
+
# - **Empty strings** and whitespace-only patterns are filtered out
|
|
90
|
+
# - **Whitespace trimming** is applied to all patterns
|
|
91
|
+
# - **Empty regex patterns** (`//`) are rejected (would match everything)
|
|
92
|
+
# - **Non-array values** are automatically converted to arrays
|
|
93
|
+
#
|
|
94
|
+
# **Invalid Pattern Handling:**
|
|
95
|
+
# - **Invalid regex patterns** (e.g., `/[/`, `/(unclosed`) are silently skipped without crashing
|
|
96
|
+
# - **Invalid arity notation** (e.g., `method/abc`, `method/`) is silently skipped
|
|
97
|
+
# - **Pattern matching is case-sensitive** for both exact names and regex
|
|
98
|
+
#
|
|
99
|
+
# **Operator Method Support:**
|
|
100
|
+
# YARD-Lint fully supports Ruby operator methods including:
|
|
101
|
+
# - Binary operators: `+`, `-`, `*`, `/`, `%`, `**`, `==`, `!=`, `===`, `<`, `>`,
|
|
102
|
+
# `<=`, `>=`, `<=>`, `&`, `|`, `^`, `<<`, `>>`
|
|
103
|
+
# - Unary operators: `+@`, `-@`, `!`, `~`
|
|
104
|
+
# - Other special methods: `[]`, `[]=`, `=~`
|
|
105
|
+
#
|
|
106
|
+
# **Pattern Matching Behavior:**
|
|
107
|
+
# - **Any match excludes**: If a method matches any pattern, it is excluded from validation
|
|
108
|
+
# - **Patterns are evaluated in order** as defined in the configuration
|
|
109
|
+
# - **Exact names have no arity restriction**: `'initialize'` excludes all initialize
|
|
110
|
+
# methods, regardless of parameters
|
|
111
|
+
# - **Arity notation is strict**: `'initialize/0'` only excludes initialize with
|
|
112
|
+
# exactly 0 parameters
|
|
113
|
+
#
|
|
114
|
+
# ## Troubleshooting
|
|
115
|
+
#
|
|
116
|
+
# ### Methods still showing as undocumented
|
|
117
|
+
#
|
|
118
|
+
# 1. Verify the method name matches exactly (case-sensitive)
|
|
119
|
+
# 2. Check if you're using arity notation - ensure the arity count is correct
|
|
120
|
+
# 3. For regex patterns, test your regex independently to ensure it matches
|
|
121
|
+
# 4. Remember: Arity counts `required + optional` parameters, **excluding**
|
|
122
|
+
# splat (`*args`) and block (`&block`)
|
|
123
|
+
#
|
|
124
|
+
# ### Regex patterns not working
|
|
125
|
+
#
|
|
126
|
+
# 1. Ensure you're using `/pattern/` format with forward slashes
|
|
127
|
+
# 2. Test the regex in Ruby: `Regexp.new('your_pattern').match?('method_name')`
|
|
128
|
+
# 3. Escape special regex characters: `\.`, `\(`, `\)`, `\[`, `\]`, etc.
|
|
129
|
+
# 4. Invalid regex patterns are silently skipped - check for syntax errors
|
|
130
|
+
#
|
|
131
|
+
# ### Arity not matching
|
|
132
|
+
#
|
|
133
|
+
# 1. Count parameters correctly: `def method(a, b = 1)` has arity 2 (required + optional)
|
|
134
|
+
# 2. Splat parameters don't count: `def method(a, *rest)` has arity 1
|
|
135
|
+
# 3. Block parameters don't count: `def method(a, &block)` has arity 1
|
|
136
|
+
# 4. Keyword arguments count as individual parameters: `def method(a:, b:)` has arity 2
|
|
137
|
+
#
|
|
9
138
|
module UndocumentedObjects
|
|
10
139
|
end
|
|
11
140
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module UndocumentedOptions
|
|
8
|
+
# Configuration for UndocumentedOptions validator
|
|
9
|
+
class Config < ::Yard::Lint::Validators::Config
|
|
10
|
+
self.id = :undocumented_options
|
|
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,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module UndocumentedOptions
|
|
8
|
+
# Parses YARD output for undocumented options violations
|
|
9
|
+
class Parser < Parsers::Base
|
|
10
|
+
# Parse YARD output into structured violations
|
|
11
|
+
# @param output [String] raw YARD output
|
|
12
|
+
# @return [Array<Hash>] array of violation hashes
|
|
13
|
+
def call(output)
|
|
14
|
+
return [] if output.nil? || output.empty?
|
|
15
|
+
|
|
16
|
+
violations = []
|
|
17
|
+
lines = output.lines.map(&:chomp)
|
|
18
|
+
|
|
19
|
+
i = 0
|
|
20
|
+
while i < lines.size
|
|
21
|
+
line = lines[i]
|
|
22
|
+
|
|
23
|
+
# Match location line: "file:line: object_name"
|
|
24
|
+
if (location_match = line.match(/^(.+):(\d+): (.+)$/))
|
|
25
|
+
file_path = location_match[1]
|
|
26
|
+
line_number = location_match[2].to_i
|
|
27
|
+
object_name = location_match[3]
|
|
28
|
+
|
|
29
|
+
# Next line contains parameter list
|
|
30
|
+
i += 1
|
|
31
|
+
next unless i < lines.size
|
|
32
|
+
|
|
33
|
+
params = lines[i]
|
|
34
|
+
|
|
35
|
+
violations << {
|
|
36
|
+
location: file_path,
|
|
37
|
+
line: line_number,
|
|
38
|
+
object_name: object_name,
|
|
39
|
+
params: params
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
i += 1
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
violations
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module UndocumentedOptions
|
|
8
|
+
# Result object for undocumented options validation
|
|
9
|
+
class Result < Results::Base
|
|
10
|
+
self.default_severity = 'warning'
|
|
11
|
+
self.offense_type = 'line'
|
|
12
|
+
self.offense_name = 'UndocumentedOptions'
|
|
13
|
+
|
|
14
|
+
# Build human-readable message for undocumented options offense
|
|
15
|
+
# @param offense [Hash] offense data with :object_name and :params
|
|
16
|
+
# @return [String] formatted message
|
|
17
|
+
def build_message(offense)
|
|
18
|
+
object_name = offense[:object_name]
|
|
19
|
+
params = offense[:params]
|
|
20
|
+
|
|
21
|
+
"Method '#{object_name}' has options parameter (#{params}) " \
|
|
22
|
+
'but no @option tags in documentation.'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module UndocumentedOptions
|
|
8
|
+
# Validates that methods with options hash parameters have @option tags
|
|
9
|
+
class Validator < Validators::Base
|
|
10
|
+
# YARD query to detect methods with options parameters but no @option tags
|
|
11
|
+
# @return [String] YARD Ruby query code
|
|
12
|
+
def query
|
|
13
|
+
<<~QUERY.strip
|
|
14
|
+
'if object.is_a?(YARD::CodeObjects::MethodObject); params = object.parameters || []; has_options_param = params.any? { |p| p[0] =~ /^(options?|opts?|kwargs)$/ || p[0] =~ /^\\*\\*/ || (p[0] =~ /^(options?|opts?|kwargs)$/ && p[1] =~ /^\\{\\}/) }; if has_options_param; option_tags = object.tags(:option); if option_tags.empty?; puts object.file + ":" + object.line.to_s + ": " + object.title; puts params.map { |p| p.join(" ") }.join(", "); end; end; end; false'
|
|
15
|
+
QUERY
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Builds and executes the YARD command to detect undocumented options
|
|
19
|
+
# @param dir [String] the directory containing the .yardoc database
|
|
20
|
+
# @param file_list_path [String] path to file containing list of files to analyze
|
|
21
|
+
# @return [String] command output
|
|
22
|
+
def yard_cmd(dir, file_list_path)
|
|
23
|
+
cmd = <<~CMD
|
|
24
|
+
cat #{Shellwords.escape(file_list_path)} | xargs yard list \
|
|
25
|
+
#{shell_arguments} \
|
|
26
|
+
--query #{query} \
|
|
27
|
+
-q \
|
|
28
|
+
-b #{Shellwords.escape(dir)}
|
|
29
|
+
CMD
|
|
30
|
+
cmd = cmd.tr("\n", ' ')
|
|
31
|
+
shell(cmd)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
# UndocumentedOptions validator
|
|
8
|
+
#
|
|
9
|
+
# Checks that options hashes have detailed documentation about their keys.
|
|
10
|
+
# When a method accepts an options hash parameter, the individual option
|
|
11
|
+
# keys should be documented using `@option` tags. This validator is enabled
|
|
12
|
+
# by default.
|
|
13
|
+
#
|
|
14
|
+
# @example Bad - Options parameter without @option tags
|
|
15
|
+
# # Configures the service
|
|
16
|
+
# # @param options [Hash] configuration options
|
|
17
|
+
# def configure(options)
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# @example Good - Options keys documented with @option tags
|
|
21
|
+
# # Configures the service
|
|
22
|
+
# # @param options [Hash] configuration options
|
|
23
|
+
# # @option options [Boolean] :enabled Whether to enable the feature
|
|
24
|
+
# # @option options [Integer] :timeout Connection timeout in seconds
|
|
25
|
+
# def configure(options)
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# ## Configuration
|
|
29
|
+
#
|
|
30
|
+
# To disable this validator:
|
|
31
|
+
#
|
|
32
|
+
# Documentation/UndocumentedOptions:
|
|
33
|
+
# Enabled: false
|
|
34
|
+
#
|
|
35
|
+
module UndocumentedOptions
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -11,15 +11,14 @@ module Yard
|
|
|
11
11
|
|
|
12
12
|
# Runs YARD list query to find abstract methods with implementation
|
|
13
13
|
# @param dir [String] dir where the YARD db is (or where it should be generated)
|
|
14
|
-
# @param
|
|
14
|
+
# @param file_list_path [String] path to temp file containing file paths (one per line)
|
|
15
15
|
# @return [Hash] shell command execution hash results
|
|
16
|
-
def yard_cmd(dir,
|
|
16
|
+
def yard_cmd(dir, file_list_path)
|
|
17
17
|
cmd = <<~CMD
|
|
18
|
-
yard list \
|
|
18
|
+
cat #{Shellwords.escape(file_list_path)} | xargs yard list \
|
|
19
19
|
--private \
|
|
20
20
|
--protected \
|
|
21
|
-
-b #{Shellwords.escape(dir)}
|
|
22
|
-
#{escaped_file_names}
|
|
21
|
+
-b #{Shellwords.escape(dir)}
|
|
23
22
|
CMD
|
|
24
23
|
cmd = cmd.tr("\n", ' ')
|
|
25
24
|
cmd = cmd.gsub('yard list', "yard list --query #{query}")
|
|
@@ -4,7 +4,37 @@ module Yard
|
|
|
4
4
|
module Lint
|
|
5
5
|
module Validators
|
|
6
6
|
module Semantic
|
|
7
|
-
# AbstractMethods validator
|
|
7
|
+
# AbstractMethods validator
|
|
8
|
+
#
|
|
9
|
+
# Ensures that methods marked with `@abstract` are actually abstract (not
|
|
10
|
+
# implemented). Abstract methods should either raise NotImplementedError or
|
|
11
|
+
# be empty stubs, not contain actual implementation logic. This validator
|
|
12
|
+
# is enabled by default.
|
|
13
|
+
#
|
|
14
|
+
# @example Bad - @abstract tag on implemented method
|
|
15
|
+
# # @abstract
|
|
16
|
+
# def process
|
|
17
|
+
# puts "This is actually implemented!"
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# @example Good - @abstract method raises NotImplementedError
|
|
21
|
+
# # @abstract
|
|
22
|
+
# def process
|
|
23
|
+
# raise NotImplementedError
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# @example Good - @abstract method is empty
|
|
27
|
+
# # @abstract
|
|
28
|
+
# def process
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# ## Configuration
|
|
32
|
+
#
|
|
33
|
+
# To disable this validator:
|
|
34
|
+
#
|
|
35
|
+
# Semantic/AbstractMethods:
|
|
36
|
+
# Enabled: false
|
|
37
|
+
#
|
|
8
38
|
module AbstractMethods
|
|
9
39
|
end
|
|
10
40
|
end
|
|
@@ -11,15 +11,14 @@ module Yard
|
|
|
11
11
|
|
|
12
12
|
# Runs yard list query to find objects missing or with invalid @api tags
|
|
13
13
|
# @param dir [String] dir where the yard db is (or where it should be generated)
|
|
14
|
-
# @param
|
|
14
|
+
# @param file_list_path [String] path to temp file containing file paths (one per line)
|
|
15
15
|
# @return [Hash] shell command execution hash results
|
|
16
|
-
def yard_cmd(dir,
|
|
16
|
+
def yard_cmd(dir, file_list_path)
|
|
17
17
|
cmd = <<~CMD
|
|
18
|
-
yard list \
|
|
18
|
+
cat #{Shellwords.escape(file_list_path)} | xargs yard list \
|
|
19
19
|
--private \
|
|
20
20
|
--protected \
|
|
21
|
-
-b #{Shellwords.escape(dir)}
|
|
22
|
-
#{escaped_file_names}
|
|
21
|
+
-b #{Shellwords.escape(dir)}
|
|
23
22
|
CMD
|
|
24
23
|
cmd = cmd.tr("\n", ' ')
|
|
25
24
|
cmd = cmd.gsub('yard list', "yard list --query #{query}")
|
|
@@ -4,7 +4,40 @@ module Yard
|
|
|
4
4
|
module Lint
|
|
5
5
|
module Validators
|
|
6
6
|
module Tags
|
|
7
|
-
# ApiTags validator
|
|
7
|
+
# ApiTags validator
|
|
8
|
+
#
|
|
9
|
+
# Enforces that all public classes, modules, and methods have an `@api` tag
|
|
10
|
+
# to explicitly document their API visibility level. This validator is disabled
|
|
11
|
+
# by default and must be explicitly enabled.
|
|
12
|
+
#
|
|
13
|
+
# ## Configuration
|
|
14
|
+
#
|
|
15
|
+
# To enable this validator (it's disabled by default):
|
|
16
|
+
#
|
|
17
|
+
# Tags/ApiTags:
|
|
18
|
+
# Enabled: true
|
|
19
|
+
# AllowedApis:
|
|
20
|
+
# - public
|
|
21
|
+
# - private
|
|
22
|
+
#
|
|
23
|
+
# @example Good - Methods and classes have @api tags
|
|
24
|
+
# # @api public
|
|
25
|
+
# class MyClass
|
|
26
|
+
# # @api public
|
|
27
|
+
# def public_method
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# # @api private
|
|
31
|
+
# def internal_helper
|
|
32
|
+
# end
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# @example Bad - Missing @api tags
|
|
36
|
+
# class AnotherClass
|
|
37
|
+
# def some_method
|
|
38
|
+
# end
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
8
41
|
module ApiTags
|
|
9
42
|
end
|
|
10
43
|
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 CollectionType
|
|
8
|
+
# Configuration for CollectionType validator
|
|
9
|
+
class Config < ::Yard::Lint::Validators::Config
|
|
10
|
+
self.id = :collection_type
|
|
11
|
+
self.defaults = {
|
|
12
|
+
'Enabled' => true,
|
|
13
|
+
'Severity' => 'convention',
|
|
14
|
+
'ValidatedTags' => %w[param option return yieldreturn],
|
|
15
|
+
'EnforcedStyle' => 'long' # 'long' (Hash{K => V}) or 'short' ({K => V})
|
|
16
|
+
}.freeze
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module CollectionType
|
|
8
|
+
# Builds human-readable messages for CollectionType violations
|
|
9
|
+
class MessagesBuilder
|
|
10
|
+
class << self
|
|
11
|
+
# Formats a violation message
|
|
12
|
+
# @param offense [Hash] the offense details
|
|
13
|
+
# @return [String] formatted message
|
|
14
|
+
def call(offense)
|
|
15
|
+
type_string = offense[:type_string]
|
|
16
|
+
tag_name = offense[:tag_name]
|
|
17
|
+
detected_style = offense[:detected_style]
|
|
18
|
+
|
|
19
|
+
# Extract the corrected version based on detected style
|
|
20
|
+
corrected = suggest_correction(type_string, detected_style)
|
|
21
|
+
style_description = detected_style == 'short' ? 'long' : 'short'
|
|
22
|
+
|
|
23
|
+
"Use #{style_description} collection syntax #{corrected} instead of " \
|
|
24
|
+
"#{type_string} in @#{tag_name} tag."
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# Suggests the corrected YARD syntax based on detected style
|
|
30
|
+
# @param type_string [String] the incorrect type string
|
|
31
|
+
# @param detected_style [String] the detected style ('short' or 'long')
|
|
32
|
+
# @return [String] the suggested correction
|
|
33
|
+
def suggest_correction(type_string, detected_style)
|
|
34
|
+
if detected_style == 'short'
|
|
35
|
+
# Convert short to long: Hash<K, V> -> Hash{K => V} or {K => V} -> Hash{K => V}
|
|
36
|
+
convert_to_long(type_string)
|
|
37
|
+
else
|
|
38
|
+
# Convert long to short: Hash{K => V} -> {K => V}
|
|
39
|
+
convert_to_short(type_string)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Converts short syntax to long syntax
|
|
44
|
+
# @param type_string [String] the type string
|
|
45
|
+
# @return [String] the converted type string
|
|
46
|
+
def convert_to_long(type_string)
|
|
47
|
+
if type_string.start_with?('{')
|
|
48
|
+
# {K => V} -> Hash{K => V}
|
|
49
|
+
"Hash#{type_string}"
|
|
50
|
+
else
|
|
51
|
+
# Hash<K, V> -> Hash{K => V}
|
|
52
|
+
type_string.gsub(/Hash<(.+?)>/) do
|
|
53
|
+
types = ::Regexp.last_match(1)
|
|
54
|
+
# Split on comma, handle nested types
|
|
55
|
+
"Hash{#{types.sub(/,\s*/, ' => ')}}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Converts long syntax to short syntax
|
|
61
|
+
# @param type_string [String] the type string
|
|
62
|
+
# @return [String] the converted type string
|
|
63
|
+
def convert_to_short(type_string)
|
|
64
|
+
# Hash{K => V} -> {K => V}
|
|
65
|
+
type_string.sub(/^Hash/, '')
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module CollectionType
|
|
8
|
+
# Parses YARD output for CollectionType violations
|
|
9
|
+
class Parser < ::Yard::Lint::Parsers::Base
|
|
10
|
+
# Parses YARD query output into structured violation data
|
|
11
|
+
# @param yard_output [String] raw output from YARD query
|
|
12
|
+
# @param _kwargs [Hash] additional keyword arguments (unused)
|
|
13
|
+
# @return [Array<Hash>] array of violation hashes
|
|
14
|
+
def call(yard_output, **_kwargs)
|
|
15
|
+
return [] if yard_output.nil? || yard_output.strip.empty?
|
|
16
|
+
|
|
17
|
+
lines = yard_output.split("\n").map(&:strip).reject(&:empty?)
|
|
18
|
+
violations = []
|
|
19
|
+
|
|
20
|
+
lines.each_slice(2) do |location_line, details_line|
|
|
21
|
+
next unless location_line && details_line
|
|
22
|
+
|
|
23
|
+
# Parse location: "file.rb:10: ClassName#method_name"
|
|
24
|
+
location_match = location_line.match(/^(.+):(\d+): (.+)$/)
|
|
25
|
+
next unless location_match
|
|
26
|
+
|
|
27
|
+
# Parse details: "tag_name|type_string|detected_style"
|
|
28
|
+
details = details_line.split('|', 3)
|
|
29
|
+
next unless details.size == 3
|
|
30
|
+
|
|
31
|
+
tag_name, type_string, detected_style = details
|
|
32
|
+
|
|
33
|
+
violations << {
|
|
34
|
+
location: location_match[1],
|
|
35
|
+
line: location_match[2].to_i,
|
|
36
|
+
object_name: location_match[3],
|
|
37
|
+
tag_name: tag_name,
|
|
38
|
+
type_string: type_string,
|
|
39
|
+
detected_style: detected_style
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
violations
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
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 CollectionType
|
|
8
|
+
# Result wrapper for CollectionType violations
|
|
9
|
+
class Result < Results::Base
|
|
10
|
+
self.default_severity = 'convention'
|
|
11
|
+
self.offense_type = 'style'
|
|
12
|
+
self.offense_name = 'CollectionType'
|
|
13
|
+
|
|
14
|
+
# Builds a human-readable message for a violation
|
|
15
|
+
# @param offense [Hash] the offense 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
|