rubocop 1.9.1 → 1.10.0

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: 86b427813730f667d5978bd4b4ca7e8f6766adf4ddf713e413cf07a0345b84b2
4
- data.tar.gz: ede1028f4ebe7b6b1cd59de4d604e655c7115d928d38393ebd7febdbbff5b3e7
3
+ metadata.gz: d690ba94b53010f458962d3c50bae5ec6198e0b54565422b1bce58351429618a
4
+ data.tar.gz: 4b0a3f5d69b88b1db292620328a02c26f8315b37fc49b066e4aa24c317c22f86
5
5
  SHA512:
6
- metadata.gz: b5e3ca723e7530916204e9bfce59bec1452232765a0903093ba2b4783fc35f109e655251c2fce8e3b08dbebcf4a4e67a4301b00c8113d45f5f0bb2a63cc4be7f
7
- data.tar.gz: d91068c77da3ea787a5c36c287f1913bc94c23d98fc315604883cb21742f4c1031014e10c9470c67dca2fa3dfdc41eb848134f0a9565ff5a82e65aed5d6d2cf3
6
+ metadata.gz: 3655b57caafb4704d05658ef53f39e09f30e0f32d2def6908cfc6ecefcf7a0b67aeeafe862bc4ad76f96fb2c6b17b00a60997ad4af31fc0727e16c20ce1141da
7
+ data.tar.gz: 69f5289513c39feb34bf4df834e45b3288b76eda570d860ab0aab3a4742283cbaa5f9bebae9663fc6e7f3d00ae18bd8638bdc4d40b4c13516966fb349496aec4
data/README.md CHANGED
@@ -51,7 +51,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
51
51
  in your `Gemfile`:
52
52
 
53
53
  ```rb
54
- gem 'rubocop', '~> 1.9', require: false
54
+ gem 'rubocop', '~> 1.10', require: false
55
55
  ```
56
56
 
57
57
  See [versioning](https://docs.rubocop.org/rubocop/1.0/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -203,6 +203,13 @@ Bundler/OrderedGems:
203
203
 
204
204
  #################### Gemspec ###############################
205
205
 
206
+ Gemspec/DateAssignment:
207
+ Description: 'Checks that `date =` is not used in gemspec file, it is set automatically when the gem is packaged.'
208
+ Enabled: pending
209
+ VersionAdded: '1.10'
210
+ Include:
211
+ - '**/*.gemspec'
212
+
206
213
  Gemspec/DuplicatedAssignment:
207
214
  Description: 'An attribute assignment method calls should be listed only once in a gemspec.'
208
215
  Enabled: true
@@ -1446,23 +1453,31 @@ Lint/Debugger:
1446
1453
  Description: 'Check for debugger calls.'
1447
1454
  Enabled: true
1448
1455
  VersionAdded: '0.14'
1449
- VersionChanged: '0.49'
1450
- DebuggerReceivers:
1451
- - binding
1452
- - Kernel
1453
- - Pry
1456
+ VersionChanged: '1.10'
1457
+ DebuggerReceivers: [] # deprecated
1454
1458
  DebuggerMethods:
1455
- - debugger
1456
- - byebug
1457
- - remote_byebug
1458
- - pry
1459
- - remote_pry
1460
- - pry_remote
1461
- - console
1462
- - rescue
1463
- - save_and_open_page
1464
- - save_and_open_screenshot
1465
- - irb
1459
+ # Groups are available so that a specific group can be disabled in
1460
+ # a user's configuration, but are otherwise not significant.
1461
+ Kernel:
1462
+ - binding.irb
1463
+ Byebug:
1464
+ - byebug
1465
+ - remote_byebug
1466
+ - Kernel.byebug
1467
+ - Kernel.remote_byebug
1468
+ Capybara:
1469
+ - save_and_open_page
1470
+ - save_and_open_screenshot
1471
+ Pry:
1472
+ - binding.pry
1473
+ - binding.remote_pry
1474
+ - binding.pry_remote
1475
+ - Pry.rescue
1476
+ Rails:
1477
+ - debugger
1478
+ - Kernel.debugger
1479
+ WebConsole:
1480
+ - binding.console
1466
1481
 
1467
1482
  Lint/DeprecatedClassMethods:
1468
1483
  Description: 'Check for deprecated class method calls.'
@@ -3087,6 +3102,8 @@ Style/ConstantVisibility:
3087
3102
  visibility declarations.
3088
3103
  Enabled: false
3089
3104
  VersionAdded: '0.66'
3105
+ VersionChanged: '1.10'
3106
+ IgnoreModules: false
3090
3107
 
3091
3108
  # Checks that you have put a copyright in a comment before any code.
3092
3109
  #
@@ -3425,6 +3442,11 @@ Style/HashAsLastArrayItem:
3425
3442
  - braces
3426
3443
  - no_braces
3427
3444
 
3445
+ Style/HashConversion:
3446
+ Description: 'Avoid Hash[] in favor of ary.to_h or literal hashes.'
3447
+ Enabled: pending
3448
+ VersionAdded: '1.10'
3449
+
3428
3450
  Style/HashEachMethods:
3429
3451
  Description: 'Use Hash#each_key and Hash#each_value.'
3430
3452
  StyleGuide: '#hash-each'
@@ -187,6 +187,10 @@ changed_parameters:
187
187
  parameters: ExcludedMethods
188
188
  alternative: IgnoredMethods
189
189
  severity: warning
190
+ - cops: Lint/Debugger
191
+ parameters: DebuggerReceivers
192
+ reason: "`DebuggerReceivers` is no longer necessary, method receivers should be specified in `DebuggerMethods` instead."
193
+ severity: warning
190
194
 
191
195
  # Enforced styles that have been removed or replaced
192
196
  changed_enforced_styles:
data/lib/rubocop.rb CHANGED
@@ -152,6 +152,7 @@ require_relative 'rubocop/cop/bundler/gem_comment'
152
152
  require_relative 'rubocop/cop/bundler/insecure_protocol_source'
153
153
  require_relative 'rubocop/cop/bundler/ordered_gems'
154
154
 
155
+ require_relative 'rubocop/cop/gemspec/date_assignment'
155
156
  require_relative 'rubocop/cop/gemspec/duplicated_assignment'
156
157
  require_relative 'rubocop/cop/gemspec/ordered_dependencies'
157
158
  require_relative 'rubocop/cop/gemspec/required_ruby_version'
@@ -472,6 +473,7 @@ require_relative 'rubocop/cop/style/global_std_stream'
472
473
  require_relative 'rubocop/cop/style/global_vars'
473
474
  require_relative 'rubocop/cop/style/guard_clause'
474
475
  require_relative 'rubocop/cop/style/hash_as_last_array_item'
476
+ require_relative 'rubocop/cop/style/hash_conversion'
475
477
  require_relative 'rubocop/cop/style/hash_each_methods'
476
478
  require_relative 'rubocop/cop/style/hash_except'
477
479
  require_relative 'rubocop/cop/style/hash_like_case'
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gemspec
6
+ # This cop checks that `date =` is not used in gemspec file.
7
+ # It is set automatically when the gem is packaged.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # Gem::Specification.new do |spec|
13
+ # s.name = 'your_cool_gem_name'
14
+ # spec.date = Time.now.strftime('%Y-%m-%d')
15
+ # end
16
+ #
17
+ # # good
18
+ # Gem::Specification.new do |spec|
19
+ # s.name = 'your_cool_gem_name'
20
+ # end
21
+ #
22
+ class DateAssignment < Base
23
+ include RangeHelp
24
+ extend AutoCorrector
25
+
26
+ MSG = 'Do not use `date =` in gemspec, it is set automatically when the gem is packaged.'
27
+
28
+ def_node_matcher :gem_specification, <<~PATTERN
29
+ (block
30
+ (send
31
+ (const
32
+ (const {cbase nil?} :Gem) :Specification) :new)
33
+ ...)
34
+ PATTERN
35
+
36
+ def on_block(block_node)
37
+ return unless gem_specification(block_node)
38
+
39
+ block_parameter = block_node.arguments.first.source
40
+
41
+ date_assignment = block_node.descendants.detect do |node|
42
+ node.send_type? && node.receiver&.source == block_parameter && node.method?(:date=)
43
+ end
44
+
45
+ return unless date_assignment
46
+
47
+ add_offense(date_assignment) do |corrector|
48
+ range = range_by_whole_lines(date_assignment.source_range, include_final_newline: true)
49
+
50
+ corrector.remove(range)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -152,7 +152,7 @@ module RuboCop
152
152
  MSG = 'Indent the first argument one step more than %<base>s.'
153
153
 
154
154
  def on_send(node)
155
- return if enforce_first_argument_with_fixed_indentation?
155
+ return if style != :consistent && enforce_first_argument_with_fixed_indentation?
156
156
  return if !node.arguments? || node.operator_method?
157
157
 
158
158
  indent = base_indentation(node) + configured_indentation_width
@@ -31,6 +31,32 @@ module RuboCop
31
31
  # foo = if expression
32
32
  # 'bar'
33
33
  # end
34
+ #
35
+ # @example SupportedTypes: ['block', 'case', 'class', 'if', 'kwbegin', 'module'] (default)
36
+ # # good
37
+ # foo =
38
+ # if expression
39
+ # 'bar'
40
+ # end
41
+ #
42
+ # # good
43
+ # foo =
44
+ # [1].map do |i|
45
+ # i + 1
46
+ # end
47
+ #
48
+ # @example SupportedTypes: ['block']
49
+ # # good
50
+ # foo = if expression
51
+ # 'bar'
52
+ # end
53
+ #
54
+ # # good
55
+ # foo =
56
+ # [1].map do |i|
57
+ # 'bar' * i
58
+ # end
59
+ #
34
60
  class MultilineAssignmentLayout < Base
35
61
  include CheckAssignment
36
62
  include ConfigurableEnforcedStyle
@@ -47,7 +47,7 @@ module RuboCop
47
47
  end_pos = node.receiver.source_range.end_pos
48
48
 
49
49
  return if begin_pos - end_pos == 1 ||
50
- (range = range_between(end_pos, begin_pos - 1)).source == '['
50
+ (range = range_between(end_pos, begin_pos - 1)).source.start_with?('[')
51
51
 
52
52
  range
53
53
  end
@@ -3,8 +3,21 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # This cop checks for calls to debugger or pry.
7
- # The cop can be configured to define which methods and receivers must be fixed.
6
+ # This cop checks for debug calls (such as `debugger` or `binding.pry`) that should
7
+ # not be kept for production code.
8
+ #
9
+ # The cop can be configured using `DebuggerMethods`. By default, a number of gems
10
+ # debug entrypoints are configured (`Kernel`, `Byebug`, `Capybara`, `Pry`, `Rails`,
11
+ # and `WebConsole`). Additional methods can be added.
12
+ #
13
+ # Specific default groups can be disabled if necessary:
14
+ #
15
+ # [source,yaml]
16
+ # ----
17
+ # Lint/Debugger:
18
+ # WebConsole: ~
19
+ # ---
20
+ #
8
21
  #
9
22
  # @example
10
23
  #
@@ -33,14 +46,32 @@ module RuboCop
33
46
  # def some_method
34
47
  # do_something
35
48
  # end
49
+ #
50
+ # @example DebuggerMethods: [my_debugger]
51
+ #
52
+ # # bad (ok during development)
53
+ #
54
+ # def some_method
55
+ # my_debugger
56
+ # end
36
57
  class Debugger < Base
37
58
  MSG = 'Remove debugger entry point `%<source>s`.'
38
59
 
39
60
  RESTRICT_ON_SEND = [].freeze
40
61
 
62
+ def_node_matcher :kernel?, <<~PATTERN
63
+ (const {nil? cbase} :Kernel)
64
+ PATTERN
65
+
66
+ def_node_matcher :valid_receiver?, <<~PATTERN
67
+ {
68
+ (const {nil? cbase} %1)
69
+ (send {nil? #kernel?} %1)
70
+ }
71
+ PATTERN
72
+
41
73
  def on_send(node)
42
- return unless debugger_method?(node.method_name)
43
- return if !node.receiver.nil? && !debugger_receiver?(node)
74
+ return unless debugger_method?(node)
44
75
 
45
76
  add_offense(node)
46
77
  end
@@ -51,19 +82,32 @@ module RuboCop
51
82
  format(MSG, source: node.source)
52
83
  end
53
84
 
54
- def debugger_method?(name)
55
- cop_config.fetch('DebuggerMethods', []).include?(name.to_s)
85
+ def debugger_methods
86
+ @debugger_methods ||= begin
87
+ config = cop_config.fetch('DebuggerMethods', [])
88
+ values = config.is_a?(Array) ? config : config.values.flatten
89
+ values.map do |v|
90
+ next unless v
91
+
92
+ *receiver, method_name = v.split('.')
93
+ {
94
+ receiver: receiver.empty? ? nil : receiver.join.to_sym,
95
+ method_name: method_name.to_sym
96
+ }
97
+ end.compact
98
+ end
56
99
  end
57
100
 
58
- def debugger_receiver?(node)
59
- receiver = case node.receiver
60
- when RuboCop::AST::SendNode
61
- node.receiver.method_name
62
- when RuboCop::AST::ConstNode
63
- node.receiver.const_name
64
- end
101
+ def debugger_method?(send_node)
102
+ debugger_methods.any? do |method|
103
+ next unless method[:method_name] == send_node.method_name
65
104
 
66
- cop_config.fetch('DebuggerReceivers', []).include?(receiver.to_s)
105
+ if method[:receiver].nil?
106
+ send_node.receiver.nil?
107
+ else
108
+ valid_receiver?(send_node.receiver, method[:receiver])
109
+ end
110
+ end
67
111
  end
68
112
  end
69
113
  end
@@ -44,6 +44,8 @@ module RuboCop
44
44
  MSG = 'Use `%<constant>s.%<method>s(%<replacement_args>s)`' \
45
45
  ' instead of `%<original>s`.'
46
46
 
47
+ NO_ARG_ALGORITHM = %w[BF DES IDEA RC4].freeze
48
+
47
49
  def_node_matcher :algorithm_const, <<~PATTERN
48
50
  (send
49
51
  $(const
@@ -104,7 +106,7 @@ module RuboCop
104
106
  def algorithm_name(node)
105
107
  name = node.loc.name.source
106
108
 
107
- if openssl_class(node) == 'OpenSSL::Cipher'
109
+ if openssl_class(node) == 'OpenSSL::Cipher' && !NO_ARG_ALGORITHM.include?(name)
108
110
  name.scan(/.{3}/).join('-')
109
111
  else
110
112
  name
@@ -124,16 +126,23 @@ module RuboCop
124
126
  algorithm_name = algorithm_name(algorithm_constant)
125
127
 
126
128
  if openssl_class(algorithm_constant) == 'OpenSSL::Cipher'
127
- build_cipher_arguments(node, algorithm_name)
129
+ build_cipher_arguments(node, algorithm_name, node.arguments.empty?)
128
130
  else
129
131
  (["'#{algorithm_name}'"] + node.arguments.map(&:source)).join(', ')
130
132
  end
131
133
  end
132
134
 
133
- def build_cipher_arguments(node, algorithm_name)
135
+ def build_cipher_arguments(node, algorithm_name, no_arguments)
134
136
  algorithm_parts = algorithm_name.downcase.split('-')
135
137
  size_and_mode = sanitize_arguments(node.arguments).map(&:downcase)
136
- "'#{(algorithm_parts + size_and_mode + ['cbc']).take(3).join('-')}'"
138
+
139
+ if NO_ARG_ALGORITHM.include?(algorithm_parts.first.upcase) && no_arguments
140
+ "'#{algorithm_parts.first}'"
141
+ else
142
+ mode = 'cbc' unless size_and_mode == ['cbc']
143
+
144
+ "'#{(algorithm_parts + size_and_mode + [mode]).compact.take(3).join('-')}'"
145
+ end
137
146
  end
138
147
  end
139
148
  end
@@ -21,11 +21,11 @@ module RuboCop
21
21
  #
22
22
  class DuplicateRequire < Base
23
23
  MSG = 'Duplicate `%<method>s` detected.'
24
- REQUIRE_METHODS = %i[require require_relative].freeze
24
+ REQUIRE_METHODS = Set.new(%i[require require_relative]).freeze
25
25
  RESTRICT_ON_SEND = REQUIRE_METHODS
26
26
 
27
27
  def_node_matcher :require_call?, <<~PATTERN
28
- (send {nil? (const _ :Kernel)} {:#{REQUIRE_METHODS.join(' :')}} _)
28
+ (send {nil? (const _ :Kernel)} %REQUIRE_METHODS _)
29
29
  PATTERN
30
30
 
31
31
  def on_new_investigation
@@ -47,7 +47,7 @@ module RuboCop
47
47
  MSG = 'Odd `else` layout detected. Did you mean to use `elsif`?'
48
48
 
49
49
  def on_if(node)
50
- return if node.ternary? || node.elsif?
50
+ return if node.ternary?
51
51
 
52
52
  check(node)
53
53
  end
@@ -11,14 +11,10 @@ module RuboCop
11
11
  # @example
12
12
  #
13
13
  # # bad
14
- #
15
14
  # x < y < z
16
15
  # 10 <= x <= 20
17
16
  #
18
- # @example
19
- #
20
17
  # # good
21
- #
22
18
  # x < y && y < z
23
19
  # 10 <= x && x <= 20
24
20
  class MultipleComparison < Base
@@ -26,6 +22,7 @@ module RuboCop
26
22
 
27
23
  MSG = 'Use the `&&` operator to compare multiple values.'
28
24
  COMPARISON_METHODS = %i[< > <= >=].freeze
25
+ SET_OPERATION_OPERATORS = %i[& | ^].freeze
29
26
  RESTRICT_ON_SEND = COMPARISON_METHODS
30
27
 
31
28
  def_node_matcher :multiple_compare?, <<~PATTERN
@@ -34,6 +31,9 @@ module RuboCop
34
31
 
35
32
  def on_send(node)
36
33
  return unless (center = multiple_compare?(node))
34
+ # It allows multiple comparison using `&`, `|`, and `^` set operation operators.
35
+ # e.g. `x >= y & y < z`
36
+ return if center.send_type? && SET_OPERATION_OPERATORS.include?(center.method_name)
37
37
 
38
38
  add_offense(node) do |corrector|
39
39
  new_center = "#{center.source} && #{center.source}"
@@ -140,13 +140,15 @@ module RuboCop
140
140
  def replacement_range_and_content(node)
141
141
  variable, = *node
142
142
  loc = node.loc
143
+ expression = loc.expression
143
144
 
144
145
  if array_new?(variable)
145
- [node.parent.loc.expression, variable.source]
146
+ expression = node.parent.loc.expression if node.parent.array_type?
147
+ [expression, variable.source]
146
148
  elsif !variable.array_type?
147
- [loc.expression, "[#{variable.source}]"]
149
+ [expression, "[#{variable.source}]"]
148
150
  elsif redundant_brackets?(node)
149
- [loc.expression, remove_brackets(variable)]
151
+ [expression, remove_brackets(variable)]
150
152
  else
151
153
  [loc.operator, '']
152
154
  end
@@ -35,10 +35,10 @@ module RuboCop
35
35
  ensure_valid_preferred_delimiters
36
36
 
37
37
  if preferred_delimiters_config.key?('default')
38
- Hash[PERCENT_LITERAL_TYPES.map do |type|
38
+ PERCENT_LITERAL_TYPES.map do |type|
39
39
  [type, preferred_delimiters_config[type] ||
40
40
  preferred_delimiters_config['default']]
41
- end]
41
+ end.to_h
42
42
  else
43
43
  preferred_delimiters_config
44
44
  end
@@ -185,7 +185,7 @@ module RuboCop
185
185
 
186
186
  def sort!
187
187
  clear_enrollment_queue
188
- @registry = Hash[@registry.sort_by { |badge, _| badge.cop_name }]
188
+ @registry = @registry.sort_by { |badge, _| badge.cop_name }.to_h
189
189
 
190
190
  self
191
191
  end
@@ -26,6 +26,24 @@ module RuboCop
26
26
  # public_constant :BAZ
27
27
  # end
28
28
  #
29
+ # @example IgnoreModules: false (default)
30
+ # # bad
31
+ # class Foo
32
+ # MyClass = Struct.new()
33
+ # end
34
+ #
35
+ # # good
36
+ # class Foo
37
+ # MyClass = Struct.new()
38
+ # public_constant :MyClass
39
+ # end
40
+ #
41
+ # @example IgnoreModules: true
42
+ # # good
43
+ # class Foo
44
+ # MyClass = Struct.new()
45
+ # end
46
+ #
29
47
  class ConstantVisibility < Base
30
48
  MSG = 'Explicitly make `%<constant_name>s` public or private using ' \
31
49
  'either `#public_constant` or `#private_constant`.'
@@ -33,6 +51,7 @@ module RuboCop
33
51
  def on_casgn(node)
34
52
  return unless class_or_module_scope?(node)
35
53
  return if visibility_declaration?(node)
54
+ return if ignore_modules? && module?(node)
36
55
 
37
56
  message = message(node)
38
57
  add_offense(node, message: message)
@@ -40,6 +59,14 @@ module RuboCop
40
59
 
41
60
  private
42
61
 
62
+ def ignore_modules?
63
+ cop_config.fetch('IgnoreModules', false)
64
+ end
65
+
66
+ def module?(node)
67
+ node.children.last.class_constructor?
68
+ end
69
+
43
70
  def message(node)
44
71
  _namespace, constant_name, _value = *node
45
72
 
@@ -5,8 +5,8 @@ module RuboCop
5
5
  module Style
6
6
  # This cop checks for uses of double negation (`!!`) to convert something to a boolean value.
7
7
  #
8
- # When using `EnforcedStyle: allowed_in_returns`, allow double nagation in contexts
9
- # that use boolean as a return value. When using `EnforcedStyle: forbidden`, double nagation
8
+ # When using `EnforcedStyle: allowed_in_returns`, allow double negation in contexts
9
+ # that use boolean as a return value. When using `EnforcedStyle: forbidden`, double negation
10
10
  # should be forbidden always.
11
11
  #
12
12
  # @example
@@ -12,6 +12,11 @@ module RuboCop
12
12
  # The cop also checks that the line number given relative to `__LINE__` is
13
13
  # correct.
14
14
  #
15
+ # This cop will autocorrect incorrect or missing filename and line number
16
+ # values. However, if `eval` is called without a binding argument, the cop
17
+ # will not attempt to automatically add a binding, or add filename and
18
+ # line values.
19
+ #
15
20
  # @example
16
21
  # # bad
17
22
  # eval <<-RUBY
@@ -36,7 +41,21 @@ module RuboCop
36
41
  # def do_something
37
42
  # end
38
43
  # RUBY
44
+ #
45
+ # This cop works only when a string literal is given as a code string.
46
+ # No offence is reported if a string variable is given as below:
47
+ #
48
+ # @example
49
+ # # not checked
50
+ # code = <<-RUBY
51
+ # def do_something
52
+ # end
53
+ # RUBY
54
+ # eval code
55
+ #
39
56
  class EvalWithLocation < Base
57
+ extend AutoCorrector
58
+
40
59
  MSG = 'Pass `__FILE__` and `__LINE__` to `%<method_name>s`.'
41
60
  MSG_EVAL = 'Pass a binding, `__FILE__` and `__LINE__` to `eval`.'
42
61
  MSG_INCORRECT_FILE = 'Incorrect file for `%<method_name>s`; ' \
@@ -66,21 +85,28 @@ module RuboCop
66
85
  code = node.arguments.first
67
86
  return unless code && (code.str_type? || code.dstr_type?)
68
87
 
88
+ check_location(node, code)
89
+ end
90
+
91
+ private
92
+
93
+ def check_location(node, code)
69
94
  file, line = file_and_line(node)
70
95
 
71
96
  if line
72
97
  check_file(node, file)
73
98
  check_line(node, code)
99
+ elsif file
100
+ check_file(node, file)
101
+ add_offense_for_missing_line(node, code)
74
102
  else
75
- register_offense(node)
103
+ add_offense_for_missing_location(node, code)
76
104
  end
77
105
  end
78
106
 
79
- private
80
-
81
- def register_offense(node)
107
+ def register_offense(node, &block)
82
108
  msg = node.method?(:eval) ? MSG_EVAL : format(MSG, method_name: node.method_name)
83
- add_offense(node, message: msg)
109
+ add_offense(node, message: msg, &block)
84
110
  end
85
111
 
86
112
  def special_file_keyword?(node)
@@ -98,6 +124,10 @@ module RuboCop
98
124
  [node.arguments[base], node.arguments[base + 1]]
99
125
  end
100
126
 
127
+ def with_binding?(node)
128
+ node.method?(:eval) ? node.arguments.size >= 2 : true
129
+ end
130
+
101
131
  # FIXME: It's a Style/ConditionalAssignment's false positive.
102
132
  # rubocop:disable Style/ConditionalAssignment
103
133
  def with_lineno?(node)
@@ -109,18 +139,16 @@ module RuboCop
109
139
  end
110
140
  # rubocop:enable Style/ConditionalAssignment
111
141
 
112
- def message_incorrect_line(method_name, actual, sign, line_diff)
113
- expected =
114
- if line_diff.zero?
115
- '__LINE__'
116
- else
117
- "__LINE__ #{sign} #{line_diff}"
118
- end
142
+ def add_offense_for_incorrect_line(method_name, line_node, sign, line_diff)
143
+ expected = expected_line(sign, line_diff)
144
+ message = format(MSG_INCORRECT_LINE,
145
+ method_name: method_name,
146
+ actual: line_node.source,
147
+ expected: expected)
119
148
 
120
- format(MSG_INCORRECT_LINE,
121
- method_name: method_name,
122
- actual: actual.source,
123
- expected: expected)
149
+ add_offense(line_node.loc.expression, message: message) do |corrector|
150
+ corrector.replace(line_node, expected)
151
+ end
124
152
  end
125
153
 
126
154
  def check_file(node, file_node)
@@ -131,13 +159,14 @@ module RuboCop
131
159
  expected: '__FILE__',
132
160
  actual: file_node.source)
133
161
 
134
- add_offense(file_node, message: message)
162
+ add_offense(file_node, message: message) do |corrector|
163
+ corrector.replace(file_node, '__FILE__')
164
+ end
135
165
  end
136
166
 
137
167
  def check_line(node, code)
138
168
  line_node = node.arguments.last
139
- lineno_range = line_node.loc.expression
140
- line_diff = string_first_line(code) - lineno_range.first_line
169
+ line_diff = line_difference(line_node, code)
141
170
  if line_diff.zero?
142
171
  add_offense_for_same_line(node, line_node)
143
172
  else
@@ -145,6 +174,10 @@ module RuboCop
145
174
  end
146
175
  end
147
176
 
177
+ def line_difference(line_node, code)
178
+ string_first_line(code) - line_node.loc.expression.first_line
179
+ end
180
+
148
181
  def string_first_line(str_node)
149
182
  if str_node.heredoc?
150
183
  str_node.loc.heredoc_body.first_line
@@ -156,20 +189,47 @@ module RuboCop
156
189
  def add_offense_for_same_line(node, line_node)
157
190
  return if special_line_keyword?(line_node)
158
191
 
159
- add_offense(
160
- line_node.loc.expression,
161
- message: message_incorrect_line(node.method_name, line_node, nil, 0)
162
- )
192
+ add_offense_for_incorrect_line(node.method_name, line_node, nil, 0)
163
193
  end
164
194
 
165
195
  def add_offense_for_different_line(node, line_node, line_diff)
166
196
  sign = line_diff.positive? ? :+ : :-
167
197
  return if line_with_offset?(line_node, sign, line_diff.abs)
168
198
 
169
- add_offense(
170
- line_node.loc.expression,
171
- message: message_incorrect_line(node.method_name, line_node, sign, line_diff.abs)
172
- )
199
+ add_offense_for_incorrect_line(node.method_name, line_node, sign, line_diff.abs)
200
+ end
201
+
202
+ def expected_line(sign, line_diff)
203
+ if line_diff.zero?
204
+ '__LINE__'
205
+ else
206
+ "__LINE__ #{sign} #{line_diff.abs}"
207
+ end
208
+ end
209
+
210
+ def add_offense_for_missing_line(node, code)
211
+ register_offense(node) do |corrector|
212
+ line_str = missing_line(node, code)
213
+ corrector.insert_after(node.loc.expression.end, ", #{line_str}")
214
+ end
215
+ end
216
+
217
+ def add_offense_for_missing_location(node, code)
218
+ if node.method?(:eval) && !with_binding?(node)
219
+ register_offense(node)
220
+ return
221
+ end
222
+
223
+ register_offense(node) do |corrector|
224
+ line_str = missing_line(node, code)
225
+ corrector.insert_after(node.loc.expression.end, ", __FILE__, #{line_str}")
226
+ end
227
+ end
228
+
229
+ def missing_line(node, code)
230
+ line_diff = line_difference(node.arguments.last, code)
231
+ sign = line_diff.positive? ? :+ : :-
232
+ expected_line(sign, line_diff)
173
233
  end
174
234
  end
175
235
  end
@@ -97,7 +97,7 @@ module RuboCop
97
97
  replacement = ' &block'
98
98
  replacement = ",#{replacement}" unless arg_range.source.end_with?(',')
99
99
  corrector.insert_after(arg_range, replacement) unless last_arg.blockarg_type?
100
- elsif node.call_type?
100
+ elsif node.call_type? || node.zsuper_type?
101
101
  corrector.insert_after(node, '(&block)')
102
102
  else
103
103
  corrector.insert_after(node.loc.name, '(&block)')
@@ -5,13 +5,12 @@ module RuboCop
5
5
  module Style
6
6
  # This cop enforces consistency when using exponential notation
7
7
  # for numbers in the code (eg 1.2e4). Different styles are supported:
8
- # * `scientific` which enforces a mantissa between 1 (inclusive)
9
- # and 10 (exclusive).
10
- # * `engineering` which enforces the exponent to be a multiple of 3
11
- # and the mantissa to be between 0.1 (inclusive)
12
- # and 10 (exclusive).
13
- # * `integral` which enforces the mantissa to always be a whole number
14
- # without trailing zeroes.
8
+ #
9
+ # * `scientific` which enforces a mantissa between 1 (inclusive) and 10 (exclusive).
10
+ # * `engineering` which enforces the exponent to be a multiple of 3 and the mantissa
11
+ # to be between 0.1 (inclusive) and 10 (exclusive).
12
+ # * `integral` which enforces the mantissa to always be a whole number without
13
+ # trailing zeroes.
15
14
  #
16
15
  # @example EnforcedStyle: scientific (default)
17
16
  # # Enforces a mantissa between 1 (inclusive) and 10 (exclusive).
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks the usage of pre-2.1 `Hash[args]` method of converting enumerables and
7
+ # sequences of values to hashes.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # Hash[ary]
12
+ #
13
+ # # good
14
+ # ary.to_h
15
+ #
16
+ # # bad
17
+ # Hash[key1, value1, key2, value2]
18
+ #
19
+ # # good
20
+ # {key1 => value1, key2 => value2}
21
+ class HashConversion < Base
22
+ extend AutoCorrector
23
+
24
+ MSG_TO_H = 'Prefer ary.to_h to Hash[ary].'
25
+ MSG_LITERAL_MULTI_ARG = 'Prefer literal hash to Hash[arg1, arg2, ...].'
26
+ MSG_LITERAL_HASH_ARG = 'Prefer literal hash to Hash[key: value, ...].'
27
+ MSG_SPLAT = 'Prefer array_of_pairs.to_h to Hash[*array].'
28
+ RESTRICT_ON_SEND = %i[[]].freeze
29
+
30
+ def_node_matcher :hash_from_array?, '(send (const {nil? cbase} :Hash) :[] ...)'
31
+
32
+ def on_send(node)
33
+ return unless hash_from_array?(node)
34
+
35
+ # There are several cases:
36
+ # If there is one argument:
37
+ # Hash[ary] => ary.to_h
38
+ # Hash[*ary] => don't suggest corrections
39
+ # If there is 0 or 2+ arguments:
40
+ # Hash[a1, a2, a3, a4] => {a1 => a2, a3 => a4}
41
+ # ...but don't suggest correction if there is odd number of them (it is a bug)
42
+ node.arguments.count == 1 ? single_argument(node) : multi_argument(node)
43
+ end
44
+
45
+ private
46
+
47
+ def single_argument(node)
48
+ first_argument = node.first_argument
49
+ if first_argument.hash_type?
50
+ add_offense(node, message: MSG_LITERAL_HASH_ARG) do |corrector|
51
+ corrector.replace(node, "{#{first_argument.source}}")
52
+ end
53
+ elsif first_argument.splat_type?
54
+ add_offense(node, message: MSG_SPLAT)
55
+ else
56
+ add_offense(node, message: MSG_TO_H) do |corrector|
57
+ corrector.replace(node, "#{first_argument.source}.to_h")
58
+ end
59
+ end
60
+ end
61
+
62
+ def multi_argument(node)
63
+ if node.arguments.count.odd?
64
+ add_offense(node, message: MSG_LITERAL_MULTI_ARG)
65
+ else
66
+ add_offense(node, message: MSG_LITERAL_MULTI_ARG) do |corrector|
67
+ corrector.replace(node, args_to_hash(node.arguments))
68
+ end
69
+ end
70
+ end
71
+
72
+ def args_to_hash(args)
73
+ content = args.each_slice(2)
74
+ .map { |arg1, arg2| "#{arg1.source} => #{arg2.source}" }
75
+ .join(', ')
76
+ "{#{content}}"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -81,13 +81,13 @@ module RuboCop
81
81
  }
82
82
 
83
83
  PERL_VARS =
84
- Hash[ENGLISH_VARS.flat_map { |k, vs| vs.map { |v| [v, [k]] } }]
84
+ ENGLISH_VARS.flat_map { |k, vs| vs.map { |v| [v, [k]] } }.to_h
85
85
 
86
86
  ENGLISH_VARS.merge!(
87
- Hash[ENGLISH_VARS.flat_map { |_, vs| vs.map { |v| [v, [v]] } }]
87
+ ENGLISH_VARS.flat_map { |_, vs| vs.map { |v| [v, [v]] } }.to_h
88
88
  )
89
89
  PERL_VARS.merge!(
90
- Hash[PERL_VARS.flat_map { |_, vs| vs.map { |v| [v, [v]] } }]
90
+ PERL_VARS.flat_map { |_, vs| vs.map { |v| [v, [v]] } }.to_h
91
91
  )
92
92
  ENGLISH_VARS.each_value(&:freeze).freeze
93
93
  PERL_VARS.each_value(&:freeze).freeze
@@ -63,7 +63,7 @@ module RuboCop
63
63
  # rubocop:enable Metrics/AbcSize
64
64
 
65
65
  def ordered_offense_counts(offense_counts)
66
- Hash[offense_counts.sort_by { |k, v| [-v, k] }]
66
+ offense_counts.sort_by { |k, v| [-v, k] }.to_h
67
67
  end
68
68
 
69
69
  def total_offense_count(offense_counts)
@@ -51,7 +51,7 @@ module RuboCop
51
51
  # rubocop:enable Metrics/AbcSize
52
52
 
53
53
  def ordered_offense_counts(offense_counts)
54
- Hash[offense_counts.sort_by { |k, v| [-v, k] }]
54
+ offense_counts.sort_by { |k, v| [-v, k] }.to_h
55
55
  end
56
56
 
57
57
  def total_offense_count(offense_counts)
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.9.1'
6
+ STRING = '1.10.0'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, '\
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.1
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2021-02-01 00:00:00.000000000 Z
13
+ date: 2021-02-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: parallel
@@ -244,6 +244,7 @@ files:
244
244
  - lib/rubocop/cop/documentation.rb
245
245
  - lib/rubocop/cop/exclude_limit.rb
246
246
  - lib/rubocop/cop/force.rb
247
+ - lib/rubocop/cop/gemspec/date_assignment.rb
247
248
  - lib/rubocop/cop/gemspec/duplicated_assignment.rb
248
249
  - lib/rubocop/cop/gemspec/ordered_dependencies.rb
249
250
  - lib/rubocop/cop/gemspec/required_ruby_version.rb
@@ -655,6 +656,7 @@ files:
655
656
  - lib/rubocop/cop/style/global_vars.rb
656
657
  - lib/rubocop/cop/style/guard_clause.rb
657
658
  - lib/rubocop/cop/style/hash_as_last_array_item.rb
659
+ - lib/rubocop/cop/style/hash_conversion.rb
658
660
  - lib/rubocop/cop/style/hash_each_methods.rb
659
661
  - lib/rubocop/cop/style/hash_except.rb
660
662
  - lib/rubocop/cop/style/hash_like_case.rb
@@ -866,7 +868,7 @@ metadata:
866
868
  homepage_uri: https://rubocop.org/
867
869
  changelog_uri: https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md
868
870
  source_code_uri: https://github.com/rubocop-hq/rubocop/
869
- documentation_uri: https://docs.rubocop.org/rubocop/1.9/
871
+ documentation_uri: https://docs.rubocop.org/rubocop/1.10/
870
872
  bug_tracker_uri: https://github.com/rubocop-hq/rubocop/issues
871
873
  post_install_message:
872
874
  rdoc_options: []