rubocop 0.31.0 → 0.35.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 (177) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +315 -0
  3. data/README.md +199 -38
  4. data/config/default.yml +91 -12
  5. data/config/disabled.yml +45 -4
  6. data/config/enabled.yml +107 -9
  7. data/lib/rubocop/ast_node.rb +48 -0
  8. data/lib/rubocop/cli.rb +11 -1
  9. data/lib/rubocop/comment_config.rb +4 -1
  10. data/lib/rubocop/config.rb +26 -17
  11. data/lib/rubocop/config_loader.rb +61 -14
  12. data/lib/rubocop/cop/commissioner.rb +7 -12
  13. data/lib/rubocop/cop/cop.rb +43 -20
  14. data/lib/rubocop/cop/lint/block_alignment.rb +1 -1
  15. data/lib/rubocop/cop/lint/circular_argument_reference.rb +69 -0
  16. data/lib/rubocop/cop/lint/debugger.rb +9 -48
  17. data/lib/rubocop/cop/lint/def_end_alignment.rb +8 -4
  18. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +42 -23
  19. data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -2
  20. data/lib/rubocop/cop/lint/duplicated_key.rb +37 -0
  21. data/lib/rubocop/cop/lint/end_alignment.rb +33 -13
  22. data/lib/rubocop/cop/lint/eval.rb +6 -2
  23. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +175 -0
  24. data/lib/rubocop/cop/lint/literal_in_condition.rb +0 -5
  25. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +10 -0
  26. data/lib/rubocop/cop/lint/nested_method_definition.rb +31 -0
  27. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +19 -1
  28. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  29. data/lib/rubocop/cop/lint/space_before_first_arg.rb +1 -1
  30. data/lib/rubocop/cop/lint/unneeded_disable.rb +72 -0
  31. data/lib/rubocop/cop/lint/unused_block_argument.rb +6 -0
  32. data/lib/rubocop/cop/lint/unused_method_argument.rb +8 -0
  33. data/lib/rubocop/cop/metrics/abc_size.rb +17 -6
  34. data/lib/rubocop/cop/metrics/class_length.rb +1 -1
  35. data/lib/rubocop/cop/metrics/method_length.rb +1 -3
  36. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  37. data/lib/rubocop/cop/metrics/parameter_lists.rb +1 -1
  38. data/lib/rubocop/cop/mixin/access_modifier_node.rb +1 -1
  39. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -2
  40. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +28 -4
  41. data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +26 -3
  42. data/lib/rubocop/cop/mixin/check_assignment.rb +2 -3
  43. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +59 -12
  44. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -1
  45. data/lib/rubocop/cop/mixin/configurable_naming.rb +14 -3
  46. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -3
  47. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +10 -1
  48. data/lib/rubocop/cop/mixin/first_element_line_break.rb +41 -0
  49. data/lib/rubocop/cop/mixin/if_node.rb +10 -0
  50. data/lib/rubocop/cop/mixin/method_preference.rb +28 -0
  51. data/lib/rubocop/cop/mixin/negative_conditional.rb +1 -1
  52. data/lib/rubocop/cop/mixin/on_method_def.rb +4 -5
  53. data/lib/rubocop/cop/mixin/safe_assignment.rb +3 -14
  54. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +8 -1
  55. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +8 -1
  56. data/lib/rubocop/cop/mixin/statement_modifier.rb +4 -7
  57. data/lib/rubocop/cop/mixin/string_help.rb +1 -1
  58. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
  59. data/lib/rubocop/cop/mixin/surrounding_space.rb +5 -4
  60. data/lib/rubocop/cop/offense.rb +16 -3
  61. data/lib/rubocop/cop/performance/case_when_splat.rb +160 -0
  62. data/lib/rubocop/cop/performance/count.rb +35 -30
  63. data/lib/rubocop/cop/performance/detect.rb +16 -3
  64. data/lib/rubocop/cop/performance/fixed_size.rb +50 -0
  65. data/lib/rubocop/cop/performance/flat_map.rb +3 -3
  66. data/lib/rubocop/cop/performance/sample.rb +103 -59
  67. data/lib/rubocop/cop/performance/size.rb +2 -1
  68. data/lib/rubocop/cop/performance/string_replacement.rb +187 -0
  69. data/lib/rubocop/cop/rails/action_filter.rb +31 -5
  70. data/lib/rubocop/cop/rails/date.rb +15 -14
  71. data/lib/rubocop/cop/rails/pluralization_grammar.rb +97 -0
  72. data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
  73. data/lib/rubocop/cop/rails/time_zone.rb +46 -18
  74. data/lib/rubocop/cop/style/alias.rb +1 -0
  75. data/lib/rubocop/cop/style/align_hash.rb +8 -15
  76. data/lib/rubocop/cop/style/align_parameters.rb +19 -7
  77. data/lib/rubocop/cop/style/and_or.rb +42 -13
  78. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +2 -1
  79. data/lib/rubocop/cop/style/block_comments.rb +4 -2
  80. data/lib/rubocop/cop/style/block_delimiters.rb +69 -24
  81. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +40 -12
  82. data/lib/rubocop/cop/style/case_indentation.rb +18 -4
  83. data/lib/rubocop/cop/style/collection_methods.rb +2 -20
  84. data/lib/rubocop/cop/style/command_literal.rb +2 -10
  85. data/lib/rubocop/cop/style/comment_annotation.rb +29 -8
  86. data/lib/rubocop/cop/style/copyright.rb +5 -3
  87. data/lib/rubocop/cop/style/documentation.rb +21 -12
  88. data/lib/rubocop/cop/style/dot_position.rb +6 -0
  89. data/lib/rubocop/cop/style/double_negation.rb +4 -15
  90. data/lib/rubocop/cop/style/each_with_object.rb +17 -4
  91. data/lib/rubocop/cop/style/else_alignment.rb +2 -1
  92. data/lib/rubocop/cop/style/empty_else.rb +25 -0
  93. data/lib/rubocop/cop/style/empty_line_between_defs.rb +39 -14
  94. data/lib/rubocop/cop/style/encoding.rb +10 -4
  95. data/lib/rubocop/cop/style/extra_spacing.rb +126 -5
  96. data/lib/rubocop/cop/style/first_array_element_line_break.rb +41 -0
  97. data/lib/rubocop/cop/style/first_hash_element_line_break.rb +35 -0
  98. data/lib/rubocop/cop/style/first_method_argument_line_break.rb +37 -0
  99. data/lib/rubocop/cop/style/first_method_parameter_line_break.rb +42 -0
  100. data/lib/rubocop/cop/style/first_parameter_indentation.rb +5 -3
  101. data/lib/rubocop/cop/style/for.rb +2 -1
  102. data/lib/rubocop/cop/style/hash_syntax.rb +5 -0
  103. data/lib/rubocop/cop/style/if_unless_modifier.rb +32 -5
  104. data/lib/rubocop/cop/style/indent_hash.rb +67 -37
  105. data/lib/rubocop/cop/style/indentation_width.rb +36 -10
  106. data/lib/rubocop/cop/style/initial_indentation.rb +37 -0
  107. data/lib/rubocop/cop/style/leading_comment_space.rb +3 -2
  108. data/lib/rubocop/cop/style/method_call_parentheses.rb +28 -1
  109. data/lib/rubocop/cop/style/method_def_parentheses.rb +10 -7
  110. data/lib/rubocop/cop/style/multiline_operation_indentation.rb +21 -24
  111. data/lib/rubocop/cop/style/mutable_constant.rb +35 -0
  112. data/lib/rubocop/cop/style/nested_modifier.rb +97 -0
  113. data/lib/rubocop/cop/style/next.rb +50 -15
  114. data/lib/rubocop/cop/style/non_nil_check.rb +12 -8
  115. data/lib/rubocop/cop/style/one_line_conditional.rb +8 -4
  116. data/lib/rubocop/cop/style/option_hash.rb +64 -0
  117. data/lib/rubocop/cop/style/optional_arguments.rb +49 -0
  118. data/lib/rubocop/cop/style/parallel_assignment.rb +218 -0
  119. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +3 -66
  120. data/lib/rubocop/cop/style/predicate_name.rb +7 -2
  121. data/lib/rubocop/cop/style/redundant_begin.rb +2 -13
  122. data/lib/rubocop/cop/style/redundant_freeze.rb +37 -0
  123. data/lib/rubocop/cop/style/redundant_return.rb +32 -3
  124. data/lib/rubocop/cop/style/regexp_literal.rb +2 -10
  125. data/lib/rubocop/cop/style/rescue_ensure_alignment.rb +81 -0
  126. data/lib/rubocop/cop/style/rescue_modifier.rb +30 -22
  127. data/lib/rubocop/cop/style/send.rb +18 -0
  128. data/lib/rubocop/cop/style/signal_exception.rb +24 -11
  129. data/lib/rubocop/cop/style/single_line_methods.rb +8 -9
  130. data/lib/rubocop/cop/style/single_space_before_first_arg.rb +1 -1
  131. data/lib/rubocop/cop/style/space_around_operators.rb +2 -0
  132. data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +61 -0
  133. data/lib/rubocop/cop/style/special_global_vars.rb +4 -2
  134. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +108 -0
  135. data/lib/rubocop/cop/style/string_methods.rb +32 -0
  136. data/lib/rubocop/cop/style/struct_inheritance.rb +11 -10
  137. data/lib/rubocop/cop/style/symbol_literal.rb +1 -1
  138. data/lib/rubocop/cop/style/symbol_proc.rb +62 -13
  139. data/lib/rubocop/cop/style/trailing_blank_lines.rb +9 -1
  140. data/lib/rubocop/cop/style/trailing_comma.rb +17 -7
  141. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -2
  142. data/lib/rubocop/cop/style/trivial_accessors.rb +10 -1
  143. data/lib/rubocop/cop/style/unneeded_percent_q.rb +31 -20
  144. data/lib/rubocop/cop/style/variable_name.rb +5 -0
  145. data/lib/rubocop/cop/style/while_until_do.rb +1 -1
  146. data/lib/rubocop/cop/style/word_array.rb +15 -2
  147. data/lib/rubocop/cop/team.rb +25 -5
  148. data/lib/rubocop/cop/util.rb +7 -2
  149. data/lib/rubocop/cop/variable_force/locatable.rb +6 -6
  150. data/lib/rubocop/cop/variable_force.rb +10 -10
  151. data/lib/rubocop/formatter/base_formatter.rb +1 -1
  152. data/lib/rubocop/formatter/disabled_config_formatter.rb +70 -8
  153. data/lib/rubocop/formatter/formatter_set.rb +27 -1
  154. data/lib/rubocop/formatter/progress_formatter.rb +10 -2
  155. data/lib/rubocop/formatter/simple_text_formatter.rb +1 -1
  156. data/lib/rubocop/node_pattern.rb +390 -0
  157. data/lib/rubocop/options.rb +148 -81
  158. data/lib/rubocop/processed_source.rb +7 -2
  159. data/lib/rubocop/rake_task.rb +1 -1
  160. data/lib/rubocop/remote_config.rb +60 -0
  161. data/lib/rubocop/result_cache.rb +123 -0
  162. data/lib/rubocop/runner.rb +85 -22
  163. data/lib/rubocop/target_finder.rb +4 -4
  164. data/lib/rubocop/token.rb +2 -1
  165. data/lib/rubocop/version.rb +1 -1
  166. data/lib/rubocop/warning.rb +11 -0
  167. data/lib/rubocop.rb +32 -3
  168. data/relnotes/v0.32.0.md +139 -0
  169. data/relnotes/v0.32.1.md +122 -0
  170. data/relnotes/v0.33.0.md +157 -0
  171. data/relnotes/v0.34.0.md +182 -0
  172. data/relnotes/v0.34.1.md +129 -0
  173. data/relnotes/v0.34.2.md +139 -0
  174. data/relnotes/v0.35.0.md +210 -0
  175. data/rubocop.gemspec +4 -4
  176. metadata +50 -12
  177. data/lib/rubocop/cop/performance/parallel_assignment.rb +0 -79
@@ -0,0 +1,175 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This lint sees if there is a mismatch between the number of
7
+ # expected fields for format/sprintf/#% and what is actually
8
+ # passed as arguments.
9
+ #
10
+ # @example
11
+ #
12
+ # format('A value: %s and another: %i', a_value)
13
+ #
14
+ class FormatParameterMismatch < Cop
15
+ # http://rubular.com/r/CvpbxkcTzy
16
+ MSG = 'Number arguments (%i) to `%s` mismatches expected fields (%i).'
17
+ FIELD_REGEX = /(%(([\s#+-0\*])?(\d*)?(.\d+)?(\.)?[bBdiouxXeEfgGaAcps]|%))/.freeze # rubocop:disable Metrics/LineLength
18
+ NAMED_FIELD_REGEX = /%\{[_a-zA-Z][_a-zA-Z]+\}/.freeze
19
+ KERNEL = 'Kernel'.freeze
20
+ SHOVEL = '<<'.freeze
21
+ PERCENT = '%'.freeze
22
+ PERCENT_PERCENT = '%%'.freeze
23
+ SPLAT = '*'.freeze
24
+ STRING_TYPES = [:str, :dstr].freeze
25
+
26
+ def on_send(node)
27
+ add_offense(node, :selector) if offending_node?(node)
28
+ end
29
+
30
+ private
31
+
32
+ def offending_node?(node)
33
+ return false unless called_on_string?(node)
34
+
35
+ if sprintf?(node) || format?(node) || percent?(node)
36
+ if named_mode?(node) || node_with_splat_args?(node)
37
+ false
38
+ else
39
+ num_of_format_args, num_of_expected_fields = count_matches(node)
40
+
41
+ num_of_format_args != :unknown &&
42
+ num_of_expected_fields != :unknown &&
43
+ num_of_expected_fields != num_of_format_args
44
+ end
45
+ else
46
+ false
47
+ end
48
+ end
49
+
50
+ def called_on_string?(node)
51
+ receiver_node, _method, format_string, = *node
52
+ if receiver_node.nil? || receiver_node.const_type?
53
+ format_string && format_string.str_type?
54
+ else
55
+ receiver_node.str_type?
56
+ end
57
+ end
58
+
59
+ def named_mode?(node)
60
+ receiver_node, _method_name, *args = *node
61
+
62
+ relevant_node = if sprintf?(node) || format?(node)
63
+ args.first
64
+ elsif percent?(node)
65
+ receiver_node
66
+ end
67
+
68
+ relevant_node.loc.expression.source.scan(NAMED_FIELD_REGEX).size > 0
69
+ end
70
+
71
+ def node_with_splat_args?(node)
72
+ return false if percent?(node)
73
+
74
+ _receiver_node, _method_name, *args = *node
75
+
76
+ args[1..-1].any? { |arg| arg.type == :splat }
77
+ end
78
+
79
+ def heredoc?(node)
80
+ _receiver, _name, args = *node
81
+
82
+ args.loc.expression.source[0, 2] == SHOVEL
83
+ end
84
+
85
+ def literal?(node)
86
+ node.int_type? ||
87
+ node.str_type? ||
88
+ node.sym_type? ||
89
+ node.float_type? ||
90
+ node.dstr_type? ||
91
+ node.dsym_type?
92
+ end
93
+
94
+ def count_matches(node)
95
+ receiver_node, _method_name, *args = *node
96
+
97
+ if (sprintf?(node) || format?(node)) && !heredoc?(node)
98
+ number_of_args_for_format = (args.size - 1)
99
+ number_of_expected_fields = expected_fields_count(args.first)
100
+ elsif percent?(node) && args.first.array_type?
101
+ number_of_expected_fields = expected_fields_count(receiver_node)
102
+
103
+ first_child_argument = args.first
104
+ if first_child_argument.array_type?
105
+ number_of_args_for_format = args.first.child_nodes.size
106
+ elsif literal?(first_child_argument)
107
+ number_of_args_for_format = 1
108
+ else
109
+ # non-literals might evaluate to an Array, or they might not
110
+ # so we can't tell just how many format args there will be
111
+ number_of_args_for_format = :unknown
112
+ end
113
+ else
114
+ number_of_args_for_format = number_of_expected_fields = :unknown
115
+ end
116
+
117
+ [number_of_args_for_format, number_of_expected_fields]
118
+ end
119
+
120
+ def format_method?(name, node)
121
+ receiver, method_name, *args = *node
122
+
123
+ if receiver && receiver.const_type?
124
+ return false unless receiver.loc.name.is?(KERNEL)
125
+ end
126
+
127
+ return false unless method_name == name
128
+
129
+ args.size > 1 && args.first.str_type?
130
+ end
131
+
132
+ def expected_fields_count(node)
133
+ return :unknown unless node.str_type?
134
+ node
135
+ .loc
136
+ .expression
137
+ .source
138
+ .scan(FIELD_REGEX)
139
+ .select { |x| x.first != PERCENT_PERCENT }
140
+ .reduce(0) { |a, e| a + (e[2] == SPLAT ? 2 : 1) }
141
+ end
142
+
143
+ def format?(node)
144
+ format_method?(:format, node)
145
+ end
146
+
147
+ def sprintf?(node)
148
+ format_method?(:sprintf, node)
149
+ end
150
+
151
+ def percent?(node)
152
+ receiver_node, method_name, *arg_nodes = *node
153
+
154
+ percent = method_name == :% &&
155
+ (STRING_TYPES.include?(receiver_node.type) ||
156
+ arg_nodes[0].array_type?)
157
+
158
+ if percent && STRING_TYPES.include?(receiver_node.type)
159
+ return false if heredoc?(node)
160
+ end
161
+
162
+ percent
163
+ end
164
+
165
+ def message(node)
166
+ _receiver, method_name, *_args = *node
167
+ num_args_for_format, num_expected_fields = count_matches(node)
168
+
169
+ method_name = 'String#%' if PERCENT == method_name.to_s
170
+ format(MSG, num_args_for_format, method_name, num_expected_fields)
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
@@ -20,11 +20,6 @@ module RuboCop
20
20
  class LiteralInCondition < Cop
21
21
  MSG = 'Literal `%s` appeared in a condition.'
22
22
 
23
- LITERALS = [:str, :dstr, :int, :float, :array,
24
- :hash, :regexp, :nil, :true, :false]
25
-
26
- BASIC_LITERALS = LITERALS - [:dstr, :array, :hash]
27
-
28
23
  def on_if(node)
29
24
  check_for_literal(node)
30
25
  end
@@ -25,6 +25,12 @@ module RuboCop
25
25
  end
26
26
  end
27
27
 
28
+ def autocorrect(node)
29
+ expr = node.parent.loc.expression
30
+ value = autocorrected_value(node)
31
+ ->(corrector) { corrector.replace(expr, value) }
32
+ end
33
+
28
34
  private
29
35
 
30
36
  def special_keyword?(node)
@@ -32,6 +38,10 @@ module RuboCop
32
38
  (node.type == :str && !node.loc.respond_to?(:begin)) ||
33
39
  node.loc.expression.is?('__LINE__')
34
40
  end
41
+
42
+ def autocorrected_value(node)
43
+ node.str_type? ? node.children.last : node.loc.expression.source
44
+ end
35
45
  end
36
46
  end
37
47
  end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop checks for nested method definitions.
7
+ #
8
+ # @example
9
+ # # `bar` definition actually produces methods in the same scope
10
+ # # as the outer `foo` method. Furthermore, the `bar` method
11
+ # # will be redefined every time the `foo` is invoked
12
+ # def foo
13
+ # def bar
14
+ # end
15
+ # end
16
+ #
17
+ class NestedMethodDefinition < Cop
18
+ include OnMethodDef
19
+
20
+ MSG = 'Method definitions must not be nested. ' \
21
+ 'Use `lambda` instead.'
22
+
23
+ def on_method_def(node, _method_name, _args, _body)
24
+ node.each_descendant(:def) do |nested_def_node|
25
+ add_offense(nested_def_node, :expression)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -5,7 +5,8 @@ module RuboCop
5
5
  module Lint
6
6
  # This cop checks for non-local exit from iterator, without return value.
7
7
  # It warns only when satisfies all of these: `return` doesn't have return
8
- # value, block followed by method chain, and block have arguments.
8
+ # value, the block is preceded by a method chain, the block has arguments,
9
+ # and the method which receives the block is not `define_method`.
9
10
  #
10
11
  # @example
11
12
  #
@@ -39,6 +40,13 @@ module RuboCop
39
40
  return if return_value?(return_node)
40
41
  return_node.each_ancestor(:block) do |block_node|
41
42
  send_node, args_node, _body_node = *block_node
43
+
44
+ # `return` does not exit to outside of lambda block, this is safe.
45
+ break if lambda?(send_node)
46
+ # if a proc is passed to `Module#define_method`, `return` will not
47
+ # cause a non-local exit error
48
+ break if define_method?(send_node)
49
+
42
50
  next if args_node.children.empty?
43
51
  if chained_send?(send_node)
44
52
  add_offense(return_node, :keyword)
@@ -55,6 +63,16 @@ module RuboCop
55
63
  receiver_node, _selector_node = *send_node
56
64
  !receiver_node.nil?
57
65
  end
66
+
67
+ def lambda?(send_node)
68
+ receiver_node, selector_node = *send_node
69
+ receiver_node.nil? && selector_node == :lambda
70
+ end
71
+
72
+ def define_method?(send_node)
73
+ _receiver, selector = *send_node
74
+ selector == :define_method
75
+ end
58
76
  end
59
77
  end
60
78
  end
@@ -14,7 +14,7 @@ module RuboCop
14
14
 
15
15
  def on_send(node)
16
16
  _receiver, method_name, args = *node
17
- return if operator?(method_name) || method_name.to_s.end_with?('=')
17
+ return if operator?(method_name) || node.asgn_method_call?
18
18
  return unless args && args.loc.expression.source.start_with?('(')
19
19
 
20
20
  space_length = spaces_before_left_parenthesis(node)
@@ -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
  # Setter calls with parentheses are parsed this way. The parentheses
26
26
  # belong to the argument, not the send node.
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop detects instances of rubocop:disable comments that can be
7
+ # removed without causing any offenses to be reported. It's implemented
8
+ # as a cop in that it inherits from the Cop base class and calls
9
+ # add_offense. The unusual part of its implementation is that it doesn't
10
+ # have any on_* methods or an investigate method. This means that it
11
+ # doesn't take part in the investigation phase when the other cops do
12
+ # their work. Instead, it waits until it's called in a later stage of the
13
+ # execution. The reason it can't be implemented as a normal cop is that
14
+ # it depends on the results of all other cops to do its work.
15
+ class UnneededDisable < Cop
16
+ COP_NAME = 'Lint/UnneededDisable'
17
+
18
+ def check(offenses, cop_disabled_line_ranges, comments)
19
+ unneeded_cops = {}
20
+ disabled_ranges = cop_disabled_line_ranges[COP_NAME] || [0..0]
21
+
22
+ cop_disabled_line_ranges.each do |cop, line_ranges|
23
+ cop_offenses = offenses.select { |o| o.cop_name == cop }
24
+ line_ranges.each do |line_range|
25
+ comment = comments.find { |c| c.loc.line == line_range.begin }
26
+ unneeded_cop = find_unneeded(comment, offenses, cop, cop_offenses,
27
+ line_range)
28
+
29
+ unless all_disabled?(comment)
30
+ next if ignore_offense?(disabled_ranges, line_range)
31
+ end
32
+
33
+ if unneeded_cop
34
+ unneeded_cops[comment.loc.expression] ||= Set.new
35
+ unneeded_cops[comment.loc.expression].add(unneeded_cop)
36
+ end
37
+ end
38
+ end
39
+
40
+ add_offenses(unneeded_cops)
41
+ end
42
+
43
+ private
44
+
45
+ def find_unneeded(comment, offenses, cop, cop_offenses, line_range)
46
+ if all_disabled?(comment)
47
+ 'all cops' if offenses.none? { |o| line_range.include?(o.line) }
48
+ elsif cop_offenses.none? { |o| line_range.include?(o.line) }
49
+ cop
50
+ end
51
+ end
52
+
53
+ def all_disabled?(comment)
54
+ comment.loc.expression.source =~ /rubocop:disable\s+all\b/
55
+ end
56
+
57
+ def ignore_offense?(disabled_ranges, line_range)
58
+ disabled_ranges.any? do |range|
59
+ range.include?(line_range.min) && range.include?(line_range.max)
60
+ end
61
+ end
62
+
63
+ def add_offenses(unneeded_cops)
64
+ unneeded_cops.each do |range, cops|
65
+ add_offense(range, range,
66
+ "Unnecessary disabling of #{cops.sort.join(', ')}.")
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -16,6 +16,12 @@ module RuboCop
16
16
 
17
17
  def check_argument(variable)
18
18
  return unless variable.block_argument?
19
+
20
+ if cop_config['IgnoreEmptyBlocks']
21
+ _send, _args, body = *variable.scope.node
22
+ return if body.nil?
23
+ end
24
+
19
25
  super
20
26
  end
21
27
 
@@ -15,6 +15,14 @@ module RuboCop
15
15
 
16
16
  def check_argument(variable)
17
17
  return unless variable.method_argument?
18
+ return if variable.keyword_argument? &&
19
+ cop_config['AllowUnusedKeywordArguments']
20
+
21
+ if cop_config['IgnoreEmptyMethods']
22
+ _name, _args, body = *variable.scope.node
23
+ return if body.nil?
24
+ end
25
+
18
26
  super
19
27
  end
20
28
 
@@ -10,16 +10,27 @@ module RuboCop
10
10
  include MethodComplexity
11
11
 
12
12
  MSG = 'Assignment Branch Condition size for %s is too high. [%.4g/%.4g]'
13
- BRANCH_NODES = [:send]
14
- CONDITION_NODES = CyclomaticComplexity::COUNTED_NODES
13
+ BRANCH_NODES = [:send].freeze
14
+ CONDITION_NODES = CyclomaticComplexity::COUNTED_NODES.freeze
15
15
 
16
16
  private
17
17
 
18
18
  def complexity(node)
19
- a = node.each_node(ASGN_NODES).count
20
- b = node.each_node(BRANCH_NODES).count
21
- c = node.each_node(CONDITION_NODES).count
22
- Math.sqrt(a**2 + b**2 + c**2).round(2)
19
+ assignment = 0
20
+ branch = 0
21
+ condition = 0
22
+
23
+ node.each_node do |child|
24
+ if child.assignment?
25
+ assignment += 1
26
+ elsif BRANCH_NODES.include?(child.type)
27
+ branch += 1
28
+ elsif CONDITION_NODES.include?(child.type)
29
+ condition += 1
30
+ end
31
+ end
32
+
33
+ Math.sqrt(assignment**2 + branch**2 + condition**2).round(2)
23
34
  end
24
35
  end
25
36
  end
@@ -16,7 +16,7 @@ module RuboCop
16
16
  private
17
17
 
18
18
  def message(length, max_length)
19
- format('Class definition is too long. [%d/%d]', length, max_length)
19
+ format('Class has too many lines. [%d/%d]', length, max_length)
20
20
  end
21
21
  end
22
22
  end
@@ -23,9 +23,7 @@ module RuboCop
23
23
  def code_length(node)
24
24
  lines = node.loc.expression.source.lines.to_a[1..-2] || []
25
25
 
26
- lines.reject! { |line| irrelevant_line(line) }
27
-
28
- lines.size
26
+ lines.count { |line| !irrelevant_line(line) }
29
27
  end
30
28
  end
31
29
  end
@@ -16,7 +16,7 @@ module RuboCop
16
16
  private
17
17
 
18
18
  def message(length, max_length)
19
- format('Module definition is too long. [%d/%d]', length, max_length)
19
+ format('Module has too many lines. [%d/%d]', length, max_length)
20
20
  end
21
21
  end
22
22
  end
@@ -27,7 +27,7 @@ module RuboCop
27
27
  if count_keyword_args?
28
28
  node.children.size
29
29
  else
30
- node.children.count { |a| a.type != :kwoptarg }
30
+ node.children.count { |a| ![:kwoptarg, :kwarg].include?(a.type) }
31
31
  end
32
32
  end
33
33
 
@@ -40,7 +40,7 @@ module RuboCop
40
40
  # Returns true when the block node looks like Class or Module.new do ... .
41
41
  def class_constructor?(block_node)
42
42
  send_node = block_node.children.first
43
- receiver_node, method_name, *_ = *send_node
43
+ receiver_node, method_name, = *send_node
44
44
  return false unless method_name == :new
45
45
  %w(Class Module).include?(Util.const_name(receiver_node))
46
46
  end
@@ -16,8 +16,7 @@ module RuboCop
16
16
  def split_comment(comment)
17
17
  match = comment.text.match(/^(# ?)([A-Za-z]+)(\s*:)?(\s+)?(\S+)?/)
18
18
  return false unless match
19
- margin, first_word, colon, space, note = *match.captures
20
- [margin, first_word, colon, space, note]
19
+ match.captures
21
20
  end
22
21
 
23
22
  def keyword_appearance?(first_word, colon, space)
@@ -6,27 +6,51 @@ module RuboCop
6
6
  # the left or to the right, amount being determined by the instance
7
7
  # variable @column_delta.
8
8
  module AutocorrectAlignment
9
+ SPACE = ' '.freeze
10
+
9
11
  def configured_indentation_width
10
12
  config.for_cop('IndentationWidth')['Width']
11
13
  end
12
14
 
15
+ def indentation(node)
16
+ offset(node) + (SPACE * configured_indentation_width)
17
+ end
18
+
19
+ def offset(node)
20
+ SPACE * node.loc.column
21
+ end
22
+
13
23
  def check_alignment(items, base_column = nil)
14
24
  base_column ||= items.first.loc.column unless items.empty?
15
25
  prev_line = -1
16
26
  items.each do |current|
17
27
  if current.loc.line > prev_line && start_of_line?(current.loc)
18
28
  @column_delta = base_column - current.loc.column
19
- add_offense(current, :expression) if @column_delta != 0
29
+ if @column_delta != 0
30
+ expr = current.loc.expression
31
+ if offenses.any? { |o| within?(expr, o.location) }
32
+ # If this offense is within a line range that is already being
33
+ # realigned by autocorrect, we report the offense without
34
+ # autocorrecting it. Two rewrites in the same area by the same
35
+ # cop can not be handled. The next iteration will find the
36
+ # offense again and correct it.
37
+ add_offense(nil, expr)
38
+ else
39
+ add_offense(current, :expression)
40
+ end
41
+ end
20
42
  end
21
43
  prev_line = current.loc.line
22
44
  end
23
45
  end
24
46
 
25
47
  def start_of_line?(loc)
26
- loc.expression.source_line[0...loc.column] =~ /^\s*$/
48
+ loc.expression.source_line[0...loc.column].blank?
27
49
  end
28
50
 
29
51
  def autocorrect(arg)
52
+ return unless arg
53
+
30
54
  heredoc_ranges = heredoc_ranges(arg)
31
55
  expr = arg.respond_to?(:loc) ? arg.loc.expression : arg
32
56
 
@@ -58,7 +82,7 @@ module RuboCop
58
82
  corrector.insert_before(range, ' ' * column_delta)
59
83
  end
60
84
  else
61
- remove(range, corrector) if range.source =~ /^[ \t]+$/
85
+ remove(range, corrector) if range.source =~ /\A[ \t]+\z/
62
86
  end
63
87
  end
64
88
 
@@ -75,7 +99,7 @@ module RuboCop
75
99
  end
76
100
 
77
101
  def block_comment_within?(expr)
78
- processed_source.comments.select(&:document?).find do |c|
102
+ processed_source.comments.select(&:document?).any? do |c|
79
103
  within?(c.loc.expression, expr)
80
104
  end
81
105
  end
@@ -5,6 +5,10 @@ module RuboCop
5
5
  # This module does auto-correction of nodes that could become grammatically
6
6
  # different after the correction. If the code change would alter the
7
7
  # abstract syntax tree, it is not done.
8
+ #
9
+ # However, if the code change merely introduces extraneous "begin" nodes
10
+ # which do not change the meaning of the code, it is still accepted.
11
+ #
8
12
  module AutocorrectUnlessChangingAST
9
13
  def autocorrect(node)
10
14
  current_buffer_src = processed_source.buffer.source
@@ -12,11 +16,12 @@ module RuboCop
12
16
  pre = current_buffer_src[0...replaced_range.begin_pos]
13
17
  post = current_buffer_src[replaced_range.end_pos..-1]
14
18
  new_buffer_src = pre + rewrite_node(node) + post
19
+ new_processed_src = ProcessedSource.new(new_buffer_src)
15
20
 
16
21
  # Make the correction only if it doesn't change the AST for the buffer.
17
- if processed_source.ast != ProcessedSource.new(new_buffer_src).ast
18
- return
19
- end
22
+ return if !new_processed_src.ast ||
23
+ (INLINE_BEGIN.process(processed_source.ast) !=
24
+ INLINE_BEGIN.process(new_processed_src.ast))
20
25
 
21
26
  correction(node)
22
27
  end
@@ -28,6 +33,24 @@ module RuboCop
28
33
  c = correction(ps.ast)
29
34
  Corrector.new(ps.buffer, [c]).rewrite
30
35
  end
36
+
37
+ # 'begin' nodes with a single child can be removed without changing
38
+ # the semantics of an AST. Canonicalizing an AST in this way can help
39
+ # us determine whether it has really changed in a meaningful way, or
40
+ # not. This means we can auto-correct in cases where we would otherwise
41
+ # refrain from doing so.
42
+ #
43
+ # If any other simplifications can be done to an AST without changing
44
+ # its meaning, they should be added here (and the class renamed).
45
+ # This will make autocorrection more powerful across the board.
46
+ #
47
+ class InlineBeginNodes < Parser::AST::Processor
48
+ def on_begin(node)
49
+ node.children.one? ? node.children[0] : node
50
+ end
51
+ end
52
+
53
+ INLINE_BEGIN = InlineBeginNodes.new
31
54
  end
32
55
  end
33
56
  end
@@ -23,13 +23,12 @@ module RuboCop
23
23
  end
24
24
 
25
25
  def on_send(node)
26
- _receiver, method_name, *_, rhs = *node
27
-
28
26
  # we only want to indent relative to the receiver
29
27
  # when the method called looks like a setter
30
- return unless method_name.to_s.end_with?('=')
28
+ return unless node.asgn_method_call?
31
29
 
32
30
  # This will match if, case, begin, blocks, etc.
31
+ rhs = node.children.last
33
32
  check_assignment(node, rhs) if rhs.is_a?(AST::Node)
34
33
  end
35
34
  end