leftovers 0.8.0 → 0.9.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.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/README.md +7 -7
  4. data/docs/Configuration.md +141 -32
  5. data/docs/Custom-Precompilers.md +6 -0
  6. data/leftovers.gemspec +2 -1
  7. data/lib/config/actioncable.yml +36 -0
  8. data/lib/config/actionmailbox.yml +28 -0
  9. data/lib/config/actionmailer.yml +87 -11
  10. data/lib/config/actionpack.yml +130 -34
  11. data/lib/config/actiontext.yml +56 -0
  12. data/lib/config/actionview.yml +194 -44
  13. data/lib/config/activejob.yml +15 -8
  14. data/lib/config/activemodel.yml +175 -18
  15. data/lib/config/activerecord.yml +397 -86
  16. data/lib/config/activestorage.yml +26 -0
  17. data/lib/config/activesupport.yml +167 -24
  18. data/lib/config/leftovers.yml +48 -0
  19. data/lib/config/rails.yml +7 -3
  20. data/lib/config/railties.yml +7 -0
  21. data/lib/config/ruby.yml +438 -83
  22. data/lib/config/test-unit.yml +8 -0
  23. data/lib/leftovers/ast/array_node.rb +12 -0
  24. data/lib/leftovers/ast/block_node.rb +12 -0
  25. data/lib/leftovers/ast/builder.rb +24 -5
  26. data/lib/leftovers/ast/casgn_node.rb +20 -0
  27. data/lib/leftovers/ast/const_node.rb +15 -0
  28. data/lib/leftovers/ast/def_node.rb +15 -0
  29. data/lib/leftovers/ast/defs_node.rb +15 -0
  30. data/lib/leftovers/ast/false_node.rb +24 -0
  31. data/lib/leftovers/ast/has_arguments.rb +31 -0
  32. data/lib/leftovers/ast/hash_node.rb +17 -0
  33. data/lib/leftovers/ast/module_node.rb +16 -0
  34. data/lib/leftovers/ast/nil_node.rb +23 -0
  35. data/lib/leftovers/ast/node.rb +33 -90
  36. data/lib/leftovers/ast/numeric_node.rb +22 -0
  37. data/lib/leftovers/ast/send_node.rb +25 -0
  38. data/lib/leftovers/ast/str_node.rb +24 -0
  39. data/lib/leftovers/ast/sym_node.rb +25 -0
  40. data/lib/leftovers/ast/true_node.rb +24 -0
  41. data/lib/leftovers/ast/var_node.rb +14 -0
  42. data/lib/leftovers/ast/vasgn_node.rb +20 -0
  43. data/lib/leftovers/ast.rb +18 -0
  44. data/lib/leftovers/cli.rb +7 -1
  45. data/lib/leftovers/comparable_instance.rb +18 -0
  46. data/lib/leftovers/config_loader/argument_position_schema.rb +3 -1
  47. data/lib/leftovers/config_loader/array_schema.rb +53 -0
  48. data/lib/leftovers/config_loader/document_schema.rb +3 -2
  49. data/lib/leftovers/config_loader/dynamic_schema.rb +1 -0
  50. data/lib/leftovers/config_loader/has_value_schema.rb +4 -0
  51. data/lib/leftovers/config_loader/keyword_argument_schema.rb +13 -0
  52. data/lib/leftovers/config_loader/regexp_schema.rb +27 -0
  53. data/lib/leftovers/config_loader/rule_pattern_schema.rb +2 -0
  54. data/lib/leftovers/config_loader/scalar_value_schema.rb +8 -0
  55. data/lib/leftovers/config_loader/schema.rb +10 -0
  56. data/lib/leftovers/config_loader/string_enum_schema.rb +1 -1
  57. data/lib/leftovers/config_loader/string_pattern_schema.rb +1 -1
  58. data/lib/leftovers/config_loader/transform_schema.rb +12 -6
  59. data/lib/leftovers/config_loader/value_matcher_condition_schema.rb +13 -0
  60. data/lib/leftovers/config_loader/value_matcher_schema.rb +4 -1
  61. data/lib/leftovers/config_loader/value_or_array_schema.rb +2 -34
  62. data/lib/leftovers/config_loader/value_processor_schema.rb +2 -2
  63. data/lib/leftovers/config_loader.rb +11 -4
  64. data/lib/leftovers/definition_collection.rb +37 -0
  65. data/lib/leftovers/definition_node_set.rb +10 -2
  66. data/lib/leftovers/file.rb +1 -1
  67. data/lib/leftovers/file_collector/comments_processor.rb +1 -1
  68. data/lib/leftovers/file_collector/node_processor.rb +7 -7
  69. data/lib/leftovers/file_collector.rb +26 -32
  70. data/lib/leftovers/file_list.rb +3 -2
  71. data/lib/leftovers/matcher_builders/and.rb +26 -9
  72. data/lib/leftovers/matcher_builders/node.rb +32 -20
  73. data/lib/leftovers/matcher_builders/node_has_keyword_argument.rb +3 -1
  74. data/lib/leftovers/matcher_builders/node_pair_key.rb +16 -0
  75. data/lib/leftovers/matcher_builders/node_type.rb +9 -9
  76. data/lib/leftovers/matcher_builders/node_value.rb +23 -9
  77. data/lib/leftovers/matcher_builders/or.rb +22 -7
  78. data/lib/leftovers/matcher_builders/path.rb +3 -1
  79. data/lib/leftovers/matcher_builders.rb +1 -1
  80. data/lib/leftovers/matchers/all.rb +4 -0
  81. data/lib/leftovers/matchers/and.rb +4 -0
  82. data/lib/leftovers/matchers/any.rb +2 -0
  83. data/lib/leftovers/matchers/node_has_any_keyword_argument.rb +7 -4
  84. data/lib/leftovers/matchers/node_has_any_positional_argument_with_value.rb +5 -4
  85. data/lib/leftovers/matchers/node_has_positional_argument.rb +5 -1
  86. data/lib/leftovers/matchers/node_has_positional_argument_with_value.rb +6 -1
  87. data/lib/leftovers/matchers/node_has_receiver.rb +4 -0
  88. data/lib/leftovers/matchers/node_is_proc.rb +13 -0
  89. data/lib/leftovers/matchers/node_name.rb +9 -3
  90. data/lib/leftovers/matchers/node_pair_key.rb +23 -0
  91. data/lib/leftovers/matchers/node_pair_value.rb +7 -3
  92. data/lib/leftovers/matchers/node_path.rb +7 -3
  93. data/lib/leftovers/matchers/node_privacy.rb +7 -3
  94. data/lib/leftovers/matchers/node_scalar_value.rb +6 -1
  95. data/lib/leftovers/matchers/node_type.rb +7 -3
  96. data/lib/leftovers/matchers/not.rb +2 -0
  97. data/lib/leftovers/matchers/or.rb +2 -0
  98. data/lib/leftovers/matchers/path.rb +21 -0
  99. data/lib/leftovers/matchers.rb +3 -1
  100. data/lib/leftovers/merged_config.rb +26 -25
  101. data/lib/leftovers/parser.rb +7 -4
  102. data/lib/leftovers/precompilers.rb +5 -5
  103. data/lib/leftovers/processor_builders/action.rb +55 -37
  104. data/lib/leftovers/processor_builders/add_prefix.rb +18 -10
  105. data/lib/leftovers/processor_builders/add_suffix.rb +18 -10
  106. data/lib/leftovers/processor_builders/argument.rb +28 -11
  107. data/lib/leftovers/processor_builders/dynamic.rb +37 -31
  108. data/lib/leftovers/processor_builders/each.rb +82 -10
  109. data/lib/leftovers/processor_builders/itself.rb +2 -2
  110. data/lib/leftovers/processor_builders/keyword.rb +7 -6
  111. data/lib/leftovers/processor_builders/keyword_argument.rb +4 -2
  112. data/lib/leftovers/processor_builders/receiver.rb +13 -0
  113. data/lib/leftovers/processor_builders/transform.rb +55 -44
  114. data/lib/leftovers/processor_builders/transform_chain.rb +1 -1
  115. data/lib/leftovers/processor_builders/transform_set.rb +9 -29
  116. data/lib/leftovers/processor_builders/value.rb +4 -4
  117. data/lib/leftovers/processor_builders.rb +1 -3
  118. data/lib/leftovers/processors/add_call.rb +14 -0
  119. data/lib/leftovers/processors/add_definition_node.rb +16 -0
  120. data/lib/leftovers/processors/add_dynamic_prefix.rb +29 -0
  121. data/lib/leftovers/processors/add_dynamic_suffix.rb +29 -0
  122. data/lib/leftovers/{value_processors → processors}/add_prefix.rb +7 -3
  123. data/lib/leftovers/{value_processors → processors}/add_suffix.rb +7 -3
  124. data/lib/leftovers/processors/append_sym.rb +13 -0
  125. data/lib/leftovers/{value_processors → processors}/camelize.rb +7 -3
  126. data/lib/leftovers/{value_processors → processors}/capitalize.rb +7 -3
  127. data/lib/leftovers/{value_processors → processors}/deconstantize.rb +7 -3
  128. data/lib/leftovers/{value_processors → processors}/delete_after.rb +9 -5
  129. data/lib/leftovers/processors/delete_after_last.rb +26 -0
  130. data/lib/leftovers/processors/delete_before.rb +27 -0
  131. data/lib/leftovers/processors/delete_before_last.rb +27 -0
  132. data/lib/leftovers/{value_processors → processors}/delete_prefix.rb +7 -3
  133. data/lib/leftovers/{value_processors → processors}/delete_suffix.rb +7 -3
  134. data/lib/leftovers/{value_processors → processors}/demodulize.rb +7 -3
  135. data/lib/leftovers/{value_processors → processors}/downcase.rb +7 -3
  136. data/lib/leftovers/processors/each.rb +25 -0
  137. data/lib/leftovers/processors/each_for_definition_set.rb +33 -0
  138. data/lib/leftovers/processors/each_keyword.rb +29 -0
  139. data/lib/leftovers/processors/each_keyword_argument.rb +29 -0
  140. data/lib/leftovers/processors/each_positional_argument.rb +27 -0
  141. data/lib/leftovers/processors/each_positional_argument_from.rb +30 -0
  142. data/lib/leftovers/processors/eval.rb +16 -0
  143. data/lib/leftovers/processors/itself.rb +21 -0
  144. data/lib/leftovers/processors/keyword_argument.rb +30 -0
  145. data/lib/leftovers/processors/match_current_node.rb +26 -0
  146. data/lib/leftovers/processors/match_matched_node.rb +26 -0
  147. data/lib/leftovers/{value_processors → processors}/parameterize.rb +7 -3
  148. data/lib/leftovers/{value_processors → processors}/placeholder.rb +5 -4
  149. data/lib/leftovers/{value_processors → processors}/pluralize.rb +7 -3
  150. data/lib/leftovers/{value_processors → processors}/positional_argument.rb +8 -6
  151. data/lib/leftovers/processors/receiver.rb +24 -0
  152. data/lib/leftovers/{value_processors → processors}/replace_value.rb +7 -3
  153. data/lib/leftovers/processors/set_default_privacy.rb +21 -0
  154. data/lib/leftovers/processors/set_privacy.rb +23 -0
  155. data/lib/leftovers/{value_processors → processors}/singularize.rb +7 -3
  156. data/lib/leftovers/{value_processors → processors}/split.rb +8 -4
  157. data/lib/leftovers/{value_processors → processors}/swapcase.rb +7 -3
  158. data/lib/leftovers/{value_processors → processors}/titleize.rb +7 -3
  159. data/lib/leftovers/{value_processors → processors}/underscore.rb +7 -3
  160. data/lib/leftovers/{value_processors → processors}/upcase.rb +7 -3
  161. data/lib/leftovers/processors.rb +49 -0
  162. data/lib/leftovers/version.rb +1 -1
  163. data/lib/leftovers.rb +3 -12
  164. metadata +97 -52
  165. data/lib/leftovers/dynamic_processors/call.rb +0 -22
  166. data/lib/leftovers/dynamic_processors/call_definition.rb +0 -34
  167. data/lib/leftovers/dynamic_processors/definition.rb +0 -27
  168. data/lib/leftovers/dynamic_processors/each.rb +0 -19
  169. data/lib/leftovers/dynamic_processors/null.rb +0 -9
  170. data/lib/leftovers/dynamic_processors/set_default_privacy.rb +0 -18
  171. data/lib/leftovers/dynamic_processors/set_privacy.rb +0 -23
  172. data/lib/leftovers/dynamic_processors.rb +0 -13
  173. data/lib/leftovers/matcher_builders/node_pair_name.rb +0 -18
  174. data/lib/leftovers/matchers/predicate.rb +0 -19
  175. data/lib/leftovers/processor_builders/each_action.rb +0 -51
  176. data/lib/leftovers/processor_builders/each_dynamic.rb +0 -50
  177. data/lib/leftovers/processor_builders/each_for_definition_set.rb +0 -40
  178. data/lib/leftovers/value_processors/add_dynamic_prefix.rb +0 -24
  179. data/lib/leftovers/value_processors/add_dynamic_suffix.rb +0 -24
  180. data/lib/leftovers/value_processors/delete_before.rb +0 -22
  181. data/lib/leftovers/value_processors/each.rb +0 -21
  182. data/lib/leftovers/value_processors/each_for_definition_set.rb +0 -23
  183. data/lib/leftovers/value_processors/each_keyword.rb +0 -27
  184. data/lib/leftovers/value_processors/each_keyword_argument.rb +0 -27
  185. data/lib/leftovers/value_processors/each_positional_argument.rb +0 -25
  186. data/lib/leftovers/value_processors/itself.rb +0 -17
  187. data/lib/leftovers/value_processors/keyword.rb +0 -28
  188. data/lib/leftovers/value_processors/keyword_argument.rb +0 -28
  189. data/lib/leftovers/value_processors/return_definition_node.rb +0 -14
  190. data/lib/leftovers/value_processors/return_sym.rb +0 -14
  191. data/lib/leftovers/value_processors.rb +0 -40
@@ -0,0 +1,16 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Leftovers
4
+ module MatcherBuilders
5
+ module NodePairKey
6
+ def self.build(key_matcher)
7
+ return unless key_matcher
8
+
9
+ ::Leftovers::MatcherBuilders::And.build([
10
+ ::Leftovers::Matchers::NodeType.new(:pair),
11
+ ::Leftovers::Matchers::NodePairKey.new(key_matcher)
12
+ ])
13
+ end
14
+ end
15
+ end
16
+ end
@@ -8,15 +8,15 @@ module Leftovers
8
8
  def self.build(types_pattern) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
9
9
  ::Leftovers::MatcherBuilders::Or.each_or_self(types_pattern) do |type|
10
10
  case type
11
- when 'Symbol' then ::Leftovers::Matchers::NodeType.new(:sym)
12
- when 'String' then ::Leftovers::Matchers::NodeType.new(:str)
13
- when 'Integer' then ::Leftovers::Matchers::NodeType.new(:int)
14
- when 'Float' then ::Leftovers::Matchers::NodeType.new(:float)
15
- when 'Array' then ::Leftovers::Matchers::NodeType.new(:array)
16
- when 'Hash' then ::Leftovers::Matchers::NodeType.new(:hash)
17
- when 'Proc' then ::Leftovers::Matchers::Predicate.new(:proc?)
18
- when 'Method' then ::Leftovers::Matchers::NodeType.new(Set[:send, :csend, :def, :defs])
19
- when 'Constant'
11
+ when :Symbol then ::Leftovers::Matchers::NodeType.new(:sym)
12
+ when :String then ::Leftovers::Matchers::NodeType.new(:str)
13
+ when :Integer then ::Leftovers::Matchers::NodeType.new(:int)
14
+ when :Float then ::Leftovers::Matchers::NodeType.new(:float)
15
+ when :Array then ::Leftovers::Matchers::NodeType.new(:array)
16
+ when :Hash then ::Leftovers::Matchers::NodeType.new(:hash)
17
+ when :Proc then ::Leftovers::Matchers::NodeIsProc
18
+ when :Method then ::Leftovers::Matchers::NodeType.new(Set[:send, :csend, :def, :defs])
19
+ when :Constant
20
20
  ::Leftovers::Matchers::NodeType.new(Set[:const, :class, :module, :casgn])
21
21
  # :nocov:
22
22
  else raise Leftovers::UnexpectedCase, "Unhandled value #{type.inspect}"
@@ -7,8 +7,10 @@ module Leftovers
7
7
  def build(patterns)
8
8
  ::Leftovers::MatcherBuilders::Or.each_or_self(patterns) do |pattern|
9
9
  case pattern
10
- when ::Integer, ::Float, true, false, nil
10
+ when ::Integer, ::Float, true, false
11
+ # matching scalar on nil will fall afoul of compact and each_or_self etc.
11
12
  ::Leftovers::Matchers::NodeScalarValue.new(pattern)
13
+ when :_leftovers_nil_value then ::Leftovers::Matchers::NodeType.new(:nil)
12
14
  when ::String then ::Leftovers::MatcherBuilders::NodeName.build(pattern)
13
15
  when ::Hash then build_from_hash(**pattern)
14
16
  # :nocov:
@@ -20,10 +22,22 @@ module Leftovers
20
22
 
21
23
  private
22
24
 
23
- def build_node_name(match, has_prefix, has_suffix)
24
- ::Leftovers::MatcherBuilders::NodeName.build(
25
- match: match, has_prefix: has_prefix, has_suffix: has_suffix
26
- )
25
+ def build_node_name_matcher(names, match, has_prefix, has_suffix)
26
+ ::Leftovers::MatcherBuilders::Or.build([
27
+ ::Leftovers::MatcherBuilders::NodeName.build(names),
28
+ ::Leftovers::MatcherBuilders::NodeName.build(
29
+ match: match, has_prefix: has_prefix, has_suffix: has_suffix
30
+ )
31
+ ])
32
+ end
33
+
34
+ def build_node_has_argument_matcher(has_arguments, at, has_value)
35
+ ::Leftovers::MatcherBuilders::Or.build([
36
+ ::Leftovers::MatcherBuilders::NodeHasArgument.build(has_arguments),
37
+ ::Leftovers::MatcherBuilders::NodeHasArgument.build(
38
+ at: at, has_value: has_value
39
+ )
40
+ ])
27
41
  end
28
42
 
29
43
  def build_unless(unless_arg)
@@ -35,15 +49,15 @@ module Leftovers
35
49
  end
36
50
 
37
51
  def build_from_hash( # rubocop:disable Metrics/ParameterLists
38
- at: nil, has_value: nil,
39
- match: nil, has_prefix: nil, has_suffix: nil,
52
+ has_arguments: nil, at: nil, has_value: nil,
53
+ names: nil, match: nil, has_prefix: nil, has_suffix: nil,
40
54
  type: nil,
41
55
  has_receiver: nil,
42
56
  unless_arg: nil
43
57
  )
44
58
  ::Leftovers::MatcherBuilders::And.build([
45
- ::Leftovers::MatcherBuilders::NodeHasArgument.build(at: at, has_value: has_value),
46
- build_node_name(match, has_prefix, has_suffix),
59
+ build_node_has_argument_matcher(has_arguments, at, has_value),
60
+ build_node_name_matcher(names, match, has_prefix, has_suffix),
47
61
  ::Leftovers::MatcherBuilders::NodeType.build(type),
48
62
  ::Leftovers::MatcherBuilders::NodeHasReceiver.build(has_receiver),
49
63
  build_unless(unless_arg)
@@ -34,30 +34,45 @@ module Leftovers
34
34
  [*flatten(value.lhs), *flatten(value.rhs)]
35
35
  when ::Leftovers::Matchers::Any
36
36
  flatten(value.matchers)
37
- when Array
37
+ when Array, Set
38
38
  value.flat_map { |v| flatten(v) }
39
39
  else
40
- value
40
+ [value]
41
41
  end
42
42
  end
43
43
 
44
44
  def group_by_compactable(matchers)
45
45
  groups = matchers.group_by do |matcher|
46
46
  case matcher
47
- when ::Integer, ::Symbol then :set
47
+ when ::Integer, ::Symbol, true, false then :set
48
48
  when ::Regexp then :regexp
49
- else :uncompactable
49
+ when nil then :nil
50
+ else matcher.class.to_s.to_sym
50
51
  end
51
52
  end
52
53
 
53
54
  groups.transform_values { |v| Leftovers.unwrap_array(v) }
54
55
  end
55
56
 
56
- def build_grouped(set: nil, regexp: nil, uncompactable: nil)
57
- set = set.to_set if set.is_a?(Array)
57
+ def mergeable?(matcher)
58
+ matcher.respond_to?(:matcher)
59
+ end
60
+
61
+ def build_grouped_for_matcher(matchers)
62
+ return matchers unless matchers.is_a?(Array)
63
+ return matchers unless mergeable?(matchers.first)
64
+
65
+ matchers.first.class.new(build(matchers.map(&:matcher)))
66
+ end
67
+
68
+ def build_grouped(set: nil, regexp: nil, nil: nil, **matcher_classes) # rubocop:disable Lint/UnusedMethodArgument i want to throw away nils
69
+ set = set.to_set.compare_by_identity if set.is_a?(Array)
58
70
  regexp = Regexp.union(regexp) if regexp.is_a?(Array)
71
+ matcher_classes = matcher_classes.each_value.flat_map do |matchers|
72
+ build_grouped_for_matcher(matchers)
73
+ end
59
74
 
60
- [set, regexp].concat(Array(uncompactable)).compact
75
+ [set, regexp].compact.concat(matcher_classes).uniq
61
76
  end
62
77
 
63
78
  def compact(matchers)
@@ -8,7 +8,9 @@ module Leftovers
8
8
  def self.build(path_pattern)
9
9
  return if path_pattern.nil? || path_pattern.empty?
10
10
 
11
- ::FastIgnore.new(include_rules: path_pattern, gitignore: false, root: Leftovers.pwd)
11
+ ::Leftovers::Matchers::Path.new(
12
+ ::FastIgnore.new(include_rules: path_pattern, gitignore: false, root: Leftovers.pwd)
13
+ )
12
14
  end
13
15
  end
14
16
  end
@@ -12,7 +12,7 @@ module Leftovers
12
12
  autoload(:NodeHasPositionalArgument, "#{__dir__}/matcher_builders/node_has_positional_argument")
13
13
  autoload(:NodeHasReceiver, "#{__dir__}/matcher_builders/node_has_receiver")
14
14
  autoload(:NodeName, "#{__dir__}/matcher_builders/node_name")
15
- autoload(:NodePairName, "#{__dir__}/matcher_builders/node_pair_name")
15
+ autoload(:NodePairKey, "#{__dir__}/matcher_builders/node_pair_key")
16
16
  autoload(:NodePairValue, "#{__dir__}/matcher_builders/node_pair_value")
17
17
  autoload(:NodePath, "#{__dir__}/matcher_builders/node_path")
18
18
  autoload(:NodePrivacy, "#{__dir__}/matcher_builders/node_privacy")
@@ -3,6 +3,10 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class All
6
+ include ComparableInstance
7
+
8
+ attr_reader :matchers
9
+
6
10
  def initialize(matchers)
7
11
  @matchers = matchers
8
12
 
@@ -3,6 +3,10 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class And
6
+ include ComparableInstance
7
+
8
+ attr_reader :lhs, :rhs
9
+
6
10
  def initialize(lhs, rhs)
7
11
  @lhs = lhs
8
12
  @rhs = rhs
@@ -3,6 +3,8 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class Any
6
+ include ComparableInstance
7
+
6
8
  attr_reader :matchers
7
9
 
8
10
  def initialize(matchers)
@@ -3,17 +3,20 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeHasAnyKeywordArgument
6
- def initialize(pair_matcher)
7
- @pair_matcher = pair_matcher
6
+ include ComparableInstance
7
+
8
+ attr_reader :matcher
9
+
10
+ def initialize(matcher)
11
+ @matcher = matcher
8
12
 
9
13
  freeze
10
14
  end
11
15
 
12
16
  def ===(node)
13
17
  kwargs = node.kwargs
14
- return false unless kwargs
15
18
 
16
- kwargs.children.any?(@pair_matcher)
19
+ kwargs.children.any?(@matcher) if kwargs # rubocop:disable Style/SafeNavigation because there are multiple steps and this should be a configuration option
17
20
  end
18
21
 
19
22
  freeze
@@ -3,6 +3,10 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeHasAnyPositionalArgumentWithValue
6
+ include ComparableInstance
7
+
8
+ attr_reader :matcher
9
+
6
10
  def initialize(matcher)
7
11
  @matcher = matcher
8
12
 
@@ -10,10 +14,7 @@ module Leftovers
10
14
  end
11
15
 
12
16
  def ===(node)
13
- args = node.positional_arguments
14
- return false unless args
15
-
16
- args.any?(@matcher)
17
+ node.positional_arguments&.any?(@matcher)
17
18
  end
18
19
 
19
20
  freeze
@@ -3,6 +3,8 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeHasPositionalArgument
6
+ include ComparableInstance
7
+
6
8
  def initialize(position)
7
9
  @position = position
8
10
 
@@ -10,7 +12,9 @@ module Leftovers
10
12
  end
11
13
 
12
14
  def ===(node)
13
- node.positional_arguments[@position]
15
+ args = node.positional_arguments
16
+
17
+ args.length > @position if args
14
18
  end
15
19
 
16
20
  freeze
@@ -3,6 +3,8 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeHasPositionalArgumentWithValue
6
+ include ComparableInstance
7
+
6
8
  def initialize(position, matcher)
7
9
  @position = position
8
10
  @matcher = matcher
@@ -11,7 +13,10 @@ module Leftovers
11
13
  end
12
14
 
13
15
  def ===(node)
14
- value_node = node.positional_arguments[@position]
16
+ args = node.positional_arguments
17
+ return unless args
18
+
19
+ value_node = args[@position]
15
20
  @matcher === value_node if value_node
16
21
  end
17
22
 
@@ -3,6 +3,10 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeHasReceiver
6
+ include ComparableInstance
7
+
8
+ attr_reader :matcher
9
+
6
10
  def initialize(matcher)
7
11
  @matcher = matcher
8
12
 
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ module Matchers
5
+ module NodeIsProc
6
+ def self.===(node)
7
+ node.proc?
8
+ end
9
+
10
+ freeze
11
+ end
12
+ end
13
+ end
@@ -3,14 +3,20 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeName
6
- def initialize(name_matcher)
7
- @name_matcher = name_matcher
6
+ include ComparableInstance
7
+
8
+ attr_reader :matcher
9
+
10
+ def initialize(matcher)
11
+ @matcher = matcher
8
12
 
9
13
  freeze
10
14
  end
11
15
 
12
16
  def ===(node)
13
- @name_matcher === node.name
17
+ name = node.name
18
+
19
+ @matcher === name if name
14
20
  end
15
21
 
16
22
  freeze
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ module Matchers
5
+ class NodePairKey
6
+ include ComparableInstance
7
+
8
+ attr_reader :matcher
9
+
10
+ def initialize(matcher)
11
+ @matcher = matcher
12
+
13
+ freeze
14
+ end
15
+
16
+ def ===(node)
17
+ @matcher === node.first
18
+ end
19
+
20
+ freeze
21
+ end
22
+ end
23
+ end
@@ -3,14 +3,18 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodePairValue
6
- def initialize(value_matcher)
7
- @value_matcher = value_matcher
6
+ include ComparableInstance
7
+
8
+ attr_reader :matcher
9
+
10
+ def initialize(matcher)
11
+ @matcher = matcher
8
12
 
9
13
  freeze
10
14
  end
11
15
 
12
16
  def ===(node)
13
- @value_matcher === node.second
17
+ @matcher === node.second
14
18
  end
15
19
 
16
20
  freeze
@@ -3,14 +3,18 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodePath
6
- def initialize(path_matcher)
7
- @path_matcher = path_matcher
6
+ include ComparableInstance
7
+
8
+ attr_reader :matcher
9
+
10
+ def initialize(matcher)
11
+ @matcher = matcher
8
12
 
9
13
  freeze
10
14
  end
11
15
 
12
16
  def ===(node)
13
- @path_matcher === node.path
17
+ @matcher === node.path
14
18
  end
15
19
 
16
20
  freeze
@@ -3,14 +3,18 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodePrivacy
6
- def initialize(privacy_matcher)
7
- @privacy_matcher = privacy_matcher
6
+ include ComparableInstance
7
+
8
+ attr_reader :matcher
9
+
10
+ def initialize(matcher)
11
+ @matcher = matcher
8
12
 
9
13
  freeze
10
14
  end
11
15
 
12
16
  def ===(node)
13
- @privacy_matcher === node.privacy
17
+ @matcher === node.privacy
14
18
  end
15
19
 
16
20
  freeze
@@ -3,6 +3,10 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeScalarValue
6
+ include ComparableInstance
7
+
8
+ attr_reader :matcher
9
+
6
10
  def initialize(matcher)
7
11
  @matcher = matcher
8
12
 
@@ -10,7 +14,8 @@ module Leftovers
10
14
  end
11
15
 
12
16
  def ===(node)
13
- return false unless node.scalar?
17
+ # can't just check to_scalar_value, it might be false/nil on purpose.
18
+ return unless node.scalar?
14
19
 
15
20
  @matcher === node.to_scalar_value
16
21
  end
@@ -3,14 +3,18 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class NodeType
6
- def initialize(type_matcher)
7
- @type_matcher = type_matcher
6
+ include ComparableInstance
7
+
8
+ attr_reader :matcher
9
+
10
+ def initialize(matcher)
11
+ @matcher = matcher
8
12
 
9
13
  freeze
10
14
  end
11
15
 
12
16
  def ===(node)
13
- @type_matcher === node.type
17
+ @matcher === node.type
14
18
  end
15
19
 
16
20
  freeze
@@ -3,6 +3,8 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class Not
6
+ include ComparableInstance
7
+
6
8
  def initialize(matcher)
7
9
  @matcher = matcher
8
10
 
@@ -3,6 +3,8 @@
3
3
  module Leftovers
4
4
  module Matchers
5
5
  class Or
6
+ include ComparableInstance
7
+
6
8
  attr_reader :lhs, :rhs
7
9
 
8
10
  def initialize(lhs, rhs)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ module Matchers
5
+ class Path
6
+ include ComparableInstance
7
+
8
+ def initialize(fast_ignore)
9
+ @fast_ignore = fast_ignore
10
+
11
+ freeze
12
+ end
13
+
14
+ def ===(path)
15
+ @fast_ignore.allowed?(path, exists: true, directory: false)
16
+ end
17
+
18
+ freeze
19
+ end
20
+ end
21
+ end
@@ -16,7 +16,9 @@ module Leftovers
16
16
  )
17
17
  autoload(:NodeHasPositionalArgument, "#{__dir__}/matchers/node_has_positional_argument")
18
18
  autoload(:NodeHasReceiver, "#{__dir__}/matchers/node_has_receiver")
19
+ autoload(:NodeIsProc, "#{__dir__}/matchers/node_is_proc")
19
20
  autoload(:NodeName, "#{__dir__}/matchers/node_name")
21
+ autoload(:NodePairKey, "#{__dir__}/matchers/node_pair_key")
20
22
  autoload(:NodePairValue, "#{__dir__}/matchers/node_pair_value")
21
23
  autoload(:NodePath, "#{__dir__}/matchers/node_path")
22
24
  autoload(:NodePrivacy, "#{__dir__}/matchers/node_privacy")
@@ -24,6 +26,6 @@ module Leftovers
24
26
  autoload(:NodeType, "#{__dir__}/matchers/node_type")
25
27
  autoload(:Not, "#{__dir__}/matchers/not")
26
28
  autoload(:Or, "#{__dir__}/matchers/or")
27
- autoload(:Predicate, "#{__dir__}/matchers/predicate")
29
+ autoload(:Path, "#{__dir__}/matchers/path")
28
30
  end
29
31
  end
@@ -5,12 +5,23 @@ require 'fast_ignore'
5
5
 
6
6
  module Leftovers
7
7
  class MergedConfig
8
+ MEMOIZED_IVARS = %i{
9
+ @exclude_paths
10
+ @include_paths
11
+ @test_paths
12
+ @precompilers
13
+ @dynamic
14
+ @keep
15
+ @test_only
16
+ }.freeze
17
+
8
18
  def initialize(load_defaults: false)
9
19
  @configs = []
10
20
  @loaded_configs = Set.new
11
21
  return unless load_defaults
12
22
 
13
23
  self << :ruby
24
+ self << :leftovers
14
25
  self << project_config
15
26
  self << project_todo
16
27
  load_bundled_gem_config
@@ -27,30 +38,6 @@ module Leftovers
27
38
  require_requires(config)
28
39
  end
29
40
 
30
- def project_config
31
- Leftovers::Config.new(:'.leftovers.yml', path: Leftovers.pwd + '.leftovers.yml')
32
- end
33
-
34
- def project_todo
35
- Leftovers::Config.new(:'.leftovers_todo.yml', path: Leftovers.pwd + '.leftovers_todo.yml')
36
- end
37
-
38
- MEMOIZED_IVARS = %i{
39
- @exclude_paths
40
- @include_paths
41
- @test_paths
42
- @precompilers
43
- @dynamic
44
- @keep
45
- @test_only
46
- }.freeze
47
-
48
- def unmemoize
49
- MEMOIZED_IVARS.each do |ivar|
50
- remove_instance_variable(ivar) if instance_variable_get(ivar)
51
- end
52
- end
53
-
54
41
  def exclude_paths
55
42
  @exclude_paths ||= @configs.flat_map(&:exclude_paths)
56
43
  end
@@ -68,7 +55,7 @@ module Leftovers
68
55
  end
69
56
 
70
57
  def dynamic
71
- @dynamic ||= ::Leftovers::ProcessorBuilders::EachDynamic.build(@configs.map(&:dynamic))
58
+ @dynamic ||= ::Leftovers::ProcessorBuilders::Each.build(@configs.map(&:dynamic))
72
59
  end
73
60
 
74
61
  def keep
@@ -81,6 +68,20 @@ module Leftovers
81
68
 
82
69
  private
83
70
 
71
+ def project_config
72
+ Leftovers::Config.new(:'.leftovers.yml', path: Leftovers.pwd + '.leftovers.yml')
73
+ end
74
+
75
+ def project_todo
76
+ Leftovers::Config.new(:'.leftovers_todo.yml', path: Leftovers.pwd + '.leftovers_todo.yml')
77
+ end
78
+
79
+ def unmemoize
80
+ MEMOIZED_IVARS.each do |ivar|
81
+ remove_instance_variable(ivar) if instance_variable_get(ivar)
82
+ end
83
+ end
84
+
84
85
  def require_requires(config)
85
86
  config.requires.each do |req|
86
87
  if req.is_a?(Hash) && req[:quiet]
@@ -10,14 +10,17 @@ module Leftovers
10
10
  # but with our parser
11
11
  def parse_with_comments(string, file = '(string)', line = 1)
12
12
  PARSER.reset
13
- source_buffer = ::Parser::CurrentRuby.send(
14
- :setup_source_buffer, file, line, string, PARSER.default_encoding
15
- )
16
- PARSER.parse_with_comments(source_buffer)
13
+ PARSER.parse_with_comments(new_source_buffer(string, file, line))
17
14
  end
18
15
 
19
16
  private
20
17
 
18
+ def new_source_buffer(string, file, line)
19
+ ::Parser::CurrentRuby.send(
20
+ :setup_source_buffer, file, line, string, PARSER.default_encoding
21
+ )
22
+ end
23
+
21
24
  # mostly copied from https://github.com/whitequark/parser/blob/master/lib/parser/base.rb
22
25
  # but with our builder
23
26
  def parser