yard-lint 1.2.3 → 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 +150 -1
- data/README.md +98 -4
- data/Rakefile +20 -0
- data/bin/yard-lint +71 -38
- data/lib/yard/lint/config.rb +5 -0
- 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 +50 -38
- data/lib/yard/lint/templates/default_config.yml +105 -0
- data/lib/yard/lint/templates/strict_config.yml +105 -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/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/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/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/validator.rb +53 -84
- data/lib/yard/lint/validators/tags/tag_type_position.rb +0 -1
- data/lib/yard/lint/validators/tags/type_syntax/parser.rb +7 -2
- 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 +38 -2
- data/lib/yard/lint/command_cache.rb +0 -93
|
@@ -8,38 +8,28 @@ module Yard
|
|
|
8
8
|
# Runs a query that will pick all the boolean methods (ending with ?) that
|
|
9
9
|
# do not have a return type or return description documented
|
|
10
10
|
class Validator < Base
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
# Accepts @return [Boolean] without description text as valid documentation
|
|
14
|
-
QUERY = <<~QUERY.tr("\n", ' ')
|
|
15
|
-
'
|
|
16
|
-
type == :method &&
|
|
17
|
-
!is_alias? &&
|
|
18
|
-
is_explicit? &&
|
|
19
|
-
name.to_s.end_with?("?") &&
|
|
20
|
-
(tag("return").nil? || tag("return").types.to_a.empty?)
|
|
21
|
-
'
|
|
22
|
-
QUERY
|
|
11
|
+
# Enable in-process execution for this validator
|
|
12
|
+
in_process visibility: :public
|
|
23
13
|
|
|
24
|
-
|
|
14
|
+
# Execute query for a single object during in-process execution.
|
|
15
|
+
# Finds boolean methods (ending with ?) without @return tag or return types.
|
|
16
|
+
# @param object [YARD::CodeObjects::Base] the code object to query
|
|
17
|
+
# @param collector [Executor::ResultCollector] collector for output
|
|
18
|
+
# @return [void]
|
|
19
|
+
def in_process_query(object, collector)
|
|
20
|
+
# Only check methods
|
|
21
|
+
return unless object.type == :method
|
|
22
|
+
# Skip aliases and implicit methods
|
|
23
|
+
return if object.is_alias?
|
|
24
|
+
return unless object.is_explicit?
|
|
25
|
+
# Only check boolean methods (ending with ?)
|
|
26
|
+
return unless object.name.to_s.end_with?('?')
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
# Check if @return tag is missing or has no types
|
|
29
|
+
return_tag = object.tag(:return)
|
|
30
|
+
return unless return_tag.nil? || return_tag.types.to_a.empty?
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
# @param dir [String] dir where we should generate the temp docs
|
|
30
|
-
# @param file_list_path [String] path to temp file containing file paths (one per line)
|
|
31
|
-
# @return [Hash] shell command execution hash results
|
|
32
|
-
def yard_cmd(dir, file_list_path)
|
|
33
|
-
cmd = <<~CMD
|
|
34
|
-
cat #{Shellwords.escape(file_list_path)} | xargs yard list \
|
|
35
|
-
#{shell_arguments} \
|
|
36
|
-
--query #{QUERY} \
|
|
37
|
-
-q \
|
|
38
|
-
-b #{Shellwords.escape(dir)}
|
|
39
|
-
CMD
|
|
40
|
-
cmd = cmd.tr("\n", ' ')
|
|
41
|
-
|
|
42
|
-
shell(cmd)
|
|
32
|
+
collector.puts "#{object.file}:#{object.line}: #{object.title}"
|
|
43
33
|
end
|
|
44
34
|
end
|
|
45
35
|
end
|
|
@@ -7,44 +7,28 @@ module Yard
|
|
|
7
7
|
module UndocumentedMethodArguments
|
|
8
8
|
# Runs yard list to check for missing args docs on methods that were documented
|
|
9
9
|
class Validator < Base
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
--list-undoc
|
|
13
|
-
].freeze
|
|
10
|
+
# Enable in-process execution for this validator
|
|
11
|
+
in_process visibility: :public
|
|
14
12
|
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
# Execute query for a single object during in-process execution.
|
|
14
|
+
# Finds methods where parameters.size > @param tags count.
|
|
15
|
+
# @param object [YARD::CodeObjects::Base] the code object to query
|
|
16
|
+
# @param collector [Executor::ResultCollector] collector for output
|
|
17
|
+
# @return [void]
|
|
18
|
+
def in_process_query(object, collector)
|
|
19
|
+
# Only check methods
|
|
20
|
+
return unless object.type == :method
|
|
21
|
+
# Skip aliases and implicit methods
|
|
22
|
+
return if object.is_alias?
|
|
23
|
+
return unless object.is_explicit?
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
# Check if parameters count exceeds @param tags count
|
|
26
|
+
param_count = object.parameters.size
|
|
27
|
+
param_tags_count = object.tags(:param).size
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
return unless param_count > param_tags_count
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
# @param dir [String] dir where we should generate the temp docs
|
|
32
|
-
# @param file_list_path [String] path to temp file containing file paths (one per line)
|
|
33
|
-
# @return [Hash] shell command execution hash results
|
|
34
|
-
def yard_cmd(dir, file_list_path)
|
|
35
|
-
shell_args = shell_arguments
|
|
36
|
-
UNWANTED_OPTIONS.each { |opt| shell_args.gsub!(opt, '') }
|
|
37
|
-
|
|
38
|
-
cmd = <<~CMD
|
|
39
|
-
cat #{Shellwords.escape(file_list_path)} | xargs yard list \
|
|
40
|
-
#{shell_args} \
|
|
41
|
-
--query #{QUERY} \
|
|
42
|
-
-q \
|
|
43
|
-
-b #{Shellwords.escape(dir)}
|
|
44
|
-
CMD
|
|
45
|
-
cmd = cmd.tr("\n", ' ')
|
|
46
|
-
|
|
47
|
-
shell(cmd)
|
|
31
|
+
collector.puts "#{object.file}:#{object.line}: #{object.title}"
|
|
48
32
|
end
|
|
49
33
|
end
|
|
50
34
|
end
|
|
@@ -7,33 +7,25 @@ module Yard
|
|
|
7
7
|
module UndocumentedObjects
|
|
8
8
|
# Runs yard list to check for undocumented objects
|
|
9
9
|
class Validator < Base
|
|
10
|
-
|
|
10
|
+
# Enable in-process execution for this validator
|
|
11
|
+
in_process visibility: :public
|
|
11
12
|
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# @param
|
|
15
|
-
# @
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
--query #{query} \
|
|
21
|
-
-q \
|
|
22
|
-
-b #{Shellwords.escape(dir)}
|
|
23
|
-
CMD
|
|
24
|
-
cmd = cmd.tr("\n", ' ')
|
|
13
|
+
# Execute query for a single object during in-process execution.
|
|
14
|
+
# Checks for empty docstrings and outputs location with arity for methods.
|
|
15
|
+
# @param object [YARD::CodeObjects::Base] the code object to query
|
|
16
|
+
# @param collector [Executor::ResultCollector] collector for output
|
|
17
|
+
# @return [void]
|
|
18
|
+
def in_process_query(object, collector)
|
|
19
|
+
# Check if docstring is empty
|
|
20
|
+
return unless object.docstring.all.empty?
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
22
|
+
if object.is_a?(YARD::CodeObjects::MethodObject)
|
|
23
|
+
# For methods, include arity (excluding splat and block params)
|
|
24
|
+
arity = object.parameters.reject { |p| p[0].to_s.start_with?('*', '&') }.size
|
|
25
|
+
collector.puts "#{object.file}:#{object.line}: #{object.title}|#{arity}"
|
|
26
|
+
else
|
|
27
|
+
collector.puts "#{object.file}:#{object.line}: #{object.title}"
|
|
28
|
+
end
|
|
37
29
|
end
|
|
38
30
|
end
|
|
39
31
|
end
|
|
@@ -22,8 +22,8 @@ module Yard
|
|
|
22
22
|
# - 'to_s' # Excludes ALL to_s methods regardless of parameters
|
|
23
23
|
# - 'inspect' # Excludes ALL inspect methods
|
|
24
24
|
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
25
|
+
# @note Exact name matching excludes the method with **any arity**. If you need
|
|
26
|
+
# arity-specific exclusions, use arity notation instead.
|
|
27
27
|
#
|
|
28
28
|
# ### 2. Arity Notation (method_name/N)
|
|
29
29
|
#
|
|
@@ -34,8 +34,8 @@ module Yard
|
|
|
34
34
|
# - 'call/1' # Only excludes call methods with exactly 1 parameter
|
|
35
35
|
# - 'initialize/2' # Only excludes initialize with exactly 2 parameters
|
|
36
36
|
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
37
|
+
# @note Arity counts total parameters (required + optional) excluding splat (*)
|
|
38
|
+
# and block (&) parameters.
|
|
39
39
|
#
|
|
40
40
|
# ### 3. Regex Patterns
|
|
41
41
|
#
|
|
@@ -134,7 +134,6 @@ module Yard
|
|
|
134
134
|
# 2. Splat parameters don't count: `def method(a, *rest)` has arity 1
|
|
135
135
|
# 3. Block parameters don't count: `def method(a, &block)` has arity 1
|
|
136
136
|
# 4. Keyword arguments count as individual parameters: `def method(a:, b:)` has arity 2
|
|
137
|
-
#
|
|
138
137
|
module UndocumentedObjects
|
|
139
138
|
end
|
|
140
139
|
end
|
|
@@ -7,28 +7,37 @@ module Yard
|
|
|
7
7
|
module UndocumentedOptions
|
|
8
8
|
# Validates that methods with options hash parameters have @option tags
|
|
9
9
|
class Validator < Validators::Base
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
# Enable in-process execution for this validator
|
|
11
|
+
in_process visibility: :public
|
|
12
|
+
|
|
13
|
+
# Execute query for a single object during in-process execution.
|
|
14
|
+
# Finds methods with options parameters but no @option tags.
|
|
15
|
+
# @param object [YARD::CodeObjects::Base] the code object to query
|
|
16
|
+
# @param collector [Executor::ResultCollector] collector for output
|
|
17
|
+
# @return [void]
|
|
18
|
+
def in_process_query(object, collector)
|
|
19
|
+
# Only check method objects
|
|
20
|
+
return unless object.is_a?(YARD::CodeObjects::MethodObject)
|
|
21
|
+
|
|
22
|
+
params = object.parameters || []
|
|
23
|
+
|
|
24
|
+
# Check for options-style parameters
|
|
25
|
+
has_options_param = params.any? do |p|
|
|
26
|
+
param_name = p[0].to_s
|
|
27
|
+
# Match options, option, opts, opt, kwargs or double-splat (**)
|
|
28
|
+
param_name.match?(/^(options?|opts?|kwargs)$/) ||
|
|
29
|
+
param_name.start_with?('**')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
return unless has_options_param
|
|
33
|
+
|
|
34
|
+
# Check if @option tags are missing
|
|
35
|
+
option_tags = object.tags(:option)
|
|
36
|
+
return unless option_tags.empty?
|
|
17
37
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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)
|
|
38
|
+
# Output method location and parameter info
|
|
39
|
+
collector.puts "#{object.file}:#{object.line}: #{object.title}"
|
|
40
|
+
collector.puts params.map { |p| p.join(' ') }.join(', ')
|
|
32
41
|
end
|
|
33
42
|
end
|
|
34
43
|
end
|
|
@@ -24,14 +24,14 @@ module Yard
|
|
|
24
24
|
# @return [Array<Hash>] array of offense hashes
|
|
25
25
|
def build_offenses
|
|
26
26
|
@parsed_data.map do |offense_data|
|
|
27
|
-
|
|
27
|
+
offense_data.merge(
|
|
28
28
|
severity: configured_severity,
|
|
29
29
|
type: self.class.offense_type,
|
|
30
30
|
name: offense_data[:name] || self.class.offense_name,
|
|
31
31
|
message: build_message(offense_data),
|
|
32
32
|
location: offense_data[:location] || offense_data[:file],
|
|
33
33
|
location_line: offense_data[:line] || offense_data[:location_line] || 0
|
|
34
|
-
|
|
34
|
+
)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
end
|
|
@@ -7,54 +7,42 @@ module Yard
|
|
|
7
7
|
module AbstractMethods
|
|
8
8
|
# Validator to check @abstract methods have proper implementation
|
|
9
9
|
class Validator < Base
|
|
10
|
-
|
|
10
|
+
# Enable in-process execution with all visibility
|
|
11
|
+
in_process visibility: :all
|
|
11
12
|
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# @param
|
|
15
|
-
# @
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
--protected \
|
|
21
|
-
-b #{Shellwords.escape(dir)}
|
|
22
|
-
CMD
|
|
23
|
-
cmd = cmd.tr("\n", ' ')
|
|
24
|
-
cmd = cmd.gsub('yard list', "yard list --query #{query}")
|
|
13
|
+
# Execute query for a single object during in-process execution.
|
|
14
|
+
# Checks if @abstract methods have implementation.
|
|
15
|
+
# @param object [YARD::CodeObjects::Base] the code object to query
|
|
16
|
+
# @param collector [Executor::ResultCollector] collector for output
|
|
17
|
+
# @return [void]
|
|
18
|
+
def in_process_query(object, collector)
|
|
19
|
+
return unless object.has_tag?(:abstract)
|
|
20
|
+
return unless object.is_a?(YARD::CodeObjects::MethodObject)
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
# Check if method has actual implementation (not just NotImplementedError)
|
|
23
|
+
source = begin
|
|
24
|
+
object.source
|
|
25
|
+
rescue StandardError
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
return unless source && !source.empty?
|
|
29
|
+
|
|
30
|
+
# Simple heuristic: abstract methods should be empty or raise NotImplementedError
|
|
31
|
+
lines = source.split("\n").map(&:strip).reject(&:empty?)
|
|
32
|
+
# Skip def line and end
|
|
33
|
+
body_lines = lines[1...-1] || []
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
source = object.source rescue nil
|
|
36
|
-
if source && !source.empty?
|
|
37
|
-
# Simple heuristic: abstract methods should be empty or raise NotImplementedError
|
|
38
|
-
lines = source.split("\\n").map(&:strip).reject(&:empty?)
|
|
39
|
-
# Skip def line and end
|
|
40
|
-
body_lines = lines[1...-1] || []
|
|
35
|
+
has_real_implementation = body_lines.any? do |line|
|
|
36
|
+
!line.start_with?('#') &&
|
|
37
|
+
!line.include?('NotImplementedError') &&
|
|
38
|
+
!line.include?('raise') &&
|
|
39
|
+
line != 'end'
|
|
40
|
+
end
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
!line.start_with?('#') &&
|
|
44
|
-
!line.include?('NotImplementedError') &&
|
|
45
|
-
!line.include?('raise') &&
|
|
46
|
-
line != 'end'
|
|
47
|
-
end
|
|
42
|
+
return unless has_real_implementation
|
|
48
43
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
puts 'has_implementation'
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
false
|
|
56
|
-
'
|
|
57
|
-
QUERY
|
|
44
|
+
collector.puts "#{object.file}:#{object.line}: #{object.title}"
|
|
45
|
+
collector.puts 'has_implementation'
|
|
58
46
|
end
|
|
59
47
|
end
|
|
60
48
|
end
|
|
@@ -7,49 +7,34 @@ module Yard
|
|
|
7
7
|
module ApiTags
|
|
8
8
|
# Validator to check for @api tag presence and validity
|
|
9
9
|
class Validator < Base
|
|
10
|
-
|
|
10
|
+
# Enable in-process execution with all visibility
|
|
11
|
+
in_process visibility: :all
|
|
11
12
|
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# @param
|
|
15
|
-
# @
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
--private \
|
|
20
|
-
--protected \
|
|
21
|
-
-b #{Shellwords.escape(dir)}
|
|
22
|
-
CMD
|
|
23
|
-
cmd = cmd.tr("\n", ' ')
|
|
24
|
-
cmd = cmd.gsub('yard list', "yard list --query #{query}")
|
|
13
|
+
# Execute query for a single object during in-process execution.
|
|
14
|
+
# Checks for @api tag presence and validity.
|
|
15
|
+
# @param object [YARD::CodeObjects::Base] the code object to query
|
|
16
|
+
# @param collector [Executor::ResultCollector] collector for output
|
|
17
|
+
# @return [void]
|
|
18
|
+
def in_process_query(object, collector)
|
|
19
|
+
allowed_list = allowed_apis
|
|
25
20
|
|
|
26
|
-
|
|
21
|
+
if object.has_tag?(:api)
|
|
22
|
+
api_value = object.tag(:api).text
|
|
23
|
+
unless allowed_list.include?(api_value)
|
|
24
|
+
collector.puts "#{object.file}:#{object.line}: #{object.title}"
|
|
25
|
+
collector.puts "invalid:#{api_value}"
|
|
26
|
+
end
|
|
27
|
+
elsif require_api_tags?
|
|
28
|
+
# Only check public methods/classes if require_api_tags is enabled
|
|
29
|
+
visibility = object.visibility.to_s
|
|
30
|
+
if visibility == 'public' && !object.root?
|
|
31
|
+
collector.puts "#{object.file}:#{object.line}: #{object.title}"
|
|
32
|
+
collector.puts 'missing'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
27
35
|
end
|
|
28
36
|
|
|
29
|
-
|
|
30
|
-
def query
|
|
31
|
-
allowed_list = allowed_apis.map { |api| "'#{api}'" }.join(', ')
|
|
32
|
-
|
|
33
|
-
<<~QUERY
|
|
34
|
-
'
|
|
35
|
-
if object.has_tag?(:api)
|
|
36
|
-
api_value = object.tag(:api).text
|
|
37
|
-
unless [#{allowed_list}].include?(api_value)
|
|
38
|
-
puts object.file + ':' + object.line.to_s + ': ' + object.title
|
|
39
|
-
puts 'invalid:' + api_value
|
|
40
|
-
end
|
|
41
|
-
elsif #{require_api_tags?}
|
|
42
|
-
# Only check public methods/classes if require_api_tags is enabled
|
|
43
|
-
visibility = object.visibility.to_s
|
|
44
|
-
if visibility == 'public' && !object.root?
|
|
45
|
-
puts object.file + ':' + object.line.to_s + ': ' + object.title
|
|
46
|
-
puts 'missing'
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
false
|
|
50
|
-
'
|
|
51
|
-
QUERY
|
|
52
|
-
end
|
|
37
|
+
private
|
|
53
38
|
|
|
54
39
|
# @return [Array<String>] list of allowed API values
|
|
55
40
|
def allowed_apis
|
|
@@ -7,83 +7,54 @@ module Yard
|
|
|
7
7
|
module CollectionType
|
|
8
8
|
# Validates Hash collection type syntax in YARD tags
|
|
9
9
|
class Validator < Base
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# @
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Tempfile.create(['yard_query', '.sh']) do |f|
|
|
21
|
-
f.write("#!/bin/bash\n")
|
|
22
|
-
f.write(cmd)
|
|
23
|
-
f.write("#{shell_arguments} -b #{Shellwords.escape(dir)}\n")
|
|
24
|
-
f.flush
|
|
25
|
-
f.chmod(0o755)
|
|
26
|
-
|
|
27
|
-
shell("bash #{Shellwords.escape(f.path)}")
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# YARD query that finds incorrect collection syntax based on EnforcedStyle
|
|
32
|
-
# Format output as two lines per violation:
|
|
33
|
-
# Line 1: file.rb:LINE: ClassName#method_name
|
|
34
|
-
# Line 2: tag_name|type_string|detected_style
|
|
35
|
-
# @return [String] YARD query string
|
|
36
|
-
def query
|
|
10
|
+
# Enable in-process execution
|
|
11
|
+
in_process visibility: :public
|
|
12
|
+
|
|
13
|
+
# Execute query for a single object during in-process execution.
|
|
14
|
+
# Validates Hash collection type syntax based on EnforcedStyle.
|
|
15
|
+
# @param object [YARD::CodeObjects::Base] the code object to query
|
|
16
|
+
# @param collector [Executor::ResultCollector] collector for output
|
|
17
|
+
# @return [void]
|
|
18
|
+
def in_process_query(object, collector)
|
|
19
|
+
validated_tags = config_or_default('ValidatedTags')
|
|
37
20
|
style = enforced_style
|
|
38
21
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
# Check for {...} syntax without Hash prefix
|
|
57
|
-
elsif type_str =~ /^\\{.*\\}$/
|
|
58
|
-
detected_style = "short"
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Report violations based on enforced style
|
|
62
|
-
if detected_style && detected_style != "#{style}"
|
|
63
|
-
puts object.file + ":" + object.line.to_s + ": " + object.title
|
|
64
|
-
puts tag.tag_name + "|" + type_str + "|" + detected_style
|
|
65
|
-
break
|
|
66
|
-
end
|
|
67
|
-
end
|
|
22
|
+
object.docstring.tags
|
|
23
|
+
.select { |tag| validated_tags.include?(tag.tag_name) }
|
|
24
|
+
.each do |tag|
|
|
25
|
+
next unless tag.types
|
|
26
|
+
|
|
27
|
+
tag.types.each do |type_str|
|
|
28
|
+
detected_style = nil
|
|
29
|
+
|
|
30
|
+
# Check for Hash<...> syntax (angle brackets)
|
|
31
|
+
if type_str =~ /Hash<.*>/
|
|
32
|
+
detected_style = 'short'
|
|
33
|
+
# Check for Hash{...} syntax (curly braces)
|
|
34
|
+
elsif type_str =~ /Hash\{.*\}/
|
|
35
|
+
detected_style = 'long'
|
|
36
|
+
# Check for {...} syntax without Hash prefix
|
|
37
|
+
elsif type_str =~ /^\{.*\}$/
|
|
38
|
+
detected_style = 'short'
|
|
68
39
|
end
|
|
69
40
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
41
|
+
# Report violations based on enforced style
|
|
42
|
+
if detected_style && detected_style != style
|
|
43
|
+
collector.puts "#{object.file}:#{object.line}: #{object.title}"
|
|
44
|
+
collector.puts "#{tag.tag_name}|#{type_str}|#{detected_style}"
|
|
45
|
+
break
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
73
49
|
end
|
|
74
50
|
|
|
51
|
+
private
|
|
52
|
+
|
|
75
53
|
# Gets the enforced collection style from configuration
|
|
76
54
|
# @return [String] 'long' or 'short'
|
|
77
55
|
def enforced_style
|
|
78
56
|
config_or_default('EnforcedStyle')
|
|
79
57
|
end
|
|
80
|
-
|
|
81
|
-
# Array of tag names to validate, formatted for YARD query
|
|
82
|
-
# @return [String] Ruby array literal string
|
|
83
|
-
def validated_tags_array
|
|
84
|
-
tags = config_or_default('ValidatedTags')
|
|
85
|
-
"[#{tags.map { |t| "\"#{t}\"" }.join(',')}]"
|
|
86
|
-
end
|
|
87
58
|
end
|
|
88
59
|
end
|
|
89
60
|
end
|