rubocop 0.39.0 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubocop might be problematic. Click here for more details.

Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -1
  3. data/config/default.yml +65 -2
  4. data/config/disabled.yml +0 -28
  5. data/config/enabled.yml +40 -0
  6. data/lib/rubocop.rb +3 -0
  7. data/lib/rubocop/ast_node.rb +28 -13
  8. data/lib/rubocop/cached_data.rb +15 -2
  9. data/lib/rubocop/cli.rb +24 -8
  10. data/lib/rubocop/config.rb +3 -3
  11. data/lib/rubocop/config_loader.rb +0 -7
  12. data/lib/rubocop/cop/cop.rb +2 -2
  13. data/lib/rubocop/cop/lint/condition_position.rb +3 -1
  14. data/lib/rubocop/cop/lint/else_layout.rb +3 -2
  15. data/lib/rubocop/cop/lint/end_alignment.rb +2 -2
  16. data/lib/rubocop/cop/lint/nested_method_definition.rb +15 -9
  17. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  18. data/lib/rubocop/cop/lint/unused_block_argument.rb +2 -0
  19. data/lib/rubocop/cop/lint/useless_access_modifier.rb +86 -20
  20. data/lib/rubocop/cop/lint/useless_array_splat.rb +56 -0
  21. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  22. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +11 -25
  23. data/lib/rubocop/cop/mixin/if_node.rb +4 -0
  24. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +14 -12
  25. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +75 -9
  26. data/lib/rubocop/cop/mixin/on_normal_if_unless.rb +1 -1
  27. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -2
  28. data/lib/rubocop/cop/mixin/trailing_comma.rb +15 -5
  29. data/lib/rubocop/cop/performance/case_when_splat.rb +71 -44
  30. data/lib/rubocop/cop/performance/detect.rb +28 -18
  31. data/lib/rubocop/cop/performance/end_with.rb +1 -1
  32. data/lib/rubocop/cop/performance/redundant_merge.rb +29 -11
  33. data/lib/rubocop/cop/performance/start_with.rb +1 -1
  34. data/lib/rubocop/cop/performance/string_replacement.rb +39 -20
  35. data/lib/rubocop/cop/rails/action_filter.rb +1 -2
  36. data/lib/rubocop/cop/rails/date.rb +2 -5
  37. data/lib/rubocop/cop/rails/time_zone.rb +3 -6
  38. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +57 -0
  39. data/lib/rubocop/cop/style/alias.rb +10 -3
  40. data/lib/rubocop/cop/style/align_parameters.rb +8 -2
  41. data/lib/rubocop/cop/style/and_or.rb +29 -21
  42. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  43. data/lib/rubocop/cop/style/collection_methods.rb +1 -2
  44. data/lib/rubocop/cop/style/comment_indentation.rb +1 -1
  45. data/lib/rubocop/cop/style/conditional_assignment.rb +13 -7
  46. data/lib/rubocop/cop/style/empty_case_condition.rb +96 -0
  47. data/lib/rubocop/cop/style/encoding.rb +9 -5
  48. data/lib/rubocop/cop/style/extra_spacing.rb +22 -7
  49. data/lib/rubocop/cop/style/file_name.rb +7 -2
  50. data/lib/rubocop/cop/style/guard_clause.rb +18 -10
  51. data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
  52. data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -1
  53. data/lib/rubocop/cop/style/indentation_width.rb +7 -4
  54. data/lib/rubocop/cop/style/lambda.rb +98 -30
  55. data/lib/rubocop/cop/style/line_end_concatenation.rb +5 -0
  56. data/lib/rubocop/cop/style/multiline_array_brace_layout.rb +34 -9
  57. data/lib/rubocop/cop/style/multiline_assignment_layout.rb +2 -1
  58. data/lib/rubocop/cop/style/multiline_hash_brace_layout.rb +42 -17
  59. data/lib/rubocop/cop/style/multiline_method_call_brace_layout.rb +39 -14
  60. data/lib/rubocop/cop/style/multiline_method_definition_brace_layout.rb +36 -15
  61. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +8 -6
  62. data/lib/rubocop/cop/style/negated_while.rb +2 -1
  63. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +15 -0
  64. data/lib/rubocop/cop/style/nested_ternary_operator.rb +5 -8
  65. data/lib/rubocop/cop/style/next.rb +1 -1
  66. data/lib/rubocop/cop/style/not.rb +5 -2
  67. data/lib/rubocop/cop/style/parentheses_around_condition.rb +2 -2
  68. data/lib/rubocop/cop/style/raise_args.rb +70 -7
  69. data/lib/rubocop/cop/style/redundant_exception.rb +34 -20
  70. data/lib/rubocop/cop/style/redundant_parentheses.rb +27 -1
  71. data/lib/rubocop/cop/style/space_after_colon.rb +14 -10
  72. data/lib/rubocop/cop/style/space_after_comma.rb +5 -0
  73. data/lib/rubocop/cop/style/space_after_not.rb +3 -4
  74. data/lib/rubocop/cop/style/space_after_semicolon.rb +5 -0
  75. data/lib/rubocop/cop/style/space_around_operators.rb +2 -1
  76. data/lib/rubocop/cop/style/special_global_vars.rb +4 -2
  77. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  78. data/lib/rubocop/cop/style/string_methods.rb +1 -2
  79. data/lib/rubocop/cop/style/symbol_proc.rb +7 -1
  80. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +25 -0
  81. data/lib/rubocop/cop/style/word_array.rb +50 -22
  82. data/lib/rubocop/cop/util.rb +0 -4
  83. data/lib/rubocop/formatter/clang_style_formatter.rb +38 -22
  84. data/lib/rubocop/options.rb +45 -10
  85. data/lib/rubocop/path_util.rb +2 -34
  86. data/lib/rubocop/result_cache.rb +10 -4
  87. data/lib/rubocop/runner.rb +5 -3
  88. data/lib/rubocop/version.rb +1 -1
  89. metadata +7 -4
@@ -21,7 +21,7 @@ module RuboCop
21
21
  SINGLE_QUOTE = "'".freeze
22
22
 
23
23
  def_node_matcher :redundant_regex?, <<-END
24
- {(send $_ {:match :=~} (regexp (str $#literal_at_end?) (regopt)))
24
+ {(send $!nil {:match :=~} (regexp (str $#literal_at_end?) (regopt)))
25
25
  (send (regexp (str $#literal_at_end?) (regopt)) {:match :=~} $_)}
26
26
  END
27
27
 
@@ -22,17 +22,7 @@ module RuboCop
22
22
  END
23
23
 
24
24
  def on_send(node)
25
- redundant_merge(node) do |receiver, pairs|
26
- if node.value_used?
27
- parent = node.parent
28
- grandparent = parent.parent if parent.begin_type?
29
- second_arg = each_with_object_node(grandparent || parent)
30
- next if second_arg.nil?
31
- next unless receiver.loc.name.source == second_arg.loc.name.source
32
- end
33
- next if pairs.size > 1 && !receiver.pure?
34
- next if pairs.size > max_key_value_pairs
35
-
25
+ each_redundant_merge(node) do |receiver, pairs|
36
26
  assignments = to_assignments(receiver, pairs).join('; ')
37
27
  message = format(MSG, assignments, node.source)
38
28
  add_offense(node, :expression, message)
@@ -62,6 +52,34 @@ module RuboCop
62
52
 
63
53
  private
64
54
 
55
+ def each_redundant_merge(node)
56
+ redundant_merge(node) do |receiver, pairs|
57
+ next if node.value_used? &&
58
+ !value_used_inside_each_with_object?(node, receiver)
59
+ next if pairs.size > 1 && !receiver.pure?
60
+ next if pairs.size > max_key_value_pairs
61
+
62
+ yield receiver, pairs
63
+ end
64
+ end
65
+
66
+ def value_used_inside_each_with_object?(node, receiver)
67
+ while receiver.respond_to?(:send_type?) && receiver.send_type?
68
+ receiver, = *receiver
69
+ end
70
+
71
+ unless receiver.respond_to?(:lvar_type?) && receiver.lvar_type?
72
+ return false
73
+ end
74
+
75
+ parent = node.parent
76
+ grandparent = parent.parent if parent.begin_type?
77
+ second_arg = each_with_object_node(grandparent || parent)
78
+ return false if second_arg.nil?
79
+
80
+ receiver.loc.name.source == second_arg.loc.name.source
81
+ end
82
+
65
83
  def to_assignments(receiver, pairs)
66
84
  pairs.map do |pair|
67
85
  key, value = *pair
@@ -21,7 +21,7 @@ module RuboCop
21
21
  SINGLE_QUOTE = "'".freeze
22
22
 
23
23
  def_node_matcher :redundant_regex?, <<-END
24
- {(send $_ {:match :=~} (regexp (str $#literal_at_start?) (regopt)))
24
+ {(send $!nil {:match :=~} (regexp (str $#literal_at_start?) (regopt)))
25
25
  (send (regexp (str $#literal_at_start?) (regopt)) {:match :=~} $_)}
26
26
  END
27
27
 
@@ -32,28 +32,12 @@ module RuboCop
32
32
 
33
33
  def on_send(node)
34
34
  _string, method, first_param, second_param = *node
35
- return unless GSUB_METHODS.include?(method)
36
- return unless string?(second_param)
37
- return unless DETERMINISTIC_TYPES.include?(first_param.type)
38
-
39
- first_source, options = first_source(first_param)
40
- second_source, = *second_param
41
-
42
- return if first_source.nil?
43
-
44
- if regex?(first_param)
45
- return unless first_source =~ DETERMINISTIC_REGEX
46
- return if options
47
- # This must be done after checking DETERMINISTIC_REGEX
48
- # Otherwise things like \s will trip us up
49
- first_source = interpret_string_escapes(first_source)
50
- end
51
35
 
52
- return if first_source.length != 1
53
- return unless second_source.length <= 1
36
+ return unless GSUB_METHODS.include?(method)
37
+ return if accept_second_param?(second_param)
38
+ return if accept_first_param?(first_param)
54
39
 
55
- message = message(method, first_source, second_source)
56
- add_offense(node, range(node), message)
40
+ offense(node, method, first_param, second_param)
57
41
  end
58
42
 
59
43
  def autocorrect(node)
@@ -84,6 +68,41 @@ module RuboCop
84
68
 
85
69
  private
86
70
 
71
+ def accept_second_param?(second_param)
72
+ return true unless string?(second_param)
73
+ second_source, = *second_param
74
+
75
+ second_source.length > 1
76
+ end
77
+
78
+ def accept_first_param?(first_param)
79
+ return true unless DETERMINISTIC_TYPES.include?(first_param.type)
80
+
81
+ first_source, options = first_source(first_param)
82
+ return true if first_source.nil?
83
+
84
+ if regex?(first_param)
85
+ return true if options
86
+ return true unless first_source =~ DETERMINISTIC_REGEX
87
+ # This must be done after checking DETERMINISTIC_REGEX
88
+ # Otherwise things like \s will trip us up
89
+ first_source = interpret_string_escapes(first_source)
90
+ end
91
+
92
+ first_source.length != 1
93
+ end
94
+
95
+ def offense(node, method, first_param, second_param)
96
+ first_source, = first_source(first_param)
97
+ if regex?(first_param)
98
+ first_source = interpret_string_escapes(first_source)
99
+ end
100
+ second_source, = *second_param
101
+ message = message(method, first_source, second_source)
102
+
103
+ add_offense(node, range(node), message)
104
+ end
105
+
87
106
  def string?(node)
88
107
  node && node.str_type?
89
108
  end
@@ -73,8 +73,7 @@ module RuboCop
73
73
  add_offense(node, :selector,
74
74
  format(MSG,
75
75
  preferred_method(method_name),
76
- method_name)
77
- )
76
+ method_name))
78
77
  end
79
78
 
80
79
  def offending_method?(method_name)
@@ -65,9 +65,7 @@ module RuboCop
65
65
 
66
66
  add_offense(node, :selector,
67
67
  format(MSG_SEND,
68
- method_name
69
- )
70
- )
68
+ method_name))
71
69
  end
72
70
 
73
71
  private
@@ -81,8 +79,7 @@ module RuboCop
81
79
  add_offense(node, :selector,
82
80
  format(MSG,
83
81
  "Date.#{method_name}",
84
- "Time.zone.#{method_name}")
85
- )
82
+ "Time.zone.#{method_name}"))
86
83
  end
87
84
 
88
85
  def extract_method_chain(node)
@@ -80,18 +80,15 @@ module RuboCop
80
80
  if acceptable?
81
81
  format(MSG_ACCEPTABLE,
82
82
  "#{klass}.#{method_name}",
83
- acceptable_methods(klass, method_name, node).join(', ')
84
- )
83
+ acceptable_methods(klass, method_name, node).join(', '))
85
84
  elsif method_name == 'current'
86
85
  format(MSG_CURRENT,
87
- "#{klass}.#{method_name}"
88
- )
86
+ "#{klass}.#{method_name}")
89
87
  else
90
88
  safe_method_name = safe_method(method_name, node)
91
89
  format(MSG,
92
90
  "#{klass}.#{method_name}",
93
- "Time.zone.#{safe_method_name}"
94
- )
91
+ "Time.zone.#{safe_method_name}")
95
92
  end
96
93
  end
97
94
 
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Rails
7
+ # Prefer the use of uniq before pluck instead of after.
8
+ #
9
+ # The use of uniq before pluck is preferred because it executes
10
+ # within the database.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # Model.where(...).pluck(:id).uniq
15
+ #
16
+ # # good
17
+ # Model.where(...).uniq.pluck(:id)
18
+ #
19
+ class UniqBeforePluck < RuboCop::Cop::Cop
20
+ MSG = 'Use uniq before pluck'.freeze
21
+ DOT_UNIQ = '.uniq'.freeze
22
+ NEWLINE = "\n".freeze
23
+
24
+ def on_send(node)
25
+ receiver, method_name, *_args = *node
26
+
27
+ unless method_name == :uniq &&
28
+ !receiver.nil? &&
29
+ receiver.send_type? &&
30
+ receiver.children[1] == :pluck
31
+ return
32
+ end
33
+ add_offense(node, :selector, MSG)
34
+ end
35
+
36
+ def autocorrect(node)
37
+ lines = node.source.split(NEWLINE)
38
+ begin_remove_pos = if lines.last.strip == DOT_UNIQ
39
+ node.source.rindex(NEWLINE)
40
+ else
41
+ node.loc.dot.begin_pos
42
+ end
43
+ receiver = node.children.first
44
+
45
+ lambda do |corrector|
46
+ corrector.remove(
47
+ Parser::Source::Range.new(node.loc.expression.source_buffer,
48
+ begin_remove_pos,
49
+ node.loc.selector.end_pos)
50
+ )
51
+ corrector.insert_before(receiver.loc.dot.begin, DOT_UNIQ)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -27,8 +27,11 @@ module RuboCop
27
27
  def on_alias(node)
28
28
  # alias_method can't be used with global variables
29
29
  return if node.children.any?(&:gvar_type?)
30
+ # alias_method can't be used in instance_eval blocks
31
+ scope_type = scope_type(node)
32
+ return if scope_type == :instance_eval
30
33
 
31
- if scope_type(node) == :dynamic || style == :prefer_alias_method
34
+ if scope_type == :dynamic || style == :prefer_alias_method
32
35
  add_offense(node, :keyword, MSG_ALIAS)
33
36
  elsif node.children.none? { |arg| bareword?(arg) }
34
37
  existing_args = node.children.map(&:source).join(' ')
@@ -54,13 +57,17 @@ module RuboCop
54
57
 
55
58
  # In this expression, will `self` be the same as the innermost enclosing
56
59
  # class or module block (:lexical)? Or will it be something else
57
- # (:dynamic)?
60
+ # (:dynamic)? If we're in an instance_eval block, return that.
58
61
  def scope_type(node)
59
62
  while (parent = node.parent)
60
63
  case parent.type
61
64
  when :class, :module
62
65
  return :lexical
63
- when :def, :defs, :block
66
+ when :def, :defs
67
+ return :dynamic
68
+ when :block
69
+ return :instance_eval if parent.method_name == :instance_eval
70
+
64
71
  return :dynamic
65
72
  end
66
73
  node = parent
@@ -10,6 +10,12 @@ module RuboCop
10
10
  include AutocorrectAlignment
11
11
  include OnMethodDef
12
12
 
13
+ ALIGN_PARAMS_MSG = 'Align the parameters of a method %s if they span ' \
14
+ 'more than one line.'.freeze
15
+
16
+ FIXED_INDENT_MSG = 'Use one level of indentation for parameters ' \
17
+ 'following the first line of a multi-line method %s.'.freeze
18
+
13
19
  def on_send(node)
14
20
  _receiver, method, *args = *node
15
21
 
@@ -27,8 +33,8 @@ module RuboCop
27
33
 
28
34
  def message(node)
29
35
  type = node && node.parent.send_type? ? 'call' : 'definition'
30
- "Align the parameters of a method #{type} if they span " \
31
- 'more than one line.'
36
+ msg = fixed_indentation? ? FIXED_INDENT_MSG : ALIGN_PARAMS_MSG
37
+ format(msg, type)
32
38
  end
33
39
 
34
40
  private
@@ -77,29 +77,27 @@ module RuboCop
77
77
 
78
78
  def correct_send(node, corrector)
79
79
  receiver, method_name, *args = *node
80
- if method_name == :!
81
- # ! is a special case:
82
- # 'x and !obj.method arg' can be auto-corrected if we
83
- # recurse down a level and add parens to 'obj.method arg'
84
- # however, 'not x' also parses as (send x :!)
85
-
86
- if node.loc.selector.source == '!'
87
- node = receiver
88
- return unless node.send_type?
89
- _receiver, _method_name, *args = *node
90
- elsif node.loc.selector.source == 'not'
91
- return correct_other(node, corrector)
92
- else
93
- raise 'unrecognized unary negation operator'
94
- end
95
- end
80
+ return correct_not(node, receiver, corrector) if method_name == :!
96
81
  return unless correctable_send?(node)
97
82
 
98
- sb = node.source_range.source_buffer
99
- begin_paren = node.loc.selector.end_pos
100
- range = Parser::Source::Range.new(sb, begin_paren, begin_paren + 1)
101
- corrector.replace(range, '(')
102
- corrector.insert_after(args.last.source_range, ')')
83
+ corrector.replace(whitespace_before_arg(node), '('.freeze)
84
+ corrector.insert_after(args.last.source_range, ')'.freeze)
85
+ end
86
+
87
+ # ! is a special case:
88
+ # 'x and !obj.method arg' can be auto-corrected if we
89
+ # recurse down a level and add parens to 'obj.method arg'
90
+ # however, 'not x' also parses as (send x :!)
91
+ def correct_not(node, receiver, corrector)
92
+ if node.loc.selector.source == '!'
93
+ return unless receiver.send_type?
94
+
95
+ correct_send(receiver, corrector)
96
+ elsif node.keyword_not?
97
+ correct_other(node, corrector)
98
+ else
99
+ raise 'unrecognized unary negation operator'
100
+ end
103
101
  end
104
102
 
105
103
  def correct_other(node, corrector)
@@ -117,6 +115,16 @@ module RuboCop
117
115
 
118
116
  true
119
117
  end
118
+
119
+ def whitespace_before_arg(node)
120
+ sb = node.source_range.source_buffer
121
+ begin_paren = node.loc.selector.end_pos
122
+ end_paren = begin_paren
123
+ # Increment position of parenthesis, unless message is a predicate
124
+ # method followed by a non-whitespace char (e.g. is_a?String).
125
+ end_paren += 1 unless node.source =~ /\?[!\S]/
126
+ Parser::Source::Range.new(sb, begin_paren, end_paren)
127
+ end
120
128
  end
121
129
  end
122
130
  end
@@ -28,7 +28,7 @@ module RuboCop
28
28
 
29
29
  def on_class(node)
30
30
  _name, superclass, body = *node
31
- return if superclass
31
+ return if superclass && style != :nested
32
32
  check_style(node, body)
33
33
  end
34
34
 
@@ -44,8 +44,7 @@ module RuboCop
44
44
  add_offense(node, :selector,
45
45
  format(MSG,
46
46
  preferred_method(method_name),
47
- method_name)
48
- )
47
+ method_name))
49
48
  end
50
49
  end
51
50
  end
@@ -52,7 +52,7 @@ module RuboCop
52
52
  end
53
53
 
54
54
  def less_indented?(line)
55
- line =~ /^\s*(end\b|[\}\]])/
55
+ line =~ /^\s*(end\b|[)}\]])/
56
56
  end
57
57
 
58
58
  def two_alternatives?(line)
@@ -275,7 +275,7 @@ module RuboCop
275
275
  private
276
276
 
277
277
  def move_assignment_outside_condition(node)
278
- if ternary_op?(node)
278
+ if ternary?(node)
279
279
  TernaryCorrector.correct(node)
280
280
  elsif node.loc.keyword.is?(IF)
281
281
  IfCorrector.correct(self, node)
@@ -288,7 +288,7 @@ module RuboCop
288
288
 
289
289
  def move_assignment_inside_condition(node)
290
290
  *_assignment, condition = *node
291
- if ternary_op?(condition) || ternary_op?(condition.children[0])
291
+ if ternary?(condition) || ternary?(condition.children[0])
292
292
  TernaryCorrector.move_assignment_inside_condition(node)
293
293
  elsif condition.case_type?
294
294
  CaseCorrector.move_assignment_inside_condition(node)
@@ -465,12 +465,8 @@ module RuboCop
465
465
  include ConditionalCorrectorHelper
466
466
 
467
467
  def correct(cop, node)
468
- _condition, if_branch, else_branch = *node
469
- if_branch = tail(if_branch)
468
+ if_branch, elsif_branches, else_branch = extract_branches(node)
470
469
  _variable, *_operator, if_assignment = *if_branch
471
- elsif_branches, else_branch = expand_elses(else_branch)
472
- elsif_branches.map! { |branch| tail(branch) }
473
- else_branch = tail(else_branch)
474
470
  _else_variable, *_operator, else_assignment = *else_branch
475
471
 
476
472
  lambda do |corrector|
@@ -507,6 +503,16 @@ module RuboCop
507
503
  end
508
504
  end
509
505
  end
506
+
507
+ private
508
+
509
+ def extract_branches(node)
510
+ _condition, if_branch, else_branch = *node
511
+ elsif_branches, else_branch = expand_elses(else_branch)
512
+ elsif_branches.map! { |branch| tail(branch) }
513
+
514
+ [tail(if_branch), elsif_branches, tail(else_branch)]
515
+ end
510
516
  end
511
517
  end
512
518