leftovers 0.2.2 → 0.4.2

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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +35 -49
  4. data/docs/Configuration.md +627 -0
  5. data/exe/leftovers +2 -2
  6. data/leftovers.gemspec +13 -7
  7. data/lib/config/attr_encrypted.yml +3 -4
  8. data/lib/config/audited.yml +9 -4
  9. data/lib/config/datagrid.yml +1 -1
  10. data/lib/config/flipper.yml +1 -3
  11. data/lib/config/graphql.yml +15 -13
  12. data/lib/config/okcomputer.yml +1 -3
  13. data/lib/config/parser.yml +89 -91
  14. data/lib/config/rails.yml +160 -97
  15. data/lib/config/redcarpet.yml +35 -38
  16. data/lib/config/rollbar.yml +1 -3
  17. data/lib/config/rspec.yml +18 -10
  18. data/lib/config/ruby.yml +42 -49
  19. data/lib/config/selenium-webdriver.yml +19 -0
  20. data/lib/config/sidekiq.yml +9 -0
  21. data/lib/config/will_paginate.yml +12 -14
  22. data/lib/leftovers.rb +61 -43
  23. data/lib/leftovers/ast.rb +8 -0
  24. data/lib/leftovers/ast/builder.rb +6 -3
  25. data/lib/leftovers/ast/node.rb +66 -107
  26. data/lib/leftovers/backports.rb +24 -40
  27. data/lib/leftovers/cli.rb +17 -14
  28. data/lib/leftovers/collector.rb +7 -11
  29. data/lib/leftovers/config.rb +38 -13
  30. data/lib/leftovers/config_validator.rb +60 -0
  31. data/lib/leftovers/config_validator/error_processor.rb +196 -0
  32. data/lib/leftovers/config_validator/schema_hash.rb +496 -0
  33. data/lib/leftovers/definition.rb +16 -41
  34. data/lib/leftovers/definition_node.rb +36 -0
  35. data/lib/leftovers/definition_set.rb +17 -24
  36. data/lib/leftovers/dynamic_processors.rb +11 -0
  37. data/lib/leftovers/dynamic_processors/call.rb +25 -0
  38. data/lib/leftovers/dynamic_processors/call_definition.rb +31 -0
  39. data/lib/leftovers/dynamic_processors/definition.rb +26 -0
  40. data/lib/leftovers/dynamic_processors/each.rb +19 -0
  41. data/lib/leftovers/dynamic_processors/null.rb +9 -0
  42. data/lib/leftovers/erb.rb +2 -2
  43. data/lib/leftovers/file.rb +3 -5
  44. data/lib/leftovers/file_collector.rb +82 -62
  45. data/lib/leftovers/file_list.rb +9 -15
  46. data/lib/leftovers/haml.rb +9 -12
  47. data/lib/leftovers/matcher_builders.rb +24 -0
  48. data/lib/leftovers/matcher_builders/and.rb +19 -0
  49. data/lib/leftovers/matcher_builders/and_not.rb +14 -0
  50. data/lib/leftovers/matcher_builders/argument_node_value.rb +21 -0
  51. data/lib/leftovers/matcher_builders/name.rb +29 -0
  52. data/lib/leftovers/matcher_builders/node.rb +40 -0
  53. data/lib/leftovers/matcher_builders/node_has_argument.rb +71 -0
  54. data/lib/leftovers/matcher_builders/node_has_keyword_argument.rb +22 -0
  55. data/lib/leftovers/matcher_builders/node_has_positional_argument.rb +24 -0
  56. data/lib/leftovers/matcher_builders/node_name.rb +15 -0
  57. data/lib/leftovers/matcher_builders/node_pair_name.rb +18 -0
  58. data/lib/leftovers/matcher_builders/node_pair_value.rb +16 -0
  59. data/lib/leftovers/matcher_builders/node_path.rb +14 -0
  60. data/lib/leftovers/matcher_builders/node_type.rb +28 -0
  61. data/lib/leftovers/matcher_builders/or.rb +73 -0
  62. data/lib/leftovers/matcher_builders/path.rb +15 -0
  63. data/lib/leftovers/matcher_builders/string.rb +11 -0
  64. data/lib/leftovers/matcher_builders/string_pattern.rb +19 -0
  65. data/lib/leftovers/matcher_builders/unless.rb +13 -0
  66. data/lib/leftovers/matchers.rb +26 -0
  67. data/lib/leftovers/matchers/all.rb +25 -0
  68. data/lib/leftovers/matchers/and.rb +24 -0
  69. data/lib/leftovers/matchers/any.rb +27 -0
  70. data/lib/leftovers/matchers/node_has_any_keyword_argument.rb +28 -0
  71. data/lib/leftovers/matchers/node_has_any_positional_argument_with_value.rb +25 -0
  72. data/lib/leftovers/matchers/node_has_positional_argument.rb +23 -0
  73. data/lib/leftovers/matchers/node_has_positional_argument_with_value.rb +25 -0
  74. data/lib/leftovers/matchers/node_name.rb +23 -0
  75. data/lib/leftovers/matchers/node_pair_value.rb +23 -0
  76. data/lib/leftovers/matchers/node_path.rb +23 -0
  77. data/lib/leftovers/matchers/node_scalar_value.rb +25 -0
  78. data/lib/leftovers/matchers/node_type.rb +23 -0
  79. data/lib/leftovers/matchers/not.rb +23 -0
  80. data/lib/leftovers/matchers/or.rb +26 -0
  81. data/lib/leftovers/merged_config.rb +24 -14
  82. data/lib/leftovers/parser.rb +2 -5
  83. data/lib/leftovers/processor_builders.rb +22 -0
  84. data/lib/leftovers/processor_builders/action.rb +63 -0
  85. data/lib/leftovers/processor_builders/add_prefix.rb +20 -0
  86. data/lib/leftovers/processor_builders/add_suffix.rb +20 -0
  87. data/lib/leftovers/processor_builders/argument.rb +25 -0
  88. data/lib/leftovers/processor_builders/dynamic.rb +27 -0
  89. data/lib/leftovers/processor_builders/each.rb +36 -0
  90. data/lib/leftovers/processor_builders/each_action.rb +51 -0
  91. data/lib/leftovers/processor_builders/each_dynamic.rb +54 -0
  92. data/lib/leftovers/processor_builders/each_for_definition_set.rb +36 -0
  93. data/lib/leftovers/processor_builders/itself.rb +13 -0
  94. data/lib/leftovers/processor_builders/keyword.rb +24 -0
  95. data/lib/leftovers/processor_builders/keyword_argument.rb +14 -0
  96. data/lib/leftovers/processor_builders/transform.rb +55 -0
  97. data/lib/leftovers/processor_builders/transform_chain.rb +24 -0
  98. data/lib/leftovers/processor_builders/transform_set.rb +47 -0
  99. data/lib/leftovers/processor_builders/value.rb +13 -0
  100. data/lib/leftovers/rake_task.rb +4 -4
  101. data/lib/leftovers/reporter.rb +1 -1
  102. data/lib/leftovers/value_processors.rb +40 -0
  103. data/lib/leftovers/value_processors/add_dynamic_prefix.rb +31 -0
  104. data/lib/leftovers/value_processors/add_dynamic_suffix.rb +31 -0
  105. data/lib/leftovers/value_processors/add_prefix.rb +20 -0
  106. data/lib/leftovers/value_processors/add_suffix.rb +20 -0
  107. data/lib/leftovers/value_processors/camelize.rb +24 -0
  108. data/lib/leftovers/value_processors/capitalize.rb +19 -0
  109. data/lib/leftovers/value_processors/deconstantize.rb +24 -0
  110. data/lib/leftovers/value_processors/delete_after.rb +22 -0
  111. data/lib/leftovers/value_processors/delete_before.rb +22 -0
  112. data/lib/leftovers/value_processors/delete_prefix.rb +26 -0
  113. data/lib/leftovers/value_processors/delete_suffix.rb +26 -0
  114. data/lib/leftovers/value_processors/demodulize.rb +24 -0
  115. data/lib/leftovers/value_processors/downcase.rb +19 -0
  116. data/lib/leftovers/value_processors/each.rb +21 -0
  117. data/lib/leftovers/value_processors/each_for_definition_set.rb +29 -0
  118. data/lib/leftovers/value_processors/each_keyword.rb +27 -0
  119. data/lib/leftovers/value_processors/each_keyword_argument.rb +27 -0
  120. data/lib/leftovers/value_processors/each_positional_argument.rb +24 -0
  121. data/lib/leftovers/value_processors/itself.rb +17 -0
  122. data/lib/leftovers/value_processors/keyword.rb +36 -0
  123. data/lib/leftovers/value_processors/keyword_argument.rb +36 -0
  124. data/lib/leftovers/value_processors/parameterize.rb +24 -0
  125. data/lib/leftovers/value_processors/placeholder.rb +18 -0
  126. data/lib/leftovers/value_processors/pluralize.rb +24 -0
  127. data/lib/leftovers/value_processors/positional_argument.rb +26 -0
  128. data/lib/leftovers/value_processors/replace_value.rb +18 -0
  129. data/lib/leftovers/value_processors/return_definition.rb +26 -0
  130. data/lib/leftovers/value_processors/return_string.rb +14 -0
  131. data/lib/leftovers/value_processors/singularize.rb +24 -0
  132. data/lib/leftovers/value_processors/split.rb +22 -0
  133. data/lib/leftovers/value_processors/swapcase.rb +19 -0
  134. data/lib/leftovers/value_processors/titleize.rb +24 -0
  135. data/lib/leftovers/value_processors/underscore.rb +24 -0
  136. data/lib/leftovers/value_processors/upcase.rb +19 -0
  137. data/lib/leftovers/version.rb +1 -1
  138. metadata +190 -28
  139. data/lib/config/selenium.yml +0 -21
  140. data/lib/leftovers/argument_rule.rb +0 -219
  141. data/lib/leftovers/core_ext.rb +0 -13
  142. data/lib/leftovers/hash_rule.rb +0 -40
  143. data/lib/leftovers/name_rule.rb +0 -53
  144. data/lib/leftovers/rule.rb +0 -74
  145. data/lib/leftovers/transform_rule.rb +0 -171
  146. data/lib/leftovers/value_rule.rb +0 -56
@@ -2,57 +2,36 @@
2
2
 
3
3
  module Leftovers
4
4
  class Definition
5
- attr_reader :name
5
+ attr_reader :name, :test, :location_s
6
6
  alias_method :names, :name
7
- alias_method :full_name, :name
8
- attr_reader :name_s
9
- alias_method :to_s, :name_s
10
- attr_reader :test
7
+
11
8
  alias_method :test?, :test
12
9
 
13
- def initialize( # rubocop:disable Metrics/MethodLength
10
+ def initialize(
14
11
  name,
15
- method_node: nil,
16
12
  location: method_node.loc.expression,
17
- file: method_node.file,
18
- test: method_node.test?
13
+ test: method_node.test_line?
19
14
  )
20
15
  @name = name
21
- @name_s = name.to_s.freeze
22
-
23
- @location = location
24
- @file = file
16
+ @path = location.source_buffer.name.to_s
17
+ @location_source_line = location.source_line.to_s
18
+ @location_column_range_begin = location.column_range.begin.to_i
19
+ @location_column_range_end = location.column_range.end.to_i
20
+ @location_source = location.source.to_s
21
+ @location_s = location.to_s
25
22
  @test = test
26
23
 
27
24
  freeze
28
25
  end
29
26
 
30
- def <=>(other)
31
- (path <=> other.path).nonzero? ||
32
- (line <=> other.line).nonzero? ||
33
- (column <=> other.column)
34
- end
35
-
36
- def path
37
- @file.relative_path
38
- end
39
-
40
- def line
41
- @location.line
27
+ def to_s
28
+ @name.to_s
42
29
  end
43
30
 
44
- def column
45
- @location.column
46
- end
47
-
48
- def full_location
49
- "#{path}:#{@location.line}:#{@location.column}"
50
- end
51
-
52
- def highlighted_source(highlight = "\e[31m", normal = "\e[0m") # rubocop:disable Metrics/AbcSize
53
- @location.source_line.to_s[0...(@location.column_range.begin)].lstrip +
54
- highlight + @location.source.to_s + normal +
55
- @location.source_line.to_s[(@location.column_range.end)..-1].rstrip
31
+ def highlighted_source(highlight = "\e[31m", normal = "\e[0m")
32
+ @location_source_line[0...@location_column_range_begin].lstrip +
33
+ highlight + @location_source + normal +
34
+ @location_source_line[@location_column_range_end..-1].rstrip
56
35
  end
57
36
 
58
37
  def in_collection?
@@ -62,9 +41,5 @@ module Leftovers
62
41
  def in_test_collection?
63
42
  Leftovers.collector.test_calls.include?(@name)
64
43
  end
65
-
66
- def skipped?
67
- Leftovers.config.skip_rules.any? { |r| r.match?(@name, @name_s, path) }
68
- end
69
44
  end
70
45
  end
@@ -0,0 +1,36 @@
1
+ # frozen-string-literal: true
2
+
3
+ # To give to matchers before creating a Definition
4
+
5
+ module Leftovers
6
+ class DefinitionNode
7
+ attr_reader :path, :name
8
+
9
+ def initialize(name, path)
10
+ @name = name
11
+ @path = path
12
+
13
+ freeze
14
+ end
15
+
16
+ # these are the methods checked by things in lib/leftovers/matchers
17
+ def kwargs
18
+ nil
19
+ end
20
+
21
+ def positional_arguments
22
+ nil
23
+ end
24
+
25
+ # these two i'm not sure are possible with the current config flags
26
+ # :nocov:
27
+ def scalar?
28
+ false
29
+ end
30
+
31
+ def type
32
+ :leftovers_definition
33
+ end
34
+ # :nocov:
35
+ end
36
+ end
@@ -1,28 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'definition'
4
3
  module Leftovers
5
- class DefinitionSet < Leftovers::Definition
6
- def initialize( # rubocop:disable Metrics/MethodLength
7
- names,
8
- method_node: nil,
9
- location: method_node.loc.expression,
10
- file: method_node.file,
11
- test: method_node.test?
12
- )
13
- @definitions = names.map do |name|
14
- Leftovers::Definition.new(name, test: test, location: location, file: file)
15
- end
16
-
17
- @test = test
18
- @location = location
19
- @file = file
4
+ class DefinitionSet
5
+ attr_reader :definitions
20
6
 
21
- freeze
22
- end
7
+ def initialize(definitions)
8
+ @definitions = definitions
23
9
 
24
- def full_name
25
- names.join(', ')
10
+ freeze
26
11
  end
27
12
 
28
13
  def names
@@ -33,16 +18,24 @@ module Leftovers
33
18
  @definitions.map(&:to_s).join(', ')
34
19
  end
35
20
 
21
+ def location_s
22
+ @definitions.first.location_s
23
+ end
24
+
25
+ def highlighted_source(*args)
26
+ @definitions.first.highlighted_source(*args)
27
+ end
28
+
36
29
  def in_collection?
37
30
  @definitions.any?(&:in_collection?)
38
31
  end
39
32
 
40
- def in_test_collection?
41
- @definitions.any?(&:in_test_collection?)
33
+ def test?
34
+ @definitions.any?(&:test?)
42
35
  end
43
36
 
44
- def skipped?
45
- @definitions.any?(&:skipped?)
37
+ def in_test_collection?
38
+ @definitions.any?(&:in_test_collection?)
46
39
  end
47
40
  end
48
41
  end
@@ -0,0 +1,11 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Leftovers
4
+ module DynamicProcessors
5
+ autoload(:CallDefinition, "#{__dir__}/dynamic_processors/call_definition")
6
+ autoload(:Call, "#{__dir__}/dynamic_processors/call")
7
+ autoload(:Definition, "#{__dir__}/dynamic_processors/definition")
8
+ autoload(:Each, "#{__dir__}/dynamic_processors/each")
9
+ autoload(:Null, "#{__dir__}/dynamic_processors/null")
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Leftovers
4
+ module DynamicProcessors
5
+ class Call
6
+ # :nocov:
7
+ using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
+ # :nocov:
9
+
10
+ def initialize(matcher, processor)
11
+ @matcher = matcher
12
+ @processor = processor
13
+ end
14
+
15
+ def process(node, file)
16
+ return unless @matcher === node
17
+
18
+ call = @processor.process(nil, node, node)
19
+ return unless call
20
+
21
+ file.calls << call
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Leftovers
4
+ module DynamicProcessors
5
+ class CallDefinition
6
+ # :nocov:
7
+ using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
+ # :nocov:
9
+
10
+ def initialize(matcher, call_processor, definition_processor)
11
+ @matcher = matcher
12
+ @call_processor = call_processor
13
+ @definition_processor = definition_processor
14
+ end
15
+
16
+ def process(node, file)
17
+ return unless @matcher === node
18
+
19
+ call = @call_processor.process(nil, node, node)
20
+ (file.calls << call) if call
21
+
22
+ return if node.keep_line?
23
+
24
+ definition = @definition_processor.process(nil, node, node)
25
+ return unless definition
26
+
27
+ file.definitions << definition
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Leftovers
4
+ module DynamicProcessors
5
+ class Definition
6
+ # :nocov:
7
+ using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
+ # :nocov:
9
+
10
+ def initialize(matcher, processor)
11
+ @matcher = matcher
12
+ @processor = processor
13
+ end
14
+
15
+ def process(node, file)
16
+ return if node.keep_line?
17
+ return unless @matcher === node
18
+
19
+ definition = @processor.process(nil, node, node)
20
+ return unless definition
21
+
22
+ file.definitions << definition
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Leftovers
4
+ module DynamicProcessors
5
+ class Each
6
+ attr_reader :processors
7
+
8
+ def initialize(processors)
9
+ @processors = processors
10
+ end
11
+
12
+ def process(node, file)
13
+ @processors.each do |processor|
14
+ processor.process(node, file)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Leftovers
4
+ module DynamicProcessors
5
+ module Null
6
+ def self.process(_node, _file); end
7
+ end
8
+ end
9
+ end
@@ -9,11 +9,11 @@ module Leftovers
9
9
  @compiler.compile(erb).first
10
10
  end
11
11
 
12
- def add_insert_cmd(out, content)
12
+ def add_insert_cmd(out, content) # leftovers:keep
13
13
  out.push("#{content}\n")
14
14
  end
15
15
 
16
- def add_put_cmd(out, _content)
16
+ def add_put_cmd(out, _content) # leftovers:keep
17
17
  out
18
18
  end
19
19
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'erb'
4
- require_relative 'haml'
5
3
  require 'pathname'
6
4
 
7
5
  module Leftovers
@@ -16,12 +14,12 @@ module Leftovers
16
14
  @test = Leftovers.config.test_paths.allowed?(relative_path)
17
15
  end
18
16
 
19
- def ruby # rubocop:disable Metrics/MethodLength
17
+ def ruby
20
18
  case extname
21
19
  when '.haml'
22
- Leftovers::Haml.precompile(read)
20
+ ::Leftovers::Haml.precompile(read, self)
23
21
  when '.rhtml', '.rjs', '.erb'
24
- Leftovers::ERB.precompile(read)
22
+ ::Leftovers::ERB.precompile(read)
25
23
  else
26
24
  read
27
25
  end
@@ -1,16 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'fast_ignore'
4
3
  require 'set'
5
- require_relative 'parser'
6
- require_relative 'definition'
4
+ require 'parser'
7
5
 
8
6
  module Leftovers
9
7
  class FileCollector < ::Parser::AST::Processor # rubocop:disable Metrics/ClassLength
10
- attr_reader :calls
11
- attr_reader :definitions
8
+ attr_reader :calls, :definitions
12
9
 
13
- def initialize(ruby, file) # rubocop:disable Metrics/MethodLength
10
+ def initialize(ruby, file) # rubocop:disable Lint/MissingSuper
14
11
  @calls = []
15
12
  @definitions = []
16
13
  @allow_lines = Set.new.compare_by_identity
@@ -24,6 +21,8 @@ module Leftovers
24
21
  end
25
22
 
26
23
  def to_h
24
+ squash!
25
+
27
26
  {
28
27
  test?: @file.test?,
29
28
  calls: calls,
@@ -31,15 +30,25 @@ module Leftovers
31
30
  }
32
31
  end
33
32
 
33
+ def squash!
34
+ calls.flatten!
35
+ calls.compact!
36
+ calls.uniq!
37
+ definitions.flatten!
38
+ definitions.compact!
39
+ definitions.uniq!
40
+ definitions.reject! { |v| v == :keep }
41
+ end
42
+
34
43
  def collect
35
- ast, comments = Leftovers::Parser.parse_with_comments(@ruby, @file)
44
+ ast, comments = Leftovers::Parser.parse_with_comments(@ruby, @file.relative_path)
36
45
  process_comments(comments)
37
46
  process(ast)
38
- rescue Parser::SyntaxError => e
39
- Leftovers.warn "#{e.class}: #{e.message} #{filename}:#{e.diagnostic.location.line}:#{e.diagnostic.location.column}" # rubocop:disable Layout/LineLength
47
+ rescue ::Parser::SyntaxError => e
48
+ Leftovers.warn "\e[31m#{filename}:#{e.diagnostic.location.line}:#{e.diagnostic.location.column} SyntaxError: #{e.message}\e[0m" # rubocop:disable Layout/LineLength
40
49
  end
41
50
 
42
- METHOD_NAME_RE = /[[:alpha:]_][[:alnum:]_]*\b[\?!=]?/.freeze
51
+ METHOD_NAME_RE = /[[:alpha:]_][[:alnum:]_]*\b[?!=]?/.freeze
43
52
  NON_ALNUM_METHOD_NAME_RE = Regexp.union(%w{
44
53
  []= [] ** ~ +@ -@ * / % + - >> << &
45
54
  ^ | <=> <= >= < > === == != =~ !~ !
@@ -48,15 +57,13 @@ module Leftovers
48
57
  NAME_RE = Regexp.union(METHOD_NAME_RE, NON_ALNUM_METHOD_NAME_RE, CONSTANT_NAME_RE)
49
58
  LEFTOVERS_CALL_RE = /\bleftovers:call(?:s|ed|er|ers|) (#{NAME_RE}(?:[, :]+#{NAME_RE})*)/.freeze
50
59
  LEFTOVERS_ALLOW_RE = /\bleftovers:(?:keeps?|skip(?:s|ped|)|allow(?:s|ed|))\b/.freeze
51
- LEFTOVERS_TEST_RE = /\bleftovers:(?:for_tests?|tests?|testing)\b/.freeze
52
- def process_comments(comments) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
60
+ LEFTOVERS_TEST_RE = /\bleftovers:(?:for_tests?|tests?|testing|test_only)\b/.freeze
61
+ def process_comments(comments) # rubocop:disable Metrics/AbcSize
53
62
  comments.each do |comment|
54
63
  @allow_lines << comment.loc.line if comment.text.match?(LEFTOVERS_ALLOW_RE)
55
-
56
64
  @test_lines << comment.loc.line if comment.text.match?(LEFTOVERS_TEST_RE)
57
65
 
58
66
  next unless (match = comment.text.match(LEFTOVERS_CALL_RE))
59
- next unless match[1]
60
67
 
61
68
  match[1].scan(NAME_RE).each { |s| add_call(s.to_sym) }
62
69
  end
@@ -64,66 +71,75 @@ module Leftovers
64
71
 
65
72
  # grab method definitions
66
73
  def on_def(node)
67
- add_definition(node.children.first, node.loc.name)
68
-
74
+ add_definition(node)
69
75
  super
70
76
  end
71
77
 
72
78
  def on_ivasgn(node)
73
- add_definition(node.children.first, node.loc.name)
79
+ collect_variable_assign(node)
80
+ super
81
+ end
82
+
83
+ def on_gvasgn(node)
84
+ collect_variable_assign(node)
85
+ super
86
+ end
74
87
 
88
+ def on_cvasgn(node)
89
+ collect_variable_assign(node)
75
90
  super
76
91
  end
77
- alias_method :on_gvasgn, :on_ivasgn
78
- alias_method :on_cvasgn, :on_ivasgn
79
92
 
80
93
  def on_ivar(node)
81
- add_call(node.children.first)
94
+ add_call(node.name)
95
+ super
96
+ end
97
+
98
+ def on_gvar(node)
99
+ add_call(node.name)
100
+ super
101
+ end
82
102
 
103
+ def on_cvar(node)
104
+ add_call(node.name)
83
105
  super
84
106
  end
85
- alias_method :on_gvar, :on_ivar
86
- alias_method :on_cvar, :on_ivar
87
107
 
88
108
  def on_op_asgn(node)
89
109
  collect_op_asgn(node)
90
-
91
110
  super
92
111
  end
93
112
 
94
113
  def on_and_asgn(node)
95
114
  collect_op_asgn(node)
96
-
97
115
  super
98
116
  end
99
117
 
100
118
  def on_or_asgn(node)
101
119
  collect_op_asgn(node)
102
-
103
120
  super
104
121
  end
105
122
 
106
123
  # grab method calls
107
124
  def on_send(node)
108
125
  super
126
+ collect_send(node)
127
+ end
109
128
 
110
- add_call(node.children[1])
111
-
112
- collect_rules(node)
129
+ def on_csend(node)
130
+ super
131
+ collect_send(node)
113
132
  end
114
- alias_method :on_csend, :on_send
115
133
 
116
134
  def on_const(node)
117
135
  super
118
-
119
- add_call(node.children[1])
136
+ add_call(node.name)
120
137
  end
121
138
 
122
139
  # grab e.g. :to_s in each(&:to_s)
123
140
  def on_block_pass(node)
124
141
  super
125
-
126
- add_call(node.children.first.to_sym) if node.children.first&.string_or_symbol?
142
+ add_call(node.children.first.to_sym) if node.children.first.string_or_symbol?
127
143
  end
128
144
 
129
145
  # grab class Constant or module Constant
@@ -134,84 +150,88 @@ module Leftovers
134
150
 
135
151
  node = node.children.first
136
152
 
137
- add_definition(node.children[1], node.loc.name)
153
+ add_definition(node)
138
154
  end
139
155
  alias_method :on_module, :on_class
140
156
 
141
157
  # grab Constant = Class.new or CONSTANT = 'string'.freeze
142
158
  def on_casgn(node)
143
159
  super
144
-
145
- add_definition(node.children[1], node.loc.name)
146
-
147
- collect_rules(node)
160
+ add_definition(node)
161
+ collect_dynamic(node)
148
162
  end
149
163
 
150
164
  # grab calls to `alias new_method original_method`
151
165
  def on_alias(node)
152
166
  super
153
-
154
167
  new_method, original_method = node.children
155
-
156
- add_definition(new_method.children.first, new_method.loc.expression)
168
+ add_definition(new_method, name: new_method.children.first, loc: new_method.loc.expression)
157
169
  add_call(original_method.children.first)
158
170
  end
159
171
 
160
172
  private
161
173
 
162
- def test?(loc)
163
- @file.test? || @test_lines.include?(loc.line)
174
+ def test_line?(loc)
175
+ @file.test? ||
176
+ @test_lines.include?(loc.line)
164
177
  end
165
178
 
166
- def add_definition(name, loc)
179
+ def test_node?(node, loc)
180
+ test_line?(loc) || ::Leftovers.config.test_only === node
181
+ end
182
+
183
+ def add_definition(node, name: node.name, loc: node.loc.name)
167
184
  return if @allow_lines.include?(loc.line)
185
+ return if Leftovers.config.keep === node
168
186
 
169
- definitions << Leftovers::Definition.new(name, location: loc, file: @file, test: test?(loc))
187
+ definitions << Leftovers::Definition.new(name, location: loc, test: test_node?(node, loc))
170
188
  end
171
189
 
172
190
  def add_call(name)
173
191
  calls << name
174
192
  end
175
193
 
194
+ def collect_send(node)
195
+ add_call(node.name)
196
+ collect_dynamic(node)
197
+ end
198
+
176
199
  # just collects the call, super will collect the definition
177
200
  def collect_var_op_asgn(node)
178
201
  name = node.children.first
179
202
 
180
- return unless name
181
-
182
203
  add_call(name)
183
204
  end
184
205
 
185
206
  def collect_send_op_asgn(node)
186
207
  name = node.children[1]
187
208
 
188
- return unless name
189
-
190
209
  add_call(:"#{name}=")
191
210
  end
192
211
 
212
+ def collect_variable_assign(node)
213
+ add_definition(node)
214
+
215
+ collect_dynamic(node)
216
+ end
217
+
193
218
  def collect_op_asgn(node)
194
219
  node = node.children.first
220
+ # :nocov: # don't need else, it's exhaustive for callers
195
221
  case node.type
222
+ # :nocov:
196
223
  when :send then collect_send_op_asgn(node)
197
224
  when :ivasgn, :gvasgn, :cvasgn then collect_var_op_asgn(node)
198
225
  end
199
226
  end
200
227
 
201
- def collect_rules(node) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
202
- Leftovers.config.rules.each do |rule|
203
- next unless rule.match?(node.name, node.name_s, filename)
204
-
205
- next if rule.skip?
206
-
207
- calls.concat(rule.calls(node))
228
+ def collect_dynamic(node) # rubocop:disable Metrics/AbcSize
229
+ node.keep_line = @allow_lines.include?(node.loc.line)
230
+ node.test_line = test_line?(node.loc) unless node.keep_line?
208
231
 
209
- next if @allow_lines.include?(node.loc.line)
210
-
211
- node.file = @file
212
- node.test = test?(node.loc)
213
- definitions.concat(rule.definitions(node))
214
- end
232
+ Leftovers.config.dynamic.process(node, self)
233
+ rescue StandardError => e
234
+ raise ::Leftovers::Error, "#{e.class}: #{e.message}\nwhen processing #{node} at #{filename}:#{node.loc.line}:#{node.loc.column}", e.backtrace # rubocop:disable Layout/LineLength
215
235
  end
216
236
  end
217
237
  end