rubocop 1.36.0 → 1.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +31 -3
  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/cli/command/suggest_extensions.rb +8 -1
  8. data/lib/rubocop/comment_config.rb +36 -1
  9. data/lib/rubocop/cop/commissioner.rb +3 -1
  10. data/lib/rubocop/cop/generator.rb +1 -2
  11. data/lib/rubocop/cop/internal_affairs/create_empty_file.rb +37 -0
  12. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +111 -0
  13. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  14. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  15. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  16. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  17. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +29 -8
  18. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  19. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  20. data/lib/rubocop/cop/layout/space_inside_array_percent_literal.rb +3 -0
  21. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +30 -3
  22. data/lib/rubocop/cop/layout/space_inside_percent_literal_delimiters.rb +34 -0
  23. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +6 -2
  24. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  25. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  26. data/lib/rubocop/cop/lint/duplicate_methods.rb +28 -9
  27. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  28. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  29. data/lib/rubocop/cop/lint/empty_conditional_body.rb +20 -8
  30. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
  31. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  32. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  33. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  34. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  35. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -1
  36. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  37. data/lib/rubocop/cop/lint/redundant_require_statement.rb +38 -10
  38. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  39. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -2
  40. data/lib/rubocop/cop/lint/shadowed_exception.rb +0 -10
  41. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +3 -0
  42. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  43. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  44. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  45. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  46. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  47. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  48. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +6 -3
  49. data/lib/rubocop/cop/mixin/range_help.rb +23 -0
  50. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  51. data/lib/rubocop/cop/mixin/surrounding_space.rb +10 -8
  52. data/lib/rubocop/cop/mixin/visibility_help.rb +40 -5
  53. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  54. data/lib/rubocop/cop/registry.rb +10 -4
  55. data/lib/rubocop/cop/style/access_modifier_declarations.rb +5 -7
  56. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  57. data/lib/rubocop/cop/style/block_delimiters.rb +2 -2
  58. data/lib/rubocop/cop/style/character_literal.rb +1 -1
  59. data/lib/rubocop/cop/style/class_equality_comparison.rb +8 -6
  60. data/lib/rubocop/cop/style/collection_compact.rb +12 -3
  61. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  62. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  63. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  64. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  65. data/lib/rubocop/cop/style/guard_clause.rb +62 -21
  66. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -0
  67. data/lib/rubocop/cop/style/hash_each_methods.rb +32 -10
  68. data/lib/rubocop/cop/style/hash_except.rb +4 -0
  69. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  70. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +25 -2
  71. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  72. data/lib/rubocop/cop/style/module_function.rb +28 -6
  73. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  74. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  75. data/lib/rubocop/cop/style/object_then.rb +3 -0
  76. data/lib/rubocop/cop/style/operator_method_call.rb +53 -0
  77. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  78. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  79. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  80. data/lib/rubocop/cop/style/redundant_each.rb +116 -0
  81. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  82. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  83. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +12 -3
  84. data/lib/rubocop/cop/style/redundant_string_escape.rb +181 -0
  85. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  86. data/lib/rubocop/cop/style/static_class.rb +32 -1
  87. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  88. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  89. data/lib/rubocop/cop/style/word_array.rb +2 -0
  90. data/lib/rubocop/cop/team.rb +3 -4
  91. data/lib/rubocop/cop/util.rb +1 -1
  92. data/lib/rubocop/cop/variable_force/variable_table.rb +1 -1
  93. data/lib/rubocop/cops_documentation_generator.rb +2 -1
  94. data/lib/rubocop/ext/processed_source.rb +2 -0
  95. data/lib/rubocop/formatter/disabled_config_formatter.rb +8 -2
  96. data/lib/rubocop/formatter/offense_count_formatter.rb +8 -5
  97. data/lib/rubocop/formatter/worst_offenders_formatter.rb +6 -3
  98. data/lib/rubocop/options.rb +19 -15
  99. data/lib/rubocop/rspec/cop_helper.rb +21 -1
  100. data/lib/rubocop/rspec/shared_contexts.rb +14 -1
  101. data/lib/rubocop/runner.rb +15 -11
  102. data/lib/rubocop/server/cache.rb +5 -1
  103. data/lib/rubocop/server/cli.rb +9 -2
  104. data/lib/rubocop/server/client_command/exec.rb +5 -0
  105. data/lib/rubocop/server/core.rb +18 -1
  106. data/lib/rubocop/server/socket_reader.rb +5 -1
  107. data/lib/rubocop/server.rb +1 -1
  108. data/lib/rubocop/version.rb +8 -3
  109. data/lib/rubocop.rb +4 -0
  110. metadata +13 -5
@@ -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,28 @@ 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
+ return node.source_range if node.location.begin.nil?
198
+
199
+ range_between(node.location.begin.end_pos, node.location.end.begin_pos)
200
+ end
201
+
202
+ def enforce_no_space_style_for_empty_braces?
203
+ cop_config['EnforcedStyleForEmptyBraces'] == 'no_space'
204
+ end
178
205
  end
179
206
  end
180
207
  end
@@ -8,14 +8,31 @@ module RuboCop
8
8
  #
9
9
  # @example
10
10
  #
11
+ # # bad
12
+ # %i( foo bar baz )
13
+ #
11
14
  # # good
12
15
  # %i(foo bar baz)
13
16
  #
14
17
  # # bad
15
18
  # %w( foo bar baz )
16
19
  #
20
+ # # good
21
+ # %w(foo bar baz)
22
+ #
17
23
  # # bad
18
24
  # %x( ls -l )
25
+ #
26
+ # # good
27
+ # %x(ls -l)
28
+ #
29
+ # # bad
30
+ # %w( )
31
+ # %w(
32
+ # )
33
+ #
34
+ # # good
35
+ # %w()
19
36
  class SpaceInsidePercentLiteralDelimiters < Base
20
37
  include MatchRange
21
38
  include PercentLiteral
@@ -34,11 +51,21 @@ module RuboCop
34
51
  end
35
52
 
36
53
  def on_percent_literal(node)
54
+ add_offenses_for_blank_spaces(node)
37
55
  add_offenses_for_unnecessary_spaces(node)
38
56
  end
39
57
 
40
58
  private
41
59
 
60
+ def add_offenses_for_blank_spaces(node)
61
+ range = body_range(node)
62
+ return if range.source.empty? || !range.source.strip.empty?
63
+
64
+ add_offense(range) do |corrector|
65
+ corrector.remove(range)
66
+ end
67
+ end
68
+
42
69
  def add_offenses_for_unnecessary_spaces(node)
43
70
  return unless node.single_line?
44
71
 
@@ -54,6 +81,13 @@ module RuboCop
54
81
  each_match_range(contents_range(node), regex, &blk)
55
82
  end
56
83
  end
84
+
85
+ def body_range(node)
86
+ node.location.expression.with(
87
+ begin_pos: node.location.begin.end_pos,
88
+ end_pos: node.location.end.begin_pos
89
+ )
90
+ end
57
91
  end
58
92
  end
59
93
  end
@@ -38,6 +38,8 @@ module RuboCop
38
38
  # # bad
39
39
  # foo[ ]
40
40
  # foo[ ]
41
+ # foo[
42
+ # ]
41
43
  #
42
44
  # # good
43
45
  # foo[]
@@ -49,6 +51,8 @@ module RuboCop
49
51
  # # bad
50
52
  # foo[]
51
53
  # foo[ ]
54
+ # foo[
55
+ # ]
52
56
  #
53
57
  # # good
54
58
  # foo[ ]
@@ -64,8 +68,6 @@ module RuboCop
64
68
  RESTRICT_ON_SEND = %i[[] []=].freeze
65
69
 
66
70
  def on_send(node)
67
- return if node.multiline?
68
-
69
71
  tokens = processed_source.tokens_within(node)
70
72
  left_token = left_ref_bracket(node, tokens)
71
73
  return unless left_token
@@ -76,6 +78,8 @@ module RuboCop
76
78
  return empty_offenses(node, left_token, right_token, EMPTY_MSG)
77
79
  end
78
80
 
81
+ return if node.multiline?
82
+
79
83
  if style == :no_space
80
84
  no_space_offenses(node, left_token, right_token, MSG)
81
85
  else
@@ -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
@@ -51,8 +51,8 @@ module RuboCop
51
51
  # alias bar foo
52
52
  class DuplicateMethods < Base
53
53
  MSG = 'Method `%<method>s` is defined at both %<defined>s and %<current>s.'
54
-
55
54
  RESTRICT_ON_SEND = %i[alias_method attr_reader attr_writer attr_accessor attr].freeze
55
+ DEF_TYPES = %i[def defs].freeze
56
56
 
57
57
  def initialize(config = nil, options = nil)
58
58
  super
@@ -127,13 +127,13 @@ module RuboCop
127
127
  found_method(node, "#{enclosing}.#{name}")
128
128
  end
129
129
 
130
- def message_for_dup(node, method_name)
131
- format(MSG, method: method_name, defined: source_location(@definitions[method_name]),
130
+ def message_for_dup(node, method_name, key)
131
+ format(MSG, method: method_name, defined: source_location(@definitions[key]),
132
132
  current: source_location(node))
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,19 +145,38 @@ 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
- if @definitions.key?(method_name)
150
- loc = case node.type
151
- when :def, :defs
159
+ key = method_key(node, method_name)
160
+
161
+ if @definitions.key?(key)
162
+ loc = if DEF_TYPES.include?(node.type)
152
163
  node.loc.keyword.join(node.loc.name)
153
164
  else
154
165
  node.loc.expression
155
166
  end
156
- message = message_for_dup(node, method_name)
167
+ message = message_for_dup(node, method_name, key)
157
168
 
158
169
  add_offense(loc, message: message)
159
170
  else
160
- @definitions[method_name] = node
171
+ @definitions[key] = node
172
+ end
173
+ end
174
+
175
+ def method_key(node, method_name)
176
+ if (ancestor_def = node.each_ancestor(*DEF_TYPES).first)
177
+ "#{ancestor_def.method_name}.#{method_name}"
178
+ else
179
+ method_name
161
180
  end
162
181
  end
163
182
 
@@ -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,14 +126,22 @@ 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
132
- node.source_range.with(end_pos: node.loc.else.begin_pos - 1)
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
144
+ node.source_range.with(end_pos: node.condition.loc.expression.end_pos)
133
145
  elsif all_branches_body_missing?(node)
134
146
  if_node = node.ancestors.detect(&:if?)
135
147
  node.source_range.with(end_pos: if_node.loc.end.end_pos)
@@ -50,9 +50,7 @@ module RuboCop
50
50
 
51
51
  def on_new_investigation
52
52
  each_missing_enable do |cop, line_range|
53
- # This has to remain a strict inequality to handle
54
- # the case when max_range is Float::INFINITY
55
- next if line_range.max - line_range.min < max_range + 2
53
+ next if acceptable_range?(cop, line_range)
56
54
 
57
55
  range = source_range(processed_source.buffer, line_range.min, (0..0))
58
56
  comment = processed_source.comment_at_line(line_range.begin)
@@ -69,6 +67,23 @@ module RuboCop
69
67
  end
70
68
  end
71
69
 
70
+ def acceptable_range?(cop, line_range)
71
+ # This has to remain a strict inequality to handle
72
+ # the case when max_range is Float::INFINITY
73
+ return true if line_range.max - line_range.min < max_range + 2
74
+ # This cop is disabled in the config, it is not expected to be re-enabled
75
+ return true if line_range.min == CommentConfig::CONFIG_DISABLED_LINE_RANGE_MIN
76
+
77
+ cop_class = RuboCop::Cop::Registry.global.find_by_cop_name cop
78
+ if cop_class &&
79
+ !processed_source.registry.enabled?(cop_class, config) &&
80
+ line_range.max == Float::INFINITY
81
+ return true
82
+ end
83
+
84
+ false
85
+ end
86
+
72
87
  def max_range
73
88
  @max_range ||= cop_config['MaximumRangeSize']
74
89
  end
@@ -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
@@ -113,6 +113,7 @@ module RuboCop
113
113
  def each_line_range(cop, line_ranges)
114
114
  line_ranges.each_with_index do |line_range, line_range_index|
115
115
  next if ignore_offense?(line_range)
116
+ next if expected_final_disable?(cop, line_range)
116
117
 
117
118
  comment = processed_source.comment_at_line(line_range.begin)
118
119
  redundant = if all_disabled?(comment)
@@ -179,11 +180,21 @@ module RuboCop
179
180
  end
180
181
 
181
182
  def ignore_offense?(line_range)
183
+ return true if line_range.min == CommentConfig::CONFIG_DISABLED_LINE_RANGE_MIN
184
+
182
185
  disabled_ranges.any? do |range|
183
186
  range.cover?(line_range.min) && range.cover?(line_range.max)
184
187
  end
185
188
  end
186
189
 
190
+ def expected_final_disable?(cop, line_range)
191
+ # A cop which is disabled in the config is being re-disabled until end of file
192
+ cop_class = RuboCop::Cop::Registry.global.find_by_cop_name cop
193
+ cop_class &&
194
+ !processed_source.registry.enabled?(cop_class, config) &&
195
+ line_range.max == Float::INFINITY
196
+ end
197
+
187
198
  def department_disabled?(cop, comment)
188
199
  directive = DirectiveComment.new(comment)
189
200
  directive.in_directive_department?(cop) && !directive.overridden_by_department?(cop)
@@ -209,7 +220,12 @@ module RuboCop
209
220
 
210
221
  add_offense(location, message: message(cop_names)) do |corrector|
211
222
  range = comment_range_with_surrounding_space(location, comment.loc.expression)
212
- corrector.remove(range)
223
+
224
+ if leave_free_comment?(comment, range)
225
+ corrector.replace(range, ' # ')
226
+ else
227
+ corrector.remove(range)
228
+ end
213
229
  end
214
230
  end
215
231
 
@@ -227,6 +243,12 @@ module RuboCop
227
243
  end
228
244
  end
229
245
 
246
+ def leave_free_comment?(comment, range)
247
+ free_comment = comment.text.gsub(range.source.strip, '')
248
+
249
+ !free_comment.empty? && !free_comment.start_with?('#')
250
+ end
251
+
230
252
  def cop_range(comment, cop)
231
253
  cop = remove_department_marker(cop)
232
254
  matching_range(comment.loc.expression, cop) ||