ruby-merge 7.1.1 → 7.1.3
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
- checksums.yaml.gz.sig +0 -0
- data/README.md +2 -10
- data/lib/ruby/merge/provider.rb +77 -0
- data/lib/ruby/merge/version.rb +1 -1
- data/lib/ruby/merge.rb +3 -11
- data/sig/ruby/merge.rbs +13 -0
- data.tar.gz.sig +0 -0
- metadata +31 -27
- metadata.gz.sig +0 -0
- data/lib/ruby/merge/block_binding_support.rb +0 -22
- data/lib/ruby/merge/block_directive_detector.rb +0 -208
- data/lib/ruby/merge/doc_comment_support.rb +0 -97
- data/lib/ruby/merge/gemspec_support.rb +0 -32
- data/lib/ruby/merge/magic_comment_support.rb +0 -128
- data/lib/ruby/merge/method_similarity.rb +0 -75
- data/lib/ruby/merge/nocov_node_base.rb +0 -75
- data/lib/ruby/merge/nocov_wrapper_base.rb +0 -46
- data/lib/ruby/merge/rescue_semantics.rb +0 -209
- data/lib/ruby/merge/scaffold_chunk_support.rb +0 -74
- data/lib/ruby/merge/signature_support.rb +0 -97
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'block_binding_support'
|
|
4
|
-
|
|
5
|
-
module Ruby
|
|
6
|
-
module Merge
|
|
7
|
-
module GemspecSupport
|
|
8
|
-
GEMSPEC_VAR_PLACEHOLDER = :__gemspec_var__
|
|
9
|
-
|
|
10
|
-
module_function
|
|
11
|
-
|
|
12
|
-
def effective_receiver(receiver, gemspec_block_var)
|
|
13
|
-
BlockBindingSupport.effective_receiver(receiver, gemspec_block_var, placeholder: GEMSPEC_VAR_PLACEHOLDER)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def preferred_block_var(template_var, dest_var)
|
|
17
|
-
BlockBindingSupport.preferred_block_var(template_var, dest_var)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def merged_block_var(var, preferred_var)
|
|
21
|
-
BlockBindingSupport.merged_block_var(var, preferred_var)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def opening_line_with_preferred_block_var(opening_line, dest_var:, preferred_var:, node_preference:)
|
|
25
|
-
return opening_line unless preferred_var && dest_var && dest_var != preferred_var
|
|
26
|
-
return opening_line unless node_preference == :destination
|
|
27
|
-
|
|
28
|
-
opening_line.sub("|#{dest_var}|", "|#{preferred_var}|")
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Ruby
|
|
4
|
-
module Merge
|
|
5
|
-
# Ruby magic comment detection and file-header prefix handling.
|
|
6
|
-
module MagicCommentSupport
|
|
7
|
-
MAGIC_COMMENT_PATTERNS = {
|
|
8
|
-
frozen_string_literal: /^frozen_string_literal:\s*(true|false)$/i,
|
|
9
|
-
encoding: /^(encoding|coding):\s*\S+$/i,
|
|
10
|
-
warn_indent: /^warn_indent:\s*(true|false)$/i,
|
|
11
|
-
shareable_constant_value: /^shareable_constant_value:\s*\S+$/i
|
|
12
|
-
}.freeze
|
|
13
|
-
|
|
14
|
-
module_function
|
|
15
|
-
|
|
16
|
-
def magic_comment_type_for_text(text)
|
|
17
|
-
stripped = DocCommentSupport.normalize_comment_content(text)
|
|
18
|
-
|
|
19
|
-
MAGIC_COMMENT_PATTERNS.each do |type, pattern|
|
|
20
|
-
return type if stripped.match?(pattern)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
nil
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def comment_only_prefix_info(lines)
|
|
27
|
-
entries = []
|
|
28
|
-
suppressed_line_nums = Set.new
|
|
29
|
-
duplicate_magic_line_nums = Set.new
|
|
30
|
-
|
|
31
|
-
if shebang_line?(lines.first)
|
|
32
|
-
entries << { line_num: 1, text: lines.first.to_s, kind: :shebang }
|
|
33
|
-
suppressed_line_nums << 1
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
header_magic_types = header_magic_comment_types_for_lines(lines)
|
|
37
|
-
seen_magic_types = Set.new
|
|
38
|
-
|
|
39
|
-
header_magic_types.keys.sort.each do |line_num|
|
|
40
|
-
magic_type = header_magic_types[line_num]
|
|
41
|
-
suppressed_line_nums << line_num
|
|
42
|
-
entries << { line_num: line_num, text: lines[line_num - 1].to_s.chomp, kind: :magic }
|
|
43
|
-
|
|
44
|
-
if seen_magic_types.include?(magic_type)
|
|
45
|
-
duplicate_magic_line_nums << line_num
|
|
46
|
-
else
|
|
47
|
-
seen_magic_types << magic_type
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
if header_magic_types.any?
|
|
52
|
-
blank_line_num = header_magic_types.keys.max + 1
|
|
53
|
-
|
|
54
|
-
while blank_line_num <= lines.length && lines[blank_line_num - 1].to_s.rstrip.empty?
|
|
55
|
-
entries << { line_num: blank_line_num, text: lines[blank_line_num - 1].to_s, kind: :blank }
|
|
56
|
-
suppressed_line_nums << blank_line_num
|
|
57
|
-
blank_line_num += 1
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
{
|
|
62
|
-
entries: entries,
|
|
63
|
-
suppressed_line_nums: suppressed_line_nums,
|
|
64
|
-
duplicate_magic_line_nums: duplicate_magic_line_nums,
|
|
65
|
-
header_magic_comment_types: header_magic_types
|
|
66
|
-
}
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def header_magic_comment_types_for_lines(lines)
|
|
70
|
-
types = {}
|
|
71
|
-
index = shebang_line?(lines.first) ? 1 : 0
|
|
72
|
-
previous_line_num = shebang_line?(lines.first) ? 1 : nil
|
|
73
|
-
|
|
74
|
-
while index < lines.length
|
|
75
|
-
line_num = index + 1
|
|
76
|
-
stripped = lines[index].to_s.rstrip
|
|
77
|
-
break if stripped.empty?
|
|
78
|
-
|
|
79
|
-
expected_line_num = previous_line_num ? previous_line_num + 1 : 1
|
|
80
|
-
break unless line_num == expected_line_num
|
|
81
|
-
|
|
82
|
-
magic_type = magic_comment_type_for_text(stripped)
|
|
83
|
-
break unless magic_type
|
|
84
|
-
|
|
85
|
-
types[line_num] = magic_type
|
|
86
|
-
previous_line_num = line_num
|
|
87
|
-
index += 1
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
types
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def prefix_comment_line_numbers_for_comments(comments)
|
|
94
|
-
prefix_line_nums = Set.new
|
|
95
|
-
previous_line_num = nil
|
|
96
|
-
index = 0
|
|
97
|
-
|
|
98
|
-
if shebang_comment?(comments.first)
|
|
99
|
-
prefix_line_nums << 1
|
|
100
|
-
previous_line_num = 1
|
|
101
|
-
index = 1
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
while index < comments.length
|
|
105
|
-
comment = comments[index]
|
|
106
|
-
line_num = comment.location.start_line
|
|
107
|
-
expected_line_num = previous_line_num ? previous_line_num + 1 : 1
|
|
108
|
-
break unless line_num == expected_line_num
|
|
109
|
-
break unless magic_comment_type_for_text(comment.slice)
|
|
110
|
-
|
|
111
|
-
prefix_line_nums << line_num
|
|
112
|
-
previous_line_num = line_num
|
|
113
|
-
index += 1
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
prefix_line_nums
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def shebang_line?(line)
|
|
120
|
-
line.to_s.start_with?('#!')
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def shebang_comment?(comment)
|
|
124
|
-
comment&.slice&.start_with?('#!')
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
end
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Ruby
|
|
4
|
-
module Merge
|
|
5
|
-
class MethodSimilarity
|
|
6
|
-
DEFAULT_NAME_WEIGHT = 0.7
|
|
7
|
-
DEFAULT_PARAMS_WEIGHT = 0.3
|
|
8
|
-
|
|
9
|
-
attr_reader :name_weight, :params_weight
|
|
10
|
-
|
|
11
|
-
def initialize(name_weight: DEFAULT_NAME_WEIGHT, params_weight: DEFAULT_PARAMS_WEIGHT)
|
|
12
|
-
@name_weight = name_weight
|
|
13
|
-
@params_weight = params_weight
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def call(template_name:, template_params:, dest_name:, dest_params:)
|
|
17
|
-
name_score = string_similarity(template_name.to_s, dest_name.to_s)
|
|
18
|
-
param_score = param_similarity(Array(template_params), Array(dest_params))
|
|
19
|
-
|
|
20
|
-
(name_score * name_weight) + (param_score * params_weight)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def param_similarity(template_params, dest_params)
|
|
24
|
-
return 1.0 if template_params.empty? && dest_params.empty?
|
|
25
|
-
return 0.0 if template_params.empty? || dest_params.empty?
|
|
26
|
-
|
|
27
|
-
common = (template_params & dest_params).size
|
|
28
|
-
total = [template_params.size, dest_params.size].max
|
|
29
|
-
count_ratio = [template_params.size, dest_params.size].min.to_f / total
|
|
30
|
-
name_match_ratio = common.to_f / total
|
|
31
|
-
|
|
32
|
-
(name_match_ratio * 0.7) + (count_ratio * 0.3)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def string_similarity(str1, str2)
|
|
36
|
-
return 1.0 if str1 == str2
|
|
37
|
-
return 0.0 if str1.empty? || str2.empty?
|
|
38
|
-
|
|
39
|
-
distance = levenshtein_distance(str1, str2)
|
|
40
|
-
max_len = [str1.length, str2.length].max
|
|
41
|
-
|
|
42
|
-
1.0 - (distance.to_f / max_len)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def levenshtein_distance(str1, str2)
|
|
46
|
-
return str2.length if str1.empty?
|
|
47
|
-
return str1.length if str2.empty?
|
|
48
|
-
|
|
49
|
-
str1, str2 = str2, str1 if str1.length > str2.length
|
|
50
|
-
|
|
51
|
-
m = str1.length
|
|
52
|
-
n = str2.length
|
|
53
|
-
previous_row = (0..m).to_a
|
|
54
|
-
current_row = Array.new(m + 1, 0)
|
|
55
|
-
|
|
56
|
-
(1..n).each do |j|
|
|
57
|
-
current_row[0] = j
|
|
58
|
-
|
|
59
|
-
(1..m).each do |i|
|
|
60
|
-
cost = str1[i - 1] == str2[j - 1] ? 0 : 1
|
|
61
|
-
current_row[i] = [
|
|
62
|
-
previous_row[i] + 1,
|
|
63
|
-
current_row[i - 1] + 1,
|
|
64
|
-
previous_row[i - 1] + cost
|
|
65
|
-
].min
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
previous_row, current_row = current_row, previous_row
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
previous_row[m]
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Ruby
|
|
4
|
-
module Merge
|
|
5
|
-
# Shared Ruby synthetic node for a balanced coverage-exclusion block.
|
|
6
|
-
#
|
|
7
|
-
# Parser-specific merge gems subclass this when they need native AST location
|
|
8
|
-
# or comment attachment behavior. The structural Ruby semantics live here:
|
|
9
|
-
# nocov blocks follow file preference and match by their inner content.
|
|
10
|
-
class NocovNodeBase
|
|
11
|
-
include Ast::Merge::BlockDirective
|
|
12
|
-
|
|
13
|
-
InvalidStructureError = Class.new(StandardError)
|
|
14
|
-
|
|
15
|
-
Location = Struct.new(:start_line, :end_line) do
|
|
16
|
-
def cover?(line)
|
|
17
|
-
(start_line..end_line).cover?(line)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
attr_reader :start_line, :end_line, :nodes, :analysis, :start_marker, :close_marker
|
|
22
|
-
|
|
23
|
-
def initialize(start_line:, end_line:, analysis:, nodes: [], start_marker: nil, close_marker: nil)
|
|
24
|
-
@start_line = start_line
|
|
25
|
-
@end_line = end_line
|
|
26
|
-
@analysis = analysis
|
|
27
|
-
@nodes = nodes
|
|
28
|
-
@start_marker = start_marker
|
|
29
|
-
@close_marker = close_marker
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def kind = :nocov
|
|
33
|
-
|
|
34
|
-
def children = @nodes
|
|
35
|
-
|
|
36
|
-
def merge_policy = nil
|
|
37
|
-
|
|
38
|
-
def location
|
|
39
|
-
@location ||= Location.new(@start_line, @end_line)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def slice
|
|
43
|
-
return unless @analysis
|
|
44
|
-
|
|
45
|
-
lines = @analysis.lines
|
|
46
|
-
return unless lines
|
|
47
|
-
|
|
48
|
-
lines[(@start_line - 1)..(@end_line - 1)]&.join
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def signature
|
|
52
|
-
return [:NocovNode, nil] if @nodes.empty? || @analysis.nil?
|
|
53
|
-
|
|
54
|
-
if @nodes.length == 1
|
|
55
|
-
@analysis.generate_signature(@nodes.first)
|
|
56
|
-
else
|
|
57
|
-
inner_lines = @analysis.lines && @analysis.lines[@start_line..(@end_line - 2)]
|
|
58
|
-
[:nocov_multi, inner_lines&.map(&:strip)&.join("\n")]
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def merge_type = :nocov_block
|
|
63
|
-
|
|
64
|
-
alias type merge_type
|
|
65
|
-
|
|
66
|
-
def nocov_node? = true
|
|
67
|
-
|
|
68
|
-
def inspect
|
|
69
|
-
"#<#{self.class} lines=#{@start_line}..#{@end_line} nodes=#{@nodes.length}>"
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
alias to_s inspect
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Ruby
|
|
4
|
-
module Merge
|
|
5
|
-
# Shared Ruby wrapper for nodes carrying unbalanced or inline nocov markers.
|
|
6
|
-
#
|
|
7
|
-
# The wrapped parser node remains responsible for structural identity; this
|
|
8
|
-
# wrapper only marks it as a Ruby coverage directive participant.
|
|
9
|
-
class NocovWrapperBase
|
|
10
|
-
include Ast::Merge::BlockDirective
|
|
11
|
-
|
|
12
|
-
attr_reader :node, :merge_type
|
|
13
|
-
|
|
14
|
-
def initialize(node, merge_type = :nocov)
|
|
15
|
-
@node = node
|
|
16
|
-
@merge_type = merge_type
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def kind = :nocov
|
|
20
|
-
def children = []
|
|
21
|
-
def merge_policy = nil
|
|
22
|
-
|
|
23
|
-
def start_line
|
|
24
|
-
@node.location&.start_line
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def end_line
|
|
28
|
-
@node.location&.end_line
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def unwrap = @node
|
|
32
|
-
|
|
33
|
-
def location = @node.location
|
|
34
|
-
|
|
35
|
-
def slice = @node.slice
|
|
36
|
-
|
|
37
|
-
def nocov_wrapper? = true
|
|
38
|
-
def nocov_node? = false
|
|
39
|
-
def block_directive? = true
|
|
40
|
-
|
|
41
|
-
def inspect
|
|
42
|
-
"#<#{self.class} merge_type=#{@merge_type.inspect} node=#{@node.inspect}>"
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Ruby
|
|
4
|
-
module Merge
|
|
5
|
-
class RescueSemantics
|
|
6
|
-
def initialize(source_defined_exception_definitions: [])
|
|
7
|
-
@source_defined_exception_definitions = source_defined_exception_definitions
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def merge_ordered_clause_types(primary_types, secondary_types)
|
|
11
|
-
ordered = primary_types.dup
|
|
12
|
-
|
|
13
|
-
secondary_types.each_with_index do |clause_type, secondary_index|
|
|
14
|
-
next if ordered.include?(clause_type)
|
|
15
|
-
|
|
16
|
-
previous_shared = secondary_types[0...secondary_index].reverse.find { |type| ordered.include?(type) }
|
|
17
|
-
next_shared = secondary_types[(secondary_index + 1)..]&.find { |type| ordered.include?(type) }
|
|
18
|
-
|
|
19
|
-
if previous_shared
|
|
20
|
-
insert_at = ordered.index(previous_shared) + 1
|
|
21
|
-
ordered.insert(insert_at, clause_type)
|
|
22
|
-
elsif next_shared
|
|
23
|
-
insert_at = ordered.index(next_shared)
|
|
24
|
-
ordered.insert(insert_at, clause_type)
|
|
25
|
-
else
|
|
26
|
-
ordered << clause_type
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
ordered
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def canonicalize_rescue_clause_order(clause_types)
|
|
34
|
-
rescue_clause_types = clause_types.select { |clause_type| rescue_clause_type?(clause_type) }
|
|
35
|
-
return clause_types if rescue_clause_types.length < 2
|
|
36
|
-
|
|
37
|
-
ordered_rescue_types = rescue_clause_types.dup
|
|
38
|
-
|
|
39
|
-
if rescue_clause_types.any? { |clause_type| broad_rescue_clause_type?(clause_type) } &&
|
|
40
|
-
rescue_clause_types.any? { |clause_type| !broad_rescue_clause_type?(clause_type) }
|
|
41
|
-
specific_rescue_types = ordered_rescue_types.reject { |clause_type| broad_rescue_clause_type?(clause_type) }
|
|
42
|
-
broad_rescue_types = ordered_rescue_types.select { |clause_type| broad_rescue_clause_type?(clause_type) }
|
|
43
|
-
ordered_rescue_types = specific_rescue_types + broad_rescue_types
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
loop do
|
|
47
|
-
swapped = false
|
|
48
|
-
|
|
49
|
-
(0...(ordered_rescue_types.length - 1)).each do |index|
|
|
50
|
-
left_clause_type = ordered_rescue_types[index]
|
|
51
|
-
right_clause_type = ordered_rescue_types[index + 1]
|
|
52
|
-
next unless broader_rescue_clause_type_than?(left_clause_type, right_clause_type)
|
|
53
|
-
|
|
54
|
-
ordered_rescue_types[index] = right_clause_type
|
|
55
|
-
ordered_rescue_types[index + 1] = left_clause_type
|
|
56
|
-
swapped = true
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
break unless swapped
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
clause_types.map do |clause_type|
|
|
63
|
-
rescue_clause_type?(clause_type) ? ordered_rescue_types.shift : clause_type
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def canonicalize_begin_clause_kind_order(clause_types)
|
|
68
|
-
clause_types.each_with_index
|
|
69
|
-
.sort_by { |(clause_type, index)| [clause_kind_sort_key(clause_type), index] }
|
|
70
|
-
.map(&:first)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def rescue_clause_signature(exception_names)
|
|
74
|
-
normalized_exceptions = Array(exception_names).filter_map { |exception| normalize_exception_name(exception) }
|
|
75
|
-
if normalized_exceptions.empty? || normalized_exceptions == ['StandardError']
|
|
76
|
-
[:standard_error]
|
|
77
|
-
else
|
|
78
|
-
normalized_exceptions.sort
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def rescue_clause_type?(clause_type)
|
|
83
|
-
clause_type.is_a?(Array) && clause_type.first == :rescue_clause
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def broad_rescue_clause_type?(clause_type)
|
|
87
|
-
rescue_clause_type?(clause_type) && clause_type[1] == [:standard_error]
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def clause_kind_sort_key(clause_type)
|
|
91
|
-
return 0 if rescue_clause_type?(clause_type)
|
|
92
|
-
return 1 if clause_type == :else_clause
|
|
93
|
-
return 2 if clause_type == :ensure_clause
|
|
94
|
-
|
|
95
|
-
3
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def normalize_exception_name(exception_name)
|
|
99
|
-
return 'StandardError' if exception_name == :standard_error
|
|
100
|
-
|
|
101
|
-
name = exception_name.to_s.sub(/\A::/, '')
|
|
102
|
-
name.empty? ? nil : name
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def qualify_source_constant_name(constant_name, namespace = nil)
|
|
106
|
-
normalized_name = normalize_exception_name(constant_name)
|
|
107
|
-
return if normalized_name.nil?
|
|
108
|
-
return normalized_name if constant_name.to_s.start_with?('::') || namespace.nil? || namespace.empty?
|
|
109
|
-
|
|
110
|
-
"#{namespace}::#{normalized_name}"
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def source_defined_exception_hierarchy
|
|
114
|
-
@source_defined_exception_hierarchy ||= begin
|
|
115
|
-
definitions = @source_defined_exception_definitions
|
|
116
|
-
defined_names = definitions.map { |definition| definition[:name] }.compact.to_set
|
|
117
|
-
|
|
118
|
-
definitions.each_with_object({}) do |definition, hierarchy|
|
|
119
|
-
next unless definition[:name] && definition[:superclass]
|
|
120
|
-
|
|
121
|
-
superclass_name = if definition[:superclass].to_s.start_with?('::')
|
|
122
|
-
normalize_exception_name(definition[:superclass])
|
|
123
|
-
else
|
|
124
|
-
candidate_name = qualify_source_constant_name(definition[:superclass],
|
|
125
|
-
definition[:namespace])
|
|
126
|
-
defined_names.include?(candidate_name) ? candidate_name : normalize_exception_name(definition[:superclass])
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
hierarchy[definition[:name]] ||= superclass_name if superclass_name
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def resolve_exception_constant(exception_name)
|
|
135
|
-
return ::StandardError if exception_name == :standard_error
|
|
136
|
-
return unless exception_name.is_a?(String) && !exception_name.empty?
|
|
137
|
-
|
|
138
|
-
exception_name.split('::').reject(&:empty?).inject(Object) { |scope, const_name| scope.const_get(const_name) }
|
|
139
|
-
rescue NameError
|
|
140
|
-
nil
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def rescue_clause_exception_names(clause_type)
|
|
144
|
-
return [] unless rescue_clause_type?(clause_type)
|
|
145
|
-
|
|
146
|
-
Array(clause_type[1]).filter_map { |exception_name| normalize_exception_name(exception_name) }
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def rescue_clause_exception_constants(clause_type)
|
|
150
|
-
rescue_clause_exception_names(clause_type).filter_map do |exception_name|
|
|
151
|
-
resolve_exception_constant(exception_name)
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def exception_constant_covers?(covering_constant, covered_constant)
|
|
156
|
-
return true if covering_constant == covered_constant
|
|
157
|
-
|
|
158
|
-
covered_constant < covering_constant
|
|
159
|
-
rescue StandardError
|
|
160
|
-
false
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def source_defined_exception_covers?(covering_name, covered_name)
|
|
164
|
-
normalized_covering = normalize_exception_name(covering_name)
|
|
165
|
-
current_name = normalize_exception_name(covered_name)
|
|
166
|
-
return false if normalized_covering.nil? || current_name.nil?
|
|
167
|
-
return true if normalized_covering == current_name
|
|
168
|
-
|
|
169
|
-
while (current_name = source_defined_exception_hierarchy[current_name])
|
|
170
|
-
return true if current_name == normalized_covering
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
false
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def exception_name_covers?(covering_name, covered_name)
|
|
177
|
-
covering_constant = resolve_exception_constant(covering_name)
|
|
178
|
-
covered_constant = resolve_exception_constant(covered_name)
|
|
179
|
-
|
|
180
|
-
if covering_constant && covered_constant
|
|
181
|
-
exception_constant_covers?(covering_constant, covered_constant)
|
|
182
|
-
else
|
|
183
|
-
source_defined_exception_covers?(covering_name, covered_name)
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
def rescue_clause_covers?(covering_clause_type, covered_clause_type)
|
|
188
|
-
return false unless rescue_clause_type?(covering_clause_type) && rescue_clause_type?(covered_clause_type)
|
|
189
|
-
|
|
190
|
-
covering_names = rescue_clause_exception_names(covering_clause_type)
|
|
191
|
-
covered_names = rescue_clause_exception_names(covered_clause_type)
|
|
192
|
-
return false if covering_names.empty? || covered_names.empty?
|
|
193
|
-
|
|
194
|
-
covered_names.all? do |covered_name|
|
|
195
|
-
covering_names.any? do |covering_name|
|
|
196
|
-
exception_name_covers?(covering_name, covered_name)
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
def broader_rescue_clause_type_than?(left_clause_type, right_clause_type)
|
|
202
|
-
return false unless rescue_clause_type?(left_clause_type) && rescue_clause_type?(right_clause_type)
|
|
203
|
-
|
|
204
|
-
rescue_clause_covers?(left_clause_type, right_clause_type) &&
|
|
205
|
-
!rescue_clause_covers?(right_clause_type, left_clause_type)
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
end
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Ruby
|
|
4
|
-
module Merge
|
|
5
|
-
module ScaffoldChunkSupport
|
|
6
|
-
ChunkSpec = Struct.new(
|
|
7
|
-
:anchor_type,
|
|
8
|
-
:anchor_value,
|
|
9
|
-
:satellite_patterns,
|
|
10
|
-
:jaccard_threshold,
|
|
11
|
-
:max_lookahead,
|
|
12
|
-
:max_lookbehind,
|
|
13
|
-
keyword_init: true
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
BUNDLER_GEM_TASKS_SPEC = ChunkSpec.new(
|
|
17
|
-
anchor_type: :require_call,
|
|
18
|
-
anchor_value: 'bundler/gem_tasks',
|
|
19
|
-
satellite_patterns: [],
|
|
20
|
-
jaccard_threshold: 0.35,
|
|
21
|
-
max_lookahead: 0,
|
|
22
|
-
max_lookbehind: 0
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
RSPEC_SPEC = ChunkSpec.new(
|
|
26
|
-
anchor_type: :require_call,
|
|
27
|
-
anchor_value: 'rspec/core/rake_task',
|
|
28
|
-
satellite_patterns: ['RSpec::Core::RakeTask.new'],
|
|
29
|
-
jaccard_threshold: 0.35,
|
|
30
|
-
max_lookahead: 5,
|
|
31
|
-
max_lookbehind: 2
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
RUBOCOP_SPEC = ChunkSpec.new(
|
|
35
|
-
anchor_type: :require_call,
|
|
36
|
-
anchor_value: 'rubocop/rake_task',
|
|
37
|
-
satellite_patterns: ['RuboCop::RakeTask.new'],
|
|
38
|
-
jaccard_threshold: 0.35,
|
|
39
|
-
max_lookahead: 5,
|
|
40
|
-
max_lookbehind: 2
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
DEFAULT_TASK_SPEC = ChunkSpec.new(
|
|
44
|
-
anchor_type: :task_call,
|
|
45
|
-
anchor_value: 'default',
|
|
46
|
-
satellite_patterns: [],
|
|
47
|
-
jaccard_threshold: 0.35,
|
|
48
|
-
max_lookahead: 0,
|
|
49
|
-
max_lookbehind: 0
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
ALL_SPECS = [BUNDLER_GEM_TASKS_SPEC, RSPEC_SPEC, RUBOCOP_SPEC, DEFAULT_TASK_SPEC].freeze
|
|
53
|
-
|
|
54
|
-
module_function
|
|
55
|
-
|
|
56
|
-
def jaccard_tokens(text)
|
|
57
|
-
text.scan(/[A-Za-z0-9_]+/).to_set
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def jaccard(a_set, b_set)
|
|
61
|
-
union = a_set | b_set
|
|
62
|
-
return 0.0 if union.empty?
|
|
63
|
-
|
|
64
|
-
(a_set & b_set).size.to_f / union.size
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def task_anchor_match?(source, anchor_value, threshold)
|
|
68
|
-
node_tokens = jaccard_tokens(source.to_s)
|
|
69
|
-
pattern_tokens = jaccard_tokens("task #{anchor_value}")
|
|
70
|
-
jaccard(pattern_tokens, node_tokens) >= threshold
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|