leftovers 0.7.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (227) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/README.md +8 -7
  4. data/docs/Configuration.md +187 -93
  5. data/docs/Custom-Precompilers.md +44 -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 -32
  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 +422 -89
  16. data/lib/config/activestorage.yml +26 -0
  17. data/lib/config/activesupport.yml +167 -24
  18. data/lib/config/haml.yml +4 -2
  19. data/lib/config/leftovers.yml +48 -0
  20. data/lib/config/rails.yml +7 -3
  21. data/lib/config/railties.yml +7 -0
  22. data/lib/config/ruby.yml +439 -95
  23. data/lib/config/slim.yml +4 -2
  24. data/lib/config/test-unit.yml +8 -0
  25. data/lib/leftovers/ast/array_node.rb +12 -0
  26. data/lib/leftovers/ast/block_node.rb +12 -0
  27. data/lib/leftovers/ast/builder.rb +24 -5
  28. data/lib/leftovers/ast/casgn_node.rb +20 -0
  29. data/lib/leftovers/ast/const_node.rb +15 -0
  30. data/lib/leftovers/ast/def_node.rb +15 -0
  31. data/lib/leftovers/ast/defs_node.rb +15 -0
  32. data/lib/leftovers/ast/false_node.rb +24 -0
  33. data/lib/leftovers/ast/has_arguments.rb +31 -0
  34. data/lib/leftovers/ast/hash_node.rb +17 -0
  35. data/lib/leftovers/ast/module_node.rb +16 -0
  36. data/lib/leftovers/ast/nil_node.rb +23 -0
  37. data/lib/leftovers/ast/node.rb +33 -90
  38. data/lib/leftovers/ast/numeric_node.rb +22 -0
  39. data/lib/leftovers/ast/send_node.rb +25 -0
  40. data/lib/leftovers/ast/str_node.rb +24 -0
  41. data/lib/leftovers/ast/sym_node.rb +25 -0
  42. data/lib/leftovers/ast/true_node.rb +24 -0
  43. data/lib/leftovers/ast/var_node.rb +14 -0
  44. data/lib/leftovers/ast/vasgn_node.rb +20 -0
  45. data/lib/leftovers/ast.rb +18 -0
  46. data/lib/leftovers/cli.rb +12 -1
  47. data/lib/leftovers/collector.rb +2 -1
  48. data/lib/leftovers/comparable_instance.rb +18 -0
  49. data/lib/leftovers/config.rb +2 -18
  50. data/lib/leftovers/config_loader/argument_position_schema.rb +5 -3
  51. data/lib/leftovers/config_loader/array_schema.rb +53 -0
  52. data/lib/leftovers/config_loader/attribute.rb +36 -10
  53. data/lib/leftovers/config_loader/built_in_precompiler_schema.rb +13 -0
  54. data/lib/leftovers/config_loader/document_schema.rb +33 -5
  55. data/lib/leftovers/config_loader/dynamic_schema.rb +1 -0
  56. data/lib/leftovers/config_loader/has_argument_schema.rb +2 -2
  57. data/lib/leftovers/config_loader/has_receiver_schema.rb +15 -0
  58. data/lib/leftovers/config_loader/has_value_schema.rb +7 -3
  59. data/lib/leftovers/config_loader/inherit_schema_attributes.rb +22 -0
  60. data/lib/leftovers/config_loader/keep_test_only_schema.rb +2 -2
  61. data/lib/leftovers/config_loader/keyword_argument_schema.rb +13 -0
  62. data/lib/leftovers/config_loader/object_schema.rb +33 -105
  63. data/lib/leftovers/config_loader/precompile_schema.rb +12 -0
  64. data/lib/leftovers/config_loader/precompiler_schema.rb +11 -0
  65. data/lib/leftovers/config_loader/privacy_processor_schema.rb +0 -2
  66. data/lib/leftovers/config_loader/regexp_schema.rb +27 -0
  67. data/lib/leftovers/config_loader/require_schema.rb +3 -3
  68. data/lib/leftovers/config_loader/rule_pattern_schema.rb +3 -1
  69. data/lib/leftovers/config_loader/scalar_value_schema.rb +8 -0
  70. data/lib/leftovers/config_loader/schema.rb +12 -0
  71. data/lib/leftovers/config_loader/string_enum_schema.rb +1 -1
  72. data/lib/leftovers/config_loader/string_pattern_schema.rb +3 -3
  73. data/lib/leftovers/config_loader/string_value_processor_schema.rb +2 -2
  74. data/lib/leftovers/config_loader/transform_schema.rb +10 -6
  75. data/lib/leftovers/config_loader/value_matcher_condition_schema.rb +13 -0
  76. data/lib/leftovers/config_loader/value_matcher_schema.rb +6 -3
  77. data/lib/leftovers/config_loader/value_or_array_schema.rb +2 -32
  78. data/lib/leftovers/config_loader/value_or_object_schema.rb +40 -0
  79. data/lib/leftovers/config_loader/value_processor_schema.rb +4 -4
  80. data/lib/leftovers/config_loader.rb +15 -3
  81. data/lib/leftovers/definition_collection.rb +37 -0
  82. data/lib/leftovers/definition_node.rb +8 -14
  83. data/lib/leftovers/definition_node_set.rb +10 -2
  84. data/lib/leftovers/file.rb +8 -54
  85. data/lib/leftovers/file_collector/comments_processor.rb +57 -0
  86. data/lib/leftovers/file_collector/node_processor.rb +131 -0
  87. data/lib/leftovers/file_collector.rb +56 -223
  88. data/lib/leftovers/file_list.rb +3 -2
  89. data/lib/leftovers/matcher_builders/and.rb +26 -9
  90. data/lib/leftovers/matcher_builders/and_not.rb +7 -5
  91. data/lib/leftovers/matcher_builders/name.rb +18 -17
  92. data/lib/leftovers/matcher_builders/node.rb +60 -34
  93. data/lib/leftovers/matcher_builders/node_has_argument.rb +48 -52
  94. data/lib/leftovers/matcher_builders/node_has_keyword_argument.rb +16 -11
  95. data/lib/leftovers/matcher_builders/node_has_positional_argument.rb +29 -15
  96. data/lib/leftovers/matcher_builders/node_has_receiver.rb +12 -3
  97. data/lib/leftovers/matcher_builders/node_pair_key.rb +16 -0
  98. data/lib/leftovers/matcher_builders/node_type.rb +10 -10
  99. data/lib/leftovers/matcher_builders/node_value.rb +46 -25
  100. data/lib/leftovers/matcher_builders/or.rb +64 -49
  101. data/lib/leftovers/matcher_builders/path.rb +3 -1
  102. data/lib/leftovers/matcher_builders/string_pattern.rb +1 -1
  103. data/lib/leftovers/matcher_builders.rb +1 -1
  104. data/lib/leftovers/matchers/all.rb +4 -0
  105. data/lib/leftovers/matchers/and.rb +4 -0
  106. data/lib/leftovers/matchers/any.rb +2 -0
  107. data/lib/leftovers/matchers/node_has_any_keyword_argument.rb +7 -4
  108. data/lib/leftovers/matchers/node_has_any_positional_argument_with_value.rb +5 -4
  109. data/lib/leftovers/matchers/node_has_any_receiver.rb +13 -0
  110. data/lib/leftovers/matchers/node_has_positional_argument.rb +5 -1
  111. data/lib/leftovers/matchers/node_has_positional_argument_with_value.rb +6 -1
  112. data/lib/leftovers/matchers/node_has_receiver.rb +4 -0
  113. data/lib/leftovers/matchers/node_is_proc.rb +13 -0
  114. data/lib/leftovers/matchers/node_name.rb +9 -3
  115. data/lib/leftovers/matchers/node_pair_key.rb +23 -0
  116. data/lib/leftovers/matchers/node_pair_value.rb +7 -3
  117. data/lib/leftovers/matchers/node_path.rb +7 -3
  118. data/lib/leftovers/matchers/node_privacy.rb +7 -3
  119. data/lib/leftovers/matchers/node_scalar_value.rb +6 -1
  120. data/lib/leftovers/matchers/node_type.rb +7 -3
  121. data/lib/leftovers/matchers/not.rb +2 -0
  122. data/lib/leftovers/matchers/or.rb +2 -0
  123. data/lib/leftovers/matchers/path.rb +21 -0
  124. data/lib/leftovers/matchers.rb +4 -1
  125. data/lib/leftovers/merged_config.rb +28 -47
  126. data/lib/leftovers/parser.rb +7 -4
  127. data/lib/leftovers/precompilers/erb.rb +22 -0
  128. data/lib/leftovers/precompilers/haml.rb +15 -0
  129. data/lib/leftovers/precompilers/json.rb +27 -0
  130. data/lib/leftovers/precompilers/precompiler.rb +28 -0
  131. data/lib/leftovers/precompilers/slim.rb +15 -0
  132. data/lib/leftovers/precompilers/yaml.rb +75 -0
  133. data/lib/leftovers/precompilers.rb +50 -0
  134. data/lib/leftovers/processor_builders/action.rb +69 -42
  135. data/lib/leftovers/processor_builders/add_prefix.rb +18 -10
  136. data/lib/leftovers/processor_builders/add_suffix.rb +18 -10
  137. data/lib/leftovers/processor_builders/argument.rb +28 -14
  138. data/lib/leftovers/processor_builders/dynamic.rb +37 -31
  139. data/lib/leftovers/processor_builders/each.rb +83 -11
  140. data/lib/leftovers/processor_builders/itself.rb +2 -2
  141. data/lib/leftovers/processor_builders/keyword.rb +9 -9
  142. data/lib/leftovers/processor_builders/keyword_argument.rb +4 -2
  143. data/lib/leftovers/processor_builders/receiver.rb +13 -0
  144. data/lib/leftovers/processor_builders/transform.rb +55 -44
  145. data/lib/leftovers/processor_builders/transform_chain.rb +16 -8
  146. data/lib/leftovers/processor_builders/transform_set.rb +16 -32
  147. data/lib/leftovers/processor_builders/value.rb +4 -4
  148. data/lib/leftovers/processor_builders.rb +1 -3
  149. data/lib/leftovers/processors/add_call.rb +14 -0
  150. data/lib/leftovers/processors/add_definition_node.rb +16 -0
  151. data/lib/leftovers/processors/add_dynamic_prefix.rb +29 -0
  152. data/lib/leftovers/processors/add_dynamic_suffix.rb +29 -0
  153. data/lib/leftovers/{value_processors → processors}/add_prefix.rb +7 -3
  154. data/lib/leftovers/{value_processors → processors}/add_suffix.rb +7 -3
  155. data/lib/leftovers/processors/append_sym.rb +13 -0
  156. data/lib/leftovers/{value_processors → processors}/camelize.rb +7 -3
  157. data/lib/leftovers/{value_processors → processors}/capitalize.rb +7 -3
  158. data/lib/leftovers/{value_processors → processors}/deconstantize.rb +7 -3
  159. data/lib/leftovers/{value_processors → processors}/delete_after.rb +9 -5
  160. data/lib/leftovers/processors/delete_after_last.rb +26 -0
  161. data/lib/leftovers/processors/delete_before.rb +27 -0
  162. data/lib/leftovers/processors/delete_before_last.rb +27 -0
  163. data/lib/leftovers/{value_processors → processors}/delete_prefix.rb +7 -3
  164. data/lib/leftovers/{value_processors → processors}/delete_suffix.rb +7 -3
  165. data/lib/leftovers/{value_processors → processors}/demodulize.rb +7 -3
  166. data/lib/leftovers/{value_processors → processors}/downcase.rb +7 -3
  167. data/lib/leftovers/processors/each.rb +25 -0
  168. data/lib/leftovers/processors/each_for_definition_set.rb +33 -0
  169. data/lib/leftovers/processors/each_keyword.rb +29 -0
  170. data/lib/leftovers/processors/each_keyword_argument.rb +29 -0
  171. data/lib/leftovers/processors/each_positional_argument.rb +27 -0
  172. data/lib/leftovers/processors/each_positional_argument_from.rb +30 -0
  173. data/lib/leftovers/processors/eval.rb +16 -0
  174. data/lib/leftovers/processors/itself.rb +21 -0
  175. data/lib/leftovers/{value_processors → processors}/keyword_argument.rb +9 -11
  176. data/lib/leftovers/processors/match_current_node.rb +26 -0
  177. data/lib/leftovers/processors/match_matched_node.rb +26 -0
  178. data/lib/leftovers/{value_processors → processors}/parameterize.rb +7 -3
  179. data/lib/leftovers/{value_processors → processors}/placeholder.rb +5 -4
  180. data/lib/leftovers/{value_processors → processors}/pluralize.rb +7 -3
  181. data/lib/leftovers/{value_processors → processors}/positional_argument.rb +8 -6
  182. data/lib/leftovers/processors/receiver.rb +24 -0
  183. data/lib/leftovers/{value_processors → processors}/replace_value.rb +7 -3
  184. data/lib/leftovers/processors/set_default_privacy.rb +21 -0
  185. data/lib/leftovers/processors/set_privacy.rb +23 -0
  186. data/lib/leftovers/{value_processors → processors}/singularize.rb +7 -3
  187. data/lib/leftovers/{value_processors → processors}/split.rb +8 -4
  188. data/lib/leftovers/{value_processors → processors}/swapcase.rb +7 -3
  189. data/lib/leftovers/{value_processors → processors}/titleize.rb +7 -3
  190. data/lib/leftovers/{value_processors → processors}/underscore.rb +7 -3
  191. data/lib/leftovers/{value_processors → processors}/upcase.rb +7 -3
  192. data/lib/leftovers/processors.rb +49 -0
  193. data/lib/leftovers/rake_task.rb +1 -1
  194. data/lib/leftovers/version.rb +1 -1
  195. data/lib/leftovers.rb +34 -8
  196. metadata +116 -58
  197. data/lib/leftovers/dynamic_processors/call.rb +0 -22
  198. data/lib/leftovers/dynamic_processors/call_definition.rb +0 -34
  199. data/lib/leftovers/dynamic_processors/definition.rb +0 -27
  200. data/lib/leftovers/dynamic_processors/each.rb +0 -19
  201. data/lib/leftovers/dynamic_processors/null.rb +0 -9
  202. data/lib/leftovers/dynamic_processors/set_default_privacy.rb +0 -18
  203. data/lib/leftovers/dynamic_processors/set_privacy.rb +0 -23
  204. data/lib/leftovers/dynamic_processors.rb +0 -13
  205. data/lib/leftovers/erb.rb +0 -20
  206. data/lib/leftovers/haml.rb +0 -21
  207. data/lib/leftovers/json.rb +0 -28
  208. data/lib/leftovers/matcher_builders/node_pair_name.rb +0 -18
  209. data/lib/leftovers/matchers/predicate.rb +0 -19
  210. data/lib/leftovers/processor_builders/each_action.rb +0 -51
  211. data/lib/leftovers/processor_builders/each_dynamic.rb +0 -54
  212. data/lib/leftovers/processor_builders/each_for_definition_set.rb +0 -36
  213. data/lib/leftovers/slim.rb +0 -21
  214. data/lib/leftovers/value_processors/add_dynamic_prefix.rb +0 -31
  215. data/lib/leftovers/value_processors/add_dynamic_suffix.rb +0 -31
  216. data/lib/leftovers/value_processors/delete_before.rb +0 -22
  217. data/lib/leftovers/value_processors/each.rb +0 -21
  218. data/lib/leftovers/value_processors/each_for_definition_set.rb +0 -26
  219. data/lib/leftovers/value_processors/each_keyword.rb +0 -27
  220. data/lib/leftovers/value_processors/each_keyword_argument.rb +0 -27
  221. data/lib/leftovers/value_processors/each_positional_argument.rb +0 -24
  222. data/lib/leftovers/value_processors/itself.rb +0 -17
  223. data/lib/leftovers/value_processors/keyword.rb +0 -32
  224. data/lib/leftovers/value_processors/return_definition_node.rb +0 -14
  225. data/lib/leftovers/value_processors/return_sym.rb +0 -14
  226. data/lib/leftovers/value_processors.rb +0 -40
  227. data/lib/leftovers/yaml.rb +0 -73
@@ -3,71 +3,67 @@
3
3
  module Leftovers
4
4
  module MatcherBuilders
5
5
  module NodeHasArgument
6
- def self.build(patterns) # rubocop:disable Metrics/MethodLength
7
- ::Leftovers::MatcherBuilders::Or.each_or_self(patterns) do |pat|
8
- case pat
9
- when ::String
10
- ::Leftovers::MatcherBuilders::NodeHasKeywordArgument.build(pat, nil)
11
- when ::Integer
12
- ::Leftovers::MatcherBuilders::NodeHasPositionalArgument.build(pat, nil)
13
- when ::Hash
14
- build_from_hash(**pat)
15
- # :nocov:
16
- else
17
- raise
6
+ class << self
7
+ def build(patterns)
8
+ ::Leftovers::MatcherBuilders::Or.each_or_self(patterns) do |pat|
9
+ case pat
10
+ when ::String
11
+ ::Leftovers::MatcherBuilders::NodeHasKeywordArgument.build(pat, nil)
12
+ when ::Integer
13
+ ::Leftovers::MatcherBuilders::NodeHasPositionalArgument.build(pat, nil)
14
+ when ::Hash then build_from_hash(**pat)
18
15
  # :nocov:
16
+ else raise Leftovers::UnexpectedCase, "Unhandled value #{pat.inspect}"
17
+ # :nocov:
18
+ end
19
19
  end
20
20
  end
21
- end
22
21
 
23
- def self.separate_argument_types(at) # rubocop:disable Metrics/MethodLength
24
- keys = []
25
- positions = []
22
+ private
26
23
 
27
- ::Leftovers.each_or_self(at) do |k|
28
- case k
29
- when '*', ::Integer
30
- positions << k
31
- when ::String, ::Hash
32
- keys << k
33
- # :nocov:
34
- else raise
35
- # :nocov:
36
- end
37
- end
24
+ def build_from_hash(at: nil, has_value: nil, unless_arg: nil)
25
+ value_matcher = ::Leftovers::MatcherBuilders::NodeValue.build(has_value)
38
26
 
39
- keys = nil if keys.empty?
40
- positions = nil if positions.empty?
27
+ ::Leftovers::MatcherBuilders::AndNot.build(
28
+ build_argument_matcher(value_matcher, **separate_argument_types(at)),
29
+ ::Leftovers::MatcherBuilders::NodeHasArgument.build(unless_arg)
30
+ )
31
+ end
41
32
 
42
- [keys, positions]
43
- end
33
+ def separate_argument_types(at)
34
+ groups = ::Leftovers.each_or_self(at).group_by do |index|
35
+ case index
36
+ when '*', ::Integer then :positions
37
+ when ::String, ::Hash then :keys
38
+ # :nocov:
39
+ else raise Leftovers::UnexpectedCase, "Unhandled value #{index.inspect}"
40
+ # :nocov:
41
+ end
42
+ end
44
43
 
45
- def self.build_from_hash( # rubocop:disable Metrics/MethodLength
46
- at: nil,
47
- has_value: nil,
48
- unless_arg: nil
49
- )
50
- keys, positions = separate_argument_types(at)
44
+ groups.transform_values { |v| Leftovers.unwrap_array(v) }
45
+ end
51
46
 
52
- value_matcher = ::Leftovers::MatcherBuilders::NodeValue.build(has_value)
53
- matcher = if (keys && positions) || (!keys && !positions)
54
- ::Leftovers::MatcherBuilders::Or.build([
55
- ::Leftovers::MatcherBuilders::NodeHasPositionalArgument.build(positions, value_matcher),
56
- ::Leftovers::MatcherBuilders::NodeHasKeywordArgument.build(keys, value_matcher)
57
- ])
58
- elsif keys
47
+ def build_has_keyword_argument(keys, value_matcher)
59
48
  ::Leftovers::MatcherBuilders::NodeHasKeywordArgument.build(keys, value_matcher)
60
- elsif positions
49
+ end
50
+
51
+ def build_has_positional_argument(positions, value_matcher)
61
52
  ::Leftovers::MatcherBuilders::NodeHasPositionalArgument.build(positions, value_matcher)
62
- # :nocov:
63
- else
64
- raise
65
- # :nocov:
66
53
  end
67
54
 
68
- ::Leftovers::MatcherBuilders::AndNot.build(
69
- matcher, ::Leftovers::MatcherBuilders::NodeHasArgument.build(unless_arg)
70
- )
55
+ def build_argument_matcher(value_matcher, keys: nil, positions: nil)
56
+ if keys && !positions
57
+ build_has_keyword_argument(keys, value_matcher)
58
+ elsif positions && !keys
59
+ build_has_positional_argument(positions, value_matcher)
60
+ else
61
+ ::Leftovers::MatcherBuilders::Or.build([
62
+ build_has_positional_argument(positions, value_matcher),
63
+ build_has_keyword_argument(keys, value_matcher)
64
+ ])
65
+ end
66
+ end
71
67
  end
72
68
  end
73
69
  end
@@ -4,22 +4,27 @@ module Leftovers
4
4
  module MatcherBuilders
5
5
  module NodeHasKeywordArgument
6
6
  class << self
7
- def build(keywords, value_matcher) # rubocop:disable Metrics/MethodLength
7
+ def build(keywords, value_matcher)
8
8
  value_matcher = ::Leftovers::MatcherBuilders::NodePairValue.build(value_matcher)
9
- keyword_matcher = if ::Leftovers.each_or_self(keywords).include?('**')
10
- ::Leftovers::Matchers::NodeType.new(:pair)
11
- else
12
- ::Leftovers::MatcherBuilders::NodePairName.build(keywords)
13
- end
9
+ keyword_matcher = build_keyword_matcher(keywords)
10
+ pair_matcher = ::Leftovers::MatcherBuilders::And.build([keyword_matcher, value_matcher])
14
11
 
15
- pair_matcher = ::Leftovers::MatcherBuilders::And.build([
16
- keyword_matcher, value_matcher
17
- ])
18
-
19
- return nil unless pair_matcher
12
+ return unless pair_matcher
20
13
 
21
14
  ::Leftovers::Matchers::NodeHasAnyKeywordArgument.new(pair_matcher)
22
15
  end
16
+
17
+ private
18
+
19
+ def build_keyword_matcher(keywords)
20
+ if ::Leftovers.each_or_self(keywords).include?('**')
21
+ ::Leftovers::Matchers::NodeType.new(:pair)
22
+ else
23
+ ::Leftovers::MatcherBuilders::NodePairKey.build(
24
+ ::Leftovers::MatcherBuilders::Node.build(keywords)
25
+ )
26
+ end
27
+ end
23
28
  end
24
29
  end
25
30
  end
@@ -4,22 +4,36 @@ module Leftovers
4
4
  module MatcherBuilders
5
5
  module NodeHasPositionalArgument
6
6
  class << self
7
- def build(positions, value_matcher) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength
8
- if positions && value_matcher
9
- ::Leftovers::MatcherBuilders::Or.each_or_self(positions) do |pos|
10
- if pos == '*'
11
- ::Leftovers::Matchers::NodeHasAnyPositionalArgumentWithValue.new(value_matcher)
12
- else
13
- ::Leftovers::Matchers::NodeHasPositionalArgumentWithValue.new(pos, value_matcher)
14
- end
15
- end
16
- elsif positions
17
- pos = 0 if ::Leftovers.each_or_self(positions).include?('*')
18
- pos ||= ::Leftovers.each_or_self(positions).min
19
-
20
- ::Leftovers::Matchers::NodeHasPositionalArgument.new(pos)
7
+ def build(positions, value_matcher)
8
+ if positions && !all_positions?(positions) && value_matcher
9
+ build_has_positional_value_matcher(positions, value_matcher)
10
+ elsif positions && !value_matcher
11
+ build_has_position_matcher(positions)
21
12
  elsif value_matcher
22
- ::Leftovers::Matchers::NodeHasAnyPositionalArgumentWithValue.new(value_matcher)
13
+ build_has_any_positional_value_matcher(value_matcher)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def all_positions?(positions)
20
+ ::Leftovers.each_or_self(positions).include?('*')
21
+ end
22
+
23
+ def build_has_position_matcher(positions)
24
+ position = 0 if all_positions?(positions)
25
+ position ||= ::Leftovers.each_or_self(positions).min
26
+
27
+ ::Leftovers::Matchers::NodeHasPositionalArgument.new(position)
28
+ end
29
+
30
+ def build_has_any_positional_value_matcher(value_matcher)
31
+ ::Leftovers::Matchers::NodeHasAnyPositionalArgumentWithValue.new(value_matcher)
32
+ end
33
+
34
+ def build_has_positional_value_matcher(positions, value_matcher)
35
+ ::Leftovers::MatcherBuilders::Or.each_or_self(positions) do |position|
36
+ ::Leftovers::Matchers::NodeHasPositionalArgumentWithValue.new(position, value_matcher)
23
37
  end
24
38
  end
25
39
  end
@@ -4,10 +4,19 @@ module Leftovers
4
4
  module MatcherBuilders
5
5
  module NodeHasReceiver
6
6
  class << self
7
- def build(pattern)
8
- matcher = ::Leftovers::MatcherBuilders::NodeValue.build(pattern)
7
+ def build(pattern) # rubocop:disable Metrics/MethodLength
8
+ case pattern
9
+ when true
10
+ ::Leftovers::Matchers::NodeHasAnyReceiver
11
+ when false, :_leftovers_nil_value
12
+ ::Leftovers::Matchers::Not.new(
13
+ ::Leftovers::Matchers::NodeHasAnyReceiver
14
+ )
15
+ else
16
+ matcher = ::Leftovers::MatcherBuilders::NodeValue.build(pattern)
9
17
 
10
- ::Leftovers::Matchers::NodeHasReceiver.new(matcher) if matcher
18
+ ::Leftovers::Matchers::NodeHasReceiver.new(matcher) if matcher
19
+ end
11
20
  end
12
21
  end
13
22
  end
@@ -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,18 +8,18 @@ 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
- else raise "Unknown type #{type}"
22
+ else raise Leftovers::UnexpectedCase, "Unhandled value #{type.inspect}"
23
23
  # :nocov:
24
24
  end
25
25
  end
@@ -4,17 +4,17 @@ module Leftovers
4
4
  module MatcherBuilders
5
5
  module NodeValue
6
6
  class << self
7
- def build(pattern) # rubocop:disable Metrics/MethodLength
8
- ::Leftovers::MatcherBuilders::Or.each_or_self(pattern) do |pat|
9
- case pat
10
- when ::Integer, ::Float, true, false, nil
11
- ::Leftovers::Matchers::NodeScalarValue.new(pat)
12
- when ::String
13
- ::Leftovers::MatcherBuilders::NodeName.build(pat)
14
- when ::Hash
15
- build_from_hash(**pat)
7
+ def build(patterns)
8
+ ::Leftovers::MatcherBuilders::Or.each_or_self(patterns) do |pattern|
9
+ case pattern
10
+ when ::Integer, ::Float, true, false
11
+ # matching scalar on nil will fall afoul of compact and each_or_self etc.
12
+ ::Leftovers::Matchers::NodeScalarValue.new(pattern)
13
+ when :_leftovers_nil_value then ::Leftovers::Matchers::NodeType.new(:nil)
14
+ when ::String then ::Leftovers::MatcherBuilders::NodeName.build(pattern)
15
+ when ::Hash then build_from_hash(**pattern)
16
16
  # :nocov:
17
- else raise
17
+ else raise Leftovers::UnexpectedCase, "Unhandled value #{pattern.inspect}"
18
18
  # :nocov:
19
19
  end
20
20
  end
@@ -22,27 +22,48 @@ module Leftovers
22
22
 
23
23
  private
24
24
 
25
- def build_from_hash( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
26
- at: nil, has_value: nil,
27
- match: nil, has_prefix: nil, has_suffix: nil,
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
+ ])
41
+ end
42
+
43
+ def build_unless(unless_arg)
44
+ return unless unless_arg
45
+
46
+ ::Leftovers::MatcherBuilders::Unless.build(
47
+ ::Leftovers::MatcherBuilders::NodeValue.build(unless_arg)
48
+ )
49
+ end
50
+
51
+ def build_from_hash( # rubocop:disable Metrics/ParameterLists
52
+ has_arguments: nil, at: nil, has_value: nil,
53
+ names: nil, match: nil, has_prefix: nil, has_suffix: nil,
28
54
  type: nil,
29
55
  has_receiver: nil,
56
+ literal: nil,
30
57
  unless_arg: nil
31
58
  )
32
- matcher = ::Leftovers::MatcherBuilders::And.build([
33
- ::Leftovers::MatcherBuilders::NodeHasArgument.build(
34
- at: at, has_value: has_value
35
- ),
36
- ::Leftovers::MatcherBuilders::NodeName.build(
37
- match: match, has_prefix: has_prefix, has_suffix: has_suffix
38
- ),
59
+ ::Leftovers::MatcherBuilders::And.build([
60
+ build_node_has_argument_matcher(has_arguments, at, has_value),
61
+ build_node_name_matcher(names, match, has_prefix, has_suffix),
39
62
  ::Leftovers::MatcherBuilders::NodeType.build(type),
40
- ::Leftovers::MatcherBuilders::NodeHasReceiver.build(has_receiver)
63
+ ::Leftovers::MatcherBuilders::NodeHasReceiver.build(has_receiver),
64
+ ::Leftovers::MatcherBuilders::NodeValue.build(literal),
65
+ build_unless(unless_arg)
41
66
  ])
42
-
43
- ::Leftovers::MatcherBuilders::AndNot.build(
44
- matcher, ::Leftovers::MatcherBuilders::NodeValue.build(unless_arg)
45
- )
46
67
  end
47
68
  end
48
69
  end
@@ -5,68 +5,83 @@ require 'set'
5
5
  module Leftovers
6
6
  module MatcherBuilders
7
7
  module Or
8
- def self.each_or_self(value, &block)
9
- case value
10
- when nil then nil
11
- when Array then build(value.map(&block))
12
- else build([yield(value)])
8
+ class << self
9
+ def each_or_self(value, &block)
10
+ case value
11
+ when nil then nil
12
+ when Array then build(value.map(&block))
13
+ else build([yield(value)])
14
+ end
13
15
  end
14
- end
15
16
 
16
- def self.build(matchers)
17
- matchers = compact(matchers)
18
- case matchers.length
19
- # :nocov:
20
- when 0 then nil
21
- # :nocov:
22
- when 1 then matchers.first
23
- when 2 then ::Leftovers::Matchers::Or.new(matchers.first, matchers[1])
24
- else ::Leftovers::Matchers::Any.new(matchers.dup)
17
+ def build(matchers)
18
+ matchers = compact(matchers)
19
+ case matchers.length
20
+ # :nocov:
21
+ when 0 then nil
22
+ # :nocov:
23
+ when 1 then matchers.first
24
+ when 2 then ::Leftovers::Matchers::Or.new(matchers.first, matchers[1])
25
+ else ::Leftovers::Matchers::Any.new(matchers.dup)
26
+ end
25
27
  end
26
- end
27
28
 
28
- def self.flatten(value) # rubocop:disable Metrics/MethodLength
29
- case value
30
- when ::Leftovers::Matchers::Or
31
- [*flatten(value.lhs), *flatten(value.rhs)]
32
- when ::Leftovers::Matchers::Any
33
- flatten(value.matchers)
34
- when Array
35
- ret = value.map { |v| flatten(v) }
36
- ret.flatten!(1)
37
- ret
38
- else
39
- value
29
+ private
30
+
31
+ def flatten(value)
32
+ case value
33
+ when ::Leftovers::Matchers::Or
34
+ [*flatten(value.lhs), *flatten(value.rhs)]
35
+ when ::Leftovers::Matchers::Any
36
+ flatten(value.matchers)
37
+ when Array, Set
38
+ value.flat_map { |v| flatten(v) }
39
+ else
40
+ [value]
41
+ end
40
42
  end
41
- end
42
43
 
43
- def self.compact(matchers) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize,
44
- return matchers if matchers.length <= 1
44
+ def group_by_compactable(matchers)
45
+ groups = matchers.group_by do |matcher|
46
+ case matcher
47
+ when ::Integer, ::Symbol, true, false then :set
48
+ when ::Regexp then :regexp
49
+ when nil then :nil
50
+ else matcher.class.to_s.to_sym
51
+ end
52
+ end
45
53
 
46
- set = Set.new
47
- regexps = []
48
- uncompactable = []
54
+ groups.transform_values { |v| Leftovers.unwrap_array(v) }
55
+ end
49
56
 
50
- matchers = flatten(matchers)
57
+ def mergeable?(matcher)
58
+ matcher.respond_to?(:matcher)
59
+ end
51
60
 
52
- matchers.each do |matcher|
53
- case matcher
54
- when nil then nil
55
- when ::Integer, ::Symbol then set << matcher
56
- # when ::Set then set.merge(matcher) # may not be necessary
57
- when ::Regexp then regexps << matcher
58
- else uncompactable << matcher
59
- end
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)))
60
66
  end
61
67
 
62
- set = set.first if set.length <= 1
63
- regexps = if regexps.length <= 1
64
- regexps.first
65
- else
66
- Regexp.union(regexps)
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)
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
74
+
75
+ [set, regexp].compact.concat(matcher_classes).uniq
67
76
  end
68
77
 
69
- [set, regexps].compact.concat(uncompactable)
78
+ def compact(matchers)
79
+ matchers = flatten(matchers)
80
+
81
+ return matchers if matchers.length <= 1
82
+
83
+ build_grouped(**group_by_compactable(matchers))
84
+ end
70
85
  end
71
86
  end
72
87
  end
@@ -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
@@ -3,7 +3,7 @@
3
3
  module Leftovers
4
4
  module MatcherBuilders
5
5
  module StringPattern
6
- def self.build(match: nil, has_prefix: nil, has_suffix: nil) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
6
+ def self.build(match: nil, has_prefix: nil, has_suffix: nil) # rubocop:disable Metrics
7
7
  has_prefix = ::Regexp.escape(has_prefix) if has_prefix
8
8
  has_suffix = ::Regexp.escape(has_suffix) if has_suffix
9
9
 
@@ -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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ module Matchers
5
+ module NodeHasAnyReceiver
6
+ def self.===(node)
7
+ node.receiver
8
+ end
9
+
10
+ freeze
11
+ end
12
+ end
13
+ end
@@ -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