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
@@ -27,22 +27,13 @@ module RuboCop
27
27
  include RangeHelp
28
28
 
29
29
  def on_block(node)
30
- return unless node.arguments?
30
+ arguments = node.arguments
31
31
 
32
- args = node.arguments
32
+ return unless node.arguments? && pipes?(arguments)
33
33
 
34
- opening_pipe = args.loc.begin
35
- closing_pipe = args.loc.end
36
- return if opening_pipe.nil? || closing_pipe.nil?
37
-
38
- check_inside_pipes(args.children, opening_pipe, closing_pipe)
39
-
40
- if node.body
41
- check_space(closing_pipe.end_pos, node.body.source_range.begin_pos,
42
- closing_pipe, 'after closing `|`')
43
- end
44
-
45
- check_each_arg(args)
34
+ check_inside_pipes(arguments)
35
+ check_after_closing_pipe(arguments) if node.body
36
+ check_each_arg(arguments)
46
37
  end
47
38
 
48
39
  def autocorrect(range)
@@ -56,18 +47,40 @@ module RuboCop
56
47
 
57
48
  private
58
49
 
50
+ def pipes(arguments)
51
+ [arguments.loc.begin, arguments.loc.end]
52
+ end
53
+
54
+ def pipes?(arguments)
55
+ pipes(arguments).none?(&:nil?)
56
+ end
57
+
59
58
  def style_parameter_name
60
59
  'EnforcedStyleInsidePipes'
61
60
  end
62
61
 
63
- def check_inside_pipes(args, opening_pipe, closing_pipe)
62
+ def check_inside_pipes(arguments)
63
+ opening_pipe, closing_pipe = pipes(arguments)
64
+
64
65
  if style == :no_space
65
- check_no_space_style_inside_pipes(args, opening_pipe, closing_pipe)
66
+ check_no_space_style_inside_pipes(arguments.children,
67
+ opening_pipe,
68
+ closing_pipe)
66
69
  elsif style == :space
67
- check_space_style_inside_pipes(args, opening_pipe, closing_pipe)
70
+ check_space_style_inside_pipes(arguments.children,
71
+ opening_pipe,
72
+ closing_pipe)
68
73
  end
69
74
  end
70
75
 
76
+ def check_after_closing_pipe(arguments)
77
+ _opening_pipe, closing_pipe = pipes(arguments)
78
+ block = arguments.parent
79
+
80
+ check_space(closing_pipe.end_pos, block.body.source_range.begin_pos,
81
+ closing_pipe, 'after closing `|`')
82
+ end
83
+
71
84
  def check_no_space_style_inside_pipes(args, opening_pipe, closing_pipe)
72
85
  first = args.first.source_range
73
86
  last = args.last.source_range
@@ -29,6 +29,7 @@ module RuboCop
29
29
  SHOVEL = '<<'.freeze
30
30
  PERCENT = '%'.freeze
31
31
  PERCENT_PERCENT = '%%'.freeze
32
+ DIGIT_DOLLAR_FLAG = /%(\d+)\$/.freeze
32
33
  STRING_TYPES = %i[str dstr].freeze
33
34
  NAMED_INTERPOLATION = /%(?:<\w+>|\{\w+\})/.freeze
34
35
 
@@ -132,6 +133,10 @@ module RuboCop
132
133
  return :unknown unless node.str_type?
133
134
  return 1 if node.source =~ NAMED_INTERPOLATION
134
135
 
136
+ max_digit_dollar_num = max_digit_dollar_num(node)
137
+ return max_digit_dollar_num if max_digit_dollar_num &&
138
+ max_digit_dollar_num.nonzero?
139
+
135
140
  node
136
141
  .source
137
142
  .scan(FIELD_REGEX)
@@ -139,6 +144,12 @@ module RuboCop
139
144
  .reduce(0) { |acc, elem| acc + arguments_count(elem[2]) }
140
145
  end
141
146
 
147
+ def max_digit_dollar_num(node)
148
+ node.source.scan(DIGIT_DOLLAR_FLAG).map do |digit_dollar_num|
149
+ digit_dollar_num.first.to_i
150
+ end.max
151
+ end
152
+
142
153
  # number of arguments required for the format sequence
143
154
  def arguments_count(format)
144
155
  format.scan('*').count + 1
@@ -86,11 +86,8 @@ module RuboCop
86
86
  end
87
87
 
88
88
  def contains_multiple_levels_of_exceptions?(group)
89
- if group.size > 1 && group.include?(Exception)
90
- # Treat `Exception` as the highest level exception unless `nil` was
91
- # also rescued
92
- return !(group.size == 2 && group.include?(NilClass))
93
- end
89
+ # Always treat `Exception` as the highest level exception.
90
+ return true if group.size > 1 && group.include?(Exception)
94
91
 
95
92
  group.combination(2).any? do |a, b|
96
93
  compare_exceptions(a, b)
@@ -134,7 +134,7 @@ module RuboCop
134
134
 
135
135
  if node.begin_type?
136
136
  check_scope(node)
137
- elsif node.send_type? && access_modifier?(node)
137
+ elsif node.send_type? && node.bare_access_modifier?
138
138
  add_offense(node, message: format(MSG, current: node.method_name))
139
139
  end
140
140
  end
@@ -63,7 +63,7 @@ module RuboCop
63
63
 
64
64
  def ignored_line?(line, index, heredocs)
65
65
  matches_ignored_pattern?(line) ||
66
- heredocs && line_in_whitelisted_heredoc?(heredocs, index.succ)
66
+ heredocs && line_in_permitted_heredoc?(heredocs, index.succ)
67
67
  end
68
68
 
69
69
  def register_offense(loc, line)
@@ -107,7 +107,7 @@ module RuboCop
107
107
  end
108
108
  end
109
109
 
110
- def line_in_whitelisted_heredoc?(heredocs, line_number)
110
+ def line_in_permitted_heredoc?(heredocs, line_number)
111
111
  heredocs.any? do |range, delimiter|
112
112
  range.cover?(line_number) &&
113
113
  (allowed_heredoc == true || allowed_heredoc.include?(delimiter))
@@ -89,31 +89,27 @@ module RuboCop
89
89
  # on different lines, each item within is on its own line, and the
90
90
  # closing bracket is on its own line.
91
91
  def multiline?(node)
92
- # No need to process anything if the whole node is not multiline
93
- # Without the 2nd check, Foo.new({}) is considered multiline, which
94
- # it should not be. Essentially, if there are no elements, the
95
- # expression can not be multiline.
96
- return false unless node.multiline?
97
-
98
- items = elements(node).map(&:source_range)
99
- return false if items.empty?
92
+ node.multiline? && !allowed_multiline_argument?(node)
93
+ end
100
94
 
101
- items << node.loc.begin << node.loc.end
102
- (items.map(&:first_line) + items.map(&:last_line)).uniq.size > 1
95
+ # A single argument with the closing bracket on the same line as the end
96
+ # of the argument is not considered multiline, even if the argument
97
+ # itself might span multiple lines.
98
+ def allowed_multiline_argument?(node)
99
+ elements(node).one? && !Util.begins_its_line?(node.loc.end)
103
100
  end
104
101
 
105
102
  def elements(node)
106
103
  return node.children unless node.send_type?
107
104
 
108
- _receiver, _method_name, *args = *node
109
- args.flat_map do |a|
105
+ node.arguments.flat_map do |argument|
110
106
  # For each argument, if it is a multi-line hash without braces,
111
107
  # then promote the hash elements to method arguments
112
108
  # for the purpose of determining multi-line-ness.
113
- if a.hash_type? && a.multiline? && !a.braces?
114
- a.children
109
+ if argument.hash_type? && argument.multiline? && !argument.braces?
110
+ argument.children
115
111
  else
116
- a
112
+ argument
117
113
  end
118
114
  end
119
115
  end
@@ -24,7 +24,7 @@ module RuboCop
24
24
  # @return [Parser::Source::Range]
25
25
  # the location where the violation is detected.
26
26
  #
27
- # @see http://rubydoc.info/github/whitequark/parser/Parser/Source/Range
27
+ # @see https://www.rubydoc.info/gems/parser/Parser/Source/Range
28
28
  # Parser::Source::Range
29
29
  attr_reader :location
30
30
 
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop checks for `OpenStruct.new` calls.
7
+ # Instantiation of an `OpenStruct` invalidates
8
+ # Ruby global method cache as it causes dynamic method
9
+ # definition during program runtime.
10
+ # This could have an effect on performance,
11
+ # especially in case of single-threaded
12
+ # applications with multiple `OpenStruct` instantiations.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # class MyClass
17
+ # def my_method
18
+ # OpenStruct.new(my_key1: 'my_value1', my_key2: 'my_value2')
19
+ # end
20
+ # end
21
+ #
22
+ # # good
23
+ # class MyClass
24
+ # MyStruct = Struct.new(:my_key1, :my_key2)
25
+ # def my_method
26
+ # MyStruct.new('my_value1', 'my_value2')
27
+ # end
28
+ # end
29
+ #
30
+ class OpenStruct < Cop
31
+ MSG = 'Consider using `Struct` over `OpenStruct` ' \
32
+ 'to optimize the performance.'.freeze
33
+
34
+ def_node_matcher :open_struct, <<-PATTERN
35
+ (send (const {nil? cbase} :OpenStruct) :new ...)
36
+ PATTERN
37
+
38
+ def on_send(node)
39
+ open_struct(node) do |method|
40
+ add_offense(node, location: :selector, message: format(MSG, method))
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -14,6 +14,12 @@ module RuboCop
14
14
  AREF_ASGN = '%<receiver>s[%<key>s] = %<value>s'.freeze
15
15
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'.freeze
16
16
 
17
+ WITH_MODIFIER_CORRECTION = <<-RUBY.strip_indent
18
+ %<keyword>s %<condition>s
19
+ %<leading_space>s%<indent>s%<body>s
20
+ %<leading_space>send
21
+ RUBY
22
+
17
23
  def_node_matcher :redundant_merge_candidate, <<-PATTERN
18
24
  (send $!nil? :merge! [(hash $...) !kwsplat_type?])
19
25
  PATTERN
@@ -60,6 +66,7 @@ module RuboCop
60
66
 
61
67
  def non_redundant_merge?(node, receiver, pairs)
62
68
  non_redundant_pairs?(receiver, pairs) ||
69
+ kwsplat_used?(pairs) ||
63
70
  non_redundant_value_used?(receiver, node)
64
71
  end
65
72
 
@@ -67,6 +74,10 @@ module RuboCop
67
74
  pairs.size > 1 && !receiver.pure? || pairs.size > max_key_value_pairs
68
75
  end
69
76
 
77
+ def kwsplat_used?(pairs)
78
+ pairs.any?(&:kwsplat_type?)
79
+ end
80
+
70
81
  def non_redundant_value_used?(receiver, node)
71
82
  node.value_used? &&
72
83
  !EachWithObjectInspector.new(node, receiver).value_used?
@@ -101,12 +112,15 @@ module RuboCop
101
112
  end
102
113
 
103
114
  def rewrite_with_modifier(node, parent, new_source)
104
- cond, = *parent
105
- padding = "\n#{(' ' * indent_width) + leading_spaces(node)}"
115
+ indent = ' ' * indent_width
116
+ padding = "\n#{indent + leading_spaces(node)}"
106
117
  new_source.gsub!(/\n/, padding)
107
118
 
108
- parent.loc.keyword.source << ' ' << cond.source << padding <<
109
- new_source << "\n" << leading_spaces(node) << 'end'
119
+ format(WITH_MODIFIER_CORRECTION, keyword: parent.loc.keyword.source,
120
+ condition: parent.condition.source,
121
+ leading_space: leading_spaces(node),
122
+ indent: indent,
123
+ body: new_source).chomp
110
124
  end
111
125
 
112
126
  def leading_spaces(node)
@@ -63,8 +63,8 @@ module RuboCop
63
63
  # end
64
64
  # end
65
65
  #
66
- # @see http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_table
67
- # @see http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html
66
+ # @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_table
67
+ # @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html
68
68
  class BulkChangeTable < Cop
69
69
  MSG_FOR_CHANGE_TABLE = <<-MSG.strip_indent.chomp
70
70
  You can combine alter queries using `bulk: true` options.
@@ -44,22 +44,29 @@ module RuboCop
44
44
  end
45
45
 
46
46
  def autocorrect(node)
47
- _receiver, method, *args = *node
48
- static_name = static_method_name(method.to_s)
49
- keywords = column_keywords(method)
47
+ keywords = column_keywords(node.method_name)
50
48
 
51
- return if keywords.size != args.size
49
+ return if keywords.size != node.arguments.size
52
50
 
53
51
  lambda do |corrector|
54
- corrector.replace(node.loc.selector, static_name)
55
- keywords.each.with_index do |keyword, idx|
56
- corrector.insert_before(args[idx].loc.expression, keyword)
57
- end
52
+ autocorrect_method_name(corrector, node)
53
+ autocorrect_argument_keywords(corrector, node, keywords)
58
54
  end
59
55
  end
60
56
 
61
57
  private
62
58
 
59
+ def autocorrect_method_name(corrector, node)
60
+ corrector.replace(node.loc.selector,
61
+ static_method_name(node.method_name.to_s))
62
+ end
63
+
64
+ def autocorrect_argument_keywords(corrector, node, keywords)
65
+ keywords.each.with_index do |keyword, idx|
66
+ corrector.insert_before(node.arguments[idx].loc.expression, keyword)
67
+ end
68
+ end
69
+
63
70
  def whitelist
64
71
  cop_config['Whitelist']
65
72
  end
@@ -52,19 +52,9 @@ module RuboCop
52
52
  # the data is the http parameters and environment sent in
53
53
  # the Rails 5 http call
54
54
  def autocorrect(node)
55
- http_path, *data = *node.arguments
56
-
57
- controller_action = http_path.source
58
- params = convert_hash_data(data.first, 'params')
59
- session = convert_hash_data(data.last, 'session') if data.size > 1
60
- # the range of the text to replace, which is the whole line
61
- code_to_replace = node.loc.expression
62
- # what to replace with
63
- format = parentheses_format(node)
64
- new_code = format(format, name: node.method_name,
65
- action: controller_action,
66
- params: params, session: session)
67
- ->(corrector) { corrector.replace(code_to_replace, new_code) }
55
+ lambda do |corrector|
56
+ corrector.replace(node.loc.expression, correction(node))
57
+ end
68
58
  end
69
59
 
70
60
  private
@@ -101,7 +91,20 @@ module RuboCop
101
91
  format(', %<type>s: %<hash_data>s', type: type, hash_data: hash_data)
102
92
  end
103
93
 
104
- def parentheses_format(node)
94
+ def correction(node)
95
+ http_path, *data = *node.arguments
96
+
97
+ controller_action = http_path.source
98
+ params = convert_hash_data(data.first, 'params')
99
+ session = convert_hash_data(data.last, 'session') if data.size > 1
100
+
101
+ format(correction_template(node), name: node.method_name,
102
+ action: controller_action,
103
+ params: params,
104
+ session: session)
105
+ end
106
+
107
+ def correction_template(node)
105
108
  if parentheses?(node)
106
109
  '%<name>s(%<action>s%<params>s%<session>s)'
107
110
  else
@@ -143,7 +143,7 @@ module RuboCop
143
143
  'to define HTTP status code.'.freeze
144
144
  DEFAULT_MSG = 'Prefer `numeric` over `symbolic` ' \
145
145
  'to define HTTP status code.'.freeze
146
- WHITELIST_STATUS = %i[error success missing redirect].freeze
146
+ PERMITTED_STATUS = %i[error success missing redirect].freeze
147
147
 
148
148
  attr_reader :node
149
149
  def initialize(node)
@@ -151,7 +151,7 @@ module RuboCop
151
151
  end
152
152
 
153
153
  def offensive?
154
- !node.int_type? && !whitelisted_symbol?
154
+ !node.int_type? && !permitted_symbol?
155
155
  end
156
156
 
157
157
  def message
@@ -176,8 +176,8 @@ module RuboCop
176
176
  node.value
177
177
  end
178
178
 
179
- def whitelisted_symbol?
180
- node.sym_type? && WHITELIST_STATUS.include?(node.value)
179
+ def permitted_symbol?
180
+ node.sym_type? && PERMITTED_STATUS.include?(node.value)
181
181
  end
182
182
  end
183
183
  end
@@ -128,8 +128,8 @@ module RuboCop
128
128
  # has_many :physicians, through: :appointments
129
129
  # end
130
130
  #
131
- # @see http://guides.rubyonrails.org/association_basics.html#bi-directional-associations
132
- # @see http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
131
+ # @see https://guides.rubyonrails.org/association_basics.html#bi-directional-associations
132
+ # @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
133
133
  class InverseOf < Cop
134
134
  extend TargetRailsVersion
135
135
 
@@ -124,7 +124,7 @@ module RuboCop
124
124
  # end
125
125
  # end
126
126
  #
127
- # @see http://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html
127
+ # @see https://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html
128
128
  class ReversibleMigration < Cop
129
129
  MSG = '%<action>s is not reversible.'.freeze
130
130
  IRREVERSIBLE_CHANGE_TABLE_CALLS = %i[
@@ -5,7 +5,7 @@ module RuboCop
5
5
  module Rails
6
6
  # This cop checks for the use of methods which skip
7
7
  # validations which are listed in
8
- # http://guides.rubyonrails.org/active_record_validations.html#skipping-validations
8
+ # https://guides.rubyonrails.org/active_record_validations.html#skipping-validations
9
9
  #
10
10
  # Methods may be ignored from this rule by configuring a `Whitelist`.
11
11
  #