yard-lint 1.0.0 → 1.2.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 +77 -0
- data/README.md +160 -268
- data/bin/yard-lint +100 -8
- data/lib/yard/lint/command_cache.rb +17 -1
- data/lib/yard/lint/config.rb +20 -2
- data/lib/yard/lint/config_generator.rb +200 -0
- data/lib/yard/lint/ext/irb_notifier_shim.rb +95 -0
- data/lib/yard/lint/git.rb +125 -0
- data/lib/yard/lint/results/aggregate.rb +22 -2
- data/lib/yard/lint/runner.rb +4 -3
- data/lib/yard/lint/stats_calculator.rb +157 -0
- data/lib/yard/lint/validators/base.rb +36 -0
- 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.rb +26 -1
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +26 -1
- 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.rb +31 -1
- data/lib/yard/lint/validators/tags/api_tags.rb +34 -1
- data/lib/yard/lint/validators/tags/collection_type/config.rb +2 -1
- data/lib/yard/lint/validators/tags/collection_type/messages_builder.rb +40 -11
- data/lib/yard/lint/validators/tags/collection_type/parser.rb +6 -5
- data/lib/yard/lint/validators/tags/collection_type/validator.rb +26 -7
- data/lib/yard/lint/validators/tags/collection_type.rb +38 -2
- 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 +2 -2
- data/lib/yard/lint/validators/tags/invalid_types.rb +25 -1
- data/lib/yard/lint/validators/tags/meaningless_tag/validator.rb +2 -4
- data/lib/yard/lint/validators/tags/meaningless_tag.rb +31 -3
- data/lib/yard/lint/validators/tags/option_tags/validator.rb +7 -1
- data/lib/yard/lint/validators/tags/option_tags.rb +26 -1
- 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/validator.rb +2 -4
- data/lib/yard/lint/validators/tags/tag_type_position.rb +39 -2
- data/lib/yard/lint/validators/tags/type_syntax.rb +26 -2
- data/lib/yard/lint/validators/warnings/duplicated_parameter_name.rb +26 -1
- data/lib/yard/lint/validators/warnings/invalid_directive_format.rb +26 -1
- data/lib/yard/lint/validators/warnings/invalid_tag_format.rb +25 -1
- data/lib/yard/lint/validators/warnings/unknown_directive.rb +26 -1
- data/lib/yard/lint/validators/warnings/unknown_parameter_name.rb +23 -1
- data/lib/yard/lint/validators/warnings/unknown_tag.rb +26 -1
- data/lib/yard/lint/version.rb +1 -1
- data/lib/yard/lint.rb +38 -2
- data/lib/yard-lint.rb +5 -0
- metadata +28 -1
data/lib/yard/lint/runner.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Yard
|
|
|
21
21
|
def run
|
|
22
22
|
raw_results = run_validators
|
|
23
23
|
parsed_results = parse_results(raw_results)
|
|
24
|
-
build_result(parsed_results)
|
|
24
|
+
build_result(parsed_results, @selection)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
private
|
|
@@ -116,9 +116,10 @@ module Yard
|
|
|
116
116
|
|
|
117
117
|
# Build final result object
|
|
118
118
|
# @param results [Array<Results::Base>] array of validator result objects
|
|
119
|
+
# @param files [Array<String>] array of files that were analyzed
|
|
119
120
|
# @return [Results::Aggregate] aggregate result object
|
|
120
|
-
def build_result(results)
|
|
121
|
-
Results::Aggregate.new(results, config)
|
|
121
|
+
def build_result(results, files)
|
|
122
|
+
Results::Aggregate.new(results, config, files)
|
|
122
123
|
end
|
|
123
124
|
end
|
|
124
125
|
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
# Calculates documentation coverage statistics
|
|
6
|
+
# Runs YARD queries to count documented vs undocumented objects
|
|
7
|
+
class StatsCalculator
|
|
8
|
+
attr_reader :config, :files
|
|
9
|
+
|
|
10
|
+
# @param config [Yard::Lint::Config] configuration object
|
|
11
|
+
# @param files [Array<String>] files to analyze
|
|
12
|
+
def initialize(config, files)
|
|
13
|
+
@config = config
|
|
14
|
+
@files = Array(files).compact
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Calculate documentation coverage statistics
|
|
18
|
+
# @return [Hash] statistics with :total, :documented, :coverage keys
|
|
19
|
+
def calculate
|
|
20
|
+
return default_stats if files.empty?
|
|
21
|
+
|
|
22
|
+
raw_stats = run_yard_stats_query
|
|
23
|
+
return default_stats if raw_stats.empty?
|
|
24
|
+
|
|
25
|
+
parsed_stats = parse_stats_output(raw_stats)
|
|
26
|
+
filtered_stats = apply_exclusions(parsed_stats)
|
|
27
|
+
|
|
28
|
+
calculate_coverage_percentage(filtered_stats)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# Default stats for empty file lists
|
|
34
|
+
# @return [Hash]
|
|
35
|
+
def default_stats
|
|
36
|
+
{ total: 0, documented: 0, coverage: 100.0 }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Run YARD query to get object documentation status
|
|
40
|
+
# @return [String] YARD query output
|
|
41
|
+
def run_yard_stats_query
|
|
42
|
+
# Create temp file with file list
|
|
43
|
+
Tempfile.create(['yard_stats', '.txt']) do |f|
|
|
44
|
+
files.each { |file| f.puts(Shellwords.escape(file)) }
|
|
45
|
+
f.flush
|
|
46
|
+
|
|
47
|
+
query = build_stats_query
|
|
48
|
+
|
|
49
|
+
# Use temp directory for YARD database (auto-cleanup)
|
|
50
|
+
Dir.mktmpdir("yard_stats_#{Process.pid}_") do |temp_dir|
|
|
51
|
+
cmd = build_yard_command(f.path, query, temp_dir)
|
|
52
|
+
|
|
53
|
+
stdout, _stderr, status = Open3.capture3(cmd)
|
|
54
|
+
|
|
55
|
+
# Return empty string if YARD command fails
|
|
56
|
+
return '' unless status.exitstatus.zero?
|
|
57
|
+
|
|
58
|
+
stdout
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Build the YARD query for stats collection
|
|
64
|
+
# @return [String] YARD query string
|
|
65
|
+
def build_stats_query
|
|
66
|
+
<<~QUERY.chomp
|
|
67
|
+
type = object.type.to_s; state = object.docstring.all.empty? ? "undoc" : "doc"; puts "\#{type}:\#{state}"
|
|
68
|
+
QUERY
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Build complete YARD command
|
|
72
|
+
# @param file_list_path [String] path to file with list of files
|
|
73
|
+
# @param query [String] YARD query to execute
|
|
74
|
+
# @param temp_dir [String] temporary directory for YARD database
|
|
75
|
+
# @return [String] complete command string
|
|
76
|
+
def build_yard_command(file_list_path, query, temp_dir)
|
|
77
|
+
<<~CMD.tr("\n", ' ').strip
|
|
78
|
+
cat #{Shellwords.escape(file_list_path)} | xargs yard list
|
|
79
|
+
--charset utf-8
|
|
80
|
+
--markup markdown
|
|
81
|
+
--no-progress
|
|
82
|
+
--query #{Shellwords.escape(query)}
|
|
83
|
+
-q
|
|
84
|
+
-b #{Shellwords.escape(temp_dir)}
|
|
85
|
+
CMD
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Parse YARD stats output
|
|
89
|
+
# Format: "type:state" (e.g., "method:doc", "class:undoc")
|
|
90
|
+
# @param output [String] YARD command output
|
|
91
|
+
# @return [Hash] counts by type and state
|
|
92
|
+
def parse_stats_output(output)
|
|
93
|
+
stats = Hash.new { |h, k| h[k] = { documented: 0, undocumented: 0 } }
|
|
94
|
+
|
|
95
|
+
output.each_line do |line|
|
|
96
|
+
line.strip!
|
|
97
|
+
next if line.empty?
|
|
98
|
+
|
|
99
|
+
type, state = line.split(':', 2)
|
|
100
|
+
next unless type && state
|
|
101
|
+
|
|
102
|
+
if state == 'doc'
|
|
103
|
+
stats[type][:documented] += 1
|
|
104
|
+
elsif state == 'undoc'
|
|
105
|
+
stats[type][:undocumented] += 1
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
stats
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Apply validator exclusions to stats
|
|
113
|
+
# Respects ExcludedMethods and other validator-specific exclusions
|
|
114
|
+
# @param stats [Hash] parsed stats
|
|
115
|
+
# @return [Hash] filtered stats
|
|
116
|
+
def apply_exclusions(stats)
|
|
117
|
+
# Get excluded methods from UndocumentedObjects validator config
|
|
118
|
+
excluded_methods = config.validator_config('Documentation/UndocumentedObjects', 'ExcludedMethods') || []
|
|
119
|
+
|
|
120
|
+
return stats if excluded_methods.empty?
|
|
121
|
+
|
|
122
|
+
# For now, we can't easily filter out specific methods without re-parsing
|
|
123
|
+
# This would require running YARD query with method names
|
|
124
|
+
# TODO: Implement precise method-level filtering if needed
|
|
125
|
+
|
|
126
|
+
stats
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Calculate coverage percentage from stats
|
|
130
|
+
# @param stats [Hash] filtered stats by type
|
|
131
|
+
# @return [Hash] final coverage statistics
|
|
132
|
+
def calculate_coverage_percentage(stats)
|
|
133
|
+
total_documented = 0
|
|
134
|
+
total_undocumented = 0
|
|
135
|
+
|
|
136
|
+
stats.each_value do |counts|
|
|
137
|
+
total_documented += counts[:documented]
|
|
138
|
+
total_undocumented += counts[:undocumented]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
total_objects = total_documented + total_undocumented
|
|
142
|
+
|
|
143
|
+
coverage = if total_objects.zero?
|
|
144
|
+
100.0
|
|
145
|
+
else
|
|
146
|
+
(total_documented.to_f / total_objects * 100)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
{
|
|
150
|
+
total: total_objects,
|
|
151
|
+
documented: total_documented,
|
|
152
|
+
coverage: coverage
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -143,6 +143,42 @@ module Yard
|
|
|
143
143
|
def shell(cmd)
|
|
144
144
|
self.class.command_cache.execute(cmd)
|
|
145
145
|
end
|
|
146
|
+
|
|
147
|
+
# Retrieves configuration value with fallback to default
|
|
148
|
+
# Automatically determines the validator name from the class namespace
|
|
149
|
+
#
|
|
150
|
+
# @param key [String] the configuration key to retrieve
|
|
151
|
+
# @return [Object] the configured value or default value from the validator's Config.defaults
|
|
152
|
+
# @note The validator name is automatically extracted from the class namespace.
|
|
153
|
+
# For example, Yard::Lint::Validators::Tags::RedundantParamDescription::Validator
|
|
154
|
+
# becomes 'Tags/RedundantParamDescription'
|
|
155
|
+
# @example Usage in a validator (e.g., Tags::RedundantParamDescription)
|
|
156
|
+
# def config_articles
|
|
157
|
+
# config_or_default('Articles')
|
|
158
|
+
# end
|
|
159
|
+
def config_or_default(key)
|
|
160
|
+
validator_name = self.class.name&.split('::')&.then do |parts|
|
|
161
|
+
idx = parts.index('Validators')
|
|
162
|
+
next nil unless idx && parts[idx + 1] && parts[idx + 2]
|
|
163
|
+
|
|
164
|
+
"#{parts[idx + 1]}/#{parts[idx + 2]}"
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Get the validator module's Config class
|
|
168
|
+
validator_config_class = begin
|
|
169
|
+
# Get parent module (e.g., Yard::Lint::Validators::Tags::RedundantParamDescription)
|
|
170
|
+
parent_module = self.class.name.split('::')[0..-2].join('::')
|
|
171
|
+
Object.const_get("#{parent_module}::Config")
|
|
172
|
+
rescue NameError
|
|
173
|
+
nil
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
defaults = validator_config_class&.defaults || {}
|
|
177
|
+
|
|
178
|
+
return defaults[key] unless validator_name
|
|
179
|
+
|
|
180
|
+
config.validator_config(validator_name, key) || defaults[key]
|
|
181
|
+
end
|
|
146
182
|
end
|
|
147
183
|
end
|
|
148
184
|
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 MarkdownSyntax
|
|
8
|
+
# Configuration for MarkdownSyntax validator
|
|
9
|
+
class Config < ::Yard::Lint::Validators::Config
|
|
10
|
+
self.id = :markdown_syntax
|
|
11
|
+
self.defaults = {
|
|
12
|
+
'Enabled' => true,
|
|
13
|
+
'Severity' => 'warning'
|
|
14
|
+
}.freeze
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module MarkdownSyntax
|
|
8
|
+
# Builds human-readable messages for MarkdownSyntax violations
|
|
9
|
+
class MessagesBuilder
|
|
10
|
+
# Maps markdown syntax error types to human-readable descriptions
|
|
11
|
+
ERROR_DESCRIPTIONS = {
|
|
12
|
+
'unclosed_backtick' => 'Unclosed backtick in documentation',
|
|
13
|
+
'unclosed_code_block' => 'Unclosed code block (```) in documentation',
|
|
14
|
+
'unclosed_bold' => 'Unclosed bold formatting (**) in documentation',
|
|
15
|
+
'invalid_list_marker' => 'Invalid list marker (use - or * instead)'
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
# Formats a violation message
|
|
20
|
+
# @param offense [Hash] the offense details
|
|
21
|
+
# @return [String] formatted message
|
|
22
|
+
def call(offense)
|
|
23
|
+
object_name = offense[:object_name]
|
|
24
|
+
errors = offense[:errors]
|
|
25
|
+
|
|
26
|
+
error_messages = errors.map do |error|
|
|
27
|
+
if error.start_with?('invalid_list_marker:')
|
|
28
|
+
line_num = error.split(':').last
|
|
29
|
+
"#{ERROR_DESCRIPTIONS['invalid_list_marker']} at line #{line_num}"
|
|
30
|
+
else
|
|
31
|
+
ERROR_DESCRIPTIONS[error] || error
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
"Markdown syntax errors in '#{object_name}': " \
|
|
36
|
+
"#{error_messages.join(', ')}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
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 MarkdownSyntax
|
|
8
|
+
# Parses YARD output for markdown syntax 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 error types
|
|
30
|
+
i += 1
|
|
31
|
+
next unless i < lines.size
|
|
32
|
+
|
|
33
|
+
errors = lines[i].split('|')
|
|
34
|
+
|
|
35
|
+
violations << {
|
|
36
|
+
location: file_path,
|
|
37
|
+
line: line_number,
|
|
38
|
+
object_name: object_name,
|
|
39
|
+
errors: errors
|
|
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,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module MarkdownSyntax
|
|
8
|
+
# Result object for markdown syntax validation
|
|
9
|
+
class Result < Results::Base
|
|
10
|
+
self.default_severity = 'warning'
|
|
11
|
+
self.offense_type = 'line'
|
|
12
|
+
self.offense_name = 'MarkdownSyntax'
|
|
13
|
+
|
|
14
|
+
# Build human-readable message for markdown syntax offense
|
|
15
|
+
# @param offense [Hash] offense data with :object_name and :errors
|
|
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
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
module MarkdownSyntax
|
|
8
|
+
# Validates markdown syntax in documentation
|
|
9
|
+
class Validator < Validators::Base
|
|
10
|
+
# YARD query to extract docstrings and check for markdown errors
|
|
11
|
+
# @return [String] YARD Ruby query code
|
|
12
|
+
def query
|
|
13
|
+
<<~QUERY.strip
|
|
14
|
+
'docstring_text = object.docstring.to_s; unless docstring_text.empty?; errors = []; backtick_count = docstring_text.scan(/\\x60/).count; errors << "unclosed_backtick" if backtick_count.odd?; code_block_count = docstring_text.scan(/^```/).count; errors << "unclosed_code_block" if code_block_count.odd?; non_code_text = docstring_text.gsub(/\\x60[^\\x60]*\\x60/, ""); bold_count = non_code_text.scan(/\\*\\*/).count; errors << "unclosed_bold" if bold_count.odd?; lines = docstring_text.lines; lines.each_with_index do |line, line_idx|; stripped = line.strip; errors << "invalid_list_marker:" + (line_idx + 1).to_s if stripped =~ /^[•·]/; end; unless errors.empty?; puts object.file + ":" + object.line.to_s + ": " + object.title; puts errors.join("|"); end; end; false'
|
|
15
|
+
QUERY
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Builds and executes the YARD command to detect markdown syntax errors
|
|
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,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Documentation
|
|
7
|
+
# MarkdownSyntax validator
|
|
8
|
+
#
|
|
9
|
+
# Validates markdown syntax in documentation comments. This validator checks
|
|
10
|
+
# for common markdown errors and formatting issues in YARD documentation
|
|
11
|
+
# strings. This validator is enabled by default.
|
|
12
|
+
#
|
|
13
|
+
# @example Bad - Invalid markdown syntax
|
|
14
|
+
# # This is [broken markdown
|
|
15
|
+
# # Another line with `unclosed code
|
|
16
|
+
# def process
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# @example Good - Valid markdown syntax
|
|
20
|
+
# # This is [valid markdown](https://example.com)
|
|
21
|
+
# # Another line with `closed code`
|
|
22
|
+
# def process
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# ## Configuration
|
|
26
|
+
#
|
|
27
|
+
# To disable this validator:
|
|
28
|
+
#
|
|
29
|
+
# Documentation/MarkdownSyntax:
|
|
30
|
+
# Enabled: false
|
|
31
|
+
#
|
|
32
|
+
module MarkdownSyntax
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -4,7 +4,32 @@ module Yard
|
|
|
4
4
|
module Lint
|
|
5
5
|
module Validators
|
|
6
6
|
module Documentation
|
|
7
|
-
# UndocumentedBooleanMethods validator
|
|
7
|
+
# UndocumentedBooleanMethods validator
|
|
8
|
+
#
|
|
9
|
+
# Ensures that boolean methods (methods ending with `?`) have an explicit
|
|
10
|
+
# `@return [Boolean]` tag. Boolean methods should clearly document that they
|
|
11
|
+
# return true or false values. This validator is enabled by default.
|
|
12
|
+
#
|
|
13
|
+
# @example Bad - Missing @return tag on boolean method
|
|
14
|
+
# # Checks if the user is active
|
|
15
|
+
# def active?
|
|
16
|
+
# @active
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# @example Good - Boolean return documented
|
|
20
|
+
# # Checks if the user is active
|
|
21
|
+
# # @return [Boolean] true if the user is active
|
|
22
|
+
# def active?
|
|
23
|
+
# @active
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# ## Configuration
|
|
27
|
+
#
|
|
28
|
+
# To disable this validator:
|
|
29
|
+
#
|
|
30
|
+
# Documentation/UndocumentedBooleanMethods:
|
|
31
|
+
# Enabled: false
|
|
32
|
+
#
|
|
8
33
|
module UndocumentedBooleanMethods
|
|
9
34
|
end
|
|
10
35
|
end
|
|
@@ -4,7 +4,32 @@ module Yard
|
|
|
4
4
|
module Lint
|
|
5
5
|
module Validators
|
|
6
6
|
module Documentation
|
|
7
|
-
# UndocumentedMethodArguments validator
|
|
7
|
+
# UndocumentedMethodArguments validator
|
|
8
|
+
#
|
|
9
|
+
# Ensures that all method parameters are documented with `@param` tags.
|
|
10
|
+
# This validator checks that every parameter in a method signature has
|
|
11
|
+
# a corresponding `@param` documentation tag. This validator is enabled
|
|
12
|
+
# by default.
|
|
13
|
+
#
|
|
14
|
+
# @example Bad - Missing @param tags
|
|
15
|
+
# # Does something with data
|
|
16
|
+
# def process(name, options)
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# @example Good - All parameters documented
|
|
20
|
+
# # Does something with data
|
|
21
|
+
# # @param name [String] the name to process
|
|
22
|
+
# # @param options [Hash] configuration options
|
|
23
|
+
# def process(name, options)
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# ## Configuration
|
|
27
|
+
#
|
|
28
|
+
# To disable this validator:
|
|
29
|
+
#
|
|
30
|
+
# Documentation/UndocumentedMethodArguments:
|
|
31
|
+
# Enabled: false
|
|
32
|
+
#
|
|
8
33
|
module UndocumentedMethodArguments
|
|
9
34
|
end
|
|
10
35
|
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
|