zombie-killer 0.4 → 0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e180c7784f993ba500fad7e16198be9e6478ba85d7c6817e701fa7b2ba85e7e
4
- data.tar.gz: cabad90fad0492478d2ded00b62b1d45435cbc5750fcc1010a36483bac314710
3
+ metadata.gz: b796ce723f6b571b0e16446ca9c2edce61ed9f92c4a71d4f095f9f87de8175b5
4
+ data.tar.gz: e4e254e3a0b2f958e29a66e22a71ed4a87e13f7be1deb1becfc434dfff9a4134
5
5
  SHA512:
6
- metadata.gz: c27fc0f8f743e91a93dc3c0edd041538c5933015f0c30ede75e537267e3baadce783c8e3d2491f063dad3b1c9d782e8045851e5371344b15e52e3bb4ef6b41d5
7
- data.tar.gz: 61a29a2baee22c6190718405e436cc7889c3315cfcb1831864091ead8ae5d551637cc783a42b649167e7f314b926389e559aa00a1490a11f772a5d8715e222bf
6
+ metadata.gz: 4290278f9a662ba0ac56781e0fdfe7f783d2632d9db446a0deb9ef69f5901f7491f8850459155c4bd18f5ab9ed89703c662dcec268be35442351f535cb8d1b70
7
+ data.tar.gz: c3236fc0b1553aea76e960d49ec84f41f2fcdc91a284a728633836484e31c3475e9825d5e13dbe6921f426e09cf517099e78452ee44cf96255cac6c4ffd19e7b
data/NEWS.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  ## unreleased
4
4
 
5
+ ## 0.5, 2018-11-08
6
+
7
+ - zk: find all *.rb files in subdirectories if an argument is a directory
8
+ - Eager mode replacements:
9
+ ```rb
10
+ Builtins.size(foo)
11
+ if Builtins.size(bar) == 0 || Builtins.size(qux) > 0
12
+ Builtins.sformat("... %1 ...", val)
13
+ end
14
+
15
+ a = a + foo
16
+ @b = @b * bar
17
+ @@c = @@c - qux
18
+ ```
19
+ becomes
20
+ ```rb
21
+ foo.size
22
+ if bar.empty? || !qux.empty?
23
+ "... #{val} ..."
24
+ end
25
+
26
+ a += foo
27
+ @b *= bar
28
+ @@c -= baz
29
+ ```
30
+
5
31
  ## 0.4, 2018-11-02
6
32
 
7
33
  - Added zk -e, EagerRewriter (don't care about niceness, replace all)
@@ -1,4 +1,6 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  usage = <<'EOT'
3
5
  Usage:
4
6
  count_method_calls PATTERN RUBY_FILE
@@ -13,6 +15,7 @@ EOT
13
15
  require "parser"
14
16
  require "parser/current"
15
17
 
18
+ # Count occurrences of particular methods
16
19
  class MethodCounter < Parser::AST::Processor
17
20
  def initialize(pattern)
18
21
  @pattern = pattern
@@ -29,11 +32,11 @@ class MethodCounter < Parser::AST::Processor
29
32
  def on_send(node)
30
33
  super
31
34
  receiver, message = *node
32
- if receiver.nil?
33
- method = message.to_s
34
- else
35
- method = "#{const_to_s(receiver)}.#{message}"
36
- end
35
+ method = if receiver.nil?
36
+ message.to_s
37
+ else
38
+ "#{const_to_s(receiver)}.#{message}"
39
+ end
37
40
  # extglob: {brace,alternatives}
38
41
  @count += 1 if File.fnmatch(@pattern, method, File::FNM_EXTGLOB)
39
42
  end
@@ -49,7 +52,7 @@ class MethodCounter < Parser::AST::Processor
49
52
  "#{const_to_s(parent)}.#{name}"
50
53
  end
51
54
  else
52
- "%" # a non-identifier placeholder for "expression"
55
+ "%" # a non-identifier placeholder for "expression"
53
56
  end
54
57
  end
55
58
  end
data/bin/zk CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "docopt"
4
5
 
@@ -25,8 +26,10 @@ begin
25
26
  killer = ZombieKiller.new(eager: options["--eager"])
26
27
 
27
28
  files = options["FILES"]
28
- files << "**/*.rb" if files.empty?
29
+ files << "." if files.empty?
29
30
  files = files.flat_map do |pattern|
31
+ pattern += "/**/*.rb" if File.directory?(pattern)
32
+
30
33
  if pattern.include? "*"
31
34
  Dir[pattern]
32
35
  else
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "zombie_killer/killer"
2
4
  require_relative "zombie_killer/node_type_counter"
3
5
  require_relative "zombie_killer/rewriter"
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Keep track of the count of occurences of things (in code)
1
4
  class CodeHistogram
2
5
  attr_reader :counts
3
6
 
@@ -32,7 +35,7 @@ class CodeHistogram
32
35
  end
33
36
 
34
37
  def merge!(other)
35
- counts.merge!(other.counts) do |key, count, other_count|
38
+ counts.merge!(other.counts) do |_key, count, other_count|
36
39
  count + other_count
37
40
  end
38
41
  end
@@ -42,7 +45,7 @@ class CodeHistogram
42
45
  def invert_hash_preserving_duplicates(h)
43
46
  ih = {}
44
47
  h.each do |k, v|
45
- ih[v] = [] unless ih.has_key?(v)
48
+ ih[v] = [] unless ih.key?(v)
46
49
  ih[v] << k
47
50
  end
48
51
  ih
@@ -1,9 +1,52 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "unparser"
2
4
  require "set"
3
5
 
6
+ require "zombie_killer/rule"
7
+
4
8
  # Rewrite Zombies with their idiomatic replacements
5
9
  class EagerRewriter < Parser::TreeRewriter
6
- OPS = Parser::AST::Node.new(:const, [nil, :Ops])
10
+ def self.s(name, *children)
11
+ Parser::AST::Node.new(name, children)
12
+ end
13
+
14
+ def s(name, *children)
15
+ self.class.s(name, *children)
16
+ end
17
+
18
+ OPS = s(:const, nil, :Ops)
19
+ BUILTINS = s(:const, nil, :Builtins)
20
+ Arg = Rule::Arg
21
+
22
+ @rules = {}
23
+ class << self
24
+ attr_reader :rules
25
+ end
26
+
27
+ def self.r(**kwargs)
28
+ rule = Rule.new(**kwargs)
29
+ type = rule.from.type
30
+ @rules[type] ||= []
31
+ @rules[type] << rule
32
+ end
33
+
34
+ [
35
+ [:lvasgn, :lvar], # a = b
36
+ [:ivasgn, :ivar], # @a = @b
37
+ [:cvasgn, :cvar], # @@a = @@b
38
+ ].each do |xvasgn, xvar|
39
+ [:+, :-, :*].each do |asop|
40
+ r from: s(xvasgn,
41
+ Arg,
42
+ s(:send, s(xvar, Arg), asop, Arg)), # @ARG1 = @ARG2 + ARG3
43
+ to: ->(a, b, c) do # rubocop:disable Style/Lambda
44
+ if a == b
45
+ s(:op_asgn, s(xvasgn, a), asop, c) # @ARG1 += ARG3
46
+ end
47
+ end
48
+ end
49
+ end
7
50
 
8
51
  INFIX = {
9
52
  add: :+,
@@ -21,36 +64,76 @@ class EagerRewriter < Parser::TreeRewriter
21
64
  greater_or_equal: :>=
22
65
  }.freeze
23
66
 
24
- def s(name, *children)
25
- Parser::AST::Node.new(name, children)
67
+ INFIX.each do |prefix, infix|
68
+ r from: s(:send, OPS, prefix, Arg, Arg), # Ops.add(Arg, ARG2)
69
+ to: ->(a, b) { s(:send, a, infix, b) } # Arg + ARG2
26
70
  end
27
71
 
28
- def replace_node(old_node, new_node)
29
- source_range = old_node.loc.expression
30
- replace(source_range, Unparser.unparse(new_node))
72
+ r from: s(:send, BUILTINS, :size, Arg), # Builtins.size(Arg)
73
+ to: ->(a) { s(:send, a, :size) } # Arg.size
74
+
75
+ r from: s(:send, s(:send, Arg, :size), :>, s(:int, 0)), # Arg.size > 0
76
+ to: ->(a) { s(:send, s(:send, a, :empty?), :!) } # !Arg.empty?
77
+
78
+ r from: s(:send, s(:send, Arg, :size), :!=, s(:int, 0)), # Arg.size != 0
79
+ to: ->(a) { s(:send, s(:send, a, :empty?), :!) } # !Arg.empty?
80
+
81
+ r from: s(:send, s(:send, Arg, :size), :==, s(:int, 0)), # Arg.size == 0
82
+ to: ->(a) { s(:send, a, :empty?) } # Arg.empty?
83
+
84
+ r from: s(:send, s(:send, Arg, :size), :<=, s(:int, 0)), # Arg.size <= 0
85
+ to: ->(a) { s(:send, a, :empty?) } # Arg.empty?
86
+
87
+ r from: s(:send, s(:send, Arg, :size), :<, s(:int, 1)), # Arg.size < 1
88
+ to: ->(a) { s(:send, a, :empty?) } # Arg.empty?
89
+
90
+ def self.sformat_replacement1(format_literal, value)
91
+ verbatims = format_literal.split("%1", -1)
92
+ return nil unless verbatims.size == 2
93
+ s(:dstr, s(:str, verbatims[0]), value, s(:str, verbatims[1]))
31
94
  end
32
95
 
33
- def on_send(node)
34
- super
35
- receiver, name, *args = *node
36
- replacement = INFIX[name]
37
- if receiver == OPS && replacement && args.size == 2
38
- replace_node(node, s(:send, args[0], replacement, args[1]))
39
- end
96
+ r from: s(:send, BUILTINS, :sformat, s(:str, Arg), Arg), # Builtins.sformat("...", val)
97
+ to: ->(fmt, val) { sformat_replacement1(fmt, val) }
98
+
99
+ # Does not improve readability much, fails on nil. Use foo&.each ?
100
+ # r from: s(:send, BUILTINS, :foreach, Arg),
101
+ # to: ->(a) { s(:send, a, :each) }
102
+
103
+ def unparser_sanitize(code_s)
104
+ # unparser converts "foo#{bar}baz"
105
+ # into "#{"foo"}#{bar}#{"baz"}"
106
+ # so this undoes the escaping of the litetrals
107
+ code_s.gsub(/
108
+ \#
109
+ \{"
110
+ (
111
+ [^"#]*
112
+ )
113
+ "\}
114
+ /x,
115
+ '\1')
40
116
  end
41
117
 
42
- AS_OPS = Set.new [:+, :-]
43
- def on_lvasgn(node)
44
- super
45
- vname1, value = *node
46
- return unless value && value.type == :send
47
- receiver, oname, *args = *value
48
- if vname1 == lvar_vname2(receiver) && AS_OPS.include?(oname)
49
- replace_node(node, s(:op_asgn, s(:lvasgn, vname1), oname, args[0]))
50
- end
118
+ def replace_node(old_node, new_node)
119
+ # puts "OLD #{old_node.inspect}"
120
+ # puts "NEW #{new_node.inspect}"
121
+ source_range = old_node.loc.expression
122
+ unp = Unparser.unparse(new_node)
123
+ unp = unparser_sanitize(unp)
124
+ # puts "UNP #{unp.inspect}"
125
+ replace(source_range, unp)
126
+ new_node
51
127
  end
52
128
 
53
- def lvar_vname2(receiver)
54
- receiver.children.first if receiver && receiver.type == :lvar
129
+ def process(node)
130
+ node = super(node)
131
+ return if node.nil?
132
+ trules = self.class.rules.fetch(node.type, [])
133
+ trules.find do |r|
134
+ replacement = r.match(node)
135
+ node = replace_node(node, replacement) if replacement
136
+ end
137
+ node
55
138
  end
56
139
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "parser"
2
4
  require "parser/current"
3
5
 
@@ -46,7 +48,7 @@ class ZombieKiller
46
48
  private
47
49
 
48
50
  def fixed_point(x, &lambda_x)
49
- while true
51
+ loop do
50
52
  y = lambda_x.call(x)
51
53
  return y if y == x
52
54
  x = y
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "set"
2
4
 
3
5
  # Niceness of a node means that it cannot be nil.
@@ -33,12 +35,12 @@ module Niceness
33
35
  # These are global, called with a nil receiver
34
36
  NICE_GLOBAL_METHODS = {
35
37
  # message, number of arguments
36
- :_ => 1,
38
+ _: 1
37
39
  }.freeze
38
40
 
39
41
  NICE_OPERATORS = {
40
42
  # message, number of arguments (other than receiver)
41
- :+ => 1,
43
+ :+ => 1
42
44
  }.freeze
43
45
 
44
46
  def nice_send(node)
@@ -51,7 +53,7 @@ module Niceness
51
53
  return false unless nice(receiver)
52
54
  arity = NICE_OPERATORS.fetch(message, -1)
53
55
  end
54
- return args.size == arity && args.all?{ |a| nice(a) }
56
+ args.size == arity && args.all? { |a| nice(a) }
55
57
  end
56
58
 
57
59
  def nice_begin(node)
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "parser"
2
4
 
3
5
  require_relative "code_histogram"
4
6
 
7
+ # Count node types (:send, :lvar etc)
5
8
  class NodeTypeCounter < Parser::Rewriter
6
9
  attr_reader :node_types
7
10
 
@@ -20,7 +23,7 @@ class NodeTypeCounter < Parser::Rewriter
20
23
  parser = Parser::CurrentRuby.new
21
24
  buffer = Parser::Source::Buffer.new(@filename)
22
25
  buffer.read
23
- ast = parser.parse(buffer)
26
+ ast = parser.parse(buffer)
24
27
 
25
28
  process(ast)
26
29
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "parser"
2
4
  require "parser/current"
3
5
  require "set"
@@ -175,10 +177,10 @@ class ZombieKillerRewriter < Parser::Rewriter
175
177
  end
176
178
 
177
179
  # def on_unless
178
- # Does not exist.
179
- # `unless` is parsed as an `if` with then_body and else_body swapped.
180
- # Compare with `while` and `until` which cannot do that and thus need
181
- # distinct node types.
180
+ # Does not exist.
181
+ # `unless` is parsed as an `if` with then_body and else_body swapped.
182
+ # Compare with `while` and `until` which cannot do that and thus need
183
+ # distinct node types.
182
184
  # end
183
185
 
184
186
  def on_case(node)
@@ -222,7 +224,7 @@ class ZombieKillerRewriter < Parser::Rewriter
222
224
 
223
225
  def on_send(node)
224
226
  super
225
- if is_call(node, :Ops, :add)
227
+ if call?(node, :Ops, :add)
226
228
  new_op = :+
227
229
 
228
230
  _ops, _add, a, b = *node
@@ -232,13 +234,13 @@ class ZombieKillerRewriter < Parser::Rewriter
232
234
  end
233
235
  end
234
236
 
235
- def on_block(node)
237
+ def on_block(_node)
236
238
  # ignore body, clean slate
237
239
  scope.clear
238
240
  end
239
241
  alias_method :on_for, :on_block
240
242
 
241
- def on_while(node)
243
+ def on_while(_node)
242
244
  # ignore both condition and body,
243
245
  # with a simplistic scope we cannot handle them
244
246
 
@@ -279,33 +281,32 @@ class ZombieKillerRewriter < Parser::Rewriter
279
281
  super
280
282
  end
281
283
 
282
- def on_ensure(node)
284
+ def on_ensure(_node)
283
285
  # (:ensure, guarded-code, ensuring-code)
284
286
  # guarded-code may be a :rescue or not
285
287
 
286
288
  scope.clear
287
289
  end
288
290
 
289
- def on_retry(node)
291
+ def on_retry(_node)
290
292
  # that makes the :rescue a loop, top-down data-flow fails
291
293
  raise TooComplexToTranslateError
292
294
  end
293
295
 
294
296
  private
295
297
 
296
- def is_call(node, namespace, message)
298
+ def call?(node, namespace, message)
297
299
  n_receiver, n_message = *node
298
300
  n_receiver && n_receiver.type == :const &&
299
- n_receiver.children[0] == nil &&
301
+ n_receiver.children[0].nil? &&
300
302
  n_receiver.children[1] == namespace &&
301
303
  n_message == message
302
304
  end
303
305
 
304
306
  def replace_node(old_node, new_node)
305
307
  source_range = old_node.loc.expression
306
- if !contains_comment?(source_range.source)
307
- replace(source_range, Unparser.unparse(new_node))
308
- end
308
+ return if contains_comment?(source_range.source)
309
+ replace(source_range, Unparser.unparse(new_node))
309
310
  end
310
311
 
311
312
  def contains_comment?(string)
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ARG; end
4
+ class ARG1 < ARG; end
5
+ class ARG2 < ARG; end
6
+ class ARG3 < ARG; end
7
+
8
+ # Rewriting rule
9
+ class Rule
10
+ Placeholder = Struct.new(:name) do
11
+ def inspect
12
+ name
13
+ end
14
+ end
15
+ Arg = Placeholder.new("Arg")
16
+ Arg1 = Placeholder.new("Arg1")
17
+
18
+ # @return [AST::Node]
19
+ attr_reader :from
20
+ # @return [Proc]
21
+ attr_reader :to
22
+
23
+ def initialize(from:, to:)
24
+ @from = from
25
+ @to = to
26
+ end
27
+
28
+ def match(node)
29
+ captures = match2(from, node)
30
+ return unless captures
31
+ if to.respond_to? :call
32
+ to.call(*captures)
33
+ else
34
+ to
35
+ end
36
+ end
37
+
38
+ # @return an array of captured values or nil
39
+ def match2(expected, actual)
40
+ # puts "M2 #{expected.inspect} #{actual.inspect}"
41
+ # p expected.class
42
+ # p actual.class
43
+ return [] if expected.nil? && actual.nil?
44
+ return nil if expected.nil? || actual.nil?
45
+
46
+ # if we're a node
47
+ case expected
48
+ when AST::Node
49
+ return nil if expected.type != actual.type
50
+ return nil if expected.children.size != actual.children.size
51
+
52
+ results = expected.children.zip(actual.children).map do |ec, ac|
53
+ match2(ec, ac)
54
+ end
55
+ # puts "#{results.inspect} for #{expected.inspect}"
56
+ results.flatten(1) if results.all?
57
+ when Rule::Arg
58
+ # puts "ARG #{actual.inspect}"
59
+ [actual]
60
+ else
61
+ expected == actual ? [] : nil
62
+ end
63
+ end
64
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Tracks state for a variable
2
4
  class VariableState
3
5
  attr_accessor :nice
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ZombieKiller
2
- VERSION = "0.4".freeze
4
+ VERSION = "0.5"
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "redcarpet"
2
4
 
3
5
  require_relative "../lib/zombie_killer"
@@ -46,9 +48,7 @@ class Describe
46
48
  def render
47
49
  parts = []
48
50
  parts << "describe #{@description.inspect} do"
49
- if !blocks.empty?
50
- parts << Code.indent(@blocks.map(&:render).join("\n\n"))
51
- end
51
+ parts << Code.indent(@blocks.map(&:render).join("\n\n")) unless blocks.empty?
52
52
  parts << "end"
53
53
  parts.join("\n")
54
54
  end
@@ -79,7 +79,7 @@ class RSpecRenderer < Redcarpet::Render::Base
79
79
 
80
80
  def paragraph(text)
81
81
  if text =~ /^\*\*(.*)\*\*$/
82
- @next_block_type = $1.downcase.to_sym
82
+ @next_block_type = Regexp.last_match(1).downcase.to_sym
83
83
  else
84
84
  first_sentence = text.split(/\.(\s+|$)/).first
85
85
  @description = first_sentence.sub(/^Zombie Killer /, "").sub(/\n/, " ")
@@ -88,16 +88,16 @@ class RSpecRenderer < Redcarpet::Render::Base
88
88
  nil
89
89
  end
90
90
 
91
- def block_code(code, language)
91
+ def block_code(code, _language)
92
92
  case @next_block_type
93
- when :original
94
- @original_code = code[0..-2]
95
- when :translated
96
- @translated_code = code[0..-2]
97
- when :unchanged
98
- @original_code = @translated_code = code[0..-2]
99
- else
100
- raise "Invalid next code block type: #@next_block_type.\n#{code}"
93
+ when :original
94
+ @original_code = code[0..-2]
95
+ when :translated
96
+ @translated_code = code[0..-2]
97
+ when :unchanged
98
+ @original_code = @translated_code = code[0..-2]
99
+ else
100
+ raise "Invalid next code block type: #{@next_block_type}.\n#{code}"
101
101
  end
102
102
  @next_block_type = :unknown
103
103
 
@@ -117,11 +117,11 @@ class RSpecRenderer < Redcarpet::Render::Base
117
117
 
118
118
  def doc_header
119
119
  Code.join([
120
- "# Generated from spec/zombie_killer_spec.md -- do not change!",
121
- "",
122
- "require \"spec_helper\"",
123
- "",
124
- ])
120
+ "# Generated from spec/zombie_killer_spec.md -- do not change!",
121
+ "",
122
+ "require \"spec_helper\"",
123
+ ""
124
+ ])
125
125
  end
126
126
 
127
127
  def doc_footer
@@ -142,9 +142,7 @@ class RSpecRenderer < Redcarpet::Render::Base
142
142
 
143
143
  def current_describe
144
144
  describe = @describe
145
- while describe.blocks.last.is_a?(Describe)
146
- describe = describe.blocks.last
147
- end
145
+ describe = describe.blocks.last while describe.blocks.last.is_a?(Describe)
148
146
  describe
149
147
  end
150
148
 
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "zombie_killer/rule"
5
+
6
+ describe Rule do
7
+ include AST::Sexp # the `s` method
8
+ extend AST::Sexp
9
+
10
+ BUILTINS = s(:const, nil, :Builtins)
11
+
12
+ context "a simple const rule" do
13
+ let(:rule) do
14
+ Rule.new(
15
+ from: s(:const, nil, :Round),
16
+ to: s(:lvar, :square)
17
+ )
18
+ end
19
+
20
+ describe "#match" do
21
+ it "matches what it should" do
22
+ node = s(:const, nil, :Round)
23
+ expect(!!rule.match(node)).to eq(true)
24
+ end
25
+
26
+ it "does not match nil" do
27
+ expect(!!rule.match(nil)).to eq(false)
28
+ end
29
+
30
+ it "does not match a different const" do
31
+ node = s(:const, nil, :Square)
32
+ expect(!!rule.match(node)).to eq(false)
33
+ end
34
+
35
+ it "does not match a differently namespaced const" do
36
+ node = s(:const, s(:const, nil, :Square), :Round)
37
+ expect(!!rule.match(node)).to eq(false)
38
+ end
39
+ end
40
+ end
41
+
42
+ context "a capturing rule" do
43
+ let(:rule) do
44
+ Rule.new(
45
+ from: s(:send, BUILTINS, :size, Rule::Arg), # Builtins.size(ARG1)
46
+ to: ->(a) { s(:send, a, :size) } # ARG1.size
47
+ )
48
+ end
49
+ let(:node) { s(:send, BUILTINS, :size, s(:send, nil, :foo)) }
50
+
51
+ describe "#match2" do
52
+ it "returns the captured node" do
53
+ expect(rule.match2(rule.from, node)).to eq([s(:send, nil, :foo)])
54
+ end
55
+ end
56
+
57
+ describe "#match" do
58
+ it "returns the replacement" do
59
+ expect(rule.match(node)).to eq(s(:send, s(:send, nil, :foo), :size))
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  if ENV["COVERAGE"]
2
4
  require "simplecov"
3
5
  SimpleCov.start
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Generated from spec/zombie_killer_spec.md -- do not change!
2
4
 
3
5
  require "spec_helper"
@@ -86,7 +88,7 @@ describe "ZombieKiller:" do
86
88
  expect(ZombieKiller.new.kill(original_code)).to eq(translated_code)
87
89
  end
88
90
 
89
- it "doesn't translate `Ops.add(nice_variable, literal)` when the variable got it's niceness via multiple assignemnt" do
91
+ it "doesn't translate `Ops.add(nice_variable, literal)` when the variable got it's niceness via multiple assignment" do
90
92
  original_code = cleanup(<<-EOT)
91
93
  v1, v2 = "Hello", "World"
92
94
  Ops.add(v1, v2)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zombie-killer
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.4'
4
+ version: '0.5'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Vidner
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-11-02 00:00:00.000000000 Z
12
+ date: 2018-11-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: docopt
@@ -127,6 +127,20 @@ dependencies:
127
127
  - - "~>"
128
128
  - !ruby/object:Gem::Version
129
129
  version: '3'
130
+ - !ruby/object:Gem::Dependency
131
+ name: rubocop
132
+ requirement: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '='
135
+ - !ruby/object:Gem::Version
136
+ version: 0.41.2
137
+ type: :development
138
+ prerelease: false
139
+ version_requirements: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - '='
142
+ - !ruby/object:Gem::Version
143
+ version: 0.41.2
130
144
  - !ruby/object:Gem::Dependency
131
145
  name: simplecov
132
146
  requirement: !ruby/object:Gem::Requirement
@@ -163,13 +177,15 @@ files:
163
177
  - lib/zombie_killer/niceness.rb
164
178
  - lib/zombie_killer/node_type_counter.rb
165
179
  - lib/zombie_killer/rewriter.rb
180
+ - lib/zombie_killer/rule.rb
166
181
  - lib/zombie_killer/variable_scope.rb
167
182
  - lib/zombie_killer/version.rb
168
183
  - spec/rspec_renderer.rb
184
+ - spec/rule_spec.rb
169
185
  - spec/spec_helper.rb
170
186
  - spec/zombie_killer_spec.md
171
187
  - spec/zombie_killer_spec.rb
172
- homepage: http://github.org/yast/zombie-killer
188
+ homepage: https://github.com/yast/zombie-killer
173
189
  licenses:
174
190
  - MIT
175
191
  metadata: {}