rubocop 0.93.1 → 1.6.1

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 (199) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -16
  3. data/config/default.yml +276 -82
  4. data/config/obsoletion.yml +196 -0
  5. data/exe/rubocop +1 -1
  6. data/lib/rubocop.rb +31 -2
  7. data/lib/rubocop/cli.rb +5 -1
  8. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  9. data/lib/rubocop/cli/command/execute_runner.rb +26 -11
  10. data/lib/rubocop/cli/command/suggest_extensions.rb +80 -0
  11. data/lib/rubocop/cli/command/version.rb +1 -1
  12. data/lib/rubocop/comment_config.rb +1 -1
  13. data/lib/rubocop/config.rb +4 -0
  14. data/lib/rubocop/config_loader.rb +34 -8
  15. data/lib/rubocop/config_loader_resolver.rb +12 -6
  16. data/lib/rubocop/config_obsoletion.rb +65 -247
  17. data/lib/rubocop/config_obsoletion/changed_enforced_styles.rb +33 -0
  18. data/lib/rubocop/config_obsoletion/changed_parameter.rb +21 -0
  19. data/lib/rubocop/config_obsoletion/cop_rule.rb +34 -0
  20. data/lib/rubocop/config_obsoletion/extracted_cop.rb +44 -0
  21. data/lib/rubocop/config_obsoletion/parameter_rule.rb +44 -0
  22. data/lib/rubocop/config_obsoletion/removed_cop.rb +41 -0
  23. data/lib/rubocop/config_obsoletion/renamed_cop.rb +34 -0
  24. data/lib/rubocop/config_obsoletion/rule.rb +41 -0
  25. data/lib/rubocop/config_obsoletion/split_cop.rb +27 -0
  26. data/lib/rubocop/config_regeneration.rb +1 -1
  27. data/lib/rubocop/config_validator.rb +25 -10
  28. data/lib/rubocop/cop/autocorrect_logic.rb +21 -6
  29. data/lib/rubocop/cop/badge.rb +9 -24
  30. data/lib/rubocop/cop/base.rb +33 -16
  31. data/lib/rubocop/cop/bundler/duplicated_gem.rb +26 -6
  32. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  33. data/lib/rubocop/cop/commissioner.rb +37 -23
  34. data/lib/rubocop/cop/cop.rb +2 -2
  35. data/lib/rubocop/cop/corrector.rb +3 -1
  36. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  37. data/lib/rubocop/cop/correctors/string_literal_corrector.rb +6 -8
  38. data/lib/rubocop/cop/force.rb +1 -1
  39. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
  40. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
  41. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
  42. data/lib/rubocop/cop/generator.rb +3 -10
  43. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  44. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +1 -1
  45. data/lib/rubocop/cop/layout/block_alignment.rb +3 -4
  46. data/lib/rubocop/cop/layout/class_structure.rb +22 -3
  47. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  48. data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
  49. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +80 -10
  50. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  51. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +6 -1
  52. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
  53. data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
  54. data/lib/rubocop/cop/layout/end_of_line.rb +5 -5
  55. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
  56. data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
  57. data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
  58. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +12 -0
  59. data/lib/rubocop/cop/layout/line_length.rb +10 -13
  60. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +7 -3
  61. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
  62. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  63. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  64. data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
  65. data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
  66. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
  67. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +26 -2
  68. data/lib/rubocop/cop/lint/debugger.rb +17 -28
  69. data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
  70. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
  71. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
  72. data/lib/rubocop/cop/lint/else_layout.rb +29 -3
  73. data/lib/rubocop/cop/lint/empty_block.rb +82 -0
  74. data/lib/rubocop/cop/lint/empty_class.rb +93 -0
  75. data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
  76. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  77. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +39 -7
  78. data/lib/rubocop/cop/lint/loop.rb +4 -4
  79. data/lib/rubocop/cop/lint/missing_super.rb +7 -4
  80. data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
  81. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
  82. data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
  83. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
  84. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
  85. data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
  86. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -0
  87. data/lib/rubocop/cop/lint/to_enum_arguments.rb +86 -0
  88. data/lib/rubocop/cop/lint/to_json.rb +1 -1
  89. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +85 -0
  90. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +199 -0
  91. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  92. data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
  93. data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
  94. data/lib/rubocop/cop/metrics/abc_size.rb +25 -1
  95. data/lib/rubocop/cop/metrics/block_length.rb +13 -7
  96. data/lib/rubocop/cop/metrics/method_length.rb +7 -2
  97. data/lib/rubocop/cop/metrics/parameter_lists.rb +68 -2
  98. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +20 -10
  99. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +146 -0
  100. data/lib/rubocop/cop/metrics/utils/repeated_csend_discount.rb +6 -1
  101. data/lib/rubocop/cop/migration/department_name.rb +1 -1
  102. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  103. data/lib/rubocop/cop/mixin/configurable_numbering.rb +4 -3
  104. data/lib/rubocop/cop/mixin/enforce_superclass.rb +9 -1
  105. data/lib/rubocop/cop/mixin/ignored_methods.rb +36 -3
  106. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  107. data/lib/rubocop/cop/mixin/method_complexity.rb +6 -0
  108. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  109. data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -4
  110. data/lib/rubocop/cop/mixin/string_help.rb +4 -1
  111. data/lib/rubocop/cop/mixin/visibility_help.rb +1 -3
  112. data/lib/rubocop/cop/naming/accessor_method_name.rb +15 -1
  113. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +12 -2
  114. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
  115. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
  116. data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
  117. data/lib/rubocop/cop/naming/variable_number.rb +100 -8
  118. data/lib/rubocop/cop/offense.rb +3 -3
  119. data/lib/rubocop/cop/security/open.rb +12 -10
  120. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  121. data/lib/rubocop/cop/style/and_or.rb +11 -3
  122. data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
  123. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
  124. data/lib/rubocop/cop/style/case_like_if.rb +0 -4
  125. data/lib/rubocop/cop/style/character_literal.rb +10 -11
  126. data/lib/rubocop/cop/style/class_and_module_children.rb +8 -3
  127. data/lib/rubocop/cop/style/collection_compact.rb +91 -0
  128. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +169 -0
  129. data/lib/rubocop/cop/style/documentation.rb +12 -1
  130. data/lib/rubocop/cop/style/double_negation.rb +6 -1
  131. data/lib/rubocop/cop/style/float_division.rb +44 -1
  132. data/lib/rubocop/cop/style/format_string.rb +8 -3
  133. data/lib/rubocop/cop/style/format_string_token.rb +47 -2
  134. data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
  135. data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
  136. data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
  137. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -3
  138. data/lib/rubocop/cop/style/if_with_semicolon.rb +39 -4
  139. data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
  140. data/lib/rubocop/cop/style/ip_addresses.rb +1 -1
  141. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
  142. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
  143. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +8 -13
  144. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
  145. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -2
  146. data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
  147. data/lib/rubocop/cop/style/multiple_comparison.rb +55 -7
  148. data/lib/rubocop/cop/style/negated_if_else_condition.rb +106 -0
  149. data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
  150. data/lib/rubocop/cop/style/numeric_literals.rb +14 -11
  151. data/lib/rubocop/cop/style/perl_backrefs.rb +86 -9
  152. data/lib/rubocop/cop/style/raise_args.rb +21 -6
  153. data/lib/rubocop/cop/style/redundant_argument.rb +88 -0
  154. data/lib/rubocop/cop/style/redundant_condition.rb +2 -1
  155. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  156. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +7 -1
  157. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +24 -8
  158. data/lib/rubocop/cop/style/redundant_self.rb +3 -0
  159. data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
  160. data/lib/rubocop/cop/style/semicolon.rb +3 -0
  161. data/lib/rubocop/cop/style/single_line_block_params.rb +30 -7
  162. data/lib/rubocop/cop/style/sole_nested_conditional.rb +65 -3
  163. data/lib/rubocop/cop/style/special_global_vars.rb +1 -13
  164. data/lib/rubocop/cop/style/static_class.rb +97 -0
  165. data/lib/rubocop/cop/style/string_concatenation.rb +39 -2
  166. data/lib/rubocop/cop/style/string_literals.rb +14 -8
  167. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +4 -3
  168. data/lib/rubocop/cop/style/swap_values.rb +108 -0
  169. data/lib/rubocop/cop/style/symbol_proc.rb +5 -3
  170. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
  171. data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
  172. data/lib/rubocop/cop/team.rb +6 -1
  173. data/lib/rubocop/cop/util.rb +6 -2
  174. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  175. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  176. data/lib/rubocop/core_ext/hash.rb +20 -0
  177. data/lib/rubocop/ext/regexp_node.rb +36 -11
  178. data/lib/rubocop/ext/regexp_parser.rb +95 -0
  179. data/lib/rubocop/formatter/disabled_config_formatter.rb +21 -6
  180. data/lib/rubocop/formatter/emacs_style_formatter.rb +2 -0
  181. data/lib/rubocop/formatter/formatter_set.rb +2 -1
  182. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
  183. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  184. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -0
  185. data/lib/rubocop/formatter/tap_formatter.rb +2 -0
  186. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  187. data/lib/rubocop/lockfile.rb +40 -0
  188. data/lib/rubocop/magic_comment.rb +2 -2
  189. data/lib/rubocop/options.rb +11 -1
  190. data/lib/rubocop/rake_task.rb +2 -2
  191. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  192. data/lib/rubocop/runner.rb +1 -1
  193. data/lib/rubocop/target_finder.rb +1 -1
  194. data/lib/rubocop/target_ruby.rb +65 -1
  195. data/lib/rubocop/version.rb +56 -6
  196. metadata +50 -9
  197. data/bin/console +0 -10
  198. data/bin/rubocop-profile +0 -32
  199. data/bin/setup +0 -7
@@ -131,7 +131,8 @@ module RuboCop
131
131
  end
132
132
 
133
133
  def without_argument_parentheses_method?(node)
134
- node.send_type? && !node.arguments.empty? && !node.parenthesized?
134
+ node.send_type? &&
135
+ !node.arguments.empty? && !node.parenthesized? && !node.operator_method?
135
136
  end
136
137
  end
137
138
  end
@@ -104,9 +104,13 @@ module RuboCop
104
104
  return offense(begin_node, 'a variable') if node.variable?
105
105
  return offense(begin_node, 'a constant') if node.const_type?
106
106
 
107
+ return offense(begin_node, 'an interpolated expression') if interpolation?(begin_node)
108
+
107
109
  check_send(begin_node, node) if node.call_type?
108
110
  end
109
111
 
112
+ def_node_matcher :interpolation?, '[^begin ^^dstr]'
113
+
110
114
  def check_send(begin_node, node)
111
115
  return check_unary(begin_node, node) if node.unary_operation?
112
116
 
@@ -19,6 +19,12 @@ module RuboCop
19
19
  # # good
20
20
  # r = /\s/
21
21
  #
22
+ # # bad
23
+ # r = %r{/[b]}
24
+ #
25
+ # # good
26
+ # r = %r{/b}
27
+ #
22
28
  # # good
23
29
  # r = /[ab]/
24
30
  class RedundantRegexpCharacterClass < Base
@@ -48,7 +54,7 @@ module RuboCop
48
54
  each_single_element_character_class(node) do |char_class|
49
55
  next unless redundant_single_element_character_class?(node, char_class)
50
56
 
51
- yield node.loc.begin.adjust(begin_pos: 1 + char_class.ts, end_pos: char_class.te)
57
+ yield char_class.loc.body
52
58
  end
53
59
  end
54
60
 
@@ -80,14 +80,30 @@ module RuboCop
80
80
  delimiters.include?(char)
81
81
  end
82
82
 
83
- def each_escape(node)
84
- node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
85
- yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
86
-
87
- if expr.type == :set
88
- char_class_depth + (event == :enter ? 1 : -1)
89
- else
90
- char_class_depth
83
+ if Gem::Version.new(Regexp::Parser::VERSION) >= Gem::Version.new('2.0')
84
+ def each_escape(node)
85
+ node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
86
+ yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
87
+
88
+ if expr.type == :set
89
+ char_class_depth + (event == :enter ? 1 : -1)
90
+ else
91
+ char_class_depth
92
+ end
93
+ end
94
+ end
95
+ # Please remove this `else` branch when support for regexp_parser 1.8 will be dropped.
96
+ # It's for compatibility with regexp_arser 1.8 and will never be maintained.
97
+ else
98
+ def each_escape(node)
99
+ node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
100
+ yield(expr.text[1], expr.start_index, !char_class_depth.zero?) if expr.type == :escape
101
+
102
+ if expr.type == :set
103
+ char_class_depth + (event == :enter ? 1 : -1)
104
+ else
105
+ char_class_depth
106
+ end
91
107
  end
92
108
  end
93
109
  end
@@ -129,6 +129,9 @@ module RuboCop
129
129
  def allowed_send_node?(node)
130
130
  @allowed_send_nodes.include?(node) ||
131
131
  @local_variables_scopes[node].include?(node.method_name) ||
132
+ node.each_ancestor.any? do |ancestor|
133
+ @local_variables_scopes[ancestor].include?(node.method_name)
134
+ end ||
132
135
  KERNEL_METHODS.include?(node.method_name)
133
136
  end
134
137
 
@@ -142,10 +142,22 @@ module RuboCop
142
142
  end
143
143
 
144
144
  def comments(node)
145
- processed_source.each_comment_in_lines(
146
- node.loc.first_line...
147
- node.loc.last_line
148
- ).to_a
145
+ relevant_comment_ranges(node).each.with_object([]) do |range, comments|
146
+ comments.concat(processed_source.each_comment_in_lines(range).to_a)
147
+ end
148
+ end
149
+
150
+ def relevant_comment_ranges(node)
151
+ # Get source lines ranges inside the if node that aren't inside an inner node
152
+ # Comments inside an inner node should remain attached to that node, and not
153
+ # moved.
154
+ begin_pos = node.loc.first_line
155
+ end_pos = node.loc.last_line
156
+
157
+ node.child_nodes.each.with_object([]) do |child, ranges|
158
+ ranges << (begin_pos...child.loc.first_line)
159
+ begin_pos = child.loc.last_line
160
+ end << (begin_pos...end_pos)
149
161
  end
150
162
 
151
163
  def allowed_if_condition?(node)
@@ -70,6 +70,9 @@ module RuboCop
70
70
  private
71
71
 
72
72
  def check_for_line_terminator_or_opener
73
+ # Make the obvious check first
74
+ return unless @processed_source.raw_source.include?(';')
75
+
73
76
  each_semicolon { |line, column| convention_on(line, column, true) }
74
77
  end
75
78
 
@@ -29,6 +29,8 @@ module RuboCop
29
29
  # c + d
30
30
  # end
31
31
  class SingleLineBlockParams < Base
32
+ extend AutoCorrector
33
+
32
34
  MSG = 'Name `%<method>s` block params `|%<params>s|`.'
33
35
 
34
36
  def on_block(node)
@@ -37,20 +39,41 @@ module RuboCop
37
39
  return unless eligible_method?(node)
38
40
  return unless eligible_arguments?(node)
39
41
 
40
- return if args_match?(node.send_node.method_name, node.arguments)
42
+ method_name = node.send_node.method_name
43
+ return if args_match?(method_name, node.arguments)
44
+
45
+ preferred_block_arguments = build_preferred_arguments_map(node, target_args(method_name))
46
+ joined_block_arguments = preferred_block_arguments.values.join(', ')
41
47
 
42
- message = message(node.arguments)
48
+ message = format(MSG, method: method_name, params: joined_block_arguments)
43
49
 
44
- add_offense(node.arguments, message: message)
50
+ add_offense(node.arguments, message: message) do |corrector|
51
+ autocorrect(corrector, node, preferred_block_arguments, joined_block_arguments)
52
+ end
45
53
  end
46
54
 
47
55
  private
48
56
 
49
- def message(node)
50
- method_name = node.parent.send_node.method_name
51
- arguments = target_args(method_name).join(', ')
57
+ def build_preferred_arguments_map(node, preferred_arguments)
58
+ preferred_arguments_map = {}
59
+ node.arguments.each_with_index do |current_lvar, index|
60
+ preferred_argument = preferred_arguments[index]
61
+ current_argument = current_lvar.source
62
+ preferred_argument = "_#{preferred_argument}" if current_argument.start_with?('_')
63
+ preferred_arguments_map[current_argument] = preferred_argument
64
+ end
65
+
66
+ preferred_arguments_map
67
+ end
68
+
69
+ def autocorrect(corrector, node, preferred_block_arguments, joined_block_arguments)
70
+ corrector.replace(node.arguments, "|#{joined_block_arguments}|")
52
71
 
53
- format(MSG, method: method_name, params: arguments)
72
+ node.each_descendant(:lvar) do |lvar|
73
+ if (preferred_lvar = preferred_block_arguments[lvar.source])
74
+ corrector.replace(lvar, preferred_lvar)
75
+ end
76
+ end
54
77
  end
55
78
 
56
79
  def eligible_arguments?(node)
@@ -33,17 +33,22 @@ module RuboCop
33
33
  # end
34
34
  #
35
35
  class SoleNestedConditional < Base
36
+ include RangeHelp
37
+ extend AutoCorrector
38
+
36
39
  MSG = 'Consider merging nested conditions into '\
37
40
  'outer `%<conditional_type>s` conditions.'
38
41
 
39
42
  def on_if(node)
40
43
  return if node.ternary? || node.else? || node.elsif?
41
44
 
42
- branch = node.if_branch
43
- return unless offending_branch?(branch)
45
+ if_branch = node.if_branch
46
+ return unless offending_branch?(if_branch)
44
47
 
45
48
  message = format(MSG, conditional_type: node.keyword)
46
- add_offense(branch.loc.keyword, message: message)
49
+ add_offense(if_branch.loc.keyword, message: message) do |corrector|
50
+ autocorrect(corrector, node, if_branch)
51
+ end
47
52
  end
48
53
 
49
54
  private
@@ -57,6 +62,63 @@ module RuboCop
57
62
  !(branch.modifier_form? && allow_modifier?)
58
63
  end
59
64
 
65
+ def autocorrect(corrector, node, if_branch)
66
+ if node.unless?
67
+ corrector.replace(node.loc.keyword, 'if')
68
+ corrector.insert_before(node.condition, '!')
69
+ end
70
+
71
+ corrector.wrap(node.condition, '(', ')') if node.condition.or_type?
72
+
73
+ and_operator = if_branch.unless? ? ' && !' : ' && '
74
+ if if_branch.modifier_form?
75
+ correct_for_guard_condition_style(corrector, node, if_branch, and_operator)
76
+ else
77
+ correct_for_basic_condition_style(corrector, node, if_branch, and_operator)
78
+ correct_for_comment(corrector, node, if_branch)
79
+ end
80
+ end
81
+
82
+ def correct_for_guard_condition_style(corrector, node, if_branch, and_operator)
83
+ condition = if_branch.condition
84
+ corrector.insert_after(node.condition, replacement_condition(and_operator, condition))
85
+
86
+ range = range_between(if_branch.loc.keyword.begin_pos, condition.source_range.end_pos)
87
+ corrector.remove(range_with_surrounding_space(range: range, newlines: false))
88
+ corrector.remove(if_branch.loc.keyword)
89
+ end
90
+
91
+ def correct_for_basic_condition_style(corrector, node, if_branch, and_operator)
92
+ range = range_between(
93
+ node.condition.source_range.end_pos, if_branch.condition.source_range.begin_pos
94
+ )
95
+ corrector.replace(range, and_operator)
96
+ corrector.remove(range_by_whole_lines(node.loc.end, include_final_newline: true))
97
+ corrector.wrap(if_branch.condition, '(', ')') if wrap_condition?(if_branch.condition)
98
+ end
99
+
100
+ def correct_for_comment(corrector, node, if_branch)
101
+ return if config.for_cop('Style/IfUnlessModifier')['Enabled']
102
+
103
+ comments = processed_source.comments_before_line(if_branch.source_range.line)
104
+ comment_text = comments.map(&:text).join("\n") << "\n"
105
+
106
+ corrector.insert_before(node.loc.keyword, comment_text) unless comments.empty?
107
+ end
108
+
109
+ def wrap_condition?(node)
110
+ node.or_type? ||
111
+ (node.send_type? && node.arguments.any? && !node.parenthesized?)
112
+ end
113
+
114
+ def replacement_condition(and_operator, condition)
115
+ if wrap_condition?(condition)
116
+ "#{and_operator}(#{condition.source})"
117
+ else
118
+ "#{and_operator}#{condition.source}"
119
+ end
120
+ end
121
+
60
122
  def allow_modifier?
61
123
  cop_config['AllowModifier']
62
124
  end
@@ -26,10 +26,6 @@ module RuboCop
26
26
  # puts $LAST_MATCH_INFO
27
27
  # puts $IGNORECASE
28
28
  # puts $ARGV # or ARGV
29
- # puts $MATCH
30
- # puts $PREMATCH
31
- # puts $POSTMATCH
32
- # puts $LAST_PAREN_MATCH
33
29
  #
34
30
  # @example EnforcedStyle: use_perl_names
35
31
  # # good
@@ -51,10 +47,6 @@ module RuboCop
51
47
  # puts $~
52
48
  # puts $=
53
49
  # puts $*
54
- # puts $&
55
- # puts $`
56
- # puts $'
57
- # puts $+
58
50
  #
59
51
  class SpecialGlobalVars < Base
60
52
  include ConfigurableEnforcedStyle
@@ -85,11 +77,7 @@ module RuboCop
85
77
  :$? => [:$CHILD_STATUS],
86
78
  :$~ => [:$LAST_MATCH_INFO],
87
79
  :$= => [:$IGNORECASE],
88
- :$* => %i[$ARGV ARGV],
89
- :$& => [:$MATCH],
90
- :$` => [:$PREMATCH],
91
- :$' => [:$POSTMATCH],
92
- :$+ => [:$LAST_PAREN_MATCH]
80
+ :$* => %i[$ARGV ARGV]
93
81
  }
94
82
 
95
83
  PERL_VARS =
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for places where classes with only class methods can be
7
+ # replaced with a module. Classes should be used only when it makes sense to create
8
+ # instances out of them.
9
+ #
10
+ # This cop is marked as unsafe, because it is possible that this class is a parent
11
+ # for some other subclass, monkey-patched with instance methods or
12
+ # a dummy instance is instantiated from it somewhere.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # class SomeClass
17
+ # def self.some_method
18
+ # # body omitted
19
+ # end
20
+ #
21
+ # def self.some_other_method
22
+ # # body omitted
23
+ # end
24
+ # end
25
+ #
26
+ # # good
27
+ # module SomeModule
28
+ # module_function
29
+ #
30
+ # def some_method
31
+ # # body omitted
32
+ # end
33
+ #
34
+ # def some_other_method
35
+ # # body omitted
36
+ # end
37
+ # end
38
+ #
39
+ # # good - has instance method
40
+ # class SomeClass
41
+ # def instance_method; end
42
+ # def self.class_method; end
43
+ # end
44
+ #
45
+ class StaticClass < Base
46
+ include VisibilityHelp
47
+
48
+ MSG = 'Prefer modules to classes with only class methods.'
49
+
50
+ def on_class(class_node)
51
+ return if class_node.parent_class
52
+
53
+ add_offense(class_node) if class_convertible_to_module?(class_node)
54
+ end
55
+
56
+ private
57
+
58
+ def class_convertible_to_module?(class_node)
59
+ nodes = class_elements(class_node)
60
+ return false if nodes.empty?
61
+
62
+ nodes.all? do |node|
63
+ node_visibility(node) == :public &&
64
+ node.defs_type? ||
65
+ sclass_convertible_to_module?(node) ||
66
+ node.equals_asgn? ||
67
+ extend_call?(node)
68
+ end
69
+ end
70
+
71
+ def extend_call?(node)
72
+ node.send_type? && node.method?(:extend)
73
+ end
74
+
75
+ def sclass_convertible_to_module?(node)
76
+ return false unless node.sclass_type?
77
+
78
+ class_elements(node).all? do |child|
79
+ node_visibility(child) == :public && (child.def_type? || child.equals_asgn?)
80
+ end
81
+ end
82
+
83
+ def class_elements(class_node)
84
+ class_def = class_node.body
85
+
86
+ if !class_def
87
+ []
88
+ elsif class_def.begin_type?
89
+ class_def.children
90
+ else
91
+ [class_def]
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -11,6 +11,10 @@ module RuboCop
11
11
  # In those cases, it might be useful to extract statements to local
12
12
  # variables or methods which you can then interpolate in a string.
13
13
  #
14
+ # NOTE: When concatenation between two strings is broken over multiple
15
+ # lines, this cop does not register an offense; instead,
16
+ # `Style/LineEndConcatenation` will pick up the offense if enabled.
17
+ #
14
18
  # @example
15
19
  # # bad
16
20
  # email_with_name = user.name + ' <' + user.email + '>'
@@ -19,6 +23,10 @@ module RuboCop
19
23
  # email_with_name = "#{user.name} <#{user.email}>"
20
24
  # email_with_name = format('%s <%s>', user.name, user.email)
21
25
  #
26
+ # # accepted, line-end concatenation
27
+ # name = 'First' +
28
+ # 'Last'
29
+ #
22
30
  class StringConcatenation < Base
23
31
  include Util
24
32
  extend AutoCorrector
@@ -33,8 +41,13 @@ module RuboCop
33
41
  }
34
42
  PATTERN
35
43
 
44
+ def on_new_investigation
45
+ @corrected_nodes = nil
46
+ end
47
+
36
48
  def on_send(node)
37
49
  return unless string_concatenation?(node)
50
+ return if line_end_concatenation?(node)
38
51
 
39
52
  topmost_plus_node = find_topmost_plus_node(node)
40
53
 
@@ -42,14 +55,28 @@ module RuboCop
42
55
  collect_parts(topmost_plus_node, parts)
43
56
 
44
57
  add_offense(topmost_plus_node) do |corrector|
45
- if parts.none? { |part| uncorrectable?(part) }
58
+ correctable_parts = parts.none? { |part| uncorrectable?(part) }
59
+ if correctable_parts && !corrected_ancestor?(topmost_plus_node)
46
60
  corrector.replace(topmost_plus_node, replacement(parts))
61
+
62
+ @corrected_nodes ||= Set.new.compare_by_identity
63
+ @corrected_nodes.add(topmost_plus_node)
47
64
  end
48
65
  end
49
66
  end
50
67
 
51
68
  private
52
69
 
70
+ def line_end_concatenation?(node)
71
+ # If the concatenation happens at the end of the line,
72
+ # and both the receiver and argument are strings, allow
73
+ # `Style/LineEndConcatenation` to handle it instead.
74
+ node.receiver.str_type? &&
75
+ node.first_argument.str_type? &&
76
+ node.multiline? &&
77
+ node.source =~ /\+\s*\n/
78
+ end
79
+
53
80
  def find_topmost_plus_node(node)
54
81
  current = node
55
82
  while (parent = current.parent) && plus_node?(parent)
@@ -80,6 +107,10 @@ module RuboCop
80
107
  part.each_descendant(:block).any?
81
108
  end
82
109
 
110
+ def corrected_ancestor?(node)
111
+ node.each_ancestor(:send).any? { |ancestor| @corrected_nodes&.include?(ancestor) }
112
+ end
113
+
83
114
  def replacement(parts)
84
115
  interpolated_parts =
85
116
  parts.map do |part|
@@ -94,7 +125,13 @@ module RuboCop
94
125
  end
95
126
  end
96
127
 
97
- "\"#{interpolated_parts.join}\""
128
+ "\"#{handle_quotes(interpolated_parts).join}\""
129
+ end
130
+
131
+ def handle_quotes(parts)
132
+ parts.map do |part|
133
+ part == '"' ? '\"' : part
134
+ end
98
135
  end
99
136
 
100
137
  def single_quoted?(str_node)