rubocop 0.65.0 → 0.66.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +53 -4
  4. data/lib/rubocop.rb +4 -5
  5. data/lib/rubocop/cli.rb +1 -1
  6. data/lib/rubocop/config.rb +1 -1
  7. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +3 -5
  8. data/lib/rubocop/cop/layout/class_structure.rb +59 -28
  9. data/lib/rubocop/cop/layout/extra_spacing.rb +18 -0
  10. data/lib/rubocop/cop/layout/indentation_width.rb +25 -5
  11. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +33 -17
  12. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +30 -11
  13. data/lib/rubocop/cop/lint/else_layout.rb +1 -0
  14. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
  15. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +38 -0
  16. data/lib/rubocop/cop/lint/shadowed_exception.rb +14 -1
  17. data/lib/rubocop/cop/lint/to_json.rb +38 -0
  18. data/lib/rubocop/cop/lint/void.rb +1 -1
  19. data/lib/rubocop/cop/message_annotator.rb +4 -4
  20. data/lib/rubocop/cop/metrics/abc_size.rb +1 -0
  21. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +31 -9
  22. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  23. data/lib/rubocop/cop/mixin/integer_node.rb +1 -1
  24. data/lib/rubocop/cop/mixin/method_preference.rb +2 -1
  25. data/lib/rubocop/cop/naming/constant_name.rb +6 -1
  26. data/lib/rubocop/cop/naming/predicate_name.rb +6 -0
  27. data/lib/rubocop/cop/rails/link_to_blank.rb +1 -1
  28. data/lib/rubocop/cop/rails/output.rb +18 -1
  29. data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
  30. data/lib/rubocop/cop/rails/time_zone.rb +10 -0
  31. data/lib/rubocop/cop/rails/validation.rb +3 -2
  32. data/lib/rubocop/cop/style/block_delimiters.rb +30 -1
  33. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  34. data/lib/rubocop/cop/style/constant_visibility.rb +66 -0
  35. data/lib/rubocop/cop/style/identical_conditional_branches.rb +8 -12
  36. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -4
  37. data/lib/rubocop/cop/style/numeric_literals.rb +16 -7
  38. data/lib/rubocop/cop/style/option_hash.rb +5 -0
  39. data/lib/rubocop/cop/style/redundant_freeze.rb +13 -1
  40. data/lib/rubocop/cop/style/redundant_self.rb +3 -1
  41. data/lib/rubocop/cop/style/stderr_puts.rb +1 -1
  42. data/lib/rubocop/cop/style/symbol_array.rb +9 -1
  43. data/lib/rubocop/cop/style/trivial_accessors.rb +5 -0
  44. data/lib/rubocop/cop/style/word_array.rb +0 -4
  45. data/lib/rubocop/cop/util.rb +4 -0
  46. data/lib/rubocop/core_ext/string.rb +47 -0
  47. data/lib/rubocop/node_pattern.rb +76 -55
  48. data/lib/rubocop/processed_source.rb +2 -2
  49. data/lib/rubocop/result_cache.rb +4 -4
  50. data/lib/rubocop/rspec/cop_helper.rb +5 -0
  51. data/lib/rubocop/rspec/expect_offense.rb +5 -5
  52. data/lib/rubocop/runner.rb +6 -13
  53. data/lib/rubocop/version.rb +1 -1
  54. metadata +15 -19
@@ -18,7 +18,7 @@ module RuboCop
18
18
  MSG = 'Specify a `:rel` option containing noopener.'.freeze
19
19
 
20
20
  def_node_matcher :blank_target?, <<-PATTERN
21
- (pair {(sym :target) (str "target")} (str "_blank"))
21
+ (pair {(sym :target) (str "target")} {(str "_blank") (sym :_blank)})
22
22
  PATTERN
23
23
 
24
24
  def_node_matcher :includes_noopener?, <<-PATTERN
@@ -21,11 +21,28 @@ module RuboCop
21
21
  (send nil? {:ap :p :pp :pretty_print :print :puts} ...)
22
22
  PATTERN
23
23
 
24
+ def_node_matcher :io_output?, <<-PATTERN
25
+ (send
26
+ {
27
+ (gvar #match_gvar?)
28
+ {(const nil? :STDOUT) (const nil? :STDERR)}
29
+ }
30
+ {:binwrite :syswrite :write :write_nonblock}
31
+ ...)
32
+ PATTERN
33
+
24
34
  def on_send(node)
25
- return unless output?(node) && node.arguments?
35
+ return unless (output?(node) || io_output?(node)) &&
36
+ node.arguments?
26
37
 
27
38
  add_offense(node, location: :selector)
28
39
  end
40
+
41
+ private
42
+
43
+ def match_gvar?(sym)
44
+ %i[$stdout $stderr].include?(sym)
45
+ end
29
46
  end
30
47
  end
31
48
  end
@@ -21,7 +21,7 @@ module RuboCop
21
21
  PATTERN
22
22
 
23
23
  def_node_search :reflection_class_name, <<-PATTERN
24
- (pair (sym :class_name) !str)
24
+ (pair (sym :class_name) [!str !sym])
25
25
  PATTERN
26
26
 
27
27
  def on_send(node)
@@ -78,6 +78,16 @@ module RuboCop
78
78
  check_time_node(klass, node.parent) if TIMECLASS.include?(klass)
79
79
  end
80
80
 
81
+ def autocorrect(node)
82
+ lambda do |corrector|
83
+ if acceptable?
84
+ corrector.insert_after(node.source_range, '.in_time_zone')
85
+ else
86
+ corrector.insert_after(node.children[0].source_range, '.zone')
87
+ end
88
+ end
89
+ end
90
+
81
91
  private
82
92
 
83
93
  def check_time_node(klass, node)
@@ -87,8 +87,9 @@ module RuboCop
87
87
  "#{validate_type}: #{braced_options(last_argument)}"
88
88
  )
89
89
  else
90
- corrector.insert_after(node.loc.expression,
91
- ", #{validate_type}: true")
90
+ range = last_argument.source_range
91
+
92
+ corrector.insert_after(range, ", #{validate_type}: true")
92
93
  end
93
94
  end
94
95
 
@@ -60,6 +60,30 @@ module RuboCop
60
60
  # x
61
61
  # }.inspect
62
62
  #
63
+ # # The AllowBracesOnProceduralOneLiners option is ignored unless the
64
+ # # EnforcedStyle is set to `semantic`. If so:
65
+ #
66
+ # # If the AllowBracesOnProceduralOneLiners option is unspecified, or
67
+ # # set to `false` or any other falsey value, then semantic purity is
68
+ # # maintained, so one-line procedural blocks must use do-end, not
69
+ # # braces.
70
+ #
71
+ # # bad
72
+ # collection.each { |element| puts element }
73
+ #
74
+ # # good
75
+ # collection.each do |element| puts element end
76
+ #
77
+ # # If the AllowBracesOnProceduralOneLiners option is set to `true`, or
78
+ # # any other truthy value, then one-line procedural blocks may use
79
+ # # either style. (There is no setting for requiring braces on them.)
80
+ #
81
+ # # good
82
+ # collection.each { |element| puts element }
83
+ #
84
+ # # also good
85
+ # collection.each do |element| puts element end
86
+ #
63
87
  # @example EnforcedStyle: braces_for_chaining
64
88
  # # bad
65
89
  # words.each do |word|
@@ -216,7 +240,8 @@ module RuboCop
216
240
  method_name = node.method_name
217
241
 
218
242
  if node.braces?
219
- functional_method?(method_name) || functional_block?(node)
243
+ functional_method?(method_name) || functional_block?(node) ||
244
+ (procedural_oneliners_may_have_braces? && !node.multiline?)
220
245
  else
221
246
  procedural_method?(method_name) || !return_value_used?(node)
222
247
  end
@@ -250,6 +275,10 @@ module RuboCop
250
275
  return_value_used?(node) || return_value_of_scope?(node)
251
276
  end
252
277
 
278
+ def procedural_oneliners_may_have_braces?
279
+ cop_config['AllowBracesOnProceduralOneLiners']
280
+ end
281
+
253
282
  def procedural_method?(method_name)
254
283
  cop_config['ProceduralMethods'].map(&:to_sym).include?(method_name)
255
284
  end
@@ -350,6 +350,8 @@ module RuboCop
350
350
  end
351
351
 
352
352
  def lhs_all_match?(branches)
353
+ return true if branches.empty?
354
+
353
355
  first_lhs = lhs(branches.first)
354
356
  branches.all? { |branch| lhs(branch) == first_lhs }
355
357
  end
@@ -372,7 +374,7 @@ module RuboCop
372
374
  def allowed_statements?(branches)
373
375
  return false unless branches.all?
374
376
 
375
- statements = branches.map { |branch| tail(branch) }
377
+ statements = branches.map { |branch| tail(branch) }.compact
376
378
 
377
379
  lhs_all_match?(statements) && statements.none?(&:masgn_type?) &&
378
380
  assignment_types_match?(*statements)
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks that constants defined in classes and modules have
7
+ # an explicit visibility declaration. By default, Ruby makes all class-
8
+ # and module constants public, which litters the public API of the
9
+ # class or module. Explicitly declaring a visibility makes intent more
10
+ # clear, and prevents outside actors from touching private state.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # class Foo
16
+ # BAR = 42
17
+ # BAZ = 43
18
+ # end
19
+ #
20
+ # # good
21
+ # class Foo
22
+ # BAR = 42
23
+ # private_constant :BAR
24
+ #
25
+ # BAZ = 43
26
+ # public_constant :BAZ
27
+ # end
28
+ #
29
+ class ConstantVisibility < Cop
30
+ MSG = 'Explicitly make `%<constant_name>s` public or private using ' \
31
+ 'either `#public_constant` or `#private_constant`.'.freeze
32
+
33
+ def on_casgn(node)
34
+ return unless class_or_module_scope?(node)
35
+ return if visibility_declaration?(node)
36
+
37
+ add_offense(node)
38
+ end
39
+
40
+ private
41
+
42
+ def message(node)
43
+ _namespace, constant_name, _value = *node
44
+
45
+ format(MSG, constant_name: constant_name)
46
+ end
47
+
48
+ def class_or_module_scope?(node)
49
+ node.parent && %i[class module].include?(node.parent.type)
50
+ end
51
+
52
+ def visibility_declaration?(node)
53
+ _namespace, constant_name, _value = *node
54
+
55
+ node.parent.each_child_node(:send).any? do |child|
56
+ visibility_declaration_for?(child, constant_name)
57
+ end
58
+ end
59
+
60
+ def_node_matcher :visibility_declaration_for?, <<-PATTERN
61
+ (send nil? {:public_constant :private_constant} ({sym str} %1))
62
+ PATTERN
63
+ end
64
+ end
65
+ end
66
+ end
@@ -69,11 +69,6 @@ module RuboCop
69
69
  return if node.elsif?
70
70
 
71
71
  branches = expand_elses(node.else_branch).unshift(node.if_branch)
72
-
73
- # return if any branch is empty. An empty branch can be an `if`
74
- # without an `else` or a branch that contains only comments.
75
- return if branches.any?(&:nil?)
76
-
77
72
  check_branches(branches)
78
73
  end
79
74
 
@@ -81,19 +76,20 @@ module RuboCop
81
76
  return unless node.else? && node.else_branch
82
77
 
83
78
  branches = node.when_branches.map(&:body).push(node.else_branch)
84
-
85
- return if branches.any?(&:nil?)
86
-
87
79
  check_branches(branches)
88
80
  end
89
81
 
90
82
  private
91
83
 
92
84
  def check_branches(branches)
93
- tails = branches.compact.map { |branch| tail(branch) }
94
- check_expressions(tails)
95
- heads = branches.compact.map { |branch| head(branch) }
96
- check_expressions(heads)
85
+ # return if any branch is empty. An empty branch can be an `if`
86
+ # without an `else` or a branch that contains only comments.
87
+ return if branches.any?(&:nil?)
88
+
89
+ tails = branches.map { |branch| tail(branch) }
90
+ check_expressions(tails) if tails.none?(&:nil?)
91
+ heads = branches.map { |branch| head(branch) }
92
+ check_expressions(heads) if tails.none?(&:nil?)
97
93
  end
98
94
 
99
95
  def check_expressions(expressions)
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/ClassLength
3
4
  module RuboCop
4
5
  module Cop
5
6
  module Style
@@ -8,7 +9,11 @@ module RuboCop
8
9
  #
9
10
  # In the default style (require_parentheses), macro methods are ignored.
10
11
  # Additional methods can be added to the `IgnoredMethods` list. This
11
- # option is valid only in the default style.
12
+ # option is valid only in the default style. Macros can be included by
13
+ # either setting `IgnoreMacros` to false or adding specific macros to
14
+ # the `IncludedMacros` list. If a method is listed in both
15
+ # `IncludedMacros` and `IgnoredMethods`, then the latter takes
16
+ # precedence (that is, the method is ignored).
12
17
  #
13
18
  # In the alternative style (omit_parentheses), there are three additional
14
19
  # options.
@@ -206,11 +211,17 @@ module RuboCop
206
211
  end
207
212
 
208
213
  def eligible_for_parentheses_omission?(node)
209
- node.operator_method? || node.setter_method? || ignore_macros?(node)
214
+ node.operator_method? || node.setter_method? || ignored_macro?(node)
210
215
  end
211
216
 
212
- def ignore_macros?(node)
213
- cop_config['IgnoreMacros'] && node.macro?
217
+ def included_macros_list
218
+ cop_config.fetch('IncludedMacros', []).map(&:to_sym)
219
+ end
220
+
221
+ def ignored_macro?(node)
222
+ cop_config['IgnoreMacros'] &&
223
+ node.macro? &&
224
+ !included_macros_list.include?(node.method_name)
214
225
  end
215
226
 
216
227
  def args_begin(node)
@@ -354,3 +365,4 @@ module RuboCop
354
365
  end
355
366
  end
356
367
  end
368
+ # rubocop:enable Metrics/ClassLength
@@ -32,6 +32,7 @@ module RuboCop
32
32
 
33
33
  MSG = 'Use underscores(_) as thousands separator and ' \
34
34
  'separate every 3 digits with them.'.freeze
35
+ DELIMITER_REGEXP = /[eE.]/.freeze
35
36
 
36
37
  def on_int(node)
37
38
  check(node)
@@ -75,7 +76,20 @@ module RuboCop
75
76
  end
76
77
 
77
78
  def format_number(node)
78
- int_part, float_part = node.source.split('.')
79
+ source = node.source.gsub(/\s+/, '')
80
+ int_part, additional_part = source.split(DELIMITER_REGEXP, 2)
81
+ formatted_int = format_int_part(int_part)
82
+ delimiter = source[DELIMITER_REGEXP]
83
+
84
+ if additional_part
85
+ formatted_int + delimiter + additional_part
86
+ else
87
+ formatted_int
88
+ end
89
+ end
90
+
91
+ # @param int_part [String]
92
+ def format_int_part(int_part)
79
93
  int_part = Integer(int_part)
80
94
  formatted_int = int_part
81
95
  .abs
@@ -84,12 +98,7 @@ module RuboCop
84
98
  .gsub(/...(?=.)/, '\&_')
85
99
  .reverse
86
100
  formatted_int.insert(0, '-') if int_part < 0
87
-
88
- if float_part
89
- format('%<int>s.%<float>s', int: formatted_int, float: float_part)
90
- else
91
- formatted_int
92
- end
101
+ formatted_int
93
102
  end
94
103
 
95
104
  def min_digits
@@ -28,6 +28,7 @@ module RuboCop
28
28
 
29
29
  def on_args(node)
30
30
  return if super_used?(node)
31
+ return if whitelist.include?(node.parent.method_name.to_s)
31
32
 
32
33
  option_hash(node) do |options|
33
34
  add_offense(options)
@@ -36,6 +37,10 @@ module RuboCop
36
37
 
37
38
  private
38
39
 
40
+ def whitelist
41
+ cop_config['Whitelist'] || []
42
+ end
43
+
39
44
  def suspicious_name?(arg_name)
40
45
  cop_config.key?('SuspiciousParamNames') &&
41
46
  cop_config['SuspiciousParamNames'].include?(arg_name.to_s)
@@ -19,7 +19,8 @@ module RuboCop
19
19
 
20
20
  def on_send(node)
21
21
  return unless node.receiver && node.method?(:freeze) &&
22
- immutable_literal?(node.receiver)
22
+ (immutable_literal?(node.receiver) ||
23
+ operation_produces_immutable_object?(node.receiver))
23
24
 
24
25
  add_offense(node)
25
26
  end
@@ -49,6 +50,17 @@ module RuboCop
49
50
  node
50
51
  end
51
52
  end
53
+
54
+ def_node_matcher :operation_produces_immutable_object?, <<-PATTERN
55
+ {
56
+ (begin (send {float int} {:+ :- :* :** :/ :% :<<} _))
57
+ (begin (send _ {:+ :- :* :** :/ :%} {float int}))
58
+ (begin (send _ {:== :=== :!= :<= :>= :< :>} _))
59
+ (send (const nil? :ENV) :[] _)
60
+ (send _ {:count :length :size} ...)
61
+ (block (send _ {:count :length :size} ...) ...)
62
+ }
63
+ PATTERN
52
64
  end
53
65
  end
54
66
  end
@@ -43,6 +43,7 @@ module RuboCop
43
43
  # end
44
44
  class RedundantSelf < Cop
45
45
  MSG = 'Redundant `self` detected.'.freeze
46
+ KERNEL_METHODS = Kernel.methods(false)
46
47
 
47
48
  def self.autocorrect_incompatible_with
48
49
  [ColonMethodCall]
@@ -117,7 +118,8 @@ module RuboCop
117
118
 
118
119
  def allowed_send_node?(node)
119
120
  @allowed_send_nodes.include?(node) ||
120
- @local_variables_scopes[node].include?(node.method_name)
121
+ @local_variables_scopes[node].include?(node.method_name) ||
122
+ KERNEL_METHODS.include?(node.method_name)
121
123
  end
122
124
 
123
125
  def regular_method_call?(node)
@@ -22,7 +22,7 @@ module RuboCop
22
22
 
23
23
  def_node_matcher :stderr_puts?, <<-PATTERN
24
24
  (send
25
- (gvar #stderr_gvar?) :puts
25
+ (gvar #stderr_gvar?) :puts $_
26
26
  ...)
27
27
  PATTERN
28
28
 
@@ -70,7 +70,15 @@ module RuboCop
70
70
  end
71
71
 
72
72
  def correct_bracketed(node)
73
- syms = node.children.map { |c| to_symbol_literal(c.value.to_s) }
73
+ syms = node.children.map do |c|
74
+ if c.dsym_type?
75
+ string_literal = to_string_literal(c.source)
76
+
77
+ ':' + trim_string_interporation_escape_character(string_literal)
78
+ else
79
+ to_symbol_literal(c.value.to_s)
80
+ end
81
+ end
74
82
 
75
83
  lambda do |corrector|
76
84
  corrector.replace(node.source_range, "[#{syms.join(', ')}]")
@@ -31,6 +31,7 @@ module RuboCop
31
31
  MSG = 'Use `attr_%<kind>s` to define trivial %<kind>s methods.'.freeze
32
32
 
33
33
  def on_def(node)
34
+ return if top_level_node?(node)
34
35
  return if in_module_or_instance_eval?(node)
35
36
  return if ignore_class_methods? && node.defs_type?
36
37
 
@@ -180,6 +181,10 @@ module RuboCop
180
181
  )
181
182
  end
182
183
  end
184
+
185
+ def top_level_node?(node)
186
+ node.parent.nil?
187
+ end
183
188
  end
184
189
  end
185
190
  end