zombie-killer 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
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: {}