yard-lint 1.2.2 → 1.3.0.rc1
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 +174 -1
- data/README.md +118 -3
- data/Rakefile +20 -0
- data/bin/yard-lint +80 -37
- data/lib/yard/lint/config.rb +5 -0
- data/lib/yard/lint/config_generator.rb +8 -179
- data/lib/yard/lint/config_updater.rb +222 -0
- data/lib/yard/lint/errors.rb +6 -0
- data/lib/yard/lint/executor/in_process_registry.rb +130 -0
- data/lib/yard/lint/executor/query_executor.rb +109 -0
- data/lib/yard/lint/executor/result_collector.rb +55 -0
- data/lib/yard/lint/executor/warning_dispatcher.rb +79 -0
- data/lib/yard/lint/results/base.rb +2 -1
- data/lib/yard/lint/runner.rb +88 -35
- data/lib/yard/lint/stats_calculator.rb +1 -1
- data/lib/yard/lint/templates/default_config.yml +279 -0
- data/lib/yard/lint/templates/strict_config.yml +283 -0
- data/lib/yard/lint/validators/base.rb +52 -118
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/config.rb +25 -0
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/messages_builder.rb +39 -0
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/parser.rb +59 -0
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/result.rb +61 -0
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/validator.rb +94 -0
- data/lib/yard/lint/validators/documentation/blank_line_before_definition.rb +63 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line/config.rb +24 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line/messages_builder.rb +34 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line/parser.rb +60 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line/result.rb +25 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line/validator.rb +109 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line.rb +58 -0
- data/lib/yard/lint/validators/documentation/markdown_syntax/validator.rb +36 -21
- data/lib/yard/lint/validators/documentation/markdown_syntax.rb +0 -1
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/validator.rb +19 -29
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods.rb +0 -1
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments/validator.rb +18 -34
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +0 -1
- data/lib/yard/lint/validators/documentation/undocumented_objects/parser.rb +2 -2
- data/lib/yard/lint/validators/documentation/undocumented_objects/validator.rb +17 -25
- data/lib/yard/lint/validators/documentation/undocumented_objects.rb +4 -5
- data/lib/yard/lint/validators/documentation/undocumented_options/validator.rb +30 -21
- data/lib/yard/lint/validators/documentation/undocumented_options.rb +0 -1
- data/lib/yard/lint/validators/semantic/abstract_methods/result.rb +2 -2
- data/lib/yard/lint/validators/semantic/abstract_methods/validator.rb +31 -43
- data/lib/yard/lint/validators/semantic/abstract_methods.rb +0 -1
- data/lib/yard/lint/validators/tags/api_tags/validator.rb +24 -39
- data/lib/yard/lint/validators/tags/api_tags.rb +0 -1
- data/lib/yard/lint/validators/tags/collection_type/parser.rb +1 -1
- data/lib/yard/lint/validators/tags/collection_type/validator.rb +37 -66
- data/lib/yard/lint/validators/tags/collection_type.rb +0 -1
- data/lib/yard/lint/validators/tags/example_syntax/validator.rb +51 -64
- data/lib/yard/lint/validators/tags/example_syntax.rb +0 -1
- data/lib/yard/lint/validators/tags/informal_notation/config.rb +40 -0
- data/lib/yard/lint/validators/tags/informal_notation/messages_builder.rb +35 -0
- data/lib/yard/lint/validators/tags/informal_notation/parser.rb +55 -0
- data/lib/yard/lint/validators/tags/informal_notation/result.rb +26 -0
- data/lib/yard/lint/validators/tags/informal_notation/validator.rb +133 -0
- data/lib/yard/lint/validators/tags/informal_notation.rb +45 -0
- data/lib/yard/lint/validators/tags/invalid_types/validator.rb +57 -70
- data/lib/yard/lint/validators/tags/invalid_types.rb +0 -1
- data/lib/yard/lint/validators/tags/meaningless_tag/parser.rb +1 -1
- data/lib/yard/lint/validators/tags/meaningless_tag/validator.rb +22 -54
- data/lib/yard/lint/validators/tags/meaningless_tag.rb +0 -1
- data/lib/yard/lint/validators/tags/non_ascii_type/config.rb +21 -0
- data/lib/yard/lint/validators/tags/non_ascii_type/messages_builder.rb +29 -0
- data/lib/yard/lint/validators/tags/non_ascii_type/parser.rb +59 -0
- data/lib/yard/lint/validators/tags/non_ascii_type/result.rb +25 -0
- data/lib/yard/lint/validators/tags/non_ascii_type/validator.rb +50 -0
- data/lib/yard/lint/validators/tags/non_ascii_type.rb +39 -0
- data/lib/yard/lint/validators/tags/option_tags/result.rb +2 -2
- data/lib/yard/lint/validators/tags/option_tags/validator.rb +25 -40
- data/lib/yard/lint/validators/tags/option_tags.rb +0 -1
- data/lib/yard/lint/validators/tags/order/validator.rb +28 -55
- data/lib/yard/lint/validators/tags/order.rb +0 -1
- data/lib/yard/lint/validators/tags/redundant_param_description/config.rb +15 -1
- data/lib/yard/lint/validators/tags/redundant_param_description/messages_builder.rb +5 -0
- data/lib/yard/lint/validators/tags/redundant_param_description/validator.rb +134 -100
- data/lib/yard/lint/validators/tags/redundant_param_description.rb +0 -1
- data/lib/yard/lint/validators/tags/tag_group_separator/config.rb +29 -0
- data/lib/yard/lint/validators/tags/tag_group_separator/messages_builder.rb +49 -0
- data/lib/yard/lint/validators/tags/tag_group_separator/parser.rb +67 -0
- data/lib/yard/lint/validators/tags/tag_group_separator/result.rb +28 -0
- data/lib/yard/lint/validators/tags/tag_group_separator/validator.rb +117 -0
- data/lib/yard/lint/validators/tags/tag_group_separator.rb +49 -0
- data/lib/yard/lint/validators/tags/tag_type_position/parser.rb +1 -1
- data/lib/yard/lint/validators/tags/tag_type_position/validator.rb +53 -84
- data/lib/yard/lint/validators/tags/tag_type_position.rb +4 -5
- data/lib/yard/lint/validators/tags/type_syntax/parser.rb +8 -3
- data/lib/yard/lint/validators/tags/type_syntax/validator.rb +29 -59
- data/lib/yard/lint/validators/tags/type_syntax.rb +0 -1
- data/lib/yard/lint/validators/warnings/duplicated_parameter_name/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/invalid_directive_format/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/invalid_tag_format/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/unknown_directive/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/messages_builder.rb +243 -0
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/result.rb +4 -3
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/unknown_tag/messages_builder.rb +144 -0
- data/lib/yard/lint/validators/warnings/unknown_tag/result.rb +4 -3
- data/lib/yard/lint/validators/warnings/unknown_tag/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/unknown_tag.rb +10 -0
- data/lib/yard/lint/version.rb +1 -1
- data/lib/yard/lint.rb +81 -13
- data/renovate.json +1 -8
- metadata +40 -6
- data/bin/console +0 -11
- data/bin/setup +0 -8
- data/lib/yard/lint/command_cache.rb +0 -93
|
@@ -4,194 +4,23 @@ module Yard
|
|
|
4
4
|
module Lint
|
|
5
5
|
# Generates default .yard-lint.yml configuration file
|
|
6
6
|
class ConfigGenerator
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
# YARD-Lint Configuration
|
|
10
|
-
# See https://github.com/mensfeld/yard-lint for documentation
|
|
11
|
-
|
|
12
|
-
# Global settings for all validators
|
|
13
|
-
AllValidators:
|
|
14
|
-
# YARD command-line options (applied to all validators by default)
|
|
15
|
-
YardOptions:
|
|
16
|
-
- --private
|
|
17
|
-
- --protected
|
|
18
|
-
|
|
19
|
-
# Global file exclusion patterns
|
|
20
|
-
Exclude:
|
|
21
|
-
- '\\.git'
|
|
22
|
-
- 'vendor/**/*'
|
|
23
|
-
- 'node_modules/**/*'
|
|
24
|
-
- 'spec/**/*'
|
|
25
|
-
- 'test/**/*'
|
|
26
|
-
|
|
27
|
-
# Exit code behavior (error, warning, convention, never)
|
|
28
|
-
FailOnSeverity: warning
|
|
29
|
-
|
|
30
|
-
# Minimum documentation coverage percentage (0-100)
|
|
31
|
-
# Fails if coverage is below this threshold
|
|
32
|
-
# MinCoverage: 80.0
|
|
33
|
-
|
|
34
|
-
# Diff mode settings
|
|
35
|
-
DiffMode:
|
|
36
|
-
# Default base ref for --diff (auto-detects main/master if not specified)
|
|
37
|
-
DefaultBaseRef: ~
|
|
38
|
-
|
|
39
|
-
# Documentation validators
|
|
40
|
-
Documentation/UndocumentedObjects:
|
|
41
|
-
Description: 'Checks for classes, modules, and methods without documentation.'
|
|
42
|
-
Enabled: true
|
|
43
|
-
Severity: warning
|
|
44
|
-
ExcludedMethods:
|
|
45
|
-
- 'initialize/0' # Exclude parameter-less initialize
|
|
46
|
-
- '/^_/' # Exclude private methods (by convention)
|
|
47
|
-
|
|
48
|
-
Documentation/UndocumentedMethodArguments:
|
|
49
|
-
Description: 'Checks for method parameters without @param tags.'
|
|
50
|
-
Enabled: true
|
|
51
|
-
Severity: warning
|
|
52
|
-
|
|
53
|
-
Documentation/UndocumentedBooleanMethods:
|
|
54
|
-
Description: 'Checks that question mark methods document their boolean return.'
|
|
55
|
-
Enabled: true
|
|
56
|
-
Severity: warning
|
|
57
|
-
|
|
58
|
-
Documentation/UndocumentedOptions:
|
|
59
|
-
Description: 'Detects methods with options hash parameters but no @option tags.'
|
|
60
|
-
Enabled: true
|
|
61
|
-
Severity: warning
|
|
62
|
-
|
|
63
|
-
Documentation/MarkdownSyntax:
|
|
64
|
-
Description: 'Detects common markdown syntax errors in documentation.'
|
|
65
|
-
Enabled: true
|
|
66
|
-
Severity: warning
|
|
67
|
-
|
|
68
|
-
# Tags validators
|
|
69
|
-
Tags/Order:
|
|
70
|
-
Description: 'Enforces consistent ordering of YARD tags.'
|
|
71
|
-
Enabled: true
|
|
72
|
-
Severity: convention
|
|
73
|
-
EnforcedOrder:
|
|
74
|
-
- param
|
|
75
|
-
- option
|
|
76
|
-
- return
|
|
77
|
-
- raise
|
|
78
|
-
- example
|
|
79
|
-
|
|
80
|
-
Tags/InvalidTypes:
|
|
81
|
-
Description: 'Validates type definitions in @param, @return, @option tags.'
|
|
82
|
-
Enabled: true
|
|
83
|
-
Severity: warning
|
|
84
|
-
ValidatedTags:
|
|
85
|
-
- param
|
|
86
|
-
- option
|
|
87
|
-
- return
|
|
88
|
-
|
|
89
|
-
Tags/TypeSyntax:
|
|
90
|
-
Description: 'Validates YARD type syntax using YARD parser.'
|
|
91
|
-
Enabled: true
|
|
92
|
-
Severity: warning
|
|
93
|
-
ValidatedTags:
|
|
94
|
-
- param
|
|
95
|
-
- option
|
|
96
|
-
- return
|
|
97
|
-
- yieldreturn
|
|
98
|
-
|
|
99
|
-
Tags/MeaninglessTag:
|
|
100
|
-
Description: 'Detects @param/@option tags on classes, modules, or constants.'
|
|
101
|
-
Enabled: true
|
|
102
|
-
Severity: warning
|
|
103
|
-
CheckedTags:
|
|
104
|
-
- param
|
|
105
|
-
- option
|
|
106
|
-
InvalidObjectTypes:
|
|
107
|
-
- class
|
|
108
|
-
- module
|
|
109
|
-
- constant
|
|
110
|
-
|
|
111
|
-
Tags/CollectionType:
|
|
112
|
-
Description: 'Validates Hash collection syntax consistency.'
|
|
113
|
-
Enabled: true
|
|
114
|
-
Severity: convention
|
|
115
|
-
EnforcedStyle: long # 'long' for Hash{K => V} (YARD standard), 'short' for {K => V}
|
|
116
|
-
ValidatedTags:
|
|
117
|
-
- param
|
|
118
|
-
- option
|
|
119
|
-
- return
|
|
120
|
-
- yieldreturn
|
|
121
|
-
|
|
122
|
-
Tags/TagTypePosition:
|
|
123
|
-
Description: 'Validates type annotation position in tags.'
|
|
124
|
-
Enabled: true
|
|
125
|
-
Severity: convention
|
|
126
|
-
CheckedTags:
|
|
127
|
-
- param
|
|
128
|
-
- option
|
|
129
|
-
# EnforcedStyle: 'type_after_name' (YARD standard: @param name [Type])
|
|
130
|
-
# or 'type_first' (@param [Type] name)
|
|
131
|
-
EnforcedStyle: type_after_name
|
|
132
|
-
|
|
133
|
-
Tags/ApiTags:
|
|
134
|
-
Description: 'Enforces @api tags on public objects.'
|
|
135
|
-
Enabled: false # Opt-in validator
|
|
136
|
-
Severity: warning
|
|
137
|
-
AllowedApis:
|
|
138
|
-
- public
|
|
139
|
-
- private
|
|
140
|
-
- internal
|
|
141
|
-
|
|
142
|
-
Tags/OptionTags:
|
|
143
|
-
Description: 'Requires @option tags for methods with options parameters.'
|
|
144
|
-
Enabled: true
|
|
145
|
-
Severity: warning
|
|
146
|
-
|
|
147
|
-
# Warnings validators - catches YARD parser errors
|
|
148
|
-
Warnings/UnknownTag:
|
|
149
|
-
Description: 'Detects unknown YARD tags.'
|
|
150
|
-
Enabled: true
|
|
151
|
-
Severity: error
|
|
152
|
-
|
|
153
|
-
Warnings/UnknownDirective:
|
|
154
|
-
Description: 'Detects unknown YARD directives.'
|
|
155
|
-
Enabled: true
|
|
156
|
-
Severity: error
|
|
157
|
-
|
|
158
|
-
Warnings/InvalidTagFormat:
|
|
159
|
-
Description: 'Detects malformed tag syntax.'
|
|
160
|
-
Enabled: true
|
|
161
|
-
Severity: error
|
|
162
|
-
|
|
163
|
-
Warnings/InvalidDirectiveFormat:
|
|
164
|
-
Description: 'Detects malformed directive syntax.'
|
|
165
|
-
Enabled: true
|
|
166
|
-
Severity: error
|
|
167
|
-
|
|
168
|
-
Warnings/DuplicatedParameterName:
|
|
169
|
-
Description: 'Detects duplicate @param tags.'
|
|
170
|
-
Enabled: true
|
|
171
|
-
Severity: error
|
|
172
|
-
|
|
173
|
-
Warnings/UnknownParameterName:
|
|
174
|
-
Description: 'Detects @param tags for non-existent parameters.'
|
|
175
|
-
Enabled: true
|
|
176
|
-
Severity: error
|
|
177
|
-
|
|
178
|
-
# Semantic validators
|
|
179
|
-
Semantic/AbstractMethods:
|
|
180
|
-
Description: 'Ensures @abstract methods do not have real implementations.'
|
|
181
|
-
Enabled: true
|
|
182
|
-
Severity: warning
|
|
183
|
-
YAML
|
|
7
|
+
# Path to templates directory
|
|
8
|
+
TEMPLATES_DIR = File.expand_path('templates', __dir__)
|
|
184
9
|
|
|
185
10
|
# Generate .yard-lint.yml file in current directory
|
|
186
11
|
# @param force [Boolean] overwrite existing file if true
|
|
12
|
+
# @param strict [Boolean] generate strict configuration (all errors, 100% coverage)
|
|
187
13
|
# @return [Boolean] true if file was created, false if already exists
|
|
188
|
-
def self.generate(force: false)
|
|
14
|
+
def self.generate(force: false, strict: false)
|
|
189
15
|
config_path = File.join(Dir.pwd, Config::DEFAULT_CONFIG_FILE)
|
|
190
16
|
|
|
191
17
|
if File.exist?(config_path) && !force
|
|
192
18
|
false
|
|
193
19
|
else
|
|
194
|
-
|
|
20
|
+
template_name = strict ? 'strict_config.yml' : 'default_config.yml'
|
|
21
|
+
template_path = File.join(TEMPLATES_DIR, template_name)
|
|
22
|
+
content = File.read(template_path)
|
|
23
|
+
File.write(config_path, content)
|
|
195
24
|
true
|
|
196
25
|
end
|
|
197
26
|
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
# Updates existing .yard-lint.yml configuration files
|
|
6
|
+
# Adds new validators, removes obsolete ones, preserves user settings
|
|
7
|
+
class ConfigUpdater
|
|
8
|
+
# Path to templates directory
|
|
9
|
+
TEMPLATES_DIR = File.expand_path('templates', __dir__)
|
|
10
|
+
|
|
11
|
+
# Category order for output
|
|
12
|
+
CATEGORY_ORDER = %w[Documentation Tags Warnings Semantic].freeze
|
|
13
|
+
|
|
14
|
+
# Category comments for output
|
|
15
|
+
CATEGORY_COMMENTS = {
|
|
16
|
+
'Documentation' => '# Documentation validators',
|
|
17
|
+
'Tags' => '# Tags validators',
|
|
18
|
+
'Warnings' => '# Warnings validators - catches YARD parser errors',
|
|
19
|
+
'Semantic' => '# Semantic validators'
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# Update an existing config file
|
|
24
|
+
# @param path [String] path to config file (default: .yard-lint.yml in current dir)
|
|
25
|
+
# @param strict [Boolean] use strict template as base for new validators
|
|
26
|
+
# @return [Hash] result hash with :added, :removed, :preserved arrays
|
|
27
|
+
def update(path: nil, strict: false)
|
|
28
|
+
new(path: path, strict: strict).update
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @param path [String, nil] path to config file
|
|
33
|
+
# @param strict [Boolean] use strict template
|
|
34
|
+
def initialize(path: nil, strict: false)
|
|
35
|
+
@path = path || File.join(Dir.pwd, Config::DEFAULT_CONFIG_FILE)
|
|
36
|
+
@strict = strict
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Perform the config update
|
|
40
|
+
# @return [Hash] result with :added, :removed, :preserved arrays
|
|
41
|
+
def update
|
|
42
|
+
validate_file_exists!
|
|
43
|
+
|
|
44
|
+
existing_config = load_existing_config
|
|
45
|
+
template_config = load_template_config
|
|
46
|
+
|
|
47
|
+
result = merge_configs(existing_config, template_config)
|
|
48
|
+
|
|
49
|
+
write_updated_config(result[:config])
|
|
50
|
+
|
|
51
|
+
{
|
|
52
|
+
added: result[:added],
|
|
53
|
+
removed: result[:removed],
|
|
54
|
+
preserved: result[:preserved]
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
# Validate that the config file exists
|
|
61
|
+
# @raise [Errors::ConfigFileNotFoundError] if file doesn't exist
|
|
62
|
+
def validate_file_exists!
|
|
63
|
+
return if File.exist?(@path)
|
|
64
|
+
|
|
65
|
+
raise Errors::ConfigFileNotFoundError,
|
|
66
|
+
"Config file not found: #{@path}. Use --init to create one."
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Load the existing user config
|
|
70
|
+
# @return [Hash] parsed YAML config
|
|
71
|
+
def load_existing_config
|
|
72
|
+
YAML.load_file(@path) || {}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Load the template config
|
|
76
|
+
# @return [Hash] parsed template YAML
|
|
77
|
+
def load_template_config
|
|
78
|
+
template_name = @strict ? 'strict_config.yml' : 'default_config.yml'
|
|
79
|
+
template_path = File.join(TEMPLATES_DIR, template_name)
|
|
80
|
+
YAML.load_file(template_path)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Merge existing config with template to add new and remove obsolete validators
|
|
84
|
+
# @param existing [Hash] existing user config
|
|
85
|
+
# @param template [Hash] template config
|
|
86
|
+
# @return [Hash] result with :config, :added, :removed, :preserved
|
|
87
|
+
def merge_configs(existing, template)
|
|
88
|
+
current_validators = ConfigLoader::ALL_VALIDATORS
|
|
89
|
+
existing_validators = extract_validator_keys(existing)
|
|
90
|
+
|
|
91
|
+
# Calculate changes
|
|
92
|
+
added = (current_validators - existing_validators).sort
|
|
93
|
+
removed = (existing_validators - current_validators).sort
|
|
94
|
+
preserved = (existing_validators & current_validators).sort
|
|
95
|
+
|
|
96
|
+
# Build merged config
|
|
97
|
+
merged = {}
|
|
98
|
+
|
|
99
|
+
# Copy AllValidators from existing, falling back to template
|
|
100
|
+
merged['AllValidators'] = existing['AllValidators'] || template['AllValidators']
|
|
101
|
+
|
|
102
|
+
# Process validators in template order (which has proper category grouping)
|
|
103
|
+
template.each_key do |key|
|
|
104
|
+
next if key == 'AllValidators'
|
|
105
|
+
next unless key.include?('/')
|
|
106
|
+
next unless current_validators.include?(key)
|
|
107
|
+
|
|
108
|
+
merged[key] = if existing.key?(key)
|
|
109
|
+
# Preserve existing user config, but merge with template defaults
|
|
110
|
+
merge_validator_config(template[key], existing[key])
|
|
111
|
+
else
|
|
112
|
+
# New validator - use template defaults
|
|
113
|
+
template[key].dup
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
{
|
|
118
|
+
config: merged,
|
|
119
|
+
added: added,
|
|
120
|
+
removed: removed,
|
|
121
|
+
preserved: preserved
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Extract validator keys (keys containing '/') from config
|
|
126
|
+
# @param config [Hash] configuration hash
|
|
127
|
+
# @return [Array<String>] validator keys
|
|
128
|
+
def extract_validator_keys(config)
|
|
129
|
+
config.keys.select { |k| k.include?('/') }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Merge template defaults with user config
|
|
133
|
+
# User config takes precedence
|
|
134
|
+
# @param template_config [Hash] template validator config
|
|
135
|
+
# @param user_config [Hash] user validator config
|
|
136
|
+
# @return [Hash] merged config
|
|
137
|
+
def merge_validator_config(template_config, user_config)
|
|
138
|
+
result = template_config.dup
|
|
139
|
+
user_config.each do |key, value|
|
|
140
|
+
result[key] = value
|
|
141
|
+
end
|
|
142
|
+
result
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Write the updated config to file
|
|
146
|
+
# @param config [Hash] merged config to write
|
|
147
|
+
def write_updated_config(config)
|
|
148
|
+
content = generate_yaml_content(config)
|
|
149
|
+
File.write(@path, content)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Generate YAML content with proper formatting and section comments
|
|
153
|
+
# @param config [Hash] merged configuration with AllValidators and validator sections
|
|
154
|
+
# @return [String] formatted YAML content
|
|
155
|
+
def generate_yaml_content(config)
|
|
156
|
+
lines = []
|
|
157
|
+
lines << '# YARD-Lint Configuration'
|
|
158
|
+
lines << '# See https://github.com/mensfeld/yard-lint for documentation'
|
|
159
|
+
lines << ''
|
|
160
|
+
|
|
161
|
+
# Write AllValidators section
|
|
162
|
+
if config['AllValidators']
|
|
163
|
+
lines << '# Global settings for all validators'
|
|
164
|
+
lines << 'AllValidators:'
|
|
165
|
+
lines.concat(yaml_hash_lines(config['AllValidators'], indent: 2))
|
|
166
|
+
lines << ''
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Group validators by category
|
|
170
|
+
categories = group_by_category(config)
|
|
171
|
+
|
|
172
|
+
# Write each category in order
|
|
173
|
+
CATEGORY_ORDER.each do |category|
|
|
174
|
+
validators = categories[category]
|
|
175
|
+
next unless validators&.any?
|
|
176
|
+
|
|
177
|
+
lines << CATEGORY_COMMENTS[category] if CATEGORY_COMMENTS[category]
|
|
178
|
+
|
|
179
|
+
validators.each do |validator_name, validator_config|
|
|
180
|
+
lines << "#{validator_name}:"
|
|
181
|
+
lines.concat(yaml_hash_lines(validator_config, indent: 2))
|
|
182
|
+
lines << ''
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
lines.join("\n")
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Group validators by their category
|
|
190
|
+
# @param config [Hash] config with validator keys
|
|
191
|
+
# @return [Hash{String => Hash}] validators grouped by category
|
|
192
|
+
def group_by_category(config)
|
|
193
|
+
categories = Hash.new { |h, k| h[k] = {} }
|
|
194
|
+
|
|
195
|
+
config.each do |key, value|
|
|
196
|
+
next unless key.include?('/')
|
|
197
|
+
|
|
198
|
+
category = key.split('/').first
|
|
199
|
+
categories[category][key] = value
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
categories
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Convert a hash to indented YAML lines
|
|
206
|
+
# @param hash [Hash] validator or AllValidators configuration to format as YAML
|
|
207
|
+
# @param indent [Integer] number of spaces to indent
|
|
208
|
+
# @return [Array<String>] indented YAML lines
|
|
209
|
+
def yaml_hash_lines(hash, indent:)
|
|
210
|
+
yaml_str = hash.to_yaml
|
|
211
|
+
# Remove the "---\n" header and convert to indented lines
|
|
212
|
+
yaml_str.lines[1..].map do |line|
|
|
213
|
+
if line.strip.empty?
|
|
214
|
+
''
|
|
215
|
+
else
|
|
216
|
+
"#{' ' * indent}#{line.rstrip}"
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
data/lib/yard/lint/errors.rb
CHANGED
|
@@ -12,6 +12,12 @@ module Yard
|
|
|
12
12
|
|
|
13
13
|
# Raised when a circular dependency is detected in configuration inheritance
|
|
14
14
|
class CircularDependencyError < BaseError; end
|
|
15
|
+
|
|
16
|
+
# Raised when an unknown validator name is specified via --only
|
|
17
|
+
class UnknownValidatorError < BaseError; end
|
|
18
|
+
|
|
19
|
+
# Raised when a specified file or directory does not exist
|
|
20
|
+
class FileNotFoundError < BaseError; end
|
|
15
21
|
end
|
|
16
22
|
end
|
|
17
23
|
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
# In-process execution components for YARD validation.
|
|
6
|
+
# Provides registry management, query execution, and result collection
|
|
7
|
+
# for running validators within the same Ruby process.
|
|
8
|
+
module Executor
|
|
9
|
+
# Manages shared YARD::Registry for in-process execution.
|
|
10
|
+
# Ensures files are parsed once and shared across all validators.
|
|
11
|
+
class InProcessRegistry
|
|
12
|
+
# @return [Array<String>] warnings captured during parsing
|
|
13
|
+
attr_reader :warnings
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@parsed = false
|
|
17
|
+
@warnings = []
|
|
18
|
+
@mutex = Mutex.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Parse Ruby source files and populate the YARD registry.
|
|
22
|
+
# Captures any warnings emitted by YARD during parsing for later dispatch.
|
|
23
|
+
# @param files [Array<String>] absolute or relative paths to Ruby source files
|
|
24
|
+
# @return [void]
|
|
25
|
+
def parse(files)
|
|
26
|
+
@mutex.synchronize do
|
|
27
|
+
return if @parsed
|
|
28
|
+
|
|
29
|
+
YARD::Registry.clear
|
|
30
|
+
|
|
31
|
+
# Suppress YARD's default output by setting log level high
|
|
32
|
+
# YARD uses its own logging levels, 4 is ERROR/FATAL level
|
|
33
|
+
original_level = YARD::Logger.instance.level
|
|
34
|
+
YARD::Logger.instance.level = 4 # Only show fatal errors
|
|
35
|
+
|
|
36
|
+
@warnings = capture_warnings { YARD.parse(files) }
|
|
37
|
+
@parsed = true
|
|
38
|
+
|
|
39
|
+
YARD::Logger.instance.level = original_level
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Check if registry has been parsed
|
|
44
|
+
# @return [Boolean]
|
|
45
|
+
def parsed?
|
|
46
|
+
@parsed
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Get all objects from the registry
|
|
50
|
+
# @return [Array<YARD::CodeObjects::Base>]
|
|
51
|
+
def all_objects
|
|
52
|
+
YARD::Registry.all
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Get objects filtered for a specific validator
|
|
56
|
+
# @param visibility [Symbol] visibility filter, either :all or :public
|
|
57
|
+
# @param file_excludes [Array<String>] glob patterns for files to exclude
|
|
58
|
+
# @param file_selection [Array<String>, nil] specific files to include (nil = all files)
|
|
59
|
+
# @return [Array<YARD::CodeObjects::Base>]
|
|
60
|
+
def objects_for_validator(visibility:, file_excludes: [], file_selection: nil)
|
|
61
|
+
objects = all_objects
|
|
62
|
+
|
|
63
|
+
# Filter by visibility
|
|
64
|
+
unless visibility == :all
|
|
65
|
+
objects = objects.select do |obj|
|
|
66
|
+
!obj.respond_to?(:visibility) || obj.visibility == :public
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Filter by file selection (if provided)
|
|
71
|
+
if file_selection && !file_selection.empty?
|
|
72
|
+
expanded_selection = file_selection.to_set { |f| File.expand_path(f) }
|
|
73
|
+
objects = objects.select do |obj|
|
|
74
|
+
obj.file && expanded_selection.include?(File.expand_path(obj.file))
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Filter by file excludes
|
|
79
|
+
unless file_excludes.empty?
|
|
80
|
+
objects = objects.reject do |obj|
|
|
81
|
+
next false unless obj.file
|
|
82
|
+
|
|
83
|
+
file_excludes.any? do |pattern|
|
|
84
|
+
File.fnmatch(pattern, obj.file, File::FNM_PATHNAME | File::FNM_EXTGLOB)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
objects
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Clear the registry and reset state
|
|
93
|
+
# @return [void]
|
|
94
|
+
def clear!
|
|
95
|
+
@mutex.synchronize do
|
|
96
|
+
YARD::Registry.clear
|
|
97
|
+
@parsed = false
|
|
98
|
+
@warnings = []
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
# Capture warnings during a block execution
|
|
105
|
+
# @yield Block to execute while capturing warnings
|
|
106
|
+
# @return [Array<String>] captured warnings
|
|
107
|
+
def capture_warnings
|
|
108
|
+
captured = []
|
|
109
|
+
|
|
110
|
+
# Store original warn method
|
|
111
|
+
original_warn = YARD::Logger.instance.method(:warn)
|
|
112
|
+
|
|
113
|
+
# Override warn to capture warnings
|
|
114
|
+
YARD::Logger.instance.define_singleton_method(:warn) do |*args|
|
|
115
|
+
message = args.first.to_s
|
|
116
|
+
captured << message
|
|
117
|
+
original_warn.call(*args)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
yield
|
|
121
|
+
|
|
122
|
+
captured
|
|
123
|
+
ensure
|
|
124
|
+
# Restore original warn method
|
|
125
|
+
YARD::Logger.instance.define_singleton_method(:warn, original_warn) if original_warn
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Executor
|
|
6
|
+
# Executes validator queries against the shared registry.
|
|
7
|
+
# Handles visibility filtering and file exclusions per validator.
|
|
8
|
+
class QueryExecutor
|
|
9
|
+
# @param registry [InProcessRegistry] the shared registry instance
|
|
10
|
+
def initialize(registry)
|
|
11
|
+
@registry = registry
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Execute a validator's query against the registry
|
|
15
|
+
# @param validator [Validators::Base] validator instance with in_process_query method
|
|
16
|
+
# @param file_selection [Array<String>] files to include in the query
|
|
17
|
+
# @return [Hash] result hash with :stdout, :stderr, :exit_code keys
|
|
18
|
+
def execute(validator, file_selection: nil)
|
|
19
|
+
validator_name = validator.class.validator_name
|
|
20
|
+
|
|
21
|
+
# Determine visibility: if config has --private/--protected, use :all
|
|
22
|
+
visibility = determine_visibility(validator)
|
|
23
|
+
|
|
24
|
+
# Get file excludes from config
|
|
25
|
+
excludes = if validator_name && validator.config
|
|
26
|
+
validator.config.validator_exclude(validator_name)
|
|
27
|
+
else
|
|
28
|
+
[]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
objects = @registry.objects_for_validator(
|
|
32
|
+
visibility: visibility,
|
|
33
|
+
file_excludes: excludes,
|
|
34
|
+
file_selection: file_selection
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
collector = ResultCollector.new
|
|
38
|
+
|
|
39
|
+
objects.each do |object|
|
|
40
|
+
# Skip objects without file/line info
|
|
41
|
+
next unless object.file && object.line
|
|
42
|
+
|
|
43
|
+
execute_query_for_object(validator, object, collector)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
build_result(collector)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# Execute query for a single object, handling errors gracefully
|
|
52
|
+
# @param validator [Validators::Base] validator instance
|
|
53
|
+
# @param object [YARD::CodeObjects::Base] code object to query
|
|
54
|
+
# @param collector [ResultCollector] output collector
|
|
55
|
+
# @return [void]
|
|
56
|
+
def execute_query_for_object(validator, object, collector)
|
|
57
|
+
validator.in_process_query(object, collector)
|
|
58
|
+
rescue NotImplementedError, NoMethodError
|
|
59
|
+
# These indicate bugs in validator implementation - re-raise them
|
|
60
|
+
raise
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
# Skip objects that cause data-related errors (mirrors YARD CLI behavior).
|
|
63
|
+
# Some code objects may have malformed data that causes errors during validation.
|
|
64
|
+
# We log these in debug mode but don't fail the entire validator run.
|
|
65
|
+
return unless ENV['DEBUG']
|
|
66
|
+
|
|
67
|
+
warn "[YARD::Lint] Validator #{validator.class} error on #{object.path}: #{e.class}: #{e.message}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Build the result hash matching shell command output format
|
|
71
|
+
# @param collector [ResultCollector] output collector
|
|
72
|
+
# @return [Hash] result hash with :stdout, :stderr, :exit_code keys
|
|
73
|
+
def build_result(collector)
|
|
74
|
+
{
|
|
75
|
+
stdout: collector.to_stdout,
|
|
76
|
+
stderr: '',
|
|
77
|
+
exit_code: 0
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Determine visibility setting based on validator and config
|
|
82
|
+
# If config has --private or --protected in YardOptions, use :all
|
|
83
|
+
# If config explicitly sets empty YardOptions, use :public (override validator default)
|
|
84
|
+
# Otherwise use the validator's declared visibility
|
|
85
|
+
# @param validator [Validators::Base] validator instance
|
|
86
|
+
# @return [Symbol] visibility setting (:public or :all)
|
|
87
|
+
def determine_visibility(validator)
|
|
88
|
+
return validator.class.in_process_visibility || :public unless validator.config
|
|
89
|
+
|
|
90
|
+
validator_name = validator.class.validator_name
|
|
91
|
+
yard_options = validator.config.validator_yard_options(validator_name)
|
|
92
|
+
|
|
93
|
+
# If YardOptions contains --private or --protected, use :all visibility
|
|
94
|
+
if yard_options.any? { |opt| opt.include?('--private') || opt.include?('--protected') }
|
|
95
|
+
return :all
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Check if validator has explicit YardOptions set in config
|
|
99
|
+
# If explicitly set (even to empty), respect that choice and use :public
|
|
100
|
+
validator_cfg = validator.config.validators[validator_name] || {}
|
|
101
|
+
return :public if validator_cfg.key?('YardOptions')
|
|
102
|
+
|
|
103
|
+
# No explicit YardOptions - fall back to validator's declared visibility
|
|
104
|
+
validator.class.in_process_visibility || :public
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|