rubocop-performance 0.0.0 → 0.0.1

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: ab4ceb1e31551e3774fc563d05282b5339c9cb132d0e9a9ce1ce6f63358cf468
4
- data.tar.gz: 8d5d42ecb4e72ed9d06e30e7c609569469d4cf2fc847c8fabf602723ac8b4c21
3
+ metadata.gz: 43633d075fac26295caf28e79d71f56cdbacd6d7853aead53b3ca35e4f29f99b
4
+ data.tar.gz: 60886d6ea540f6ed25014424376b2e0cf0f489a2291e298f1f4cbf6084f3e580
5
5
  SHA512:
6
- metadata.gz: a42a85f9011b35eae2a99ffa9763bcfa2f409079b4c1aba3e0659adb351046cc5d8c42e5b374dda1f1d78289870339ee5fe8a34460a89d38a8fe2c24589398b5
7
- data.tar.gz: 2acc0104f805827d517ebca4d9a29a382642af8b53fec7691b86bb53eaabe22a29b7c1df2b91c0d53e27429430fa191788477b19545dec4d17c286432bc03a03
6
+ metadata.gz: b7b30f444965b49aa1597f9f1e483802b3c2f2f786c71fd577f6af18dbdf268e0100793982aa2d8979b86c7a9f302b0b211694e5f8e04c6b2eeaae85c7dd0591
7
+ data.tar.gz: ac1707a5f94ae008d1baf8c90b2ff269a6bfb3da8e27ed4adb1fa968b69e6dfdbc7ae8a074d18c4fa5d6e7c6b3d5de814f5b5dfbd38832338e80d107b854ad33
@@ -0,0 +1,24 @@
1
+ # This is the default configuration file.
2
+
3
+ Performance/CaseWhenSplat:
4
+ Description: >-
5
+ Reordering `when` conditions with a splat to the end
6
+ of the `when` branches can improve performance.
7
+ Enabled: false
8
+ AutoCorrect: false
9
+
10
+ Performance/ChainArrayAllocation:
11
+ Description: >-
12
+ Instead of chaining array methods that allocate new arrays, mutate an
13
+ existing array.
14
+ Reference: 'https://twitter.com/schneems/status/1034123879978029057'
15
+ Enabled: false
16
+
17
+ Performance/DoubleStartEndWith:
18
+ # Used to check for `starts_with?` and `ends_with?`.
19
+ # These methods are defined by `ActiveSupport`.
20
+ IncludeActiveSupportAliases: false
21
+
22
+ Performance/RedundantMerge:
23
+ # Max number of key-value pairs to consider an offense
24
+ MaxKeyValuePairs: 2
@@ -3,16 +3,19 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # Place `when` conditions that use splat at the end
7
- # of the list of `when` branches.
6
+ # Reordering `when` conditions with a splat to the end
7
+ # of the `when` branches can improve performance.
8
8
  #
9
9
  # Ruby has to allocate memory for the splat expansion every time
10
10
  # that the `case` `when` statement is run. Since Ruby does not support
11
11
  # fall through inside of `case` `when`, like some other languages do,
12
- # the order of the `when` branches does not matter. By placing any
12
+ # the order of the `when` branches should not matter. By placing any
13
13
  # splat expansions at the end of the list of `when` branches we will
14
14
  # reduce the number of times that memory has to be allocated for
15
- # the expansion.
15
+ # the expansion. The exception to this is if multiple of your `when`
16
+ # conditions can be true for any given condition. A likely scenario for
17
+ # this defining a higher level when condition to override a condition
18
+ # that is inside of the splat expansion.
16
19
  #
17
20
  # This is not a guaranteed performance improvement. If the data being
18
21
  # processed by the `case` condition is normalized in a manner that favors
@@ -54,9 +57,10 @@ module RuboCop
54
57
  include Alignment
55
58
  include RangeHelp
56
59
 
57
- MSG = 'Place `when` conditions with a splat ' \
58
- 'at the end of the `when` branches.'.freeze
59
- ARRAY_MSG = 'Do not expand array literals in `when` conditions.'.freeze
60
+ MSG = 'Reordering `when` conditions with a splat to the end ' \
61
+ 'of the `when` branches can improve performance.'.freeze
62
+ ARRAY_MSG = 'Pass the contents of array literals ' \
63
+ 'directly to `when` conditions.'.freeze
60
64
 
61
65
  def on_case(case_node)
62
66
  when_conditions = case_node.when_branches.flat_map(&:conditions)
@@ -18,7 +18,7 @@ module RuboCop
18
18
  # str.casecmp('ABC').zero?
19
19
  # 'abc'.casecmp(str).zero?
20
20
  class Casecmp < Cop
21
- MSG = 'Use `casecmp` instead of `%<methods>s`.'.freeze
21
+ MSG = 'Use `%<good>s` instead of `%<bad>s`.'.freeze
22
22
  CASE_METHODS = %i[downcase upcase].freeze
23
23
 
24
24
  def_node_matcher :downcase_eq, <<-PATTERN
@@ -43,16 +43,29 @@ module RuboCop
43
43
  PATTERN
44
44
 
45
45
  def on_send(node)
46
- return if part_of_ignored_node?(node)
46
+ return unless downcase_eq(node) || eq_downcase(node)
47
+ return unless (parts = take_method_apart(node))
47
48
 
48
- inefficient_comparison(node) do |range, is_other_part, *methods|
49
- ignore_node(node) if is_other_part
50
- add_offense(node, location: range,
51
- message: format(MSG, methods: methods.join(' ')))
52
- end
49
+ _, _, arg, variable = parts
50
+ good_method = build_good_method(arg, variable)
51
+
52
+ add_offense(
53
+ node,
54
+ message: format(MSG, good: good_method, bad: node.source)
55
+ )
53
56
  end
54
57
 
55
58
  def autocorrect(node)
59
+ return unless (parts = take_method_apart(node))
60
+
61
+ receiver, method, arg, variable = parts
62
+
63
+ correction(node, receiver, method, arg, variable)
64
+ end
65
+
66
+ private
67
+
68
+ def take_method_apart(node)
56
69
  if downcase_downcase(node)
57
70
  receiver, method, rhs = *node
58
71
  arg, = *rhs
@@ -65,52 +78,30 @@ module RuboCop
65
78
  end
66
79
 
67
80
  variable, = *receiver
68
- correction(node, receiver, method, arg, variable)
69
- end
70
-
71
- private
72
-
73
- def inefficient_comparison(node)
74
- loc = node.loc
75
81
 
76
- downcase_eq(node) do |send_downcase, case_method, eq_method, other|
77
- *_, method = *other
78
- range, is_other_part = downcase_eq_range(method, loc, send_downcase)
79
-
80
- yield range, is_other_part, case_method, eq_method
81
- return
82
- end
83
-
84
- eq_downcase(node) do |eq_method, send_downcase, case_method|
85
- range = loc.selector.join(send_downcase.loc.selector)
86
- yield range, false, eq_method, case_method
87
- end
88
- end
89
-
90
- def downcase_eq_range(method, loc, send_downcase)
91
- if CASE_METHODS.include?(method)
92
- [loc.expression, true]
93
- else
94
- [loc.selector.join(send_downcase.loc.selector), false]
95
- end
82
+ [receiver, method, arg, variable]
96
83
  end
97
84
 
98
85
  def correction(node, _receiver, method, arg, variable)
99
86
  lambda do |corrector|
100
87
  corrector.insert_before(node.loc.expression, '!') if method == :!=
101
88
 
102
- # we want resulting call to be parenthesized
103
- # if arg already includes one or more sets of parens, don't add more
104
- # or if method call already used parens, again, don't add more
105
- replacement = if arg.send_type? || !parentheses?(arg)
106
- "#{variable.source}.casecmp(#{arg.source}).zero?"
107
- else
108
- "#{variable.source}.casecmp#{arg.source}.zero?"
109
- end
89
+ replacement = build_good_method(arg, variable)
110
90
 
111
91
  corrector.replace(node.loc.expression, replacement)
112
92
  end
113
93
  end
94
+
95
+ def build_good_method(arg, variable)
96
+ # We want resulting call to be parenthesized
97
+ # if arg already includes one or more sets of parens, don't add more
98
+ # or if method call already used parens, again, don't add more
99
+ if arg.send_type? || !parentheses?(arg)
100
+ "#{variable.source}.casecmp(#{arg.source}).zero?"
101
+ else
102
+ "#{variable.source}.casecmp#{arg.source}.zero?"
103
+ end
104
+ end
114
105
  end
115
106
  end
116
107
  end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop is used to identify usages of
7
+ # @example
8
+ # # bad
9
+ # array = ["a", "b", "c"]
10
+ # array.compact.flatten.map { |x| x.downcase }
11
+ #
12
+ # Each of these methods (`compact`, `flatten`, `map`) will generate a
13
+ # new intermediate array that is promptly thrown away. Instead it is
14
+ # faster to mutate when we know it's safe.
15
+ #
16
+ # @example
17
+ # # good.
18
+ # array = ["a", "b", "c"]
19
+ # array.compact!
20
+ # array.flatten!
21
+ # array.map! { |x| x.downcase }
22
+ # array
23
+ class ChainArrayAllocation < Cop
24
+ include RangeHelp
25
+
26
+ # These methods return a new array but only sometimes. They must be
27
+ # called with an argument. For example:
28
+ #
29
+ # [1,2].first # => 1
30
+ # [1,2].first(1) # => [1]
31
+ #
32
+ RETURN_NEW_ARRAY_WHEN_ARGS = ':first :last :pop :sample :shift '.freeze
33
+
34
+ # These methods return a new array only when called without a block.
35
+ RETURNS_NEW_ARRAY_WHEN_NO_BLOCK = ':zip :product '.freeze
36
+
37
+ # These methods ALWAYS return a new array
38
+ # after they're called it's safe to mutate the the resulting array
39
+ ALWAYS_RETURNS_NEW_ARRAY = ':* :+ :- :collect :compact :drop '\
40
+ ':drop_while :flatten :map :reject ' \
41
+ ':reverse :rotate :select :shuffle :sort ' \
42
+ ':take :take_while :transpose :uniq ' \
43
+ ':values_at :| '.freeze
44
+
45
+ # These methods have a mutation alternative. For example :collect
46
+ # can be called as :collect!
47
+ HAS_MUTATION_ALTERNATIVE = ':collect :compact :flatten :map :reject '\
48
+ ':reverse :rotate :select :shuffle :sort '\
49
+ ':uniq '.freeze
50
+ MSG = 'Use `%<method>s...%<second_method>s!` instead of `%<method>s' \
51
+ '...%<second_method>s`.'.freeze
52
+
53
+ def_node_matcher :flat_map_candidate?, <<-PATTERN
54
+ {
55
+ (send (send _ ${#{RETURN_NEW_ARRAY_WHEN_ARGS}} {int lvar ivar cvar gvar}) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
56
+ (send (block (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY} }) ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
57
+ (send (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK}} ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
58
+ }
59
+ PATTERN
60
+
61
+ def on_send(node)
62
+ flat_map_candidate?(node) do |fm, sm, _|
63
+ range = range_between(
64
+ node.loc.dot.begin_pos,
65
+ node.source_range.end_pos
66
+ )
67
+ add_offense(
68
+ node,
69
+ location: range,
70
+ message: format(MSG, method: fm, second_method: sm)
71
+ )
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -48,6 +48,7 @@ module RuboCop
48
48
  compare?(node) do |send, var_a, var_b, body|
49
49
  replaceable_body?(body, var_a, var_b) do |method, args_a, args_b|
50
50
  return unless slow_compare?(method, args_a, args_b)
51
+
51
52
  range = compare_range(send, node)
52
53
 
53
54
  add_offense(
@@ -78,8 +79,10 @@ module RuboCop
78
79
 
79
80
  def slow_compare?(method, args_a, args_b)
80
81
  return false unless args_a == args_b
82
+
81
83
  if method == :[]
82
84
  return false unless args_a.size == 1
85
+
83
86
  key = args_a.first
84
87
  return false unless %i[sym str int].include?(key.type)
85
88
  else
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # In Ruby 2.4, `String#match?`, `Regexp#match?` and `Symbol#match?`
6
+ # In Ruby 2.4, `String#match?`, `Regexp#match?`, and `Symbol#match?`
7
7
  # have been added. The methods are faster than `match`.
8
8
  # Because the methods avoid creating a `MatchData` object or saving
9
9
  # backref.
@@ -101,6 +101,7 @@ module RuboCop
101
101
 
102
102
  def match_with_lvasgn?(node)
103
103
  return false unless node.match_with_lvasgn_type?
104
+
104
105
  regexp, _rhs = *node
105
106
  regexp.to_regexp.named_captures.empty?
106
107
  end
@@ -3,8 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop is used to identify usages of `shuffle.first`, `shuffle.last`
7
- # and `shuffle[]` and change them to use `sample` instead.
6
+ # This cop is used to identify usages of `shuffle.first`,
7
+ # `shuffle.last`, and `shuffle[]` and change them to use
8
+ # `sample` instead.
8
9
  #
9
10
  # @example
10
11
  # # bad
@@ -88,12 +89,14 @@ module RuboCop
88
89
 
89
90
  def sample_size_for_two_args(first, second)
90
91
  return :unknown unless first.int_type? && first.to_a.first.zero?
92
+
91
93
  second.int_type? ? second.to_a.first : :unknown
92
94
  end
93
95
 
94
96
  def range_size(range_node)
95
97
  vals = range_node.to_a
96
98
  return :unknown unless vals.all?(&:int_type?)
99
+
97
100
  low, high = vals.map { |val| val.children[0] }
98
101
  return :unknown unless low.zero? && high >= 0
99
102
 
@@ -55,15 +55,21 @@ module RuboCop
55
55
  end
56
56
 
57
57
  def array?(node)
58
+ return true if node.array_type?
59
+ return false unless node.send_type?
60
+
58
61
  _, constant = *node.receiver
59
62
 
60
- node.array_type? || constant == :Array || node.method_name == :to_a
63
+ constant == :Array || node.method_name == :to_a
61
64
  end
62
65
 
63
66
  def hash?(node)
67
+ return true if node.hash_type?
68
+ return false unless node.send_type?
69
+
64
70
  _, constant = *node.receiver
65
71
 
66
- node.hash_type? || constant == :Hash || node.method_name == :to_h
72
+ constant == :Hash || node.method_name == :to_h
67
73
  end
68
74
  end
69
75
  end
@@ -26,7 +26,7 @@ module RuboCop
26
26
 
27
27
  def literal_at_start?(regex_str)
28
28
  # is this regexp 'literal' in the sense of only matching literal
29
- # chars, rather than using metachars like . and * and so on?
29
+ # chars, rather than using metachars like `.` and `*` and so on?
30
30
  # also, is it anchored at the start of the string?
31
31
  # (tricky: \s, \d, and so on are metacharacters, but other characters
32
32
  # escaped with a slash are just literals. LITERAL_REGEX takes all
@@ -22,7 +22,7 @@ module RuboCop
22
22
  include RangeHelp
23
23
 
24
24
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'.freeze
25
- DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/
25
+ DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
26
26
  DELETE = 'delete'.freeze
27
27
  TR = 'tr'.freeze
28
28
  BANG = '!'.freeze
@@ -87,6 +87,7 @@ module RuboCop
87
87
  unless first_param.str_type?
88
88
  return true if options
89
89
  return true unless first_source =~ DETERMINISTIC_REGEX
90
+
90
91
  # This must be done after checking DETERMINISTIC_REGEX
91
92
  # Otherwise things like \s will trip us up
92
93
  first_source = interpret_string_escapes(first_source)
@@ -35,3 +35,4 @@ require_relative 'performance/times_map'
35
35
  require_relative 'performance/unfreeze_string'
36
36
  require_relative 'performance/unneeded_sort'
37
37
  require_relative 'performance/uri_default_parser'
38
+ require_relative 'performance/chain_array_allocation'
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Performance
5
5
  module Version
6
- STRING = '0.0.0'
6
+ STRING = '0.0.1'
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-performance
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,22 +10,22 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-07-10 00:00:00.000000000 Z
13
+ date: 2018-10-31 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - "~>"
19
+ - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: '0.57'
21
+ version: 0.58.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
- - - "~>"
26
+ - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: '0.57'
28
+ version: 0.58.0
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: simplecov
31
31
  requirement: !ruby/object:Gem::Requirement
@@ -52,10 +52,12 @@ extra_rdoc_files:
52
52
  files:
53
53
  - LICENSE.txt
54
54
  - README.md
55
+ - config/default.yml
55
56
  - lib/rubocop-performance.rb
56
57
  - lib/rubocop/cop/performance/caller.rb
57
58
  - lib/rubocop/cop/performance/case_when_splat.rb
58
59
  - lib/rubocop/cop/performance/casecmp.rb
60
+ - lib/rubocop/cop/performance/chain_array_allocation.rb
59
61
  - lib/rubocop/cop/performance/compare_with_block.rb
60
62
  - lib/rubocop/cop/performance/count.rb
61
63
  - lib/rubocop/cop/performance/detect.rb
@@ -99,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
101
  requirements:
100
102
  - - ">="
101
103
  - !ruby/object:Gem::Version
102
- version: 2.3.0
104
+ version: 2.2.0
103
105
  required_rubygems_version: !ruby/object:Gem::Requirement
104
106
  requirements:
105
107
  - - ">="
@@ -107,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
109
  version: '0'
108
110
  requirements: []
109
111
  rubyforge_project:
110
- rubygems_version: 2.7.7
112
+ rubygems_version: 2.7.6
111
113
  signing_key:
112
114
  specification_version: 4
113
115
  summary: Automatic performance checking tool for Ruby code.