rubocop 0.34.2 → 0.35.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -0
  3. data/README.md +103 -31
  4. data/config/default.yml +32 -2
  5. data/config/disabled.yml +24 -0
  6. data/config/enabled.yml +20 -2
  7. data/lib/rubocop.rb +13 -0
  8. data/lib/rubocop/ast_node.rb +48 -0
  9. data/lib/rubocop/cli.rb +9 -0
  10. data/lib/rubocop/config.rb +8 -6
  11. data/lib/rubocop/config_loader.rb +30 -8
  12. data/lib/rubocop/cop/commissioner.rb +1 -1
  13. data/lib/rubocop/cop/cop.rb +19 -6
  14. data/lib/rubocop/cop/lint/circular_argument_reference.rb +33 -2
  15. data/lib/rubocop/cop/lint/debugger.rb +9 -56
  16. data/lib/rubocop/cop/lint/end_alignment.rb +29 -9
  17. data/lib/rubocop/cop/lint/eval.rb +6 -2
  18. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +24 -6
  19. data/lib/rubocop/cop/lint/literal_in_condition.rb +0 -5
  20. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +10 -1
  21. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  22. data/lib/rubocop/cop/lint/space_before_first_arg.rb +1 -1
  23. data/lib/rubocop/cop/lint/unused_block_argument.rb +6 -0
  24. data/lib/rubocop/cop/lint/unused_method_argument.rb +8 -0
  25. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  26. data/lib/rubocop/cop/mixin/access_modifier_node.rb +1 -1
  27. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +1 -1
  28. data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +26 -3
  29. data/lib/rubocop/cop/mixin/check_assignment.rb +2 -3
  30. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +59 -12
  31. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -1
  32. data/lib/rubocop/cop/mixin/configurable_naming.rb +1 -1
  33. data/lib/rubocop/cop/mixin/first_element_line_break.rb +41 -0
  34. data/lib/rubocop/cop/mixin/negative_conditional.rb +1 -1
  35. data/lib/rubocop/cop/mixin/safe_assignment.rb +3 -14
  36. data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -2
  37. data/lib/rubocop/cop/performance/detect.rb +5 -1
  38. data/lib/rubocop/cop/performance/fixed_size.rb +50 -0
  39. data/lib/rubocop/cop/performance/size.rb +1 -1
  40. data/lib/rubocop/cop/performance/string_replacement.rb +14 -8
  41. data/lib/rubocop/cop/rails/pluralization_grammar.rb +97 -0
  42. data/lib/rubocop/cop/style/align_hash.rb +1 -12
  43. data/lib/rubocop/cop/style/align_parameters.rb +19 -7
  44. data/lib/rubocop/cop/style/and_or.rb +42 -13
  45. data/lib/rubocop/cop/style/block_comments.rb +4 -2
  46. data/lib/rubocop/cop/style/block_delimiters.rb +57 -18
  47. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -1
  48. data/lib/rubocop/cop/style/command_literal.rb +2 -10
  49. data/lib/rubocop/cop/style/copyright.rb +5 -3
  50. data/lib/rubocop/cop/style/documentation.rb +9 -6
  51. data/lib/rubocop/cop/style/dot_position.rb +6 -0
  52. data/lib/rubocop/cop/style/double_negation.rb +4 -15
  53. data/lib/rubocop/cop/style/each_with_object.rb +17 -4
  54. data/lib/rubocop/cop/style/empty_line_between_defs.rb +1 -5
  55. data/lib/rubocop/cop/style/encoding.rb +10 -4
  56. data/lib/rubocop/cop/style/extra_spacing.rb +23 -13
  57. data/lib/rubocop/cop/style/first_array_element_line_break.rb +41 -0
  58. data/lib/rubocop/cop/style/first_hash_element_line_break.rb +35 -0
  59. data/lib/rubocop/cop/style/first_method_argument_line_break.rb +37 -0
  60. data/lib/rubocop/cop/style/first_method_parameter_line_break.rb +42 -0
  61. data/lib/rubocop/cop/style/for.rb +2 -1
  62. data/lib/rubocop/cop/style/if_unless_modifier.rb +31 -0
  63. data/lib/rubocop/cop/style/indent_hash.rb +67 -37
  64. data/lib/rubocop/cop/style/indentation_width.rb +1 -1
  65. data/lib/rubocop/cop/style/leading_comment_space.rb +3 -2
  66. data/lib/rubocop/cop/style/method_call_parentheses.rb +8 -0
  67. data/lib/rubocop/cop/style/method_def_parentheses.rb +10 -7
  68. data/lib/rubocop/cop/style/multiline_operation_indentation.rb +8 -13
  69. data/lib/rubocop/cop/style/nested_modifier.rb +97 -0
  70. data/lib/rubocop/cop/style/next.rb +18 -0
  71. data/lib/rubocop/cop/style/parallel_assignment.rb +57 -15
  72. data/lib/rubocop/cop/style/predicate_name.rb +7 -2
  73. data/lib/rubocop/cop/style/regexp_literal.rb +2 -10
  74. data/lib/rubocop/cop/style/single_line_methods.rb +7 -5
  75. data/lib/rubocop/cop/style/single_space_before_first_arg.rb +1 -1
  76. data/lib/rubocop/cop/style/space_around_operators.rb +2 -0
  77. data/lib/rubocop/cop/style/special_global_vars.rb +4 -2
  78. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +108 -0
  79. data/lib/rubocop/cop/style/trailing_comma.rb +9 -6
  80. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -2
  81. data/lib/rubocop/cop/style/unneeded_percent_q.rb +31 -20
  82. data/lib/rubocop/cop/style/variable_name.rb +5 -0
  83. data/lib/rubocop/cop/style/word_array.rb +2 -1
  84. data/lib/rubocop/cop/team.rb +17 -4
  85. data/lib/rubocop/cop/util.rb +5 -0
  86. data/lib/rubocop/cop/variable_force/locatable.rb +1 -1
  87. data/lib/rubocop/formatter/base_formatter.rb +1 -1
  88. data/lib/rubocop/formatter/disabled_config_formatter.rb +22 -10
  89. data/lib/rubocop/formatter/simple_text_formatter.rb +1 -1
  90. data/lib/rubocop/node_pattern.rb +390 -0
  91. data/lib/rubocop/options.rb +48 -36
  92. data/lib/rubocop/processed_source.rb +3 -1
  93. data/lib/rubocop/rake_task.rb +1 -1
  94. data/lib/rubocop/remote_config.rb +60 -0
  95. data/lib/rubocop/result_cache.rb +4 -2
  96. data/lib/rubocop/runner.rb +33 -10
  97. data/lib/rubocop/token.rb +2 -1
  98. data/lib/rubocop/version.rb +1 -1
  99. data/lib/rubocop/warning.rb +11 -0
  100. data/relnotes/v0.35.0.md +210 -0
  101. data/rubocop.gemspec +2 -2
  102. metadata +20 -6
@@ -0,0 +1,97 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for nested use of if, unless, while and until in their
7
+ # modifier form.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # something if a if b
13
+ #
14
+ # # good
15
+ # something if b && a
16
+ class NestedModifier < Cop
17
+ include IfNode
18
+
19
+ MSG = 'Avoid using nested modifiers.'
20
+
21
+ def on_while(node)
22
+ check(node)
23
+ end
24
+
25
+ def on_until(node)
26
+ check(node)
27
+ end
28
+
29
+ def on_if(node)
30
+ check(node)
31
+ end
32
+
33
+ def check(node)
34
+ return if part_of_ignored_node?(node)
35
+ return unless modifier?(node)
36
+
37
+ ancestor = node.ancestors.first
38
+ return unless ancestor &&
39
+ [:if, :while, :until].include?(ancestor.type) &&
40
+ modifier?(ancestor)
41
+
42
+ add_offense(node, :keyword)
43
+ ignore_node(node)
44
+ end
45
+
46
+ def modifier?(node)
47
+ modifier_if?(node) || modifier_while_or_until?(node)
48
+ end
49
+
50
+ def modifier_while_or_until?(node)
51
+ node.loc.respond_to?(:keyword) &&
52
+ %w(while until).include?(node.loc.keyword.source) &&
53
+ node.loc.respond_to?(:end) && node.loc.end.nil?
54
+ end
55
+
56
+ def autocorrect(node)
57
+ return unless node.if_type?
58
+
59
+ ancestor = node.ancestors.first
60
+ return unless ancestor.if_type?
61
+
62
+ autocorrect_if_unless(ancestor, node)
63
+ end
64
+
65
+ def autocorrect_if_unless(outer_node, inner_node)
66
+ outer_cond, = *outer_node
67
+
68
+ range =
69
+ Parser::Source::Range.new(inner_node.loc.expression.source_buffer,
70
+ inner_node.loc.keyword.begin_pos,
71
+ outer_cond.loc.expression.end_pos)
72
+
73
+ lambda do |corrector|
74
+ corrector.replace(range, new_expression(outer_node, inner_node))
75
+ end
76
+ end
77
+
78
+ def new_expression(outer_node, inner_node)
79
+ outer_cond, = *outer_node
80
+ inner_cond, = *inner_node
81
+
82
+ outer_keyword = outer_node.loc.keyword.source
83
+ inner_keyword = inner_node.loc.keyword.source
84
+
85
+ operator = outer_keyword == 'if' ? '&&' : '||'
86
+
87
+ inner_expr = inner_cond.loc.expression.source
88
+ inner_expr = "(#{inner_expr})" if inner_cond.or_type?
89
+ inner_expr = "!#{inner_expr}" unless outer_keyword == inner_keyword
90
+
91
+ "#{outer_node.loc.keyword.source} " \
92
+ "#{outer_cond.loc.expression.source} #{operator} #{inner_expr}"
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -98,6 +98,24 @@ module RuboCop
98
98
  offense_begin_pos = offense_node.loc.expression.begin
99
99
  offense_begin_pos.join(condition_expression.loc.expression)
100
100
  end
101
+
102
+ def autocorrect(node)
103
+ lambda do |corrector|
104
+ cond, if_body, else_body = *node
105
+ if if_body.nil?
106
+ opposite_kw = 'if'
107
+ body = else_body
108
+ else
109
+ opposite_kw = 'unless'
110
+ body = if_body
111
+ end
112
+ next_code = 'next ' << opposite_kw << ' ' <<
113
+ cond.loc.expression.source << "\n"
114
+ corrector.insert_before(node.loc.expression, next_code)
115
+ corrector.replace(node.loc.expression,
116
+ body.loc.expression.source)
117
+ end
118
+ end
101
119
  end
102
120
  end
103
121
  end
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'tsort'
4
+
3
5
  module RuboCop
4
6
  module Cop
5
7
  module Style
@@ -45,21 +47,27 @@ module RuboCop
45
47
  # allow mass assignment when using splat
46
48
  return if (left_elements + right_elements).any?(&:splat_type?)
47
49
 
48
- # a, b = b, a
49
- return if swapping_variables?(left_elements, right_elements)
50
+ order = find_valid_order(left_elements, right_elements)
51
+ # For `a, b = b, a` or similar, there is no valid order
52
+ return if order.nil?
50
53
 
51
54
  add_offense(node, :expression)
52
55
  end
53
56
 
54
57
  def autocorrect(node)
55
58
  lambda do |corrector|
59
+ left, right = *node
60
+ left_elements = *left
61
+ right_elements = [*right].compact
62
+ order = find_valid_order(left_elements, right_elements)
63
+
56
64
  assignment_corrector =
57
65
  if modifier_statement?(node.parent)
58
- ModifierCorrector.new(node, config)
66
+ ModifierCorrector.new(node, config, order)
59
67
  elsif rescue_modifier?(node.parent)
60
- RescueCorrector.new(node, config)
68
+ RescueCorrector.new(node, config, order)
61
69
  else
62
- GenericCorrector.new(node, config)
70
+ GenericCorrector.new(node, config, order)
63
71
  end
64
72
 
65
73
  corrector.replace(assignment_corrector.correction_range,
@@ -69,10 +77,45 @@ module RuboCop
69
77
 
70
78
  private
71
79
 
72
- def swapping_variables?(left_elements, right_elements)
73
- left_elements.any? do |le|
74
- right_elements.any? do |re|
75
- re.loc.expression.is?(le.loc.expression.source)
80
+ def find_valid_order(left_elements, right_elements)
81
+ # arrange left_elements in an order such that no corresponding right
82
+ # element refers to a left element earlier in the sequence
83
+ # this can be done using an algorithm called a "topological sort"
84
+ # fortunately for us, Ruby's stdlib contains an implementation
85
+ assignments = left_elements.zip(right_elements)
86
+
87
+ begin
88
+ AssignmentSorter.new(assignments).tsort
89
+ rescue TSort::Cyclic
90
+ nil
91
+ end
92
+ end
93
+
94
+ # Helper class necessitated by silly design of TSort prior to Ruby 2.1
95
+ # Newer versions have a better API, but that doesn't help us
96
+ class AssignmentSorter
97
+ include TSort
98
+ extend RuboCop::NodePattern::Macros
99
+
100
+ def_node_matcher :var_name, '{(casgn _ $_) (_ $_)}'
101
+ def_node_search :uses_var?, '{({lvar ivar cvar gvar} %) (const _ %)}'
102
+
103
+ def initialize(assignments)
104
+ @assignments = assignments
105
+ end
106
+
107
+ def tsort_each_node
108
+ @assignments.each { |a| yield a }
109
+ end
110
+
111
+ def tsort_each_child(assignment)
112
+ # yield all the assignments which must come after `assignment`
113
+ # (due to dependencies on the previous value of the assigned var)
114
+ my_lhs, _my_rhs = *assignment
115
+
116
+ @assignments.each do |other|
117
+ _other_lhs, other_rhs = *other
118
+ yield other if uses_var?(other_rhs, var_name(my_lhs))
76
119
  end
77
120
  end
78
121
  end
@@ -101,9 +144,10 @@ module RuboCop
101
144
 
102
145
  attr_reader :config, :node, :correction, :correction_range
103
146
 
104
- def initialize(node, config)
147
+ def initialize(node, config, new_elements)
105
148
  @node = node
106
149
  @config = config
150
+ @new_elements = new_elements
107
151
  end
108
152
 
109
153
  def correction
@@ -117,11 +161,9 @@ module RuboCop
117
161
  protected
118
162
 
119
163
  def assignment
120
- left, right = *node
121
- l_vars = extract_sources(left)
122
- r_vars = extract_sources(right)
123
- groups = l_vars.zip(r_vars)
124
- groups.map { |pair| pair.join(' = ') }
164
+ @new_elements.map do |lhs, rhs|
165
+ "#{lhs.loc.expression.source} = #{rhs.loc.expression.source}"
166
+ end
125
167
  end
126
168
 
127
169
  private
@@ -23,10 +23,11 @@ module RuboCop
23
23
  private
24
24
 
25
25
  def on_method_def(node, method_name, _args, _body)
26
- predicate_prefices.each do |prefix|
26
+ predicate_prefixes.each do |prefix|
27
27
  method_name = method_name.to_s
28
28
  next unless method_name.start_with?(prefix)
29
29
  next if method_name == expected_name(method_name, prefix)
30
+ next if predicate_whitelist.include?(method_name)
30
31
  add_offense(
31
32
  node,
32
33
  :name,
@@ -53,9 +54,13 @@ module RuboCop
53
54
  cop_config['NamePrefixBlacklist']
54
55
  end
55
56
 
56
- def predicate_prefices
57
+ def predicate_prefixes
57
58
  cop_config['NamePrefix']
58
59
  end
60
+
61
+ def predicate_whitelist
62
+ cop_config['NameWhitelist']
63
+ end
59
64
  end
60
65
  end
61
66
  end
@@ -47,7 +47,7 @@ module RuboCop
47
47
  def check_slash_literal(node)
48
48
  return if style == :slashes && !contains_disallowed_slash?(node)
49
49
  return if style == :mixed &&
50
- single_line?(node) &&
50
+ node.single_line? &&
51
51
  !contains_disallowed_slash?(node)
52
52
 
53
53
  add_offense(node, :expression, MSG_USE_PERCENT_R)
@@ -56,7 +56,7 @@ module RuboCop
56
56
  def check_percent_r_literal(node)
57
57
  return if style == :slashes && contains_disallowed_slash?(node)
58
58
  return if style == :percent_r
59
- return if style == :mixed && multi_line?(node)
59
+ return if style == :mixed && node.multiline?
60
60
  return if style == :mixed && contains_disallowed_slash?(node)
61
61
 
62
62
  add_offense(node, :expression, MSG_USE_SLASHES)
@@ -83,14 +83,6 @@ module RuboCop
83
83
  node.loc.begin.source == '/'
84
84
  end
85
85
 
86
- def single_line?(node)
87
- !multi_line?(node)
88
- end
89
-
90
- def multi_line?(node)
91
- block_length(node) > 1
92
- end
93
-
94
86
  def preferred_delimiters
95
87
  config.for_cop('Style/PercentLiteralDelimiters') \
96
88
  ['PreferredDelimiters']['%r'].split(//)
@@ -34,12 +34,14 @@ module RuboCop
34
34
  c.loc.line == node.loc.expression.line
35
35
  end
36
36
  lambda do |corrector|
37
- if body.type == :begin
38
- body.children.each do |part|
39
- break_line_before(part.loc.expression, node, corrector, 1)
37
+ if body
38
+ if body.type == :begin
39
+ body.children.each do |part|
40
+ break_line_before(part.loc.expression, node, corrector, 1)
41
+ end
42
+ else
43
+ break_line_before(body.loc.expression, node, corrector, 1)
40
44
  end
41
- else
42
- break_line_before(body.loc.expression, node, corrector, 1)
43
45
  end
44
46
 
45
47
  break_line_before(node.loc.end, node, corrector, 0)
@@ -20,7 +20,7 @@ module RuboCop
20
20
  _receiver, method_name, *args = *node
21
21
  return if args.empty?
22
22
  return if operator?(method_name)
23
- return if method_name.to_s.end_with?('=')
23
+ return if node.asgn_method_call?
24
24
 
25
25
  arg1 = args.first.loc.expression
26
26
  return if arg1.line > node.loc.line
@@ -57,6 +57,8 @@ module RuboCop
57
57
 
58
58
  def check_operator(op)
59
59
  with_space = range_with_surrounding_space(op)
60
+ return if with_space.source.start_with?("\n")
61
+
60
62
  if op.is?('**')
61
63
  unless with_space.is?('**')
62
64
  add_offense(with_space, op,
@@ -5,8 +5,10 @@ module RuboCop
5
5
  module Style
6
6
  # This cop looks for uses of Perl-style global variables.
7
7
  class SpecialGlobalVars < Cop
8
- MSG_BOTH = 'Prefer `%s` from the English library, or `%s` over `%s`.'
9
- MSG_ENGLISH = 'Prefer `%s` from the English library over `%s`.'
8
+ MSG_BOTH = 'Prefer `%s` from the stdlib \'English\' module, ' \
9
+ 'or `%s` over `%s`.'
10
+ MSG_ENGLISH = 'Prefer `%s` from the stdlib \'English\' module ' \
11
+ 'over `%s`.'
10
12
  MSG_REGULAR = 'Prefer `%s` over `%s`.'
11
13
 
12
14
  PREFERRED_VARS = {
@@ -0,0 +1,108 @@
1
+ # encoding: utf-8
2
+ module RuboCop
3
+ module Cop
4
+ module Style
5
+ # Check for parentheses around stabby lambda arguments.
6
+ # There are two different styles. Defaults to `require_parentheses`.
7
+ #
8
+ # @example
9
+ # # require_parentheses - bad
10
+ # ->a,b,c { a + b + c }
11
+ #
12
+ # # require_parentheses - good
13
+ # ->(a,b,c) { a + b + c}
14
+ #
15
+ # # require_no_parentheses - bad
16
+ # ->(a,b,c) { a + b + c }
17
+ #
18
+ # # require_parentheses - good
19
+ # ->a,b,c { a + b + c}
20
+ class StabbyLambdaParentheses < Cop
21
+ MSG_REQUIRE = 'Wrap stabby lambda arguments with parentheses.'
22
+ MSG_NO_REQUIRE = 'Do not wrap stabby lambda arguments with parentheses.'
23
+
24
+ include ConfigurableEnforcedStyle
25
+
26
+ def on_send(node)
27
+ return unless lambda_with_args?(node)
28
+
29
+ if style == :require_parentheses
30
+ if parentheses?(node)
31
+ correct_style_detected
32
+ else
33
+ missing_parentheses(node)
34
+ end
35
+ elsif parentheses?(node)
36
+ unwanted_parentheses(node)
37
+ else
38
+ correct_style_detected
39
+ end
40
+ end
41
+
42
+ def autocorrect(node)
43
+ if style == :require_parentheses
44
+ missing_parentheses_corrector(node)
45
+ elsif style == :require_no_parentheses
46
+ unwanted_parentheses_corrector(node)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def missing_parentheses(node)
53
+ add_offense(node_args(node), :expression, MSG_REQUIRE) do
54
+ opposite_style_detected
55
+ end
56
+ end
57
+
58
+ def unwanted_parentheses(node)
59
+ add_offense(node_args(node), :expression, MSG_NO_REQUIRE) do
60
+ opposite_style_detected
61
+ end
62
+ end
63
+
64
+ def missing_parentheses_corrector(node)
65
+ lambda do |corrector|
66
+ args_loc = node_args(node).loc.expression
67
+
68
+ corrector.insert_before(args_loc, '(')
69
+ corrector.insert_after(args_loc, ')')
70
+ end
71
+ end
72
+
73
+ def unwanted_parentheses_corrector(node)
74
+ lambda do |corrector|
75
+ args_loc = node_args(node).loc
76
+
77
+ corrector.replace(args_loc.begin, '')
78
+ corrector.remove(args_loc.end)
79
+ end
80
+ end
81
+
82
+ def lambda_with_args?(node)
83
+ lambda_node?(node) && args?(node)
84
+ end
85
+
86
+ def lambda_node?(node)
87
+ _nil, call = *node
88
+ call == :lambda
89
+ end
90
+
91
+ def node_args(node)
92
+ _call, args, _body = *node.parent
93
+ args
94
+ end
95
+
96
+ def args?(node)
97
+ args = node_args(node)
98
+ args.children.count > 0
99
+ end
100
+
101
+ def parentheses?(node)
102
+ args = node_args(node)
103
+ args.loc.begin
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end