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
@@ -25,32 +25,55 @@ module RuboCop
25
25
  def on_block(node)
26
26
  return if ignored_node?(node)
27
27
 
28
- if proper_block_style?(node)
29
- correct_style_detected
30
- else
31
- add_offense(node, :begin) { opposite_style_detected }
32
- end
28
+ return if proper_block_style?(node)
29
+
30
+ add_offense(node, :begin)
33
31
  end
34
32
 
35
33
  private
36
34
 
37
- def message(node)
35
+ def line_count_based_message(node)
36
+ block_length = Util.block_length(node)
37
+
38
+ if block_length > 0
39
+ 'Avoid using `{...}` for multi-line blocks.'
40
+ else
41
+ 'Prefer `{...}` over `do...end` for single-line blocks.'
42
+ end
43
+ end
44
+
45
+ def semantic_message(node)
38
46
  block_begin = node.loc.begin.source
47
+
48
+ if block_begin == '{'
49
+ 'Prefer `do...end` over `{...}` for procedural blocks.'
50
+ else
51
+ 'Prefer `{...}` over `do...end` for functional blocks.'
52
+ end
53
+ end
54
+
55
+ def braces_for_chaining_message(node)
39
56
  block_length = Util.block_length(node)
40
57
 
41
- case style
42
- when :line_count_based
43
- if block_length > 0
44
- 'Avoid using `{...}` for multi-line blocks.'
58
+ if block_length > 0
59
+ if return_value_chaining?(node)
60
+ 'Prefer `{...}` over `do...end` for multi-line chained blocks.'
45
61
  else
46
- 'Prefer `{...}` over `do...end` for single-line blocks.'
62
+ 'Prefer `do...end` for multi-line blocks without chaining.'
47
63
  end
64
+ else
65
+ 'Prefer `{...}` over `do...end` for single-line blocks.'
66
+ end
67
+ end
68
+
69
+ def message(node)
70
+ case style
71
+ when :line_count_based
72
+ line_count_based_message(node)
48
73
  when :semantic
49
- if block_begin == '{'
50
- 'Prefer `do...end` over `{...}` for procedural blocks.'
51
- else
52
- 'Prefer `{...}` over `do...end` for functional blocks.'
53
- end
74
+ semantic_message(node)
75
+ when :braces_for_chaining
76
+ braces_for_chaining_message(node)
54
77
  end
55
78
  end
56
79
 
@@ -59,11 +82,8 @@ module RuboCop
59
82
  b = node.loc.begin
60
83
  e = node.loc.end
61
84
  if b.is?('{')
62
- # If the left brace is not preceded by a whitespace character,
63
- # then we need a space before `do` to get valid Ruby code.
64
- if b.source_buffer.source[b.begin_pos - 1, 1] =~ /\S/
65
- corrector.insert_before(b, ' ')
66
- end
85
+ corrector.insert_before(b, ' ') unless whitespace_before?(b)
86
+ corrector.insert_before(e, ' ') unless whitespace_before?(e)
67
87
  corrector.replace(b, 'do')
68
88
  corrector.replace(e, 'end')
69
89
  else
@@ -73,6 +93,10 @@ module RuboCop
73
93
  end
74
94
  end
75
95
 
96
+ def whitespace_before?(node)
97
+ node.source_buffer.source[node.begin_pos - 1, 1] =~ /\s/
98
+ end
99
+
76
100
  def get_block(node)
77
101
  case node.type
78
102
  when :block
@@ -89,6 +113,8 @@ module RuboCop
89
113
  line_count_based_block_style?(node)
90
114
  when :semantic
91
115
  semantic_block_style?(node)
116
+ when :braces_for_chaining
117
+ braces_for_chaining_style?(node)
92
118
  end
93
119
  end
94
120
 
@@ -116,6 +142,21 @@ module RuboCop
116
142
  end
117
143
  end
118
144
 
145
+ def braces_for_chaining_style?(node)
146
+ block_length = Util.block_length(node)
147
+ block_begin = node.loc.begin.source
148
+
149
+ if block_length > 0
150
+ block_begin == (return_value_chaining?(node) ? '{' : 'do')
151
+ else
152
+ block_begin == '{'
153
+ end
154
+ end
155
+
156
+ def return_value_chaining?(node)
157
+ node.parent && node.parent.send_type? && node.parent.loc.dot
158
+ end
159
+
119
160
  def extract_method_name_from_block(block)
120
161
  node, _args, _body = *block
121
162
  _receiver, method_name, *_args = *node
@@ -147,15 +188,15 @@ module RuboCop
147
188
  if node.parent.begin_type?
148
189
  return_value_used?(node.parent)
149
190
  else
150
- Util::ASGN_NODES.include?(node.parent.type) ||
151
- node.parent.send_type?
191
+ node.parent.assignment? || node.parent.send_type?
152
192
  end
153
193
  end
154
194
 
155
195
  def return_value_of_scope?(node)
156
196
  return unless node.parent
157
197
 
158
- conditional?(node.parent) || node.parent.children.last == node
198
+ conditional?(node.parent) || array_or_range?(node.parent) ||
199
+ node.parent.children.last == node
159
200
  end
160
201
 
161
202
  def procedural_methods
@@ -173,6 +214,10 @@ module RuboCop
173
214
  def conditional?(node)
174
215
  node.if_type? || node.or_type? || node.and_type?
175
216
  end
217
+
218
+ def array_or_range?(node)
219
+ node.array_type? || node.irange_type? || node.erange_type?
220
+ end
176
221
  end
177
222
  end
178
223
  end
@@ -7,6 +7,7 @@ module RuboCop
7
7
  # if the last parameter is a hash.
8
8
  class BracesAroundHashParameters < Cop
9
9
  include ConfigurableEnforcedStyle
10
+ include AutocorrectUnlessChangingAST
10
11
 
11
12
  MSG = '%s curly braces around a hash parameter.'
12
13
 
@@ -14,7 +15,7 @@ module RuboCop
14
15
  _receiver, method_name, *args = *node
15
16
 
16
17
  # Discard attr writer methods.
17
- return if method_name.to_s.end_with?('=')
18
+ return if node.asgn_method_call?
18
19
  # Discard operator methods.
19
20
  return if operator?(method_name)
20
21
 
@@ -28,9 +29,10 @@ module RuboCop
28
29
 
29
30
  def check(arg, args)
30
31
  if style == :braces && !braces?(arg)
31
- add_offense(arg, :expression, format(MSG, 'Missing'))
32
+ add_offense(arg.parent, arg.loc.expression, format(MSG, 'Missing'))
32
33
  elsif style == :no_braces && braces?(arg)
33
- add_offense(arg, :expression, format(MSG, 'Redundant'))
34
+ add_offense(arg.parent, arg.loc.expression,
35
+ format(MSG, 'Redundant'))
34
36
  elsif style == :context_dependent
35
37
  check_context_dependent(arg, args)
36
38
  end
@@ -40,27 +42,53 @@ module RuboCop
40
42
  braces_around_2nd_from_end = args.length > 1 && args[-2].type == :hash
41
43
  if braces?(arg)
42
44
  unless braces_around_2nd_from_end
43
- add_offense(arg, :expression, format(MSG, 'Redundant'))
45
+ add_offense(arg.parent, arg.loc.expression,
46
+ format(MSG, 'Redundant'))
44
47
  end
45
48
  elsif braces_around_2nd_from_end
46
- add_offense(arg, :expression, format(MSG, 'Missing'))
49
+ add_offense(arg.parent, arg.loc.expression, format(MSG, 'Missing'))
47
50
  end
48
51
  end
49
52
 
50
- def autocorrect(node)
53
+ # We let AutocorrectUnlessChangingAST#autocorrect work with the send
54
+ # node, becuase that context is needed. When parsing the code to see if
55
+ # the AST has changed, a braceless hash would not be parsed as a hash
56
+ # otherwise.
57
+ def correction(send_node)
58
+ _receiver, _method_name, *args = *send_node
59
+ node = args.last
51
60
  lambda do |corrector|
52
61
  if braces?(node)
53
- right_range = range_with_surrounding_space(node.loc.begin, :right)
54
- corrector.remove(right_range)
55
- left_range = range_with_surrounding_space(node.loc.end, :left)
56
- corrector.remove(left_range)
62
+ remove_braces(corrector, node)
57
63
  else
58
- corrector.insert_before(node.loc.expression, '{')
59
- corrector.insert_after(node.loc.expression, '}')
64
+ add_braces(corrector, node)
60
65
  end
61
66
  end
62
67
  end
63
68
 
69
+ def remove_braces(corrector, node)
70
+ comments = processed_source.comments
71
+ right_brace_and_space = range_with_surrounding_space(node.loc.end,
72
+ :left)
73
+ if comments.any? { |c| c.loc.line == right_brace_and_space.line }
74
+ # Removing a line break between a comment and the closing
75
+ # parenthesis would cause a syntax error, so we only remove the
76
+ # braces in that case.
77
+ corrector.remove(node.loc.begin)
78
+ corrector.remove(node.loc.end)
79
+ else
80
+ left_brace_and_space = range_with_surrounding_space(node.loc.begin,
81
+ :right)
82
+ corrector.remove(left_brace_and_space)
83
+ corrector.remove(right_brace_and_space)
84
+ end
85
+ end
86
+
87
+ def add_braces(corrector, node)
88
+ corrector.insert_before(node.loc.expression, '{')
89
+ corrector.insert_after(node.loc.expression, '}')
90
+ end
91
+
64
92
  def non_empty_hash?(arg)
65
93
  arg && arg.type == :hash && arg.children.any?
66
94
  end
@@ -8,6 +8,7 @@ module RuboCop
8
8
  #
9
9
  # It will register a separate offense for each misaligned *when*.
10
10
  class CaseIndentation < Cop
11
+ include AutocorrectAlignment
11
12
  include ConfigurableEnforcedStyle
12
13
 
13
14
  def on_case(case_node)
@@ -50,10 +51,6 @@ module RuboCop
50
51
  end
51
52
  end
52
53
 
53
- def configured_indentation_width
54
- config.for_cop('IndentationWidth')['Width']
55
- end
56
-
57
54
  def parameter_name
58
55
  'IndentWhenRelativeTo'
59
56
  end
@@ -64,6 +61,23 @@ module RuboCop
64
61
  when :end then case_node.location.end.column
65
62
  end
66
63
  end
64
+
65
+ def autocorrect(node)
66
+ when_column = node.location.keyword.column
67
+ source_buffer = node.loc.expression.source_buffer
68
+ begin_pos = node.loc.keyword.begin_pos
69
+ whitespace = Parser::Source::Range.new(source_buffer,
70
+ begin_pos - when_column,
71
+ begin_pos)
72
+ return false unless whitespace.source.strip.empty?
73
+
74
+ case_node = node.each_ancestor(:case).first
75
+ base_type = cop_config[parameter_name] == 'end' ? :end : :case
76
+ column = base_column(case_node, base_type)
77
+ column += configured_indentation_width if cop_config['IndentOneStep']
78
+
79
+ ->(corrector) { corrector.replace(whitespace, ' ' * column) }
80
+ end
67
81
  end
68
82
  end
69
83
  end
@@ -10,6 +10,8 @@ module RuboCop
10
10
  # Enumerable or not (static analysis limitation), so this cop
11
11
  # can yield some false positives.
12
12
  class CollectionMethods < Cop
13
+ include MethodPreference
14
+
13
15
  MSG = 'Prefer `%s` over `%s`.'
14
16
 
15
17
  def on_block(node)
@@ -44,26 +46,6 @@ module RuboCop
44
46
  method_name)
45
47
  )
46
48
  end
47
-
48
- def preferred_method(method)
49
- preferred_methods[method.to_sym]
50
- end
51
-
52
- def preferred_methods
53
- @preferred_methods ||=
54
- begin
55
- # Make sure default configuration 'foo' => 'bar' is removed from
56
- # the total configuration if there is a 'bar' => 'foo' override.
57
- default = default_cop_config['PreferredMethods']
58
- merged = cop_config['PreferredMethods']
59
- overrides = merged.values - default.values
60
- merged.reject { |key, _| overrides.include?(key) }.symbolize_keys
61
- end
62
- end
63
-
64
- def default_cop_config
65
- ConfigLoader.default_configuration[cop_name]
66
- end
67
49
  end
68
50
  end
69
51
  end
@@ -47,7 +47,7 @@ module RuboCop
47
47
  def check_backtick_literal(node)
48
48
  return if style == :backticks && !contains_disallowed_backtick?(node)
49
49
  return if style == :mixed &&
50
- single_line?(node) &&
50
+ node.single_line? &&
51
51
  !contains_disallowed_backtick?(node)
52
52
 
53
53
  add_offense(node, :expression, MSG_USE_PERCENT_X)
@@ -56,7 +56,7 @@ module RuboCop
56
56
  def check_percent_x_literal(node)
57
57
  return if style == :backticks && contains_disallowed_backtick?(node)
58
58
  return if style == :percent_x
59
- return if style == :mixed && multi_line?(node)
59
+ return if style == :mixed && node.multiline?
60
60
  return if style == :mixed && contains_disallowed_backtick?(node)
61
61
 
62
62
  add_offense(node, :expression, MSG_USE_BACKTICKS)
@@ -87,14 +87,6 @@ module RuboCop
87
87
  node.loc.begin.source == '`'
88
88
  end
89
89
 
90
- def single_line?(node)
91
- !multi_line?(node)
92
- end
93
-
94
- def multi_line?(node)
95
- block_length(node) > 1
96
- end
97
-
98
90
  def preferred_delimiters
99
91
  config.for_cop('Style/PercentLiteralDelimiters') \
100
92
  ['PreferredDelimiters']['%x'].split(//)
@@ -8,30 +8,51 @@ module RuboCop
8
8
  class CommentAnnotation < Cop
9
9
  include AnnotationComment
10
10
 
11
- MSG = 'Annotation keywords should be all upper case, followed by a ' \
12
- 'colon and a space, then a note describing the problem.'
11
+ MSG = 'Annotation keywords like `%s` should be all upper case, ' \
12
+ 'followed by a colon, and a space, ' \
13
+ 'then a note describing the problem.'
14
+ MISSING_NOTE = 'Annotation comment, with keyword `%s`, ' \
15
+ 'is missing a note.'
13
16
 
14
17
  def investigate(processed_source)
15
- processed_source.comments.each do |comment|
18
+ processed_source.comments.each_with_index do |comment, ix|
19
+ next unless first_comment_line?(processed_source.comments, ix)
20
+
16
21
  margin, first_word, colon, space, note = split_comment(comment)
17
22
  next unless annotation?(comment) &&
18
23
  !correct_annotation?(first_word, colon, space, note)
19
24
 
20
25
  start = comment.loc.expression.begin_pos + margin.length
21
26
  length = first_word.length + colon.to_s.length + space.to_s.length
22
- range = Parser::Source::Range.new(processed_source.buffer,
27
+ source_buffer = comment.loc.expression.source_buffer
28
+ range = Parser::Source::Range.new(source_buffer,
23
29
  start,
24
30
  start + length)
25
- add_offense(range, range)
31
+ if note
32
+ add_offense(comment, range, format(MSG, first_word))
33
+ else
34
+ add_offense(comment, range, format(MISSING_NOTE, first_word))
35
+ end
26
36
  end
27
37
  end
28
38
 
29
39
  private
30
40
 
31
- def autocorrect(range)
41
+ def first_comment_line?(comments, ix)
42
+ ix == 0 || comments[ix - 1].loc.line < comments[ix].loc.line - 1
43
+ end
44
+
45
+ def autocorrect(comment)
46
+ margin, first_word, colon, space, note = split_comment(comment)
47
+ start = comment.loc.expression.begin_pos + margin.length
48
+ return if note.nil?
49
+
32
50
  lambda do |corrector|
33
- annotation_keyword = range.source.split(/:?\s+/).first
34
- corrector.replace(range, annotation_keyword.upcase << ': ')
51
+ length = first_word.length + colon.to_s.length + space.to_s.length
52
+ range = Parser::Source::Range.new(comment.loc.expression.source,
53
+ start,
54
+ start + length)
55
+ corrector.replace(range, "#{first_word.upcase}: ")
35
56
  end
36
57
  end
37
58
 
@@ -68,10 +68,12 @@ module RuboCop
68
68
  end
69
69
 
70
70
  def autocorrect(token)
71
- fail 'An AutocorrectNotice must be defined in ' \
71
+ fail Warning, 'An AutocorrectNotice must be defined in ' \
72
72
  'your RuboCop config' if autocorrect_notice.empty?
73
- fail "AutocorrectNotice '#{autocorrect_notice}' must match " \
74
- "Notice /#{notice}/" unless autocorrect_notice =~ Regexp.new(notice)
73
+ regex = Regexp.new(notice)
74
+ fail Warning, "AutocorrectNotice '#{autocorrect_notice}' must " \
75
+ "match Notice /#{notice}/" unless autocorrect_notice =~ regex
76
+
75
77
  lambda do |corrector|
76
78
  if token.nil?
77
79
  range = Parser::Source::Range.new('', 0, 0)
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # This cop checks for missing top-level documentation of
7
7
  # classes and modules. Classes with no body are exempt from the
8
8
  # check and so are namespace modules - modules that have nothing in
9
- # their bodies except classes or other other modules.
9
+ # their bodies except classes or other modules.
10
10
  #
11
11
  # The documentation requirement is annulled if the class or module has
12
12
  # a "#:nodoc:" comment next to it. Likewise, "#:nodoc: all" does the
@@ -47,34 +47,43 @@ module RuboCop
47
47
  end
48
48
  end
49
49
 
50
+ # 'Const = Class.new' or 'Const = Module.new' are module definitions
51
+ mod_new = '(send (const nil {:Class :Module}) :new ...)'
52
+ def_node_matcher :module_definition?,
53
+ "{class
54
+ module
55
+ (casgn _ _ {#{mod_new} (block #{mod_new} ...)})}"
56
+
50
57
  def namespace?(body_node)
51
58
  return false unless body_node
52
59
 
53
60
  case body_node.type
54
61
  when :begin
55
- body_node.children.all? do |node|
56
- [:class, :module].include?(node.type)
57
- end
58
- when :class, :module
59
- true
62
+ body_node.children.all? { |node| module_definition?(node) }
60
63
  else
61
- false
64
+ module_definition?(body_node)
62
65
  end
63
66
  end
64
67
 
65
68
  # Returns true if the node has a comment on the line above it that
66
69
  # isn't an annotation.
67
70
  def associated_comment?(node, ast_with_comments)
68
- return false if ast_with_comments[node].empty?
71
+ preceding_comments = preceding_comments(node, ast_with_comments)
72
+ return false if preceding_comments.empty?
69
73
 
70
- preceding_comment = ast_with_comments[node].last
71
- distance = node.loc.keyword.line - preceding_comment.loc.line
74
+ distance = node.loc.keyword.line - preceding_comments.last.loc.line
72
75
  return false if distance > 1
73
- return false unless comment_line_only?(preceding_comment)
76
+ return false unless comment_line_only?(preceding_comments.last)
74
77
 
75
78
  # As long as there's at least one comment line that isn't an
76
79
  # annotation, it's OK.
77
- ast_with_comments[node].any? { |comment| !annotation?(comment) }
80
+ preceding_comments.any? { |comment| !annotation?(comment) }
81
+ end
82
+
83
+ def preceding_comments(node, ast_with_comments)
84
+ return [] unless node && ast_with_comments
85
+
86
+ ast_with_comments[node].select { |c| c.loc.line < node.loc.line }
78
87
  end
79
88
 
80
89
  def comment_line_only?(comment)
@@ -46,6 +46,12 @@ module RuboCop
46
46
 
47
47
  dot_line = node.loc.dot.line
48
48
 
49
+ # don't register an offense if there is a line comment between
50
+ # the dot and the selector
51
+ # otherwise, we might break the code while "correcting" it
52
+ # (even if there is just an extra blank line, treat it the same)
53
+ return true if (selector_line - dot_line) > 1
54
+
49
55
  case style
50
56
  when :leading then dot_line == selector_line
51
57
  when :trailing then dot_line != selector_line
@@ -22,22 +22,11 @@ module RuboCop
22
22
  class DoubleNegation < Cop
23
23
  MSG = 'Avoid the use of double negation (`!!`).'
24
24
 
25
- def on_send(node)
26
- return unless not_node?(node)
27
-
28
- receiver, _method_name, *_args = *node
29
-
30
- add_offense(node, :selector) if not_node?(receiver)
31
- end
25
+ def_node_matcher :double_negative?, '(send (send _ :!) :!)'
32
26
 
33
- private
34
-
35
- def not_node?(node)
36
- _receiver, method_name, *args = *node
37
-
38
- # ! does not take any arguments
39
- args.empty? && method_name == :! &&
40
- node.loc.selector.is?('!')
27
+ def on_send(node)
28
+ return unless double_negative?(node) && node.loc.selector.is?('!')
29
+ add_offense(node, :selector)
41
30
  end
42
31
  end
43
32
  end
@@ -7,6 +7,9 @@ module RuboCop
7
7
  # returned at the end and so could be replace by each_with_object without
8
8
  # the need to return the object at the end.
9
9
  #
10
+ # However, we can't replace with each_with_object if the accumulator
11
+ # parameter is assigned to within the block.
12
+ #
10
13
  # @example
11
14
  # # bad
12
15
  # [1, 2].inject({}) { |a, e| a[e] = e; a }
@@ -23,16 +26,26 @@ module RuboCop
23
26
  # filter out super and zsuper nodes
24
27
  return unless method.type == :send
25
28
 
26
- _, method_name, method_args = *method
29
+ _, method_name, method_arg = *method
27
30
 
28
31
  return unless METHODS.include? method_name
29
- return if method_args && method_args.type == :sym
32
+ return if method_arg && BASIC_LITERALS.include?(method_arg.type)
30
33
 
31
34
  return_value = return_value(body)
32
35
  return unless return_value
33
36
 
34
37
  return unless first_argument_returned?(args, return_value)
35
38
 
39
+ # if the accumulator parameter is assigned to in the block,
40
+ # then we can't convert to each_with_object
41
+ first_arg, = *args
42
+ accumulator_var, = *first_arg
43
+ return if body.each_descendant.any? do |n|
44
+ next unless n.assignment?
45
+ lhs, _rhs = *n
46
+ lhs.equal?(accumulator_var)
47
+ end
48
+
36
49
  add_offense(method, :selector, format(MSG, method_name))
37
50
  end
38
51
 
@@ -47,8 +60,8 @@ module RuboCop
47
60
 
48
61
  def first_argument_returned?(args, return_value)
49
62
  first_arg, = *args
50
- accumulator_var = *first_arg
51
- return_var = *return_value
63
+ accumulator_var, = *first_arg
64
+ return_var, = *return_value
52
65
 
53
66
  accumulator_var == return_var
54
67
  end
@@ -8,6 +8,7 @@ module RuboCop
8
8
  # are special cases when they should follow the same rules as the
9
9
  # alignment of end.
10
10
  class ElseAlignment < Cop
11
+ include EndKeywordAlignment
11
12
  include AutocorrectAlignment
12
13
  include CheckAssignment
13
14
 
@@ -86,7 +87,7 @@ module RuboCop
86
87
 
87
88
  end_config = config.for_cop('Lint/EndAlignment')
88
89
  style = end_config['Enabled'] ? end_config['AlignWith'] : 'keyword'
89
- base = style == 'variable' ? node : rhs
90
+ base = variable_alignment?(node.loc, rhs, style.to_sym) ? node : rhs
90
91
 
91
92
  return if rhs.type != :if
92
93
 
@@ -110,6 +110,31 @@ module RuboCop
110
110
  add_offense(node, :else, MSG)
111
111
  end
112
112
  end
113
+
114
+ def autocorrect(node)
115
+ return false if autocorrect_forbidden?(node.type.to_s)
116
+
117
+ lambda do |corrector|
118
+ end_pos = if node.loc.end
119
+ node.loc.end.begin_pos
120
+ else
121
+ node.loc.expression.end_pos + 1
122
+ end
123
+ range = Parser::Source::Range.new(node.loc.expression.source_buffer,
124
+ node.loc.else.begin_pos,
125
+ end_pos)
126
+ corrector.remove(range)
127
+ end
128
+ end
129
+
130
+ def autocorrect_forbidden?(type)
131
+ [type, 'both'].include? missing_else_style
132
+ end
133
+
134
+ def missing_else_style
135
+ missing_config = config.for_cop('Style/MissingElse')
136
+ missing_config['Enabled'] ? missing_config['EnforcedStyle'] : nil
137
+ end
113
138
  end
114
139
  end
115
140
  end