spoom 1.2.4 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +54 -55
  3. data/lib/spoom/cli/deadcode.rb +172 -0
  4. data/lib/spoom/cli/helper.rb +20 -0
  5. data/lib/spoom/cli/srb/bump.rb +200 -0
  6. data/lib/spoom/cli/srb/coverage.rb +224 -0
  7. data/lib/spoom/cli/srb/lsp.rb +159 -0
  8. data/lib/spoom/cli/srb/tc.rb +150 -0
  9. data/lib/spoom/cli/srb.rb +27 -0
  10. data/lib/spoom/cli.rb +72 -32
  11. data/lib/spoom/context/git.rb +2 -2
  12. data/lib/spoom/context/sorbet.rb +2 -2
  13. data/lib/spoom/deadcode/definition.rb +11 -0
  14. data/lib/spoom/deadcode/indexer.rb +222 -224
  15. data/lib/spoom/deadcode/location.rb +2 -2
  16. data/lib/spoom/deadcode/plugins/action_mailer.rb +2 -2
  17. data/lib/spoom/deadcode/plugins/action_mailer_preview.rb +19 -0
  18. data/lib/spoom/deadcode/plugins/actionpack.rb +4 -6
  19. data/lib/spoom/deadcode/plugins/active_model.rb +8 -8
  20. data/lib/spoom/deadcode/plugins/active_record.rb +9 -12
  21. data/lib/spoom/deadcode/plugins/active_support.rb +11 -0
  22. data/lib/spoom/deadcode/plugins/base.rb +1 -1
  23. data/lib/spoom/deadcode/plugins/graphql.rb +4 -4
  24. data/lib/spoom/deadcode/plugins/namespaces.rb +2 -4
  25. data/lib/spoom/deadcode/plugins/ruby.rb +8 -17
  26. data/lib/spoom/deadcode/plugins/sorbet.rb +4 -10
  27. data/lib/spoom/deadcode/plugins.rb +1 -0
  28. data/lib/spoom/deadcode/remover.rb +209 -174
  29. data/lib/spoom/deadcode/send.rb +9 -10
  30. data/lib/spoom/deadcode/visitor.rb +755 -0
  31. data/lib/spoom/deadcode.rb +40 -10
  32. data/lib/spoom/file_tree.rb +0 -16
  33. data/lib/spoom/sorbet/errors.rb +1 -1
  34. data/lib/spoom/sorbet/lsp/structures.rb +2 -2
  35. data/lib/spoom/version.rb +1 -1
  36. metadata +19 -15
  37. data/lib/spoom/cli/bump.rb +0 -198
  38. data/lib/spoom/cli/coverage.rb +0 -222
  39. data/lib/spoom/cli/lsp.rb +0 -168
  40. data/lib/spoom/cli/run.rb +0 -148
@@ -0,0 +1,19 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Spoom
5
+ module Deadcode
6
+ module Plugins
7
+ class ActionMailerPreview < Base
8
+ extend T::Sig
9
+
10
+ ignore_classes_inheriting_from("ActionMailer::Preview")
11
+
12
+ sig { override.params(indexer: Indexer, definition: Definition).void }
13
+ def on_define_method(indexer, definition)
14
+ definition.ignored! if indexer.nesting_class_superclass_name == "ActionMailer::Preview"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -38,18 +38,16 @@ module Spoom
38
38
 
39
39
  arg = send.args.first
40
40
  case arg
41
- when SyntaxTree::SymbolLiteral
42
- indexer.reference_method(indexer.node_string(arg.value), send.node)
43
- when SyntaxTree::VarRef
44
- indexer.reference_constant(indexer.node_string(arg), send.node)
41
+ when Prism::SymbolNode
42
+ indexer.reference_method(arg.unescaped, send.node)
45
43
  end
46
44
 
47
45
  send.each_arg_assoc do |key, value|
48
- key = indexer.node_string(key).delete_suffix(":")
46
+ key = key.slice.delete_suffix(":")
49
47
 
50
48
  case key
51
49
  when "if", "unless"
52
- indexer.reference_method(indexer.symbol_string(value), send.node) if value
50
+ indexer.reference_method(value.slice.delete_prefix(":"), send.node) if value
53
51
  else
54
52
  indexer.reference_constant(camelize(key), send.node)
55
53
  end
@@ -16,27 +16,27 @@ module Spoom
16
16
 
17
17
  case send.name
18
18
  when "attribute", "attributes"
19
- send.each_arg(SyntaxTree::SymbolLiteral) do |arg|
20
- indexer.reference_method(indexer.node_string(arg.value), send.node)
19
+ send.each_arg(Prism::SymbolNode) do |arg|
20
+ indexer.reference_method(arg.unescaped, send.node)
21
21
  end
22
22
  when "validate", "validates", "validates!", "validates_each"
23
- send.each_arg(SyntaxTree::SymbolLiteral) do |arg|
24
- indexer.reference_method(indexer.node_string(arg.value), send.node)
23
+ send.each_arg(Prism::SymbolNode) do |arg|
24
+ indexer.reference_method(arg.unescaped, send.node)
25
25
  end
26
26
  send.each_arg_assoc do |key, value|
27
- key = indexer.node_string(key).delete_suffix(":")
27
+ key = key.slice.delete_suffix(":")
28
28
 
29
29
  case key
30
30
  when "if", "unless"
31
- indexer.reference_method(indexer.symbol_string(value), send.node) if value
31
+ indexer.reference_method(value.slice.delete_prefix(":"), send.node) if value
32
32
  else
33
33
  indexer.reference_constant(camelize(key), send.node)
34
34
  end
35
35
  end
36
36
  when "validates_with"
37
37
  arg = send.args.first
38
- if arg.is_a?(SyntaxTree::SymbolLiteral)
39
- indexer.reference_constant(indexer.node_string(arg.value), send.node)
38
+ if arg.is_a?(Prism::SymbolNode)
39
+ indexer.reference_constant(arg.unescaped, send.node)
40
40
  end
41
41
  end
42
42
  end
@@ -73,8 +73,8 @@ module Spoom
73
73
  sig { override.params(indexer: Indexer, send: Send).void }
74
74
  def on_send(indexer, send)
75
75
  if send.recv.nil? && CALLBACKS.include?(send.name)
76
- send.each_arg(SyntaxTree::SymbolLiteral) do |arg|
77
- indexer.reference_method(indexer.node_string(arg.value), send.node)
76
+ send.each_arg(Prism::SymbolNode) do |arg|
77
+ indexer.reference_method(arg.unescaped, send.node)
78
78
  end
79
79
  return
80
80
  end
@@ -84,21 +84,18 @@ module Spoom
84
84
  case send.name
85
85
  when *CRUD_METHODS
86
86
  send.each_arg_assoc do |key, _value|
87
- key = indexer.symbol_string(key).delete_suffix(":")
87
+ key = key.slice.delete_suffix(":")
88
88
  indexer.reference_method("#{key}=", send.node)
89
89
  end
90
90
  when *ARRAY_METHODS
91
- send.each_arg(SyntaxTree::ArrayLiteral) do |arg|
92
- args = arg.contents
93
- next unless args.is_a?(SyntaxTree::Args)
91
+ send.each_arg(Prism::ArrayNode) do |arg|
92
+ arg.elements.each do |part|
93
+ next unless part.is_a?(Prism::HashNode)
94
94
 
95
- args.parts.each do |part|
96
- next unless part.is_a?(SyntaxTree::HashLiteral)
95
+ part.elements.each do |assoc|
96
+ next unless assoc.is_a?(Prism::AssocNode)
97
97
 
98
- part.assocs.each do |assoc|
99
- next unless assoc.is_a?(SyntaxTree::Assoc)
100
-
101
- key = indexer.symbol_string(assoc.key).delete_suffix(":")
98
+ key = assoc.key.slice.delete_suffix(":")
102
99
  indexer.reference_method("#{key}=", send.node)
103
100
  end
104
101
  end
@@ -15,6 +15,17 @@ module Spoom
15
15
  "before_setup",
16
16
  "before_teardown",
17
17
  )
18
+
19
+ SETUP_AND_TEARDOWN_METHODS = T.let(["setup", "teardown"], T::Array[String])
20
+
21
+ sig { override.params(indexer: Indexer, send: Send).void }
22
+ def on_send(indexer, send)
23
+ return unless send.recv.nil? && SETUP_AND_TEARDOWN_METHODS.include?(send.name)
24
+
25
+ send.each_arg(Prism::SymbolNode) do |arg|
26
+ indexer.reference_method(T.must(arg.value), send.node)
27
+ end
28
+ end
18
29
  end
19
30
  end
20
31
  end
@@ -269,7 +269,7 @@ module Spoom
269
269
  # return unless send.name == "dsl_method"
270
270
  # return if send.args.empty?
271
271
  #
272
- # method_name = indexer.node_string(send.args.first).delete_prefix(":")
272
+ # method_name = send.args.first.slice.delete_prefix(":")
273
273
  # indexer.reference_method(method_name, send.node)
274
274
  # end
275
275
  # end
@@ -29,16 +29,16 @@ module Spoom
29
29
  return unless send.recv.nil? && send.name == "field"
30
30
 
31
31
  arg = send.args.first
32
- return unless arg.is_a?(SyntaxTree::SymbolLiteral)
32
+ return unless arg.is_a?(Prism::SymbolNode)
33
33
 
34
- indexer.reference_method(indexer.node_string(arg.value), send.node)
34
+ indexer.reference_method(arg.unescaped, send.node)
35
35
 
36
36
  send.each_arg_assoc do |key, value|
37
- key = indexer.node_string(key).delete_suffix(":")
37
+ key = key.slice.delete_suffix(":")
38
38
  next unless key == "resolver_method"
39
39
  next unless value
40
40
 
41
- indexer.reference_method(indexer.symbol_string(value), send.node)
41
+ indexer.reference_method(value.slice.delete_prefix(":"), send.node)
42
42
  end
43
43
  end
44
44
  end
@@ -22,11 +22,9 @@ module Spoom
22
22
  sig { params(indexer: Indexer).returns(T::Boolean) }
23
23
  def used_as_namespace?(indexer)
24
24
  node = indexer.current_node
25
- return false unless node.is_a?(SyntaxTree::ClassDeclaration) || node.is_a?(SyntaxTree::ModuleDeclaration)
25
+ return false unless node.is_a?(Prism::ClassNode) || node.is_a?(Prism::ModuleNode)
26
26
 
27
- node.bodystmt.statements.body.any? do |stmt|
28
- !stmt.is_a?(SyntaxTree::VoidStmt)
29
- end
27
+ !!node.body
30
28
  end
31
29
  end
32
30
  end
@@ -27,34 +27,25 @@ module Spoom
27
27
  reference_symbol_as_constant(indexer, send, T.must(send.args.first))
28
28
  when "send", "__send__", "try"
29
29
  arg = send.args.first
30
- indexer.reference_method(indexer.node_string(arg.value), send.node) if arg.is_a?(SyntaxTree::SymbolLiteral)
30
+ indexer.reference_method(arg.unescaped, send.node) if arg.is_a?(Prism::SymbolNode)
31
31
  when "alias_method"
32
32
  last_arg = send.args.last
33
33
 
34
- name = case last_arg
35
- when SyntaxTree::SymbolLiteral
36
- indexer.node_string(last_arg.value)
37
- when SyntaxTree::StringLiteral
38
- last_arg.parts.map { |part| indexer.node_string(part) }.join
34
+ if last_arg.is_a?(Prism::SymbolNode) || last_arg.is_a?(Prism::StringNode)
35
+ indexer.reference_method(last_arg.unescaped, send.node)
39
36
  end
40
-
41
- return unless name
42
-
43
- indexer.reference_method(name, send.node)
44
37
  end
45
38
  end
46
39
 
47
40
  private
48
41
 
49
- sig { params(indexer: Indexer, send: Send, node: SyntaxTree::Node).void }
42
+ sig { params(indexer: Indexer, send: Send, node: Prism::Node).void }
50
43
  def reference_symbol_as_constant(indexer, send, node)
51
44
  case node
52
- when SyntaxTree::SymbolLiteral
53
- name = indexer.node_string(node.value)
54
- indexer.reference_constant(name, send.node)
55
- when SyntaxTree::StringLiteral
56
- string = T.must(indexer.node_string(node)[1..-2])
57
- string.split("::").each do |name|
45
+ when Prism::SymbolNode
46
+ indexer.reference_constant(node.unescaped, send.node)
47
+ when Prism::StringNode
48
+ node.unescaped.split("::").each do |name|
58
49
  indexer.reference_constant(name, send.node) unless name.empty?
59
50
  end
60
51
  end
@@ -21,24 +21,18 @@ module Spoom
21
21
 
22
22
  sig { params(indexer: Indexer, definition: Definition).returns(T::Boolean) }
23
23
  def sorbet_type_member?(indexer, definition)
24
- assign = indexer.nesting_node(SyntaxTree::Assign)
24
+ assign = indexer.nesting_node(Prism::ConstantWriteNode)
25
25
  return false unless assign
26
26
 
27
27
  value = assign.value
28
+ return false unless value.is_a?(Prism::CallNode)
28
29
 
29
- case value
30
- when SyntaxTree::MethodAddBlock
31
- indexer.node_string(value.call).match?(/^(type_member|type_template)/)
32
- when SyntaxTree::VCall
33
- indexer.node_string(value.value).match?(/^(type_member|type_template)/)
34
- else
35
- false
36
- end
30
+ value.name == :type_member || value.name == :type_template
37
31
  end
38
32
 
39
33
  sig { params(indexer: Indexer, definition: Definition).returns(T::Boolean) }
40
34
  def sorbet_enum_constant?(indexer, definition)
41
- /^(::)?T::Enum$/.match?(indexer.nesting_class_superclass_name) && indexer.nesting_block_call_name == "enums"
35
+ /^(::)?T::Enum$/.match?(indexer.nesting_class_superclass_name) && indexer.nesting_call&.name == :enums
42
36
  end
43
37
  end
44
38
  end
@@ -5,6 +5,7 @@ require_relative "plugins/base"
5
5
 
6
6
  require_relative "plugins/actionpack"
7
7
  require_relative "plugins/active_job"
8
+ require_relative "plugins/action_mailer_preview"
8
9
  require_relative "plugins/action_mailer"
9
10
  require_relative "plugins/active_model"
10
11
  require_relative "plugins/active_record"