rubocop 0.60.0 → 0.61.0

Sign up to get free protection for your applications and to get access to all the features.
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
  #