rubocop 0.60.0 → 0.61.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/config/default.yml +573 -560
  4. data/lib/rubocop.rb +5 -0
  5. data/lib/rubocop/ast/node.rb +1 -1
  6. data/lib/rubocop/ast/sexp.rb +1 -1
  7. data/lib/rubocop/cli.rb +9 -14
  8. data/lib/rubocop/config.rb +4 -3
  9. data/lib/rubocop/config_loader.rb +25 -22
  10. data/lib/rubocop/config_loader_resolver.rb +3 -2
  11. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +53 -0
  12. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +73 -0
  13. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +138 -0
  14. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +52 -46
  15. data/lib/rubocop/cop/generator.rb +13 -17
  16. data/lib/rubocop/cop/generator/configuration_injector.rb +60 -0
  17. data/lib/rubocop/cop/layout/align_hash.rb +3 -0
  18. data/lib/rubocop/cop/layout/comment_indentation.rb +32 -2
  19. data/lib/rubocop/cop/layout/indent_heredoc.rb +11 -5
  20. data/lib/rubocop/cop/layout/indentation_width.rb +7 -1
  21. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +11 -11
  22. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +1 -1
  23. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  24. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +1 -1
  25. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +16 -3
  26. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +30 -17
  27. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +11 -0
  28. data/lib/rubocop/cop/lint/shadowed_exception.rb +2 -5
  29. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -1
  30. data/lib/rubocop/cop/metrics/line_length.rb +2 -2
  31. data/lib/rubocop/cop/mixin/trailing_comma.rb +11 -15
  32. data/lib/rubocop/cop/offense.rb +1 -1
  33. data/lib/rubocop/cop/performance/open_struct.rb +46 -0
  34. data/lib/rubocop/cop/performance/redundant_merge.rb +18 -4
  35. data/lib/rubocop/cop/rails/bulk_change_table.rb +2 -2
  36. data/lib/rubocop/cop/rails/dynamic_find_by.rb +15 -8
  37. data/lib/rubocop/cop/rails/http_positional_arguments.rb +17 -14
  38. data/lib/rubocop/cop/rails/http_status.rb +4 -4
  39. data/lib/rubocop/cop/rails/inverse_of.rb +2 -2
  40. data/lib/rubocop/cop/rails/reversible_migration.rb +1 -1
  41. data/lib/rubocop/cop/rails/skips_model_validations.rb +1 -1
  42. data/lib/rubocop/cop/rails/validation.rb +4 -4
  43. data/lib/rubocop/cop/security/open.rb +31 -11
  44. data/lib/rubocop/cop/style/begin_block.rb +6 -0
  45. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -1
  46. data/lib/rubocop/cop/style/empty_case_condition.rb +13 -7
  47. data/lib/rubocop/cop/style/for.rb +9 -78
  48. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +6 -4
  49. data/lib/rubocop/cop/style/global_vars.rb +1 -1
  50. data/lib/rubocop/cop/style/infinite_loop.rb +42 -6
  51. data/lib/rubocop/cop/style/lambda.rb +4 -87
  52. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +221 -16
  53. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  54. data/lib/rubocop/cop/style/regexp_literal.rb +62 -10
  55. data/lib/rubocop/cop/style/unneeded_condition.rb +2 -2
  56. data/lib/rubocop/cop/variable_force.rb +4 -2
  57. data/lib/rubocop/cop/variable_force/variable.rb +2 -0
  58. data/lib/rubocop/magic_comment.rb +1 -1
  59. data/lib/rubocop/remote_config.rb +13 -4
  60. data/lib/rubocop/rspec/expect_offense.rb +1 -1
  61. data/lib/rubocop/runner.rb +15 -4
  62. data/lib/rubocop/version.rb +1 -1
  63. metadata +7 -2
@@ -5,58 +5,64 @@ module RuboCop
5
5
  # Autocorrection logic for the closing brace of a literal either
6
6
  # on the same line as the last contained elements, or a new line.
7
7
  class MultilineLiteralBraceCorrector
8
- extend MultilineLiteralBraceLayout
9
- extend RangeHelp
10
-
11
- class << self
12
- attr_reader :processed_source
13
-
14
- # rubocop:disable Metrics/MethodLength
15
- def correct(processed_source, node)
16
- @processed_source = processed_source
17
- if closing_brace_on_same_line?(node)
18
- lambda do |corrector|
19
- corrector.insert_before(node.loc.end, "\n".freeze)
20
- end
21
- else
22
- # When a comment immediately before the closing brace gets in the
23
- # way of an easy correction, the offense is reported but not auto-
24
- # corrected. The user must handle the delicate decision of where to
25
- # put the comment.
26
- return if new_line_needed_before_closing_brace?(node)
27
-
28
- lambda do |corrector|
29
- corrector.remove(range_with_surrounding_space(range: node.loc.end,
30
- side: :left))
31
-
32
- corrector.insert_after(
33
- last_element_range_with_trailing_comma(node),
34
- node.loc.end.source
35
- )
36
- end
37
- end
38
- end
39
- # rubocop:enable Metrics/MethodLength
8
+ include MultilineLiteralBraceLayout
9
+ include RangeHelp
10
+
11
+ def initialize(node, processed_source)
12
+ @node = node
13
+ @processed_source = processed_source
14
+ end
40
15
 
41
- private
16
+ def call(corrector)
17
+ if closing_brace_on_same_line?(node)
18
+ correct_same_line_brace(corrector)
19
+ else
20
+ # When a comment immediately before the closing brace gets in the
21
+ # way of an easy correction, the offense is reported but not auto-
22
+ # corrected. The user must handle the delicate decision of where to
23
+ # put the comment.
24
+ return if new_line_needed_before_closing_brace?(node)
42
25
 
43
- def last_element_range_with_trailing_comma(node)
44
- trailing_comma_range = last_element_trailing_comma_range(node)
45
- if trailing_comma_range
46
- children(node).last.source_range.join(trailing_comma_range)
47
- else
48
- children(node).last.source_range
49
- end
26
+ correct_next_line_brace(corrector)
50
27
  end
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :node, :processed_source
33
+
34
+ def correct_same_line_brace(corrector)
35
+ corrector.insert_before(node.loc.end, "\n".freeze)
36
+ end
37
+
38
+ def correct_next_line_brace(corrector)
39
+ corrector.remove(
40
+ range_with_surrounding_space(range: node.loc.end, side: :left)
41
+ )
42
+
43
+ corrector.insert_after(
44
+ last_element_range_with_trailing_comma(node),
45
+ node.loc.end.source
46
+ )
47
+ end
51
48
 
52
- def last_element_trailing_comma_range(node)
53
- range = range_with_surrounding_space(
54
- range: children(node).last.source_range,
55
- side: :right
56
- ).end.resize(1)
57
- range.source == ',' ? range : nil
49
+ def last_element_range_with_trailing_comma(node)
50
+ trailing_comma_range = last_element_trailing_comma_range(node)
51
+ if trailing_comma_range
52
+ children(node).last.source_range.join(trailing_comma_range)
53
+ else
54
+ children(node).last.source_range
58
55
  end
59
56
  end
57
+
58
+ def last_element_trailing_comma_range(node)
59
+ range = range_with_surrounding_space(
60
+ range: children(node).last.source_range,
61
+ side: :right
62
+ ).end.resize(1)
63
+
64
+ range.source == ',' ? range : nil
65
+ end
60
66
  end
61
67
  end
62
68
  end
@@ -98,6 +98,11 @@ module RuboCop
98
98
  end
99
99
  SPEC
100
100
 
101
+ CONFIGURATION_ADDED_MESSAGE = <<-MESSAGE.strip_indent
102
+ [modify] A configuration for the cop is added into %<configuration_file_path>s.
103
+ If you want to disable the cop by default, set `Enabled` option to false.
104
+ MESSAGE
105
+
101
106
  def initialize(name, github_user, output: $stdout)
102
107
  @badge = Badge.parse(name)
103
108
  @github_user = github_user
@@ -123,24 +128,15 @@ module RuboCop
123
128
  end
124
129
 
125
130
  def inject_config(config_file_path: 'config/default.yml')
126
- config = File.readlines(config_file_path)
127
- content = <<-YAML.strip_indent
128
- #{badge}:
129
- Description: 'TODO: Write a description of the cop.'
130
- Enabled: true
131
- VersionAdded: #{bump_minor_version}
132
-
133
- YAML
134
- target_line = config.find.with_index(1) do |line, index|
135
- next if line =~ /^[\s#]/
136
- break index - 1 if badge.to_s < line
131
+ injector =
132
+ ConfigurationInjector.new(configuration_file_path: config_file_path,
133
+ badge: badge,
134
+ version_added: bump_minor_version)
135
+
136
+ injector.inject do
137
+ output.puts(format(CONFIGURATION_ADDED_MESSAGE,
138
+ configuration_file_path: config_file_path))
137
139
  end
138
- config.insert(target_line, content)
139
- File.write(config_file_path, config.join)
140
- output.puts <<-MESSAGE.strip_indent
141
- [modify] A configuration for the cop is added into #{config_file_path}.
142
- If you want to disable the cop by default, set `Enabled` option to false.
143
- MESSAGE
144
140
  end
145
141
 
146
142
  def todo
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ class Generator
6
+ # A class that injects a require directive into the root RuboCop file.
7
+ # It looks for other directives that require files in the same (cop)
8
+ # namespace and injects the provided one in alpha
9
+ class ConfigurationInjector
10
+ TEMPLATE = <<-YAML.strip_indent
11
+ %<badge>s:
12
+ Description: 'TODO: Write a description of the cop.'
13
+ Enabled: true
14
+ VersionAdded: '%<version_added>s'
15
+
16
+ YAML
17
+
18
+ def initialize(configuration_file_path:, badge:, version_added:)
19
+ @configuration_file_path = configuration_file_path
20
+ @badge = badge
21
+ @version_added = version_added
22
+ @output = output
23
+ end
24
+
25
+ def inject
26
+ configuration_entries.insert(find_target_line,
27
+ new_configuration_entry)
28
+
29
+ File.write(configuration_file_path, configuration_entries.join)
30
+
31
+ yield if block_given?
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :configuration_file_path, :badge, :version_added, :output
37
+
38
+ def configuration_entries
39
+ @configuration_entries ||= File.readlines(configuration_file_path)
40
+ end
41
+
42
+ def new_configuration_entry
43
+ format(TEMPLATE, badge: badge, version_added: version_added)
44
+ end
45
+
46
+ def find_target_line
47
+ configuration_entries.find.with_index do |line, index|
48
+ next if comment?(line)
49
+
50
+ break index if badge.to_s < line
51
+ end
52
+ end
53
+
54
+ def comment?(yaml)
55
+ yaml =~ /^[\s#]/
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -182,6 +182,7 @@ module RuboCop
182
182
 
183
183
  def on_send(node)
184
184
  return if double_splat?(node)
185
+ return unless node.arguments?
185
186
 
186
187
  last_argument = node.last_argument
187
188
 
@@ -190,6 +191,8 @@ module RuboCop
190
191
 
191
192
  ignore_node(last_argument)
192
193
  end
194
+ alias on_super on_send
195
+ alias on_yield on_send
193
196
 
194
197
  def on_hash(node)
195
198
  return if ignored_node?(node)
@@ -42,12 +42,42 @@ module RuboCop
42
42
  processed_source.each_comment { |comment| check(comment) }
43
43
  end
44
44
 
45
- def autocorrect(node)
46
- AlignmentCorrector.correct(processed_source, node, @column_delta)
45
+ def autocorrect(comment)
46
+ corrections = autocorrect_preceding_comments(comment) <<
47
+ autocorrect_one(comment)
48
+ ->(corrector) { corrections.each { |corr| corr.call(corrector) } }
47
49
  end
48
50
 
49
51
  private
50
52
 
53
+ # Corrects all comment lines that occur immediately before the given
54
+ # comment and have the same indentation. This is to avoid a long chain
55
+ # of correcting, saving the file, parsing and inspecting again, and
56
+ # then correcting one more line, and so on.
57
+ def autocorrect_preceding_comments(comment)
58
+ corrections = []
59
+ line_no = comment.loc.line
60
+ column = comment.loc.column
61
+ comments = processed_source.comments
62
+ (comments.index(comment) - 1).downto(0) do |ix|
63
+ previous_comment = comments[ix]
64
+ break unless should_correct?(previous_comment, column, line_no - 1)
65
+
66
+ corrections << autocorrect_one(previous_comment)
67
+ line_no -= 1
68
+ end
69
+ corrections
70
+ end
71
+
72
+ def should_correct?(comment, column, line_no)
73
+ loc = comment.loc
74
+ loc.line == line_no && loc.column == column
75
+ end
76
+
77
+ def autocorrect_one(comment)
78
+ AlignmentCorrector.correct(processed_source, comment, @column_delta)
79
+ end
80
+
51
81
  def check(comment)
52
82
  return unless own_line_comment?(comment)
53
83
 
@@ -99,7 +99,7 @@ module RuboCop
99
99
  return unless body_indent_level.zero?
100
100
  end
101
101
 
102
- return if too_long_line?(node)
102
+ return if line_too_long?(node)
103
103
 
104
104
  add_offense(node, location: :heredoc_body)
105
105
  end
@@ -173,8 +173,8 @@ module RuboCop
173
173
  )
174
174
  end
175
175
 
176
- def too_long_line?(node)
177
- return false if config.for_cop('Metrics/LineLength')['AllowHeredoc']
176
+ def line_too_long?(node)
177
+ return false if unlimited_heredoc_length?
178
178
 
179
179
  body = heredoc_body(node)
180
180
 
@@ -182,9 +182,15 @@ module RuboCop
182
182
  actual_indent = indent_level(body)
183
183
  increase_indent_level = expected_indent - actual_indent
184
184
 
185
- max_line = body.each_line.map { |line| line.chomp.size }.max
185
+ longest_line(body).size + increase_indent_level >= max_line_length
186
+ end
187
+
188
+ def longest_line(lines)
189
+ lines.each_line.max_by { |line| line.chomp.size }.chomp
190
+ end
186
191
 
187
- max_line + increase_indent_level >= max_line_length
192
+ def unlimited_heredoc_length?
193
+ config.for_cop('Metrics/LineLength')['AllowHeredoc']
188
194
  end
189
195
 
190
196
  def max_line_length
@@ -96,6 +96,12 @@ module RuboCop
96
96
  check_members(node.loc.keyword, members)
97
97
  end
98
98
 
99
+ def on_sclass(node)
100
+ _class_name, *members = *node
101
+
102
+ check_members(node.loc.keyword, members)
103
+ end
104
+
99
105
  def on_send(node)
100
106
  super
101
107
  return unless node.adjacent_def_modifier?
@@ -141,7 +147,7 @@ module RuboCop
141
147
  end
142
148
 
143
149
  def on_if(node, base = node)
144
- return if ignored_node?(node) || !node.body
150
+ return if ignored_node?(node)
145
151
  return if node.ternary? || node.modifier_form?
146
152
 
147
153
  check_if(node, node.body, node.else_branch, base.loc)
@@ -91,26 +91,26 @@ module RuboCop
91
91
  class MultilineArrayBraceLayout < Cop
92
92
  include MultilineLiteralBraceLayout
93
93
 
94
- SAME_LINE_MESSAGE = 'Closing array brace must be on the same line as ' \
95
- 'the last array element when opening brace is on the same line as ' \
96
- 'the first array element.'.freeze
94
+ SAME_LINE_MESSAGE = 'The closing array brace must be on the same ' \
95
+ 'line as the last array element when the opening brace is on the ' \
96
+ 'same line as the first array element.'.freeze
97
97
 
98
- NEW_LINE_MESSAGE = 'Closing array brace must be on the line after ' \
99
- 'the last array element when opening brace is on a separate line ' \
100
- 'from the first array element.'.freeze
98
+ NEW_LINE_MESSAGE = 'The closing array brace must be on the line ' \
99
+ 'after the last array element when the opening brace is on a ' \
100
+ 'separate line from the first array element.'.freeze
101
101
 
102
- ALWAYS_NEW_LINE_MESSAGE = 'Closing array brace must be on the line ' \
103
- 'after the last array element.'.freeze
102
+ ALWAYS_NEW_LINE_MESSAGE = 'The closing array brace must be on the ' \
103
+ 'line after the last array element.'.freeze
104
104
 
105
- ALWAYS_SAME_LINE_MESSAGE = 'Closing array brace must be on the same ' \
106
- 'line as the last array element.'.freeze
105
+ ALWAYS_SAME_LINE_MESSAGE = 'The closing array brace must be on the ' \
106
+ 'same line as the last array element.'.freeze
107
107
 
108
108
  def on_array(node)
109
109
  check_brace_layout(node)
110
110
  end
111
111
 
112
112
  def autocorrect(node)
113
- MultilineLiteralBraceCorrector.correct(processed_source, node)
113
+ MultilineLiteralBraceCorrector.new(node, processed_source)
114
114
  end
115
115
  end
116
116
  end
@@ -114,7 +114,7 @@ module RuboCop
114
114
  end
115
115
 
116
116
  def autocorrect(node)
117
- MultilineLiteralBraceCorrector.correct(processed_source, node)
117
+ MultilineLiteralBraceCorrector.new(node, processed_source)
118
118
  end
119
119
  end
120
120
  end
@@ -110,7 +110,7 @@ module RuboCop
110
110
  end
111
111
 
112
112
  def autocorrect(node)
113
- MultilineLiteralBraceCorrector.correct(processed_source, node)
113
+ MultilineLiteralBraceCorrector.new(node, processed_source)
114
114
  end
115
115
 
116
116
  private
@@ -123,7 +123,7 @@ module RuboCop
123
123
  alias on_defs on_def
124
124
 
125
125
  def autocorrect(node)
126
- MultilineLiteralBraceCorrector.correct(processed_source, node)
126
+ MultilineLiteralBraceCorrector.new(node, processed_source)
127
127
  end
128
128
  end
129
129
  end
@@ -28,8 +28,9 @@ module RuboCop
28
28
  'aligned with `%<beginning>s` at ' \
29
29
  '%<begin_loc_line>d, %<begin_loc_column>d.'.freeze
30
30
  ANCESTOR_TYPES = %i[kwbegin def defs class module].freeze
31
- RUBY_2_5_ANCESTOR_TYPES = (ANCESTOR_TYPES + [:block]).freeze
31
+ RUBY_2_5_ANCESTOR_TYPES = (ANCESTOR_TYPES + %i[block]).freeze
32
32
  ANCESTOR_TYPES_WITH_ACCESS_MODIFIERS = %i[def defs].freeze
33
+ ASSIGNMENT_TYPES = %i[lvasgn].freeze
33
34
 
34
35
  def on_resbody(node)
35
36
  check(node) unless modifier?(node)
@@ -115,10 +116,13 @@ module RuboCop
115
116
  ancestor_node = ancestor_node(node)
116
117
  return nil if ancestor_node.nil?
117
118
 
119
+ assignment_node = assignment_node(ancestor_node)
120
+ return assignment_node unless assignment_node.nil?
121
+
118
122
  access_modifier_node = access_modifier_node(ancestor_node)
119
- return ancestor_node if access_modifier_node.nil?
123
+ return access_modifier_node unless access_modifier_node.nil?
120
124
 
121
- access_modifier_node
125
+ ancestor_node
122
126
  end
123
127
 
124
128
  def ancestor_node(node)
@@ -132,6 +136,15 @@ module RuboCop
132
136
  node.each_ancestor(*ancestor_types).first
133
137
  end
134
138
 
139
+ def assignment_node(node)
140
+ assignment_node = node.ancestors.first
141
+ return nil unless
142
+ assignment_node &&
143
+ ASSIGNMENT_TYPES.include?(assignment_node.type)
144
+
145
+ assignment_node
146
+ end
147
+
135
148
  def access_modifier_node(node)
136
149
  return nil unless
137
150
  ANCESTOR_TYPES_WITH_ACCESS_MODIFIERS.include?(node.type)