yard-lint 0.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 +7 -0
- data/.coditsu/ci.yml +3 -0
- data/CHANGELOG.md +28 -0
- data/LICENSE.txt +21 -0
- data/README.md +454 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/bin/yard-lint +109 -0
- data/lib/yard/lint/command_cache.rb +77 -0
- data/lib/yard/lint/config.rb +255 -0
- data/lib/yard/lint/config_loader.rb +198 -0
- data/lib/yard/lint/errors.rb +17 -0
- data/lib/yard/lint/formatters/progress.rb +50 -0
- data/lib/yard/lint/parsers/base.rb +23 -0
- data/lib/yard/lint/parsers/one_line_base.rb +35 -0
- data/lib/yard/lint/parsers/two_line_base.rb +45 -0
- data/lib/yard/lint/result_builder.rb +130 -0
- data/lib/yard/lint/results/aggregate.rb +86 -0
- data/lib/yard/lint/results/base.rb +156 -0
- data/lib/yard/lint/runner.rb +125 -0
- data/lib/yard/lint/validators/base.rb +120 -0
- data/lib/yard/lint/validators/config.rb +30 -0
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/config.rb +20 -0
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/parser.rb +43 -0
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/result.rb +26 -0
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/validator.rb +48 -0
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods.rb +13 -0
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments/config.rb +20 -0
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments/messages_builder.rb +24 -0
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments/parser.rb +45 -0
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments/result.rb +25 -0
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments/validator.rb +55 -0
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +13 -0
- data/lib/yard/lint/validators/documentation/undocumented_objects/config.rb +21 -0
- data/lib/yard/lint/validators/documentation/undocumented_objects/messages_builder.rb +23 -0
- data/lib/yard/lint/validators/documentation/undocumented_objects/parser.rb +39 -0
- data/lib/yard/lint/validators/documentation/undocumented_objects/result.rb +25 -0
- data/lib/yard/lint/validators/documentation/undocumented_objects/validator.rb +39 -0
- data/lib/yard/lint/validators/documentation/undocumented_objects.rb +14 -0
- data/lib/yard/lint/validators/semantic/abstract_methods/config.rb +24 -0
- data/lib/yard/lint/validators/semantic/abstract_methods/messages_builder.rb +25 -0
- data/lib/yard/lint/validators/semantic/abstract_methods/parser.rb +45 -0
- data/lib/yard/lint/validators/semantic/abstract_methods/result.rb +42 -0
- data/lib/yard/lint/validators/semantic/abstract_methods/validator.rb +65 -0
- data/lib/yard/lint/validators/semantic/abstract_methods.rb +13 -0
- data/lib/yard/lint/validators/tags/api_tags/config.rb +21 -0
- data/lib/yard/lint/validators/tags/api_tags/messages_builder.rb +29 -0
- data/lib/yard/lint/validators/tags/api_tags/parser.rb +50 -0
- data/lib/yard/lint/validators/tags/api_tags/result.rb +42 -0
- data/lib/yard/lint/validators/tags/api_tags/validator.rb +69 -0
- data/lib/yard/lint/validators/tags/api_tags.rb +13 -0
- data/lib/yard/lint/validators/tags/invalid_types/config.rb +22 -0
- data/lib/yard/lint/validators/tags/invalid_types/messages_builder.rb +24 -0
- data/lib/yard/lint/validators/tags/invalid_types/parser.rb +16 -0
- data/lib/yard/lint/validators/tags/invalid_types/result.rb +25 -0
- data/lib/yard/lint/validators/tags/invalid_types/validator.rb +106 -0
- data/lib/yard/lint/validators/tags/invalid_types.rb +13 -0
- data/lib/yard/lint/validators/tags/option_tags/config.rb +21 -0
- data/lib/yard/lint/validators/tags/option_tags/messages_builder.rb +24 -0
- data/lib/yard/lint/validators/tags/option_tags/parser.rb +45 -0
- data/lib/yard/lint/validators/tags/option_tags/result.rb +42 -0
- data/lib/yard/lint/validators/tags/option_tags/validator.rb +61 -0
- data/lib/yard/lint/validators/tags/option_tags.rb +13 -0
- data/lib/yard/lint/validators/tags/order/config.rb +33 -0
- data/lib/yard/lint/validators/tags/order/messages_builder.rb +30 -0
- data/lib/yard/lint/validators/tags/order/parser.rb +66 -0
- data/lib/yard/lint/validators/tags/order/result.rb +26 -0
- data/lib/yard/lint/validators/tags/order/validator.rb +89 -0
- data/lib/yard/lint/validators/tags/order.rb +13 -0
- data/lib/yard/lint/validators/warnings/duplicated_parameter_name/config.rb +22 -0
- data/lib/yard/lint/validators/warnings/duplicated_parameter_name/parser.rb +22 -0
- data/lib/yard/lint/validators/warnings/duplicated_parameter_name/result.rb +25 -0
- data/lib/yard/lint/validators/warnings/duplicated_parameter_name/validator.rb +33 -0
- data/lib/yard/lint/validators/warnings/duplicated_parameter_name.rb +14 -0
- data/lib/yard/lint/validators/warnings/invalid_directive_format/config.rb +22 -0
- data/lib/yard/lint/validators/warnings/invalid_directive_format/parser.rb +22 -0
- data/lib/yard/lint/validators/warnings/invalid_directive_format/result.rb +25 -0
- data/lib/yard/lint/validators/warnings/invalid_directive_format/validator.rb +33 -0
- data/lib/yard/lint/validators/warnings/invalid_directive_format.rb +14 -0
- data/lib/yard/lint/validators/warnings/invalid_tag_format/config.rb +22 -0
- data/lib/yard/lint/validators/warnings/invalid_tag_format/parser.rb +22 -0
- data/lib/yard/lint/validators/warnings/invalid_tag_format/result.rb +25 -0
- data/lib/yard/lint/validators/warnings/invalid_tag_format/validator.rb +33 -0
- data/lib/yard/lint/validators/warnings/invalid_tag_format.rb +14 -0
- data/lib/yard/lint/validators/warnings/unknown_directive/config.rb +22 -0
- data/lib/yard/lint/validators/warnings/unknown_directive/parser.rb +22 -0
- data/lib/yard/lint/validators/warnings/unknown_directive/result.rb +25 -0
- data/lib/yard/lint/validators/warnings/unknown_directive/validator.rb +33 -0
- data/lib/yard/lint/validators/warnings/unknown_directive.rb +14 -0
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/config.rb +22 -0
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/parser.rb +22 -0
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/result.rb +25 -0
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/validator.rb +33 -0
- data/lib/yard/lint/validators/warnings/unknown_parameter_name.rb +14 -0
- data/lib/yard/lint/validators/warnings/unknown_tag/config.rb +22 -0
- data/lib/yard/lint/validators/warnings/unknown_tag/parser.rb +24 -0
- data/lib/yard/lint/validators/warnings/unknown_tag/result.rb +25 -0
- data/lib/yard/lint/validators/warnings/unknown_tag/validator.rb +33 -0
- data/lib/yard/lint/validators/warnings/unknown_tag.rb +14 -0
- data/lib/yard/lint/version.rb +8 -0
- data/lib/yard/lint.rb +76 -0
- data/lib/yard-lint.rb +11 -0
- data/renovate.json +22 -0
- metadata +178 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Semantic
|
|
7
|
+
module AbstractMethods
|
|
8
|
+
# Validator to check @abstract methods have proper implementation
|
|
9
|
+
class Validator < Base
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
# Runs YARD list query to find abstract methods with implementation
|
|
13
|
+
# @param dir [String] dir where the YARD db is (or where it should be generated)
|
|
14
|
+
# @param escaped_file_names [String] files for which we want to run YARD
|
|
15
|
+
# @return [Hash] shell command execution hash results
|
|
16
|
+
def yard_cmd(dir, escaped_file_names)
|
|
17
|
+
cmd = <<~CMD
|
|
18
|
+
yard list \
|
|
19
|
+
--private \
|
|
20
|
+
--protected \
|
|
21
|
+
-b #{Shellwords.escape(dir)} \
|
|
22
|
+
#{escaped_file_names}
|
|
23
|
+
CMD
|
|
24
|
+
cmd = cmd.tr("\n", ' ')
|
|
25
|
+
cmd = cmd.gsub('yard list', "yard list --query #{query}")
|
|
26
|
+
|
|
27
|
+
shell(cmd)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @return [String] yard query to find abstract methods with implementation
|
|
31
|
+
def query
|
|
32
|
+
<<~QUERY
|
|
33
|
+
'
|
|
34
|
+
if object.has_tag?(:abstract) && object.is_a?(YARD::CodeObjects::MethodObject)
|
|
35
|
+
# Check if method has actual implementation (not just NotImplementedError)
|
|
36
|
+
source = object.source rescue nil
|
|
37
|
+
if source && !source.empty?
|
|
38
|
+
# Simple heuristic: abstract methods should be empty or raise NotImplementedError
|
|
39
|
+
lines = source.split("\\n").map(&:strip).reject(&:empty?)
|
|
40
|
+
# Skip def line and end
|
|
41
|
+
body_lines = lines[1...-1] || []
|
|
42
|
+
|
|
43
|
+
has_real_implementation = body_lines.any? do |line|
|
|
44
|
+
!line.start_with?('#') &&
|
|
45
|
+
!line.include?('NotImplementedError') &&
|
|
46
|
+
!line.include?('raise') &&
|
|
47
|
+
line != 'end'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if has_real_implementation
|
|
51
|
+
puts object.file + ':' + object.line.to_s + ': ' + object.title
|
|
52
|
+
puts 'has_implementation'
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
false
|
|
57
|
+
'
|
|
58
|
+
QUERY
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module ApiTags
|
|
8
|
+
# Configuration for ApiTags validator
|
|
9
|
+
class Config < ::Yard::Lint::Validators::Config
|
|
10
|
+
self.id = :api_tags
|
|
11
|
+
self.defaults = {
|
|
12
|
+
'Enabled' => false,
|
|
13
|
+
'Severity' => 'warning',
|
|
14
|
+
'AllowedApis' => %w[public private internal]
|
|
15
|
+
}.freeze
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
# Tags validators - validate YARD tag quality and consistency
|
|
7
|
+
module Tags
|
|
8
|
+
module ApiTags
|
|
9
|
+
# Builds messages for API tag offenses
|
|
10
|
+
class MessagesBuilder
|
|
11
|
+
class << self
|
|
12
|
+
# Build message for API tag offense
|
|
13
|
+
# @param offense [Hash] offense data with :status and :object_name keys
|
|
14
|
+
# @return [String] formatted message
|
|
15
|
+
def call(offense)
|
|
16
|
+
if offense[:status] == 'missing'
|
|
17
|
+
"Public object `#{offense[:object_name]}` is missing @api tag"
|
|
18
|
+
else
|
|
19
|
+
api_value = offense[:api_value] || offense[:status]&.sub('invalid:', '')
|
|
20
|
+
"Object `#{offense[:object_name]}` has invalid @api tag value: '#{api_value}'"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
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 ApiTags
|
|
8
|
+
# Parser for @api tag validation results
|
|
9
|
+
class Parser < Parsers::Base
|
|
10
|
+
# @param yard_output [String] raw yard output with API tag issues
|
|
11
|
+
# @return [Array<Hash>] array with API tag violation details
|
|
12
|
+
def call(yard_output)
|
|
13
|
+
return [] if yard_output.nil? || yard_output.empty?
|
|
14
|
+
|
|
15
|
+
lines = yard_output.split("\n").reject(&:empty?)
|
|
16
|
+
results = []
|
|
17
|
+
|
|
18
|
+
lines.each_slice(2) do |location_line, status_line|
|
|
19
|
+
next unless location_line && status_line
|
|
20
|
+
|
|
21
|
+
# Parse location line: "file.rb:10: ClassName#method_name"
|
|
22
|
+
match = location_line.match(/^(.+):(\d+): (.+)$/)
|
|
23
|
+
next unless match
|
|
24
|
+
|
|
25
|
+
file = match[1]
|
|
26
|
+
line = match[2].to_i
|
|
27
|
+
object_name = match[3]
|
|
28
|
+
|
|
29
|
+
api_value = if status_line.start_with?('invalid:')
|
|
30
|
+
status_line.sub('invalid:', '')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
results << {
|
|
34
|
+
name: 'ApiTag',
|
|
35
|
+
object_name: object_name,
|
|
36
|
+
status: status_line,
|
|
37
|
+
api_value: api_value,
|
|
38
|
+
location: file,
|
|
39
|
+
line: line
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
results
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module ApiTags
|
|
8
|
+
# Result object for API tag validation
|
|
9
|
+
class Result < Results::Base
|
|
10
|
+
self.default_severity = 'warning'
|
|
11
|
+
self.offense_type = 'line'
|
|
12
|
+
self.offense_name = 'ApiTagViolation'
|
|
13
|
+
|
|
14
|
+
# Build human-readable message for API tag offense
|
|
15
|
+
# @param offense [Hash] offense data with :name key
|
|
16
|
+
# @return [String] formatted message
|
|
17
|
+
def build_message(offense)
|
|
18
|
+
MessagesBuilder.call(offense)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
# Override to build offenses with dynamic names from parsed data
|
|
24
|
+
# @return [Array<Hash>] array of offense hashes
|
|
25
|
+
def build_offenses
|
|
26
|
+
@parsed_data.map do |offense_data|
|
|
27
|
+
{
|
|
28
|
+
severity: configured_severity,
|
|
29
|
+
type: self.class.offense_type,
|
|
30
|
+
name: offense_data[:name] || self.class.offense_name,
|
|
31
|
+
message: build_message(offense_data),
|
|
32
|
+
location: offense_data[:location] || offense_data[:file],
|
|
33
|
+
location_line: offense_data[:line] || offense_data[:location_line] || 0
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module ApiTags
|
|
8
|
+
# Validator to check for @api tag presence and validity
|
|
9
|
+
class Validator < Base
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
# Runs yard list query to find objects missing or with invalid @api tags
|
|
13
|
+
# @param dir [String] dir where the yard db is (or where it should be generated)
|
|
14
|
+
# @param escaped_file_names [String] files for which we want to get the stats
|
|
15
|
+
# @return [Hash] shell command execution hash results
|
|
16
|
+
def yard_cmd(dir, escaped_file_names)
|
|
17
|
+
cmd = <<~CMD
|
|
18
|
+
yard list \
|
|
19
|
+
--private \
|
|
20
|
+
--protected \
|
|
21
|
+
-b #{Shellwords.escape(dir)} \
|
|
22
|
+
#{escaped_file_names}
|
|
23
|
+
CMD
|
|
24
|
+
cmd = cmd.tr("\n", ' ')
|
|
25
|
+
cmd = cmd.gsub('yard list', "yard list --query #{query}")
|
|
26
|
+
|
|
27
|
+
shell(cmd)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @return [String] yard query to find objects with missing or invalid @api tags
|
|
31
|
+
def query
|
|
32
|
+
allowed_list = allowed_apis.map { |api| "'#{api}'" }.join(', ')
|
|
33
|
+
|
|
34
|
+
<<~QUERY
|
|
35
|
+
'
|
|
36
|
+
if object.has_tag?(:api)
|
|
37
|
+
api_value = object.tag(:api).text
|
|
38
|
+
unless [#{allowed_list}].include?(api_value)
|
|
39
|
+
puts object.file + ':' + object.line.to_s + ': ' + object.title
|
|
40
|
+
puts 'invalid:' + api_value
|
|
41
|
+
end
|
|
42
|
+
elsif #{require_api_tags?}
|
|
43
|
+
# Only check public methods/classes if require_api_tags is enabled
|
|
44
|
+
visibility = object.visibility.to_s
|
|
45
|
+
if visibility == 'public' && !object.root?
|
|
46
|
+
puts object.file + ':' + object.line.to_s + ': ' + object.title
|
|
47
|
+
puts 'missing'
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
false
|
|
51
|
+
'
|
|
52
|
+
QUERY
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @return [Array<String>] list of allowed API values
|
|
56
|
+
def allowed_apis
|
|
57
|
+
config.validator_config('Tags/ApiTags', 'AllowedApis') || %w[public private internal]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @return [Boolean] whether @api tags are required on public objects
|
|
61
|
+
def require_api_tags?
|
|
62
|
+
config.validator_enabled?('Tags/ApiTags')
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
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 InvalidTypes
|
|
8
|
+
# Configuration for InvalidTypes validator
|
|
9
|
+
class Config < ::Yard::Lint::Validators::Config
|
|
10
|
+
self.id = :invalid_types
|
|
11
|
+
self.defaults = {
|
|
12
|
+
'Enabled' => true,
|
|
13
|
+
'Severity' => 'warning',
|
|
14
|
+
'ValidatedTags' => %w[param option return yieldreturn],
|
|
15
|
+
'ExtraTypes' => []
|
|
16
|
+
}.freeze
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module InvalidTypes
|
|
8
|
+
# Builds messages for invalid tag types offenses
|
|
9
|
+
class MessagesBuilder
|
|
10
|
+
class << self
|
|
11
|
+
# Build message for invalid tag types
|
|
12
|
+
# @param offense [Hash] offense data with :method_name key
|
|
13
|
+
# @return [String] formatted message
|
|
14
|
+
def call(offense)
|
|
15
|
+
"The `#{offense[:method_name]}` has at least one tag " \
|
|
16
|
+
'with an invalid type definition.'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module InvalidTypes
|
|
8
|
+
# Parser for invalid tags types output
|
|
9
|
+
# Reuses location parsing logic from undocumented method arguments
|
|
10
|
+
class Parser < Validators::Documentation::UndocumentedMethodArguments::Parser
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
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 InvalidTypes
|
|
8
|
+
# Result object for invalid tag types validation
|
|
9
|
+
class Result < Results::Base
|
|
10
|
+
self.default_severity = 'warning'
|
|
11
|
+
self.offense_type = 'method'
|
|
12
|
+
self.offense_name = 'InvalidTagType'
|
|
13
|
+
|
|
14
|
+
# Build human-readable message for invalid tag type offense
|
|
15
|
+
# @param offense [Hash] offense data
|
|
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,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module InvalidTypes
|
|
8
|
+
# Runs a query that will pick all the objects that have invalid type definitions
|
|
9
|
+
# By invalid we mean, that they are not classes nor any of the allowed defaults or
|
|
10
|
+
# exclusions.
|
|
11
|
+
class Validator < Base
|
|
12
|
+
# All non-class yard types that are considered valid
|
|
13
|
+
ALLOWED_DEFAULTS = %w[
|
|
14
|
+
false
|
|
15
|
+
true
|
|
16
|
+
nil
|
|
17
|
+
self
|
|
18
|
+
vold
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
private_constant :ALLOWED_DEFAULTS
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# Runs yard list query with proper settings on a given dir and files
|
|
26
|
+
# @param dir [String] dir where the yard db is (or where it should be generated)
|
|
27
|
+
# @param escaped_file_names [String] files for which we want to get the stats
|
|
28
|
+
# @return [Hash] shell command execution hash results
|
|
29
|
+
def yard_cmd(dir, escaped_file_names)
|
|
30
|
+
# Write query to a temporary file to avoid shell escaping issues
|
|
31
|
+
require 'tempfile'
|
|
32
|
+
|
|
33
|
+
Tempfile.create(['yard_query', '.sh']) do |f|
|
|
34
|
+
f.write("#!/bin/bash\n")
|
|
35
|
+
f.write("yard list --query #{Shellwords.escape(query)} ")
|
|
36
|
+
f.write("--private --protected -b #{Shellwords.escape(dir)} ")
|
|
37
|
+
f.write("#{escaped_file_names}\n")
|
|
38
|
+
f.flush
|
|
39
|
+
f.chmod(0o755)
|
|
40
|
+
|
|
41
|
+
shell("bash #{Shellwords.escape(f.path)}")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [String] multiline yard query that we use to find methods with
|
|
46
|
+
# tags with invalid types definitions
|
|
47
|
+
def query
|
|
48
|
+
<<-QUERY
|
|
49
|
+
'
|
|
50
|
+
sanitize = ->(type) do
|
|
51
|
+
type
|
|
52
|
+
.tr('=>', '')
|
|
53
|
+
.tr('<', '')
|
|
54
|
+
.tr('>', '')
|
|
55
|
+
.tr(' ', '')
|
|
56
|
+
.tr(',', '')
|
|
57
|
+
.tr('{', '')
|
|
58
|
+
.tr('}', '')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
docstring
|
|
62
|
+
.tags
|
|
63
|
+
.select { |tag| #{checked_tags_names}.include?(tag.tag_name) }
|
|
64
|
+
.map(&:types)
|
|
65
|
+
.flatten
|
|
66
|
+
.uniq
|
|
67
|
+
.compact
|
|
68
|
+
.map(&sanitize)
|
|
69
|
+
.reject { |type| #{allowed_types_code}.include?(type) }
|
|
70
|
+
.reject { |type| !(Kernel.const_defined?(type) rescue nil).nil? }
|
|
71
|
+
.reject { |type| type.include?('#') }
|
|
72
|
+
.then { |types| !types.empty? }
|
|
73
|
+
'
|
|
74
|
+
QUERY
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @return [String] tags names for which we want to check the invalid tags
|
|
78
|
+
# types definitions
|
|
79
|
+
def checked_tags_names
|
|
80
|
+
validated_tags = config.validator_config('Tags/InvalidTypes', 'ValidatedTags')
|
|
81
|
+
query_array(validated_tags)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# @return [String] extra names that we allow for types definitions in a yard
|
|
85
|
+
# query acceptable form
|
|
86
|
+
def allowed_types_code
|
|
87
|
+
extra_types = config.validator_config('Tags/InvalidTypes', 'ExtraTypes') || []
|
|
88
|
+
query_array(ALLOWED_DEFAULTS + extra_types)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @param elements [Array<String>] array of elements that we want to convert into
|
|
92
|
+
# a string ruby yard query array form
|
|
93
|
+
# @return [String] array of elements for yard query converted into a string
|
|
94
|
+
def query_array(elements)
|
|
95
|
+
"
|
|
96
|
+
[
|
|
97
|
+
#{elements.map { |type| "'#{type}'" }.join(',')}
|
|
98
|
+
]
|
|
99
|
+
"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module OptionTags
|
|
8
|
+
# Configuration for OptionTags validator
|
|
9
|
+
class Config < ::Yard::Lint::Validators::Config
|
|
10
|
+
self.id = :option_tags
|
|
11
|
+
self.defaults = {
|
|
12
|
+
'Enabled' => true,
|
|
13
|
+
'Severity' => 'warning',
|
|
14
|
+
'ParameterNames' => %w[options opts kwargs]
|
|
15
|
+
}.freeze
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module OptionTags
|
|
8
|
+
# Builds messages for option tag offenses
|
|
9
|
+
class MessagesBuilder
|
|
10
|
+
class << self
|
|
11
|
+
# Build message for option tag offense
|
|
12
|
+
# @param offense [Hash] offense data with :method_name key
|
|
13
|
+
# @return [String] formatted message
|
|
14
|
+
def call(offense)
|
|
15
|
+
"Method `#{offense[:method_name]}` has options parameter but no @option tags " \
|
|
16
|
+
'documenting the available options'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module OptionTags
|
|
8
|
+
# Parser for @option tag validation results
|
|
9
|
+
class Parser < Parsers::Base
|
|
10
|
+
# @param yard_output [String] raw yard output with option tag issues
|
|
11
|
+
# @return [Array<Hash>] array with option tag violation details
|
|
12
|
+
def call(yard_output)
|
|
13
|
+
return [] if yard_output.nil? || yard_output.empty?
|
|
14
|
+
|
|
15
|
+
lines = yard_output.split("\n").reject(&:empty?)
|
|
16
|
+
results = []
|
|
17
|
+
|
|
18
|
+
lines.each_slice(2) do |location_line, status_line|
|
|
19
|
+
next unless location_line && status_line
|
|
20
|
+
next unless status_line == 'missing_option_tags'
|
|
21
|
+
|
|
22
|
+
# Parse location line: "file.rb:10: ClassName#method_name"
|
|
23
|
+
match = location_line.match(/^(.+):(\d+): (.+)$/)
|
|
24
|
+
next unless match
|
|
25
|
+
|
|
26
|
+
file = match[1]
|
|
27
|
+
line = match[2].to_i
|
|
28
|
+
method_name = match[3]
|
|
29
|
+
|
|
30
|
+
results << {
|
|
31
|
+
name: 'MissingOptionTags',
|
|
32
|
+
method_name: method_name,
|
|
33
|
+
location: file,
|
|
34
|
+
line: line
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
results
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Tags
|
|
7
|
+
module OptionTags
|
|
8
|
+
# Result object for option tag validation
|
|
9
|
+
class Result < Results::Base
|
|
10
|
+
self.default_severity = 'warning'
|
|
11
|
+
self.offense_type = 'method'
|
|
12
|
+
self.offense_name = 'OptionTagViolation'
|
|
13
|
+
|
|
14
|
+
# Build human-readable message for option tag offense
|
|
15
|
+
# @param offense [Hash] offense data with :name key
|
|
16
|
+
# @return [String] formatted message
|
|
17
|
+
def build_message(offense)
|
|
18
|
+
MessagesBuilder.call(offense)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
# Override to build offenses with dynamic names from parsed data
|
|
24
|
+
# @return [Array<Hash>] array of offense hashes
|
|
25
|
+
def build_offenses
|
|
26
|
+
@parsed_data.map do |offense_data|
|
|
27
|
+
{
|
|
28
|
+
severity: configured_severity,
|
|
29
|
+
type: self.class.offense_type,
|
|
30
|
+
name: offense_data[:name] || self.class.offense_name,
|
|
31
|
+
message: build_message(offense_data),
|
|
32
|
+
location: offense_data[:location] || offense_data[:file],
|
|
33
|
+
location_line: offense_data[:line] || offense_data[:location_line] || 0
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|