rubocop 0.28.0 → 0.29.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 (211) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +7 -7
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +59 -0
  5. data/README.md +62 -28
  6. data/config/default.yml +31 -0
  7. data/config/disabled.yml +1 -1
  8. data/config/enabled.yml +17 -0
  9. data/lib/rubocop.rb +4 -0
  10. data/lib/rubocop/cli.rb +1 -1
  11. data/lib/rubocop/config.rb +12 -8
  12. data/lib/rubocop/config_loader.rb +20 -10
  13. data/lib/rubocop/cop/cop.rb +13 -7
  14. data/lib/rubocop/cop/corrector.rb +10 -10
  15. data/lib/rubocop/cop/lint/assignment_in_condition.rb +6 -1
  16. data/lib/rubocop/cop/lint/block_alignment.rb +9 -2
  17. data/lib/rubocop/cop/lint/debugger.rb +13 -1
  18. data/lib/rubocop/cop/lint/duplicate_methods.rb +104 -0
  19. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  20. data/lib/rubocop/cop/lint/require_parentheses.rb +3 -3
  21. data/lib/rubocop/cop/mixin/access_modifier_node.rb +27 -0
  22. data/lib/rubocop/cop/mixin/on_normal_if_unless.rb +0 -4
  23. data/lib/rubocop/cop/rails/delegate.rb +4 -5
  24. data/lib/rubocop/cop/rails/read_write_attribute.rb +33 -0
  25. data/lib/rubocop/cop/style/access_modifier_indentation.rb +0 -7
  26. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +4 -2
  27. data/lib/rubocop/cop/style/class_methods.rb +25 -9
  28. data/lib/rubocop/cop/style/empty_lines_around_access_modifier.rb +7 -1
  29. data/lib/rubocop/cop/style/empty_literal.rb +25 -10
  30. data/lib/rubocop/cop/style/even_odd.rb +32 -14
  31. data/lib/rubocop/cop/style/first_parameter_indentation.rb +106 -0
  32. data/lib/rubocop/cop/style/format_string.rb +2 -2
  33. data/lib/rubocop/cop/style/global_vars.rb +1 -5
  34. data/lib/rubocop/cop/style/hash_syntax.rb +0 -4
  35. data/lib/rubocop/cop/style/indentation_consistency.rb +5 -5
  36. data/lib/rubocop/cop/style/indentation_width.rb +13 -14
  37. data/lib/rubocop/cop/style/lambda.rb +48 -2
  38. data/lib/rubocop/cop/style/line_end_concatenation.rb +43 -47
  39. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +3 -3
  40. data/lib/rubocop/cop/style/module_function.rb +3 -3
  41. data/lib/rubocop/cop/style/multiline_block_layout.rb +65 -17
  42. data/lib/rubocop/cop/style/multiline_operation_indentation.rb +2 -1
  43. data/lib/rubocop/cop/style/perl_backrefs.rb +2 -2
  44. data/lib/rubocop/cop/style/redundant_exception.rb +12 -0
  45. data/lib/rubocop/cop/style/self_assignment.rb +27 -0
  46. data/lib/rubocop/cop/style/semicolon.rb +2 -1
  47. data/lib/rubocop/cop/style/space_around_block_parameters.rb +92 -0
  48. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +0 -8
  49. data/lib/rubocop/cop/style/struct_inheritance.rb +42 -0
  50. data/lib/rubocop/cop/style/trivial_accessors.rb +10 -4
  51. data/lib/rubocop/cop/util.rb +32 -7
  52. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -15
  53. data/lib/rubocop/options.rb +28 -41
  54. data/lib/rubocop/processed_source.rb +6 -0
  55. data/lib/rubocop/rake_task.rb +6 -3
  56. data/lib/rubocop/runner.rb +55 -15
  57. data/lib/rubocop/version.rb +1 -1
  58. data/relnotes/v0.29.0.md +116 -0
  59. data/rubocop.gemspec +3 -3
  60. data/spec/rubocop/cli_spec.rb +329 -25
  61. data/spec/rubocop/config_loader_spec.rb +20 -0
  62. data/spec/rubocop/cop/lint/assignment_in_condition_spec.rb +27 -3
  63. data/spec/rubocop/cop/lint/block_alignment_spec.rb +3 -4
  64. data/spec/rubocop/cop/lint/condition_position_spec.rb +1 -1
  65. data/spec/rubocop/cop/lint/debugger_spec.rb +20 -7
  66. data/spec/rubocop/cop/lint/duplicate_methods_spec.rb +189 -0
  67. data/spec/rubocop/cop/lint/empty_interpolation_spec.rb +1 -1
  68. data/spec/rubocop/cop/lint/end_in_method_spec.rb +1 -1
  69. data/spec/rubocop/cop/lint/eval_spec.rb +4 -4
  70. data/spec/rubocop/cop/lint/literal_in_condition_spec.rb +18 -0
  71. data/spec/rubocop/cop/lint/literal_in_interpolation_spec.rb +2 -2
  72. data/spec/rubocop/cop/lint/loop_spec.rb +4 -4
  73. data/spec/rubocop/cop/lint/parentheses_as_grouped_expression_spec.rb +7 -7
  74. data/spec/rubocop/cop/lint/space_before_first_arg_spec.rb +5 -4
  75. data/spec/rubocop/cop/lint/useless_assignment_spec.rb +3 -9
  76. data/spec/rubocop/cop/lint/useless_comparison_spec.rb +1 -1
  77. data/spec/rubocop/cop/lint/void_spec.rb +1 -1
  78. data/spec/rubocop/cop/metrics/abc_size_spec.rb +10 -0
  79. data/spec/rubocop/cop/metrics/line_length_spec.rb +2 -2
  80. data/spec/rubocop/cop/metrics/parameter_lists_spec.rb +2 -2
  81. data/spec/rubocop/cop/rails/action_filter_spec.rb +6 -12
  82. data/spec/rubocop/cop/rails/default_scope_spec.rb +5 -5
  83. data/spec/rubocop/cop/rails/delegate_spec.rb +8 -0
  84. data/spec/rubocop/cop/rails/has_and_belongs_to_many_spec.rb +1 -1
  85. data/spec/rubocop/cop/rails/read_write_attribute_spec.rb +119 -8
  86. data/spec/rubocop/cop/rails/scope_args_spec.rb +3 -3
  87. data/spec/rubocop/cop/rails/validation_spec.rb +3 -3
  88. data/spec/rubocop/cop/style/alias_spec.rb +5 -5
  89. data/spec/rubocop/cop/style/align_hash_spec.rb +1 -1
  90. data/spec/rubocop/cop/style/align_parameters_spec.rb +8 -8
  91. data/spec/rubocop/cop/style/and_or_spec.rb +15 -30
  92. data/spec/rubocop/cop/style/array_join_spec.rb +4 -4
  93. data/spec/rubocop/cop/style/ascii_comments_spec.rb +1 -2
  94. data/spec/rubocop/cop/style/ascii_identifiers_spec.rb +2 -2
  95. data/spec/rubocop/cop/style/begin_block_spec.rb +1 -1
  96. data/spec/rubocop/cop/style/block_comments_spec.rb +1 -1
  97. data/spec/rubocop/cop/style/block_end_newline_spec.rb +1 -1
  98. data/spec/rubocop/cop/style/blocks_spec.rb +2 -2
  99. data/spec/rubocop/cop/style/braces_around_hash_parameters_spec.rb +38 -33
  100. data/spec/rubocop/cop/style/case_equality_spec.rb +1 -1
  101. data/spec/rubocop/cop/style/character_literal_spec.rb +4 -4
  102. data/spec/rubocop/cop/style/class_and_module_children_spec.rb +4 -2
  103. data/spec/rubocop/cop/style/class_methods_spec.rb +12 -0
  104. data/spec/rubocop/cop/style/class_vars_spec.rb +2 -2
  105. data/spec/rubocop/cop/style/collection_methods_spec.rb +4 -4
  106. data/spec/rubocop/cop/style/colon_method_call_spec.rb +8 -8
  107. data/spec/rubocop/cop/style/comment_annotation_spec.rb +10 -10
  108. data/spec/rubocop/cop/style/constant_name_spec.rb +7 -7
  109. data/spec/rubocop/cop/style/def_with_parentheses_spec.rb +1 -1
  110. data/spec/rubocop/cop/style/deprecated_hash_methods_spec.rb +4 -4
  111. data/spec/rubocop/cop/style/dot_position_spec.rb +8 -6
  112. data/spec/rubocop/cop/style/each_with_object_spec.rb +2 -2
  113. data/spec/rubocop/cop/style/else_alignment_spec.rb +2 -4
  114. data/spec/rubocop/cop/style/empty_lines_around_access_modifier_spec.rb +78 -0
  115. data/spec/rubocop/cop/style/empty_lines_around_class_body_spec.rb +60 -0
  116. data/spec/rubocop/cop/style/empty_lines_spec.rb +3 -3
  117. data/spec/rubocop/cop/style/empty_literal_spec.rb +29 -12
  118. data/spec/rubocop/cop/style/encoding_spec.rb +3 -3
  119. data/spec/rubocop/cop/style/end_block_spec.rb +1 -1
  120. data/spec/rubocop/cop/style/end_of_line_spec.rb +2 -2
  121. data/spec/rubocop/cop/style/even_odd_spec.rb +109 -20
  122. data/spec/rubocop/cop/style/extra_spacing_spec.rb +3 -3
  123. data/spec/rubocop/cop/style/first_parameter_indentation_spec.rb +293 -0
  124. data/spec/rubocop/cop/style/for_spec.rb +2 -2
  125. data/spec/rubocop/cop/style/format_string_spec.rb +45 -21
  126. data/spec/rubocop/cop/style/global_vars_spec.rb +4 -4
  127. data/spec/rubocop/cop/style/guard_clause_spec.rb +17 -0
  128. data/spec/rubocop/cop/style/hash_syntax_spec.rb +15 -15
  129. data/spec/rubocop/cop/{metrics → style}/if_unless_modifier_spec.rb +2 -2
  130. data/spec/rubocop/cop/style/if_with_semicolon_spec.rb +2 -2
  131. data/spec/rubocop/cop/style/indent_array_spec.rb +3 -6
  132. data/spec/rubocop/cop/style/indent_hash_spec.rb +4 -4
  133. data/spec/rubocop/cop/style/indentation_consistency_spec.rb +1 -2
  134. data/spec/rubocop/cop/style/indentation_width_spec.rb +1 -2
  135. data/spec/rubocop/cop/style/infinite_loop_spec.rb +1 -1
  136. data/spec/rubocop/cop/style/lambda_call_spec.rb +4 -4
  137. data/spec/rubocop/cop/style/lambda_spec.rb +37 -2
  138. data/spec/rubocop/cop/style/leading_comment_space_spec.rb +7 -12
  139. data/spec/rubocop/cop/style/line_end_concatenation_spec.rb +41 -1
  140. data/spec/rubocop/cop/style/method_call_parentheses_spec.rb +4 -4
  141. data/spec/rubocop/cop/style/method_called_on_do_end_block_spec.rb +3 -3
  142. data/spec/rubocop/cop/style/method_name_spec.rb +1 -1
  143. data/spec/rubocop/cop/style/multiline_block_layout_spec.rb +61 -0
  144. data/spec/rubocop/cop/style/multiline_if_then_spec.rb +1 -3
  145. data/spec/rubocop/cop/style/multiline_operation_indentation_spec.rb +8 -0
  146. data/spec/rubocop/cop/style/multiline_ternary_operator_spec.rb +1 -1
  147. data/spec/rubocop/cop/style/nested_ternary_operator_spec.rb +1 -1
  148. data/spec/rubocop/cop/style/next_spec.rb +16 -0
  149. data/spec/rubocop/cop/style/numeric_literals_spec.rb +5 -5
  150. data/spec/rubocop/cop/style/one_line_conditional_spec.rb +1 -1
  151. data/spec/rubocop/cop/style/parentheses_around_condition_spec.rb +22 -4
  152. data/spec/rubocop/cop/style/percent_literal_delimiters_spec.rb +31 -31
  153. data/spec/rubocop/cop/style/percent_q_literals_spec.rb +12 -12
  154. data/spec/rubocop/cop/style/perl_backrefs_spec.rb +3 -3
  155. data/spec/rubocop/cop/style/proc_spec.rb +3 -3
  156. data/spec/rubocop/cop/style/raise_args_spec.rb +9 -9
  157. data/spec/rubocop/cop/style/redundant_begin_spec.rb +1 -1
  158. data/spec/rubocop/cop/style/redundant_exception_spec.rb +36 -4
  159. data/spec/rubocop/cop/style/redundant_self_spec.rb +89 -45
  160. data/spec/rubocop/cop/style/regexp_literal_spec.rb +9 -9
  161. data/spec/rubocop/cop/style/rescue_modifier_spec.rb +2 -2
  162. data/spec/rubocop/cop/style/self_assignment_spec.rb +16 -10
  163. data/spec/rubocop/cop/style/semicolon_spec.rb +9 -9
  164. data/spec/rubocop/cop/style/single_line_block_params_spec.rb +2 -2
  165. data/spec/rubocop/cop/style/single_space_before_first_arg_spec.rb +1 -1
  166. data/spec/rubocop/cop/style/space_after_colon_spec.rb +5 -5
  167. data/spec/rubocop/cop/style/space_after_comma_spec.rb +3 -3
  168. data/spec/rubocop/cop/style/space_after_control_keyword_spec.rb +4 -4
  169. data/spec/rubocop/cop/style/space_after_not_spec.rb +2 -2
  170. data/spec/rubocop/cop/style/space_after_semicolon_spec.rb +2 -2
  171. data/spec/rubocop/cop/style/space_around_block_parameters_spec.rb +150 -0
  172. data/spec/rubocop/cop/style/space_around_equals_in_parameter_default_spec.rb +18 -9
  173. data/spec/rubocop/cop/style/space_around_operators_spec.rb +24 -21
  174. data/spec/rubocop/cop/style/space_before_block_braces_spec.rb +4 -4
  175. data/spec/rubocop/cop/style/space_before_comma_spec.rb +4 -4
  176. data/spec/rubocop/cop/style/space_before_comment_spec.rb +3 -3
  177. data/spec/rubocop/cop/style/space_before_semicolon_spec.rb +2 -2
  178. data/spec/rubocop/cop/style/space_inside_block_braces_spec.rb +33 -24
  179. data/spec/rubocop/cop/style/space_inside_brackets_spec.rb +2 -2
  180. data/spec/rubocop/cop/style/space_inside_hash_literal_braces_spec.rb +9 -9
  181. data/spec/rubocop/cop/style/space_inside_parens_spec.rb +1 -1
  182. data/spec/rubocop/cop/style/special_global_vars_spec.rb +6 -6
  183. data/spec/rubocop/cop/style/string_literals_in_interpolation_spec.rb +3 -3
  184. data/spec/rubocop/cop/style/string_literals_spec.rb +16 -16
  185. data/spec/rubocop/cop/style/struct_inheritance_spec.rb +44 -0
  186. data/spec/rubocop/cop/style/symbol_array_spec.rb +9 -9
  187. data/spec/rubocop/cop/style/symbol_proc_spec.rb +12 -12
  188. data/spec/rubocop/cop/style/tab_spec.rb +4 -4
  189. data/spec/rubocop/cop/style/trailing_blank_lines_spec.rb +2 -2
  190. data/spec/rubocop/cop/style/trailing_whitespace_spec.rb +2 -2
  191. data/spec/rubocop/cop/style/trivial_accessors_spec.rb +16 -0
  192. data/spec/rubocop/cop/style/unneeded_capital_w_spec.rb +11 -22
  193. data/spec/rubocop/cop/style/variable_interpolation_spec.rb +7 -7
  194. data/spec/rubocop/cop/style/while_until_do_spec.rb +2 -2
  195. data/spec/rubocop/cop/{metrics → style}/while_until_modifier_spec.rb +2 -2
  196. data/spec/rubocop/cop/style/word_array_spec.rb +11 -11
  197. data/spec/rubocop/cop/util_spec.rb +51 -0
  198. data/spec/rubocop/cop/variable_force/reference_spec.rb +19 -0
  199. data/spec/rubocop/cop/variable_force/variable_table_spec.rb +7 -0
  200. data/spec/rubocop/formatter/disabled_lines_formatter_spec.rb +7 -8
  201. data/spec/rubocop/formatter/text_util_spec.rb +55 -0
  202. data/spec/rubocop/options_spec.rb +26 -20
  203. data/spec/rubocop/rake_task_spec.rb +122 -0
  204. data/spec/rubocop/runner_spec.rb +37 -2
  205. data/spec/rubocop/token_spec.rb +5 -1
  206. data/spec/spec_helper.rb +5 -2
  207. data/spec/support/cop_helper.rb +3 -0
  208. data/spec/support/cops/class_must_be_a_module_cop.rb +19 -0
  209. data/spec/support/cops/module_must_be_a_class_cop.rb +19 -0
  210. data/spec/support/custom_matchers.rb +1 -1
  211. metadata +35 -12
@@ -55,7 +55,8 @@ module RuboCop
55
55
  end
56
56
 
57
57
  def autocorrect(range)
58
- @corrections << ->(corrector) { corrector.remove(range) } if range
58
+ fail CorrectionNotPossible unless range
59
+ @corrections << ->(corrector) { corrector.remove(range) }
59
60
  end
60
61
  end
61
62
  end
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks the spacing inside and after block parameters pipes.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # {}.each { | x, y |puts x }
12
+ #
13
+ # # good
14
+ # {}.each { |x, y| puts x }
15
+ class SpaceAroundBlockParameters < Cop
16
+ include ConfigurableEnforcedStyle
17
+
18
+ def on_block(node)
19
+ _method, args, body = *node
20
+ opening_pipe, closing_pipe = args.loc.begin, args.loc.end
21
+ return unless opening_pipe
22
+
23
+ check_inside_pipes(args.children, opening_pipe, closing_pipe)
24
+
25
+ if body
26
+ check_space(closing_pipe.end_pos, body.loc.expression.begin_pos,
27
+ closing_pipe, 'after closing `|`')
28
+ end
29
+
30
+ check_each_arg(args)
31
+ end
32
+
33
+ private
34
+
35
+ def parameter_name
36
+ 'EnforcedStyleInsidePipes'
37
+ end
38
+
39
+ def check_inside_pipes(args, opening_pipe, closing_pipe)
40
+ first, last = args.first.loc.expression, args.last.loc.expression
41
+
42
+ if style == :no_space
43
+ check_no_space(opening_pipe.end_pos, first.begin_pos,
44
+ 'Space before first')
45
+ check_no_space(last.end_pos, closing_pipe.begin_pos,
46
+ 'Space after last')
47
+ elsif style == :space
48
+ check_space(opening_pipe.end_pos, first.begin_pos, first,
49
+ 'before first block parameter')
50
+ check_space(last.end_pos, closing_pipe.begin_pos, last,
51
+ 'after last block parameter')
52
+ check_no_space(opening_pipe.end_pos, first.begin_pos - 1,
53
+ 'Extra space before first')
54
+ check_no_space(last.end_pos + 1, closing_pipe.begin_pos,
55
+ 'Extra space after last')
56
+ end
57
+ end
58
+
59
+ def check_each_arg(args)
60
+ args.children[1..-1].each do |arg|
61
+ expr = arg.loc.expression
62
+ check_no_space(range_with_surrounding_space(expr, :left).begin_pos,
63
+ expr.begin_pos - 1, 'Extra space before')
64
+ end
65
+ end
66
+
67
+ def check_space(space_begin_pos, space_end_pos, range, msg)
68
+ return if space_begin_pos != space_end_pos
69
+
70
+ add_offense(range, range, "Space #{msg} missing.")
71
+ end
72
+
73
+ def check_no_space(space_begin_pos, space_end_pos, msg)
74
+ return if space_begin_pos >= space_end_pos
75
+
76
+ range = Parser::Source::Range.new(processed_source.buffer,
77
+ space_begin_pos, space_end_pos)
78
+ add_offense(range, range, "#{msg} block parameter detected.")
79
+ end
80
+
81
+ def autocorrect(range)
82
+ @corrections << lambda do |corrector|
83
+ case range.source
84
+ when /^\s+$/ then corrector.remove(range)
85
+ else corrector.insert_after(range, ' ')
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -24,14 +24,6 @@ module RuboCop
24
24
 
25
25
  wrong_quotes?(node, style)
26
26
  end
27
-
28
- def autocorrect(node)
29
- @corrections << lambda do |corrector|
30
- replacement = node.loc.begin.is?('"') ? "'" : '"'
31
- corrector.replace(node.loc.begin, replacement)
32
- corrector.replace(node.loc.end, replacement)
33
- end
34
- end
35
27
  end
36
28
  end
37
29
  end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for inheritance from Struct.new.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # class Person < Struct.new(:first_name, :last_name)
11
+ # end
12
+ #
13
+ # # good
14
+ # Person = Struct.new(:first_name, :last_name)
15
+ class StructInheritance < Cop
16
+ MSG = "Don't extend an instance initialized by `Struct.new`."
17
+
18
+ def on_class(node)
19
+ _name, superclass, _body = *node
20
+ return unless struct_constructor?(superclass)
21
+
22
+ add_offense(node, superclass.loc.expression, MSG)
23
+ end
24
+
25
+ private
26
+
27
+ def struct_constructor?(node)
28
+ if node && node.send_type?
29
+ receiver, method_name = *node
30
+
31
+ receiver &&
32
+ receiver.const_type? &&
33
+ receiver.children.last == :Struct &&
34
+ method_name == :new
35
+ else
36
+ false
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -107,13 +107,17 @@ module RuboCop
107
107
  autocorrect_instance(node)
108
108
  elsif node.type == :defs && node.children.first.type == :self
109
109
  autocorrect_class(node)
110
+ else
111
+ fail CorrectionNotPossible
110
112
  end
111
113
  end
112
114
 
113
115
  def autocorrect_instance(node)
114
116
  method_name, args, body = *node
115
- return unless names_match?(method_name, body)
116
- return unless (kind = trivial_accessor_kind(method_name, args, body))
117
+ unless names_match?(method_name, body) &&
118
+ (kind = trivial_accessor_kind(method_name, args, body))
119
+ fail CorrectionNotPossible
120
+ end
117
121
 
118
122
  @corrections << lambda do |corrector|
119
123
  corrector.replace(
@@ -125,8 +129,10 @@ module RuboCop
125
129
 
126
130
  def autocorrect_class(node)
127
131
  _, method_name, args, body = *node
128
- return unless names_match?(method_name, body)
129
- return unless (kind = trivial_accessor_kind(method_name, args, body))
132
+ unless names_match?(method_name, body) &&
133
+ (kind = trivial_accessor_kind(method_name, args, body))
134
+ fail CorrectionNotPossible
135
+ end
130
136
 
131
137
  @corrections << lambda do |corrector|
132
138
  indent = ' ' * node.loc.column
@@ -141,16 +141,41 @@ module RuboCop
141
141
  Parser::Source::Range.new(source_buffer, begin_pos, end_pos)
142
142
  end
143
143
 
144
- def range_with_surrounding_space(range, side = :both)
145
- src = @processed_source.buffer.source
146
- go_left = side == :left || side == :both
147
- go_right = side == :right || side == :both
148
- begin_pos = range.begin_pos
144
+ def range_with_surrounding_comma(range, side = :both, buffer = nil)
145
+ buffer ||= @processed_source.buffer
146
+ src = buffer.source
147
+
148
+ if side == :both
149
+ go_left, go_right = true, true
150
+ else
151
+ go_left = side == :left
152
+ go_right = side == :right
153
+ end
154
+
155
+ begin_pos, end_pos = range.begin_pos, range.end_pos
156
+ begin_pos -= 1 if go_left && src[begin_pos - 1] == ','
157
+ end_pos += 1 if go_right && src[end_pos] == ','
158
+
159
+ Parser::Source::Range.new(buffer, begin_pos, end_pos)
160
+ end
161
+
162
+ def range_with_surrounding_space(range, side = :both, buffer = nil)
163
+ buffer ||= @processed_source.buffer
164
+ src = buffer.source
165
+
166
+ if side == :both
167
+ go_left, go_right = true, true
168
+ else
169
+ go_left = side == :left
170
+ go_right = side == :right
171
+ end
172
+
173
+ begin_pos, end_pos = range.begin_pos, range.end_pos
149
174
  begin_pos -= 1 while go_left && src[begin_pos - 1] =~ /[ \t]/
150
- end_pos = range.end_pos
175
+ begin_pos -= 1 if go_left && src[begin_pos - 1] == "\n"
151
176
  end_pos += 1 while go_right && src[end_pos] =~ /[ \t]/
152
177
  end_pos += 1 if go_right && src[end_pos] == "\n"
153
- Parser::Source::Range.new(@processed_source.buffer, begin_pos, end_pos)
178
+ Parser::Source::Range.new(buffer, begin_pos, end_pos)
154
179
  end
155
180
 
156
181
  def begins_its_line?(range)
@@ -97,21 +97,6 @@ module RuboCop
97
97
  message = offense.corrected? ? green('[Corrected] ') : ''
98
98
  message << annotate_message(offense.message)
99
99
  end
100
-
101
- def pluralize(number, thing, options = {})
102
- text = ''
103
-
104
- if number == 0 && options[:no_for_zero]
105
- text = 'no'
106
- else
107
- text << number.to_s
108
- end
109
-
110
- text << " #{thing}"
111
- text << 's' unless number == 1
112
-
113
- text
114
- end
115
100
  end
116
101
  end
117
102
  end
@@ -9,6 +9,7 @@ module RuboCop
9
9
  only: 'Run only the given cop(s).',
10
10
  only_guide_cops: ['Run only cops for rules that link to a',
11
11
  'style guide.'],
12
+ except: 'Disable the given cop(s).',
12
13
  require: 'Require Ruby file.',
13
14
  config: 'Specify configuration file.',
14
15
  auto_gen_config: ['Generate a configuration file acting as a',
@@ -34,7 +35,8 @@ module RuboCop
34
35
  'This option applies to the previously',
35
36
  'specified --format, or the default format',
36
37
  'if no format is specified.'],
37
- fail_level: 'Minimum severity for exit with error code.',
38
+ fail_level: ['Minimum severity (A/R/C/W/E/F) for exit',
39
+ 'with error code.'],
38
40
  show_cops: ['Shows the given cops, or all cops by',
39
41
  'default, and their configurations for the',
40
42
  'current directory.'],
@@ -62,9 +64,6 @@ module RuboCop
62
64
  end
63
65
 
64
66
  def parse(args)
65
- ignore_dropped_options(args)
66
- convert_deprecated_options(args)
67
-
68
67
  define_options(args).parse!(args)
69
68
 
70
69
  validate_compatibility
@@ -72,6 +71,19 @@ module RuboCop
72
71
  [@options, args]
73
72
  end
74
73
 
74
+ # Cop name validation must be done later than option parsing, so it's not
75
+ # called from within this class.
76
+ def self.validate_cop_list(names)
77
+ return unless names
78
+
79
+ namespaces = Cop::Cop.all.types.map { |t| t.to_s.capitalize }
80
+ names.each do |name|
81
+ next if Cop::Cop.all.any? { |c| c.cop_name == name } ||
82
+ namespaces.include?(name)
83
+ fail ArgumentError, "Unrecognized cop or namespace: #{name}."
84
+ end
85
+ end
86
+
75
87
  private
76
88
 
77
89
  def define_options(args)
@@ -96,13 +108,17 @@ module RuboCop
96
108
  end
97
109
 
98
110
  def add_only_options(opts)
99
- option(opts, '--only [COP1,COP2,...]') do |list|
100
- @options[:only] = list.split(',').map do |c|
101
- Cop::Cop.qualified_cop_name(c, '--only option')
111
+ add_cop_selection_csv_option('except', opts)
112
+ add_cop_selection_csv_option('only', opts)
113
+ option(opts, '--only-guide-cops')
114
+ end
115
+
116
+ def add_cop_selection_csv_option(option, opts)
117
+ option(opts, "--#{option} [COP1,COP2,...]") do |list|
118
+ @options[:"#{option}"] = list.split(',').map do |c|
119
+ Cop::Cop.qualified_cop_name(c, "--#{option} option")
102
120
  end
103
121
  end
104
-
105
- option(opts, '--only-guide-cops')
106
122
  end
107
123
 
108
124
  def add_configuration_options(opts, args)
@@ -131,9 +147,10 @@ module RuboCop
131
147
  end
132
148
 
133
149
  def add_severity_option(opts)
150
+ table = RuboCop::Cop::Severity::CODE_TABLE.merge(A: :autocorrect)
134
151
  option(opts, '--fail-level SEVERITY',
135
- RuboCop::Cop::Severity::NAMES,
136
- RuboCop::Cop::Severity::CODE_TABLE) do |severity|
152
+ RuboCop::Cop::Severity::NAMES + [:autocorrect],
153
+ table) do |severity|
137
154
  @options[:fail_level] = severity
138
155
  end
139
156
  end
@@ -177,36 +194,6 @@ module RuboCop
177
194
  long_opt[2..-1].sub(/ .*/, '').gsub(/-/, '_').to_sym
178
195
  end
179
196
 
180
- def ignore_dropped_options(args)
181
- # Currently we don't make -s/--silent option raise error
182
- # since those are mostly used by external tools.
183
- rejected = args.reject! { |a| %w(-s --silent).include?(a) }
184
- return unless rejected
185
-
186
- warn '-s/--silent options is dropped. ' \
187
- '`emacs` and `files` formatters no longer display summary.'
188
- end
189
-
190
- def convert_deprecated_options(args)
191
- args.map! do |arg|
192
- case arg
193
- when '-e', '--emacs'
194
- deprecate("#{arg} option", '--format emacs', '1.0.0')
195
- %w(--format emacs)
196
- else
197
- arg
198
- end
199
- end.flatten!
200
- end
201
-
202
- def deprecate(subject, alternative = nil, version = nil)
203
- message = "#{subject} is deprecated"
204
- message << " and will be removed in RuboCop #{version}" if version
205
- message << '.'
206
- message << " Please use #{alternative} instead." if alternative
207
- warn message
208
- end
209
-
210
197
  def validate_auto_gen_config_option(args)
211
198
  return unless args.any?
212
199
 
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'astrolabe/builder'
4
+ require 'digest/md5'
4
5
 
5
6
  module RuboCop
6
7
  # ProcessedSource contains objects which are generated by Parser
@@ -48,6 +49,11 @@ module RuboCop
48
49
  @diagnostics.none? { |d| [:error, :fatal].include?(d.level) }
49
50
  end
50
51
 
52
+ # Raw source checksum for tracking infinite loops.
53
+ def checksum
54
+ Digest::MD5.hexdigest(@raw_source)
55
+ end
56
+
51
57
  private
52
58
 
53
59
  def parse(source)
@@ -31,7 +31,7 @@ module RuboCop
31
31
  end
32
32
  end
33
33
 
34
- setup_subtasks(name)
34
+ setup_subtasks(name, *args, &task_block)
35
35
  end
36
36
 
37
37
  def run_main_task(verbose)
@@ -73,11 +73,14 @@ module RuboCop
73
73
  @formatters = [RuboCop::Options::DEFAULT_FORMATTER]
74
74
  end
75
75
 
76
- def setup_subtasks(name)
76
+ def setup_subtasks(name, *args, &task_block)
77
77
  namespace name do
78
78
  desc 'Auto-correct RuboCop offenses'
79
79
 
80
- task :auto_correct do
80
+ task(:auto_correct, *args) do |_, task_args|
81
+ if task_block
82
+ task_block.call(*[self, task_args].slice(0, task_block.arity))
83
+ end
81
84
  options = full_options.unshift('--auto-correct')
82
85
  run_cli(verbose, options)
83
86
  end
@@ -4,6 +4,17 @@ module RuboCop
4
4
  # This class handles the processing of files, which includes dealing with
5
5
  # formatters and letting cops inspect the files.
6
6
  class Runner
7
+ # An exception indicating that the inspection loop got stuck correcting
8
+ # offenses back and forth.
9
+ class InfiniteCorrectionLoop < Exception
10
+ attr_reader :offenses
11
+
12
+ def initialize(path, offenses)
13
+ super "Infinite loop detected in #{path}."
14
+ @offenses = offenses
15
+ end
16
+ end
17
+
7
18
  attr_reader :errors, :aborting
8
19
  alias_method :aborting?, :aborting
9
20
 
@@ -30,10 +41,10 @@ module RuboCop
30
41
  break if @options[:fail_fast] && !all_passed
31
42
  end
32
43
 
44
+ all_passed
45
+ ensure
33
46
  formatter_set.finished(inspected_files.freeze)
34
47
  formatter_set.close_output_files
35
-
36
- all_passed
37
48
  end
38
49
 
39
50
  def abort
@@ -58,34 +69,57 @@ module RuboCop
58
69
  offenses = do_inspection_loop(file, processed_source)
59
70
 
60
71
  formatter_set.file_finished(file, offenses.compact.sort.freeze)
72
+
61
73
  offenses
74
+ rescue InfiniteCorrectionLoop => e
75
+ formatter_set.file_finished(file, e.offenses.compact.sort.freeze)
76
+ raise
62
77
  end
63
78
 
64
79
  def do_inspection_loop(file, processed_source)
65
80
  offenses = []
66
81
 
82
+ # Keep track of the state of the source. If a cop modifies the source
83
+ # and another cop undoes it producing identical source we have an
84
+ # infinite loop.
85
+ @processed_sources = []
86
+
67
87
  # When running with --auto-correct, we need to inspect the file (which
68
88
  # includes writing a corrected version of it) until no more corrections
69
89
  # are made. This is because automatic corrections can introduce new
70
90
  # offenses. In the normal case the loop is only executed once.
71
91
  loop do
92
+ check_for_infinite_loop(processed_source, offenses)
93
+
72
94
  # The offenses that couldn't be corrected will be found again so we
73
95
  # only keep the corrected ones in order to avoid duplicate reporting.
74
96
  offenses.select!(&:corrected?)
75
-
76
97
  new_offenses, updated_source_file = inspect_file(processed_source)
77
98
  offenses.concat(new_offenses).uniq!
78
- break unless updated_source_file
79
99
 
80
100
  # We have to reprocess the source to pickup the changes. Since the
81
101
  # change could (theoretically) introduce parsing errors, we break the
82
102
  # loop if we find any.
103
+ break unless updated_source_file
104
+
83
105
  processed_source = ProcessedSource.from_file(file)
84
106
  end
85
107
 
86
108
  offenses
87
109
  end
88
110
 
111
+ # Check whether a run created source identical to a previous run, which
112
+ # means that we definitely have an infinite loop.
113
+ def check_for_infinite_loop(processed_source, offenses)
114
+ checksum = processed_source.checksum
115
+
116
+ if @processed_sources.include?(checksum)
117
+ fail InfiniteCorrectionLoop.new(processed_source.path, offenses)
118
+ end
119
+
120
+ @processed_sources << checksum
121
+ end
122
+
89
123
  def inspect_file(processed_source)
90
124
  config = @config_store.for(processed_source.path)
91
125
  team = Cop::Team.new(mobilized_cop_classes(config), config, @options)
@@ -99,20 +133,31 @@ module RuboCop
99
133
  @mobilized_cop_classes[config.object_id] ||= begin
100
134
  cop_classes = Cop::Cop.all
101
135
 
102
- if @options[:only]
103
- validate_only_option
136
+ [:only, :except].each { |opt| Options.validate_cop_list(@options[opt]) }
104
137
 
138
+ if @options[:only]
105
139
  cop_classes.select! do |c|
106
- @options[:only].include?(c.cop_name) || @options[:lint] && c.lint?
140
+ cop_match?(c, @options[:only]) || @options[:lint] && c.lint?
107
141
  end
108
142
  else
109
143
  filter_cop_classes(cop_classes, config)
110
144
  end
111
145
 
146
+ cop_classes.reject! { |c| cop_match?(c, @options[:except]) }
147
+
112
148
  cop_classes
113
149
  end
114
150
  end
115
151
 
152
+ # Returns true if the cop name or the cop namespace matches any of the
153
+ # given names.
154
+ def cop_match?(cop, given_names)
155
+ return false unless given_names
156
+
157
+ given_names.include?(cop.cop_name) ||
158
+ given_names.include?(cop.cop_type.to_s.capitalize)
159
+ end
160
+
116
161
  def filter_cop_classes(cop_classes, config)
117
162
  # use only cops that link to a style guide if requested
118
163
  if style_guide_cops_only?(config)
@@ -126,13 +171,6 @@ module RuboCop
126
171
  cop_classes.select!(&:lint?) if @options[:lint]
127
172
  end
128
173
 
129
- def validate_only_option
130
- @options[:only].each do |cop_to_run|
131
- next unless Cop::Cop.all.none? { |c| c.cop_name == cop_to_run }
132
- fail ArgumentError, "Unrecognized cop name: #{cop_to_run}."
133
- end
134
- end
135
-
136
174
  def run_rails_cops?(config)
137
175
  @options[:rails] || config['AllCops']['RunRailsCops']
138
176
  end
@@ -157,7 +195,9 @@ module RuboCop
157
195
  end
158
196
 
159
197
  def considered_failure?(offense)
160
- !offense.corrected? && offense.severity >= minimum_severity_to_fail
198
+ # For :autocorrect level, any offense - corrected or not - is a failure.
199
+ @options[:fail_level] == :autocorrect ||
200
+ !offense.corrected? && offense.severity >= minimum_severity_to_fail
161
201
  end
162
202
 
163
203
  def minimum_severity_to_fail