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
@@ -23,54 +23,60 @@ module RuboCop
23
23
  include ConfigurableEnforcedStyle
24
24
  include MinBodyLength
25
25
 
26
- MSG = 'Use `next` to skip iteration.'
27
- ENUMERATORS = [:collect, :detect, :downto, :each, :find, :find_all,
28
- :inject, :loop, :map!, :map, :reduce, :reverse_each,
29
- :select, :times, :upto]
26
+ MSG = 'Use `next` to skip iteration.'.freeze
27
+ EXIT_TYPES = [:break, :return].freeze
28
+ EACH_ = 'each_'.freeze
29
+ ENUMERATORS = [:collect, :collect_concat, :detect, :downto, :each,
30
+ :find, :find_all, :find_index, :inject, :loop, :map!,
31
+ :map, :reduce, :reject, :reject!, :reverse_each, :select,
32
+ :select!, :times, :upto].freeze
30
33
 
31
34
  def on_block(node)
32
35
  block_owner, _, body = *node
33
- return unless block_owner.type == :send
34
- return if body.nil?
36
+ return unless block_owner.send_type?
37
+ return unless body && ends_with_condition?(body)
35
38
 
36
39
  _, method_name = *block_owner
37
40
  return unless enumerator?(method_name)
38
- return unless ends_with_condition?(body)
39
41
 
40
- add_offense(block_owner, :selector, MSG)
42
+ offense_node = offense_node(body)
43
+ add_offense(offense_node, offense_location(offense_node), MSG)
41
44
  end
42
45
 
43
46
  def on_while(node)
44
47
  _, body = *node
45
48
  return unless body && ends_with_condition?(body)
46
49
 
47
- add_offense(node, :keyword, MSG)
50
+ offense_node = offense_node(body)
51
+ add_offense(offense_node, offense_location(offense_node), MSG)
48
52
  end
49
53
  alias_method :on_until, :on_while
50
54
 
51
55
  def on_for(node)
52
56
  _, _, body = *node
53
- return unless ends_with_condition?(body)
57
+ return unless body && ends_with_condition?(body)
54
58
 
55
- add_offense(node, :keyword, MSG)
59
+ offense_node = offense_node(body)
60
+ add_offense(offense_node, offense_location(offense_node), MSG)
56
61
  end
57
62
 
58
63
  private
59
64
 
60
65
  def enumerator?(method_name)
61
- ENUMERATORS.include?(method_name) || /\Aeach_/.match(method_name)
66
+ ENUMERATORS.include?(method_name) ||
67
+ method_name.to_s.start_with?(EACH_)
62
68
  end
63
69
 
64
70
  def ends_with_condition?(body)
65
71
  return true if simple_if_without_break?(body)
66
72
 
67
- body.type == :begin && simple_if_without_break?(body.children.last)
73
+ body.begin_type? && simple_if_without_break?(body.children.last)
68
74
  end
69
75
 
70
76
  def simple_if_without_break?(node)
77
+ return false unless node.if_type?
71
78
  return false if ternary_op?(node)
72
79
  return false if if_else?(node)
73
- return false unless node.type == :if
74
80
  return false if style == :skip_modifier_ifs && modifier_if?(node)
75
81
  return false if !modifier_if?(node) && !min_body_length?(node)
76
82
 
@@ -79,7 +85,36 @@ module RuboCop
79
85
  _conditional, if_body, _else_body = *node
80
86
  return true unless if_body
81
87
 
82
- ![:break, :return].include?(if_body.type)
88
+ !EXIT_TYPES.include?(if_body.type)
89
+ end
90
+
91
+ def offense_node(body)
92
+ *_, condition = *body
93
+ (condition && condition.if_type?) ? condition : body
94
+ end
95
+
96
+ def offense_location(offense_node)
97
+ condition_expression, = *offense_node
98
+ offense_begin_pos = offense_node.loc.expression.begin
99
+ offense_begin_pos.join(condition_expression.loc.expression)
100
+ end
101
+
102
+ def autocorrect(node)
103
+ lambda do |corrector|
104
+ cond, if_body, else_body = *node
105
+ if if_body.nil?
106
+ opposite_kw = 'if'
107
+ body = else_body
108
+ else
109
+ opposite_kw = 'unless'
110
+ body = if_body
111
+ end
112
+ next_code = 'next ' << opposite_kw << ' ' <<
113
+ cond.loc.expression.source << "\n"
114
+ corrector.insert_before(node.loc.expression, next_code)
115
+ corrector.replace(node.loc.expression,
116
+ body.loc.expression.source)
117
+ end
83
118
  end
84
119
  end
85
120
  end
@@ -84,15 +84,19 @@ module RuboCop
84
84
  end
85
85
 
86
86
  def autocorrect_comparison(node)
87
+ expr = node.loc.expression.source
88
+
89
+ new_code =
90
+ if include_semantic_changes?
91
+ expr.sub(/\s*!=\s*nil/, '')
92
+ else
93
+ expr.sub(/^(\S*)\s*!=\s*nil/, '!\1.nil?')
94
+ end
95
+
96
+ return if expr == new_code
97
+
87
98
  lambda do |corrector|
88
- expr = node.loc.expression
89
- new_code =
90
- if include_semantic_changes?
91
- expr.source.sub(/\s*!=\s*nil/, '')
92
- else
93
- expr.source.sub(/^(\S*)\s*!=\s*nil/, '!\1.nil?')
94
- end
95
- corrector.replace(expr, new_code)
99
+ corrector.replace(node.loc.expression, new_code)
96
100
  end
97
101
  end
98
102
 
@@ -8,12 +8,16 @@ module RuboCop
8
8
  class OneLineConditional < Cop
9
9
  include OnNormalIfUnless
10
10
 
11
- MSG = 'Favor the ternary operator (?:) ' \
12
- 'over if/then/else/end constructs.'
11
+ MSG = 'Favor the ternary operator (`?:`) ' \
12
+ 'over `%s/then/else/end` constructs.'
13
13
 
14
14
  def on_normal_if_unless(node)
15
- return if node.loc.expression.source.include?("\n")
16
- add_offense(node, :expression, MSG)
15
+ exp = node.loc.expression.source
16
+ return if exp.include?("\n")
17
+ return unless node.loc.respond_to?(:else) && node.loc.else
18
+ condition = exp.include?('if') ? 'if' : 'unless'
19
+
20
+ add_offense(node, :expression, format(MSG, condition))
17
21
  end
18
22
  end
19
23
  end
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for options hashes and discourages them if the
7
+ # current Ruby version supports keyword arguments.
8
+ #
9
+ # @example
10
+ # Instead of:
11
+ #
12
+ # def fry(options = {})
13
+ # temperature = options.fetch(:temperature, 300)
14
+ # ...
15
+ # end
16
+ #
17
+ # Prefer:
18
+ #
19
+ # def fry(temperature: 300)
20
+ # ...
21
+ # end
22
+ class OptionHash < Cop
23
+ MSG = 'Prefer keyword arguments to options hashes.'
24
+
25
+ def on_args(node)
26
+ return unless supports_keyword_arguments?
27
+
28
+ *_but_last, last_arg = *node
29
+
30
+ # asserting that there was an argument at all
31
+ return unless last_arg
32
+
33
+ # asserting last argument is an optional argument
34
+ return unless last_arg.optarg_type?
35
+
36
+ arg, default_value = *last_arg
37
+
38
+ # asserting default value is a hash
39
+ return unless default_value.hash_type?
40
+
41
+ # asserting default value is empty hash
42
+ *key_value_pairs = *default_value
43
+ return unless key_value_pairs.empty?
44
+
45
+ # Check for suspicious argument names
46
+ return unless name_in_suspicious_param_names?(arg)
47
+
48
+ add_offense(last_arg, :expression, MSG)
49
+ end
50
+
51
+ private
52
+
53
+ def supports_keyword_arguments?
54
+ RUBY_VERSION >= '2.0.0'
55
+ end
56
+
57
+ def name_in_suspicious_param_names?(arg_name)
58
+ cop_config.key?('SuspiciousParamNames') &&
59
+ cop_config['SuspiciousParamNames'].include?(arg_name.to_s)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for optional arguments to methods
7
+ # that do not come at the end of the argument list
8
+ #
9
+ # @example
10
+ # # bad
11
+ # def foo(a = 1, b, c)
12
+ # end
13
+ #
14
+ # # good
15
+ # def baz(a, b, c = 1)
16
+ # end
17
+ #
18
+ # def foobar(a = 1, b = 2, c = 3)
19
+ # end
20
+ class OptionalArguments < Cop
21
+ MSG =
22
+ 'Optional arguments should appear at the end of the argument list.'
23
+
24
+ def on_def(node)
25
+ _method, arguments, = *node
26
+ arguments = *arguments
27
+ optarg_positions = []
28
+ arg_positions = []
29
+
30
+ arguments.each_with_index do |argument, index|
31
+ optarg_positions << index if argument.optarg_type?
32
+ arg_positions << index if argument.arg_type?
33
+ end
34
+
35
+ return if optarg_positions.empty? || arg_positions.empty?
36
+
37
+ optarg_positions.each do |optarg_position|
38
+ # there can only be one group of optional arguments
39
+ break if optarg_position > arg_positions.max
40
+ argument = arguments[optarg_position]
41
+ arg, = *argument
42
+
43
+ add_offense(argument, :expression, format(MSG, arg))
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,218 @@
1
+ # encoding: utf-8
2
+
3
+ require 'tsort'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Style
8
+ # Checks for simple usages of parallel assignment.
9
+ # This will only complain when the number of variables
10
+ # being assigned matched the number of assigning variables.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # a, b, c = 1, 2, 3
15
+ # a, b, c = [1, 2, 3]
16
+ #
17
+ # # good
18
+ # one, two = *foo
19
+ # a, b = foo()
20
+ # a, b = b, a
21
+ #
22
+ # a = 1
23
+ # b = 2
24
+ # c = 3
25
+ class ParallelAssignment < Cop
26
+ include IfNode
27
+
28
+ MSG = 'Do not use parallel assignment.'
29
+
30
+ def on_masgn(node)
31
+ left, right = *node
32
+ left_elements = *left
33
+ right_elements = [*right].compact # edge case for one constant
34
+
35
+ # only complain when the number of variables matches
36
+ return if left_elements.size != right_elements.size
37
+
38
+ # account for edge cases using one variable with a comma
39
+ return if left_elements.size == 1
40
+
41
+ # account for edge case of Constant::CONSTANT
42
+ return unless right.array_type?
43
+
44
+ # allow mass assignment as the return of a method call
45
+ return if right.block_type? || right.send_type?
46
+
47
+ # allow mass assignment when using splat
48
+ return if (left_elements + right_elements).any?(&:splat_type?)
49
+
50
+ order = find_valid_order(left_elements, right_elements)
51
+ # For `a, b = b, a` or similar, there is no valid order
52
+ return if order.nil?
53
+
54
+ add_offense(node, :expression)
55
+ end
56
+
57
+ def autocorrect(node)
58
+ lambda do |corrector|
59
+ left, right = *node
60
+ left_elements = *left
61
+ right_elements = [*right].compact
62
+ order = find_valid_order(left_elements, right_elements)
63
+
64
+ assignment_corrector =
65
+ if modifier_statement?(node.parent)
66
+ ModifierCorrector.new(node, config, order)
67
+ elsif rescue_modifier?(node.parent)
68
+ RescueCorrector.new(node, config, order)
69
+ else
70
+ GenericCorrector.new(node, config, order)
71
+ end
72
+
73
+ corrector.replace(assignment_corrector.correction_range,
74
+ assignment_corrector.correction)
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ def find_valid_order(left_elements, right_elements)
81
+ # arrange left_elements in an order such that no corresponding right
82
+ # element refers to a left element earlier in the sequence
83
+ # this can be done using an algorithm called a "topological sort"
84
+ # fortunately for us, Ruby's stdlib contains an implementation
85
+ assignments = left_elements.zip(right_elements)
86
+
87
+ begin
88
+ AssignmentSorter.new(assignments).tsort
89
+ rescue TSort::Cyclic
90
+ nil
91
+ end
92
+ end
93
+
94
+ # Helper class necessitated by silly design of TSort prior to Ruby 2.1
95
+ # Newer versions have a better API, but that doesn't help us
96
+ class AssignmentSorter
97
+ include TSort
98
+ extend RuboCop::NodePattern::Macros
99
+
100
+ def_node_matcher :var_name, '{(casgn _ $_) (_ $_)}'
101
+ def_node_search :uses_var?, '{({lvar ivar cvar gvar} %) (const _ %)}'
102
+
103
+ def initialize(assignments)
104
+ @assignments = assignments
105
+ end
106
+
107
+ def tsort_each_node
108
+ @assignments.each { |a| yield a }
109
+ end
110
+
111
+ def tsort_each_child(assignment)
112
+ # yield all the assignments which must come after `assignment`
113
+ # (due to dependencies on the previous value of the assigned var)
114
+ my_lhs, _my_rhs = *assignment
115
+
116
+ @assignments.each do |other|
117
+ _other_lhs, other_rhs = *other
118
+ yield other if uses_var?(other_rhs, var_name(my_lhs))
119
+ end
120
+ end
121
+ end
122
+
123
+ def modifier_statement?(node)
124
+ node &&
125
+ ((node.if_type? && modifier_if?(node)) ||
126
+ ((node.while_type? || node.until_type?) && modifier_while?(node)))
127
+ end
128
+
129
+ def modifier_while?(node)
130
+ node.loc.respond_to?(:keyword) &&
131
+ %w(while until).include?(node.loc.keyword.source) &&
132
+ node.loc.respond_to?(:end) && node.loc.end.nil?
133
+ end
134
+
135
+ def rescue_modifier?(node)
136
+ node &&
137
+ node.rescue_type? &&
138
+ (node.parent.nil? || !node.parent.kwbegin_type?)
139
+ end
140
+
141
+ # An internal class for correcting parallel assignment
142
+ class GenericCorrector
143
+ include AutocorrectAlignment
144
+
145
+ attr_reader :config, :node, :correction, :correction_range
146
+
147
+ def initialize(node, config, new_elements)
148
+ @node = node
149
+ @config = config
150
+ @new_elements = new_elements
151
+ end
152
+
153
+ def correction
154
+ "#{assignment.join("\n#{offset(node)}")}"
155
+ end
156
+
157
+ def correction_range
158
+ node.loc.expression
159
+ end
160
+
161
+ protected
162
+
163
+ def assignment
164
+ @new_elements.map do |lhs, rhs|
165
+ "#{lhs.loc.expression.source} = #{rhs.loc.expression.source}"
166
+ end
167
+ end
168
+
169
+ private
170
+
171
+ def extract_sources(node)
172
+ node.children.map { |child| child.loc.expression.source }
173
+ end
174
+ end
175
+
176
+ # An internal class for correcting parallel assignment
177
+ # protected by rescue
178
+ class RescueCorrector < GenericCorrector
179
+ def correction
180
+ _node, rescue_clause = *node.parent
181
+ _, _, rescue_result = *rescue_clause
182
+
183
+ "begin\n" <<
184
+ indentation(node) << assignment.join("\n#{indentation(node)}") <<
185
+ "\n#{offset(node)}rescue\n" <<
186
+ indentation(node) << rescue_result.loc.expression.source <<
187
+ "\n#{offset(node)}end"
188
+ end
189
+
190
+ def correction_range
191
+ node.parent.loc.expression
192
+ end
193
+ end
194
+
195
+ # An internal class for correcting parallel assignment
196
+ # guarded by if, unless, while, or until
197
+ class ModifierCorrector < GenericCorrector
198
+ def correction
199
+ parent = node.parent
200
+
201
+ modifier_range =
202
+ Parser::Source::Range.new(parent.loc.expression.source_buffer,
203
+ parent.loc.keyword.begin_pos,
204
+ parent.loc.expression.end_pos)
205
+
206
+ "#{modifier_range.source}\n" <<
207
+ indentation(node) << assignment.join("\n#{indentation(node)}") <<
208
+ "\n#{offset(node)}end"
209
+ end
210
+
211
+ def correction_range
212
+ node.parent.loc.expression
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
@@ -33,7 +33,7 @@ module RuboCop
33
33
  delimiters = preferred_delimiters(type)
34
34
 
35
35
  "`#{type}`-literals should be delimited by " \
36
- "`#{delimiters[0]}` and `#{delimiters[1]}`"
36
+ "`#{delimiters[0]}` and `#{delimiters[1]}`."
37
37
  end
38
38
 
39
39
  private
@@ -43,20 +43,9 @@ module RuboCop
43
43
 
44
44
  opening_delimiter, closing_delimiter = preferred_delimiters(type)
45
45
 
46
- first_child, *_middle, last_child = *node
47
- opening_newline = new_line(node.loc.begin, first_child)
48
- expression_indentation = leading_whitespace(first_child, :expression)
49
- closing_newline = new_line(node.loc.end, last_child)
50
- closing_indentation = leading_whitespace(node, :end)
51
- expression, reg_opt = *contents(node)
52
-
53
- corrected_source =
54
- type + opening_delimiter + opening_newline +
55
- expression_indentation + expression + closing_newline +
56
- closing_indentation + closing_delimiter + reg_opt
57
-
58
46
  lambda do |corrector|
59
- corrector.replace(node.loc.expression, corrected_source)
47
+ corrector.replace(node.loc.begin, "#{type}#{opening_delimiter}")
48
+ corrector.replace(node.loc.end, closing_delimiter)
60
49
  end
61
50
  end
62
51
 
@@ -72,49 +61,6 @@ module RuboCop
72
61
  cop_config['PreferredDelimiters'][type].split(//)
73
62
  end
74
63
 
75
- def leading_whitespace(object, part)
76
- case object
77
- when String
78
- ''
79
- when NilClass
80
- ''
81
- when Parser::AST::Node
82
- part_range = object.loc.send(part)
83
- left_of_part = part_range.source_line[0...part_range.column]
84
- /^(\s*)$/.match(left_of_part) ? left_of_part : ''
85
- else
86
- fail "Unsupported object #{object}"
87
- end
88
- end
89
-
90
- def contents(node)
91
- first_child, *middle, last_child = *node
92
- last_child ||= first_child
93
- if node.type == :regexp
94
- *_, next_to_last_child = *middle
95
- next_to_last_child ||= first_child
96
- expression = source(node, first_child, next_to_last_child)
97
- reg_opt = last_child.loc.expression.source
98
- else
99
- expression = if first_child.is_a?(Parser::AST::Node)
100
- source(node, first_child, last_child)
101
- else
102
- first_child.to_s
103
- end
104
- reg_opt = ''
105
- end
106
-
107
- [expression, reg_opt]
108
- end
109
-
110
- def source(node, begin_node, end_node)
111
- Parser::Source::Range.new(
112
- node.loc.expression.source_buffer,
113
- begin_node.loc.expression.begin_pos,
114
- end_node.loc.expression.end_pos
115
- ).source
116
- end
117
-
118
64
  def uses_preferred_delimiter?(node, type)
119
65
  preferred_delimiters(type)[0] == begin_source(node)[-1]
120
66
  end
@@ -133,15 +79,6 @@ module RuboCop
133
79
  node.loc.expression.source
134
80
  end
135
81
  end
136
-
137
- def new_line(range, child_node)
138
- same_line?(range, child_node) ? '' : "\n"
139
- end
140
-
141
- def same_line?(range, child_node)
142
- !child_node.is_a?(Parser::AST::Node) ||
143
- range.begin.line == child_node.loc.line
144
- end
145
82
  end
146
83
  end
147
84
  end
@@ -23,10 +23,11 @@ module RuboCop
23
23
  private
24
24
 
25
25
  def on_method_def(node, method_name, _args, _body)
26
- predicate_prefices.each do |prefix|
26
+ predicate_prefixes.each do |prefix|
27
27
  method_name = method_name.to_s
28
28
  next unless method_name.start_with?(prefix)
29
29
  next if method_name == expected_name(method_name, prefix)
30
+ next if predicate_whitelist.include?(method_name)
30
31
  add_offense(
31
32
  node,
32
33
  :name,
@@ -53,9 +54,13 @@ module RuboCop
53
54
  cop_config['NamePrefixBlacklist']
54
55
  end
55
56
 
56
- def predicate_prefices
57
+ def predicate_prefixes
57
58
  cop_config['NamePrefix']
58
59
  end
60
+
61
+ def predicate_whitelist
62
+ cop_config['NameWhitelist']
63
+ end
59
64
  end
60
65
  end
61
66
  end
@@ -37,19 +37,8 @@ module RuboCop
37
37
 
38
38
  def autocorrect(node)
39
39
  lambda do |corrector|
40
- child = node.children.first
41
-
42
- begin_indent = node.loc.column
43
- child_indent = child.loc.column
44
-
45
- indent_diff = child_indent - begin_indent
46
-
47
- corrector.replace(
48
- range_with_surrounding_space(node.loc.expression),
49
- range_with_surrounding_space(
50
- child.loc.expression
51
- ).source.gsub(/^[ \t]{#{indent_diff}}/, '')
52
- )
40
+ corrector.remove(node.loc.begin)
41
+ corrector.remove(node.loc.end)
53
42
  end
54
43
  end
55
44
  end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop check for uses of Object#freeze on immutable objects.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # CONST = 1.freeze
11
+ #
12
+ # # good
13
+ # CONST = 1
14
+ class RedundantFreeze < Cop
15
+ MSG = 'Freezing immutable objects is pointless.'.freeze
16
+
17
+ TARGET_NODES = [:int, :float, :sym].freeze
18
+
19
+ def on_send(node)
20
+ receiver, method_name, *args = *node
21
+
22
+ return unless receiver && TARGET_NODES.include?(receiver.type)
23
+ return unless method_name == :freeze && args.empty?
24
+
25
+ add_offense(node, :expression)
26
+ end
27
+
28
+ def autocorrect(node)
29
+ lambda do |corrector|
30
+ corrector.remove(node.loc.dot)
31
+ corrector.remove(node.loc.selector)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end