rubocop 1.36.0 → 1.37.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +21 -1
  4. data/lib/rubocop/arguments_env.rb +17 -0
  5. data/lib/rubocop/arguments_file.rb +17 -0
  6. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  7. data/lib/rubocop/cop/generator.rb +1 -2
  8. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  9. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  10. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  11. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +28 -3
  12. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  13. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  14. data/lib/rubocop/cop/lint/duplicate_methods.rb +11 -1
  15. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  16. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  17. data/lib/rubocop/cop/lint/empty_conditional_body.rb +19 -7
  18. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  19. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  20. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  21. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  22. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +12 -1
  23. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  24. data/lib/rubocop/cop/lint/redundant_require_statement.rb +29 -9
  25. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  26. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -2
  27. data/lib/rubocop/cop/lint/shadowed_exception.rb +0 -10
  28. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +3 -0
  29. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  30. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  31. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  32. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  33. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +6 -3
  34. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  35. data/lib/rubocop/cop/mixin/surrounding_space.rb +6 -5
  36. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  37. data/lib/rubocop/cop/style/access_modifier_declarations.rb +21 -1
  38. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  39. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  40. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  41. data/lib/rubocop/cop/style/collection_compact.rb +6 -1
  42. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  43. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  44. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  45. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  46. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  47. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  48. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  49. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  50. data/lib/rubocop/cop/style/operator_method_call.rb +39 -0
  51. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  52. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  53. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  54. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  55. data/lib/rubocop/cop/style/redundant_string_escape.rb +173 -0
  56. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  57. data/lib/rubocop/cop/style/static_class.rb +32 -1
  58. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  59. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  60. data/lib/rubocop/cop/style/word_array.rb +2 -0
  61. data/lib/rubocop/formatter/disabled_config_formatter.rb +8 -2
  62. data/lib/rubocop/options.rb +13 -13
  63. data/lib/rubocop/rspec/shared_contexts.rb +13 -1
  64. data/lib/rubocop/server/cache.rb +5 -1
  65. data/lib/rubocop/server/cli.rb +9 -2
  66. data/lib/rubocop/server/client_command/exec.rb +5 -0
  67. data/lib/rubocop/server/core.rb +2 -1
  68. data/lib/rubocop/server/socket_reader.rb +5 -1
  69. data/lib/rubocop/server.rb +1 -1
  70. data/lib/rubocop/version.rb +8 -3
  71. data/lib/rubocop.rb +3 -0
  72. metadata +10 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4bc2d4bd29fd7e81f4d7118fef9f55bcd2f086fd20ce383d85c2a1b7b26ea4e9
4
- data.tar.gz: 9cb3cef8736b03dd0834b94fefe4415a301d78d842df184a1a1d290973376070
3
+ metadata.gz: e928a8547def9cafd9cddc36b5a407aef7d46fdf9825941c7906073492d88668
4
+ data.tar.gz: 3ef9727850f93815ba310b7c3a8cf74e53a0ee18a0fcf6731fd97a3640ffe28d
5
5
  SHA512:
6
- metadata.gz: abeba580bae46458f6427c3d21c6532e95ff88e7d981d196f5013fee29c8f911146f229daa8e6f92d2f28bb14790edc46373a4366f9881700df18a7f5a1352fa
7
- data.tar.gz: 4bee5fc42db940e95cd6e3fba83680e6d7fd573a8ed3b7c65136a48d5f3b67ba8d299ecdd30a422afb95961d84c9b0452e3d1b6c1952e0edee1cea97ca704952
6
+ metadata.gz: f57e5d25375fe47734a6e198924db74b341501762dc8f54277a888788ab821215c96847940cec1af4d90673fe79ab906b451bf25bec90f9c8c080e78efc01111
7
+ data.tar.gz: 69cafbca956eaa49887596d255f6888057ede94f3621349398de95da5c477201f7bee7eae074ab4750ce0131dcb26d6dfdc77c31f5fcb478d2fc25d5ca73e68c
data/README.md CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
53
53
  in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 1.36', require: false
56
+ gem 'rubocop', '~> 1.37', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -1707,6 +1707,11 @@ Lint/DuplicateHashKey:
1707
1707
  VersionAdded: '0.34'
1708
1708
  VersionChanged: '0.77'
1709
1709
 
1710
+ Lint/DuplicateMagicComment:
1711
+ Description: 'Check for duplicated magic comments.'
1712
+ Enabled: pending
1713
+ VersionAdded: '1.37'
1714
+
1710
1715
  Lint/DuplicateMethods:
1711
1716
  Description: 'Check for duplicate method definitions.'
1712
1717
  Enabled: true
@@ -1957,6 +1962,8 @@ Lint/NestedMethodDefinition:
1957
1962
  Description: 'Do not use nested method definitions.'
1958
1963
  StyleGuide: '#no-nested-methods'
1959
1964
  Enabled: true
1965
+ AllowedMethods: []
1966
+ AllowedPatterns: []
1960
1967
  VersionAdded: '0.32'
1961
1968
 
1962
1969
  Lint/NestedPercentLiteral:
@@ -2021,7 +2028,9 @@ Lint/OrAssignmentToConstant:
2021
2028
  Lint/OrderedMagicComments:
2022
2029
  Description: 'Checks the proper ordering of magic comments and whether a magic comment is not placed before a shebang.'
2023
2030
  Enabled: true
2031
+ SafeAutoCorrect: false
2024
2032
  VersionAdded: '0.53'
2033
+ VersionChanged: '1.37'
2025
2034
 
2026
2035
  Lint/OutOfRangeRegexpRef:
2027
2036
  Description: 'Checks for out of range reference for Regexp because it always returns nil.'
@@ -3850,7 +3859,7 @@ Style/HashSyntax:
3850
3859
  - never
3851
3860
  # accepts both shorthand and explicit use of hash literal value.
3852
3861
  - either
3853
- # like "always", but will avoid mixing styles in a single hash
3862
+ # like "either", but will avoid mixing styles in a single hash
3854
3863
  - consistent
3855
3864
  # Force hashes that have a symbol value to use hash rockets
3856
3865
  UseHashRocketsWithSymbolValues: false
@@ -4498,6 +4507,12 @@ Style/OpenStructUse:
4498
4507
  Enabled: pending
4499
4508
  VersionAdded: '1.23'
4500
4509
 
4510
+ Style/OperatorMethodCall:
4511
+ Description: 'Checks for redundant dot before operator method call.'
4512
+ StyleGuide: '#operator-method-call'
4513
+ Enabled: pending
4514
+ VersionAdded: '1.37'
4515
+
4501
4516
  Style/OptionHash:
4502
4517
  Description: "Don't use option hashes when you can use keyword arguments."
4503
4518
  Enabled: false
@@ -4792,6 +4807,11 @@ Style/RedundantSortBy:
4792
4807
  Enabled: true
4793
4808
  VersionAdded: '0.36'
4794
4809
 
4810
+ Style/RedundantStringEscape:
4811
+ Description: 'Checks for redundant escapes in string literals.'
4812
+ Enabled: pending
4813
+ VersionAdded: '1.37'
4814
+
4795
4815
  Style/RegexpLiteral:
4796
4816
  Description: 'Use / or %r around regular expressions.'
4797
4817
  StyleGuide: '#percent-r'
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This is a class that reads optional command line arguments to rubocop from environment variable.
5
+ # @api private
6
+ class ArgumentsEnv
7
+ def self.read_as_arguments
8
+ if (arguments = ENV.fetch('RUBOCOP_OPTS', '')).empty?
9
+ []
10
+ else
11
+ require 'shellwords'
12
+
13
+ Shellwords.split(arguments)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This is a class that reads optional command line arguments to rubocop from .rubocop file.
5
+ # @api private
6
+ class ArgumentsFile
7
+ def self.read_as_arguments
8
+ if File.exist?('.rubocop') && !File.directory?('.rubocop')
9
+ require 'shellwords'
10
+
11
+ File.read('.rubocop').shellsplit
12
+ else
13
+ []
14
+ end
15
+ end
16
+ end
17
+ end
@@ -41,13 +41,13 @@ module RuboCop
41
41
 
42
42
  def with_redirect
43
43
  if @options[:stderr]
44
- orig_stdout = $stdout.dup
45
- $stdout.reopen($stderr)
46
-
47
- result = yield
48
-
49
- $stdout.reopen(orig_stdout)
50
- result
44
+ orig_stdout = $stdout
45
+ begin
46
+ $stdout = $stderr
47
+ yield
48
+ ensure
49
+ $stdout = orig_stdout
50
+ end
51
51
  else
52
52
  yield
53
53
  end
@@ -206,9 +206,8 @@ module RuboCop
206
206
  end
207
207
 
208
208
  def snake_case(camel_case_string)
209
- return 'rspec' if camel_case_string == 'RSpec'
210
-
211
209
  camel_case_string
210
+ .gsub('RSpec', 'Rspec')
212
211
  .gsub(%r{([^A-Z/])([A-Z]+)}, '\1_\2')
213
212
  .gsub(%r{([A-Z])([A-Z][^A-Z\d/]+)}, '\1_\2')
214
213
  .downcase
@@ -162,6 +162,7 @@ module RuboCop
162
162
  check_alignment([node.first_argument], indent)
163
163
  end
164
164
  alias on_csend on_send
165
+ alias on_super on_send
165
166
 
166
167
  private
167
168
 
@@ -343,7 +343,7 @@ module RuboCop
343
343
  end
344
344
 
345
345
  def skip_check?(base_loc, body_node)
346
- return true if ignored_line?(base_loc)
346
+ return true if allowed_line?(base_loc)
347
347
  return true unless body_node
348
348
 
349
349
  # Don't check if expression is on same line as "then" keyword, etc.
@@ -169,19 +169,22 @@ module RuboCop
169
169
 
170
170
  def qualifies_for_compact?(node, token, side: :right)
171
171
  if side == :right
172
- multi_dimensional_array?(node, token) && !next_to_bracket?(token)
172
+ multi_dimensional_array?(node, token) && token.space_before?
173
173
  else
174
- multi_dimensional_array?(node, token, side: :left) &&
175
- !next_to_bracket?(token, side: :left)
174
+ multi_dimensional_array?(node, token, side: :left) && token.space_after?
176
175
  end
177
176
  end
178
177
 
179
178
  def multi_dimensional_array?(node, token, side: :right)
180
- i = index_for(node, token)
179
+ offset = side == :right ? -1 : +1
180
+ i = index_for(node, token) + offset
181
+ # TODO: change this type check once
182
+ # https://github.com/rubocop/rubocop-ast/pull/240 is merged
183
+ i += offset while processed_source.tokens_within(node)[i].new_line?
181
184
  if side == :right
182
- processed_source.tokens_within(node)[i - 1].right_bracket?
185
+ processed_source.tokens_within(node)[i].right_bracket?
183
186
  else
184
- processed_source.tokens_within(node)[i + 1].left_array_bracket?
187
+ processed_source.tokens_within(node)[i].left_array_bracket?
185
188
  end
186
189
  end
187
190
 
@@ -200,12 +203,13 @@ module RuboCop
200
203
  end
201
204
 
202
205
  def compact_corrections(corrector, node, left, right)
203
- if qualifies_for_compact?(node, left, side: :left)
206
+ if multi_dimensional_array?(node, left, side: :left)
204
207
  compact(corrector, left, :right)
205
208
  elsif !left.space_after?
206
209
  corrector.insert_after(left.pos, ' ')
207
210
  end
208
- if qualifies_for_compact?(node, right)
211
+
212
+ if multi_dimensional_array?(node, right)
209
213
  compact(corrector, right, :left)
210
214
  elsif !right.space_before?
211
215
  corrector.insert_before(right.pos, ' ')
@@ -213,7 +217,7 @@ module RuboCop
213
217
  end
214
218
 
215
219
  def compact(corrector, bracket, side)
216
- range = side_space_range(range: bracket.pos, side: side)
220
+ range = side_space_range(range: bracket.pos, side: side, include_newlines: true)
217
221
  corrector.remove(range)
218
222
  end
219
223
  end
@@ -46,10 +46,13 @@ module RuboCop
46
46
  # # bad
47
47
  # foo = { }
48
48
  # bar = { }
49
+ # baz = {
50
+ # }
49
51
  #
50
52
  # # good
51
53
  # foo = {}
52
54
  # bar = {}
55
+ # baz = {}
53
56
  #
54
57
  # @example EnforcedStyleForEmptyBraces: space
55
58
  # # The `space` EnforcedStyleForEmptyBraces style enforces that
@@ -60,8 +63,9 @@ module RuboCop
60
63
  #
61
64
  # # good
62
65
  # foo = { }
63
- # foo = { }
64
- # foo = { }
66
+ # foo = { }
67
+ # foo = {
68
+ # }
65
69
  #
66
70
  class SpaceInsideHashLiteralBraces < Base
67
71
  include SurroundingSpace
@@ -77,6 +81,7 @@ module RuboCop
77
81
 
78
82
  check(tokens[0], tokens[1])
79
83
  check(tokens[-2], tokens[-1]) if tokens.size > 2
84
+ check_whitespace_only_hash(node) if enforce_no_space_style_for_empty_braces?
80
85
  end
81
86
 
82
87
  private
@@ -103,7 +108,7 @@ module RuboCop
103
108
  if is_same_braces && style == :compact
104
109
  false
105
110
  elsif is_empty_braces
106
- cop_config['EnforcedStyleForEmptyBraces'] != 'no_space'
111
+ !enforce_no_space_style_for_empty_braces?
107
112
  else
108
113
  style != :no_space
109
114
  end
@@ -175,6 +180,26 @@ module RuboCop
175
180
 
176
181
  range_between(begin_pos, range.end_pos - 1)
177
182
  end
183
+
184
+ def check_whitespace_only_hash(node)
185
+ range = range_inside_hash(node)
186
+ return unless range.source.match?(/\A\s+\z/m)
187
+
188
+ add_offense(
189
+ range,
190
+ message: format(MSG, problem: 'empty hash literal braces detected')
191
+ ) do |corrector|
192
+ corrector.remove(range)
193
+ end
194
+ end
195
+
196
+ def range_inside_hash(node)
197
+ range_between(node.location.begin.end_pos, node.location.end.begin_pos)
198
+ end
199
+
200
+ def enforce_no_space_style_for_empty_braces?
201
+ cop_config['EnforcedStyleForEmptyBraces'] == 'no_space'
202
+ end
178
203
  end
179
204
  end
180
205
  end
@@ -45,7 +45,7 @@ module RuboCop
45
45
  # # bad
46
46
  # expect { do_something }.to change { object.attribute }
47
47
  #
48
- # @example AllowedPatterns: [/change/]
48
+ # @example AllowedPatterns: ['change']
49
49
  #
50
50
  # # good
51
51
  # expect { do_something }.to change { object.attribute }
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for duplicated magic comments.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ #
12
+ # # encoding: ascii
13
+ # # encoding: ascii
14
+ #
15
+ # # good
16
+ #
17
+ # # encoding: ascii
18
+ #
19
+ # # bad
20
+ #
21
+ # # frozen_string_literal: true
22
+ # # frozen_string_literal: true
23
+ #
24
+ # # good
25
+ #
26
+ # # frozen_string_literal: true
27
+ #
28
+ class DuplicateMagicComment < Base
29
+ include FrozenStringLiteral
30
+ include RangeHelp
31
+ extend AutoCorrector
32
+
33
+ MSG = 'Duplicate magic comment detected.'
34
+
35
+ def on_new_investigation
36
+ return if processed_source.buffer.source.empty?
37
+
38
+ magic_comment_lines.each_value do |comment_lines|
39
+ next if comment_lines.count <= 1
40
+
41
+ comment_lines[1..].each do |comment_line|
42
+ range = processed_source.buffer.line_range(comment_line + 1)
43
+
44
+ register_offense(range)
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def magic_comment_lines
52
+ comment_lines = { encoding_magic_comments: [], frozen_string_literal_magic_comments: [] }
53
+
54
+ leading_magic_comments.each.with_index do |magic_comment, index|
55
+ if magic_comment.encoding_specified?
56
+ comment_lines[:encoding_magic_comments] << index
57
+ elsif magic_comment.frozen_string_literal_specified?
58
+ comment_lines[:frozen_string_literal_magic_comments] << index
59
+ end
60
+ end
61
+
62
+ comment_lines
63
+ end
64
+
65
+ def register_offense(range)
66
+ add_offense(range) do |corrector|
67
+ corrector.remove(range_by_whole_lines(range, include_final_newline: true))
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -133,7 +133,7 @@ module RuboCop
133
133
  end
134
134
 
135
135
  def found_instance_method(node, name)
136
- return unless (scope = node.parent_module_name)
136
+ return found_sclass_method(node, name) unless (scope = node.parent_module_name)
137
137
 
138
138
  # Humanize the scope
139
139
  scope = scope.sub(
@@ -145,6 +145,16 @@ module RuboCop
145
145
  found_method(node, "#{scope}#{name}")
146
146
  end
147
147
 
148
+ def found_sclass_method(node, name)
149
+ singleton_ancestor = node.each_ancestor.find(&:sclass_type?)
150
+ return unless singleton_ancestor
151
+
152
+ singleton_receiver_node = singleton_ancestor.children[0]
153
+ return unless singleton_receiver_node.send_type?
154
+
155
+ found_method(node, "#{singleton_receiver_node.method_name}.#{name}")
156
+ end
157
+
148
158
  def found_method(node, method_name)
149
159
  if @definitions.key?(method_name)
150
160
  loc = case node.type
@@ -32,26 +32,40 @@ module RuboCop
32
32
  end
33
33
  end
34
34
 
35
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
35
36
  def each_repeated_character_class_element_loc(node)
36
37
  node.parsed_tree&.each_expression do |expr|
37
- next if expr.type != :set || expr.token == :intersection
38
+ next if skip_expression?(expr)
38
39
 
39
40
  seen = Set.new
41
+ enum = expr.expressions.to_enum
42
+ expression_count = expr.expressions.count
40
43
 
41
- expr.expressions.each do |child|
42
- next if within_interpolation?(node, child)
44
+ expression_count.times do |current_number|
45
+ current_child = enum.next
46
+ next if within_interpolation?(node, current_child)
43
47
 
44
- child_source = child.to_s
48
+ current_child_source = current_child.to_s
49
+ next_child = enum.peek if current_number + 1 < expression_count
45
50
 
46
- yield child.expression if seen.include?(child_source)
51
+ if seen.include?(current_child_source)
52
+ next if start_with_escaped_zero_number?(current_child_source, next_child.to_s)
47
53
 
48
- seen << child_source
54
+ yield current_child.expression
55
+ end
56
+
57
+ seen << current_child_source
49
58
  end
50
59
  end
51
60
  end
61
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
52
62
 
53
63
  private
54
64
 
65
+ def skip_expression?(expr)
66
+ expr.type != :set || expr.token == :intersection
67
+ end
68
+
55
69
  # Since we blank interpolations with a space for every char of the interpolation, we would
56
70
  # mark every space (except the first) as duplicate if we do not skip regexp_parser nodes
57
71
  # that are within an interpolation.
@@ -61,6 +75,11 @@ module RuboCop
61
75
  interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) }
62
76
  end
63
77
 
78
+ def start_with_escaped_zero_number?(current_child, next_child)
79
+ # Represents escaped code from `"\00"` (`"\u0000"`) to `"\07"` (`"\a"`).
80
+ current_child == '\\0' && next_child.match?(/[0-7]/)
81
+ end
82
+
64
83
  def interpolation_locs(node)
65
84
  @interpolation_locs ||= {}
66
85
 
@@ -85,7 +85,9 @@ module RuboCop
85
85
  private
86
86
 
87
87
  def body_or_allowed_comment_lines?(node)
88
- node.body || (cop_config['AllowComments'] && comment_lines?(node))
88
+ return true if node.body
89
+
90
+ cop_config['AllowComments'] && processed_source.contains_comment?(node.source_range)
89
91
  end
90
92
  end
91
93
  end
@@ -92,13 +92,17 @@ module RuboCop
92
92
  end
93
93
 
94
94
  def remove_empty_branch(corrector, node)
95
- corrector.remove(deletion_range(branch_range(node)))
95
+ if empty_if_branch?(node) && else_branch?(node)
96
+ corrector.remove(branch_range(node))
97
+ else
98
+ corrector.remove(deletion_range(branch_range(node)))
99
+ end
96
100
  end
97
101
 
98
102
  def correct_other_branches(corrector, node)
99
103
  return unless require_other_branches_correction?(node)
100
104
 
101
- if node.else_branch.if_type?
105
+ if node.else_branch&.if_type?
102
106
  # Replace an orphaned `elsif` with `if`
103
107
  corrector.replace(node.else_branch.loc.keyword, 'if')
104
108
  else
@@ -108,10 +112,10 @@ module RuboCop
108
112
  end
109
113
 
110
114
  def require_other_branches_correction?(node)
111
- return false unless node.if_type? && node.else_branch
115
+ return false unless node.if_type? && node.else?
112
116
  return false if !empty_if_branch?(node) && node.elsif?
113
117
 
114
- !empty_else_branch?(node)
118
+ !empty_elsif_branch?(node)
115
119
  end
116
120
 
117
121
  def empty_if_branch?(node)
@@ -122,13 +126,21 @@ module RuboCop
122
126
  if_branch.if_type? && !if_branch.body
123
127
  end
124
128
 
125
- def empty_else_branch?(node)
126
- node.else_branch.if_type? && !node.else_branch.body
129
+ def empty_elsif_branch?(node)
130
+ return false unless (else_branch = node.else_branch)
131
+
132
+ else_branch.if_type? && !else_branch.body
133
+ end
134
+
135
+ def else_branch?(node)
136
+ node.else_branch && !node.else_branch.if_type?
127
137
  end
128
138
 
129
139
  # rubocop:disable Metrics/AbcSize
130
140
  def branch_range(node)
131
- if node.loc.else
141
+ if empty_if_branch?(node) && else_branch?(node)
142
+ node.source_range.with(end_pos: node.loc.else.begin_pos)
143
+ elsif node.loc.else
132
144
  node.source_range.with(end_pos: node.loc.else.begin_pos - 1)
133
145
  elsif all_branches_body_missing?(node)
134
146
  if_node = node.ancestors.detect(&:if?)
@@ -30,6 +30,9 @@ module RuboCop
30
30
  #
31
31
  # # good
32
32
  #
33
+ # # `class_eval`, `instance_eval`, `module_eval`, `class_exec`, `instance_exec`, and
34
+ # # `module_exec` blocks are allowed by default.
35
+ #
33
36
  # def foo
34
37
  # self.class.class_eval do
35
38
  # def bar
@@ -54,7 +57,47 @@ module RuboCop
54
57
  # end
55
58
  # end
56
59
  # end
60
+ #
61
+ # @example AllowedMethods: [] (default)
62
+ # # bad
63
+ # def do_something
64
+ # has_many :articles do
65
+ # def find_or_create_by_name(name)
66
+ # end
67
+ # end
68
+ # end
69
+ #
70
+ # @example AllowedMethods: ['has_many']
71
+ # # bad
72
+ # def do_something
73
+ # has_many :articles do
74
+ # def find_or_create_by_name(name)
75
+ # end
76
+ # end
77
+ # end
78
+ #
79
+ # @example AllowedPatterns: [] (default)
80
+ # # bad
81
+ # def foo(obj)
82
+ # obj.do_baz do
83
+ # def bar
84
+ # end
85
+ # end
86
+ # end
87
+ #
88
+ # @example AllowedPatterns: ['baz']
89
+ # # good
90
+ # def foo(obj)
91
+ # obj.do_baz do
92
+ # def bar
93
+ # end
94
+ # end
95
+ # end
96
+ #
57
97
  class NestedMethodDefinition < Base
98
+ include AllowedMethods
99
+ include AllowedPattern
100
+
58
101
  MSG = 'Method definitions must not be nested. Use `lambda` instead.'
59
102
 
60
103
  def on_def(node)
@@ -77,7 +120,13 @@ module RuboCop
77
120
 
78
121
  def scoping_method_call?(child)
79
122
  child.sclass_type? || eval_call?(child) || exec_call?(child) ||
80
- class_or_module_or_struct_new_call?(child)
123
+ class_or_module_or_struct_new_call?(child) || allowed_method_name?(child)
124
+ end
125
+
126
+ def allowed_method_name?(node)
127
+ name = node.method_name
128
+
129
+ allowed_method?(name) || matches_allowed_pattern?(name)
81
130
  end
82
131
 
83
132
  # @!method eval_call?(node)
@@ -61,7 +61,7 @@ module RuboCop
61
61
  # # bad
62
62
  # 10.minutes.to_i
63
63
  #
64
- # @example AllowedPatterns: [/min*/]
64
+ # @example AllowedPatterns: ['min*']
65
65
  #
66
66
  # # good
67
67
  # 10.minutes.to_i
@@ -7,6 +7,9 @@ module RuboCop
7
7
  # Checks the proper ordering of magic comments and whether
8
8
  # a magic comment is not placed before a shebang.
9
9
  #
10
+ # @safety
11
+ # This cop's autocorrection is unsafe because file encoding may change.
12
+ #
10
13
  # @example
11
14
  # # bad
12
15
  #
@@ -61,7 +64,7 @@ module RuboCop
61
64
  def magic_comment_lines
62
65
  lines = [nil, nil]
63
66
 
64
- magic_comments.each.with_index do |comment, index|
67
+ leading_magic_comments.each.with_index do |comment, index|
65
68
  if comment.encoding_specified?
66
69
  lines[0] = index
67
70
  elsif comment.frozen_string_literal_specified?
@@ -73,10 +76,6 @@ module RuboCop
73
76
 
74
77
  lines
75
78
  end
76
-
77
- def magic_comments
78
- leading_comment_lines.map { |line| MagicComment.parse(line) }
79
- end
80
79
  end
81
80
  end
82
81
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # This cops looks for references of Regexp captures that are out of range
6
+ # Looks for references of Regexp captures that are out of range
7
7
  # and thus always returns nil.
8
8
  #
9
9
  # @safety