rubocop 0.78.0 → 0.82.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 (209) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -4
  4. data/config/default.yml +158 -48
  5. data/lib/rubocop.rb +19 -4
  6. data/lib/rubocop/ast/builder.rb +45 -42
  7. data/lib/rubocop/ast/node.rb +12 -19
  8. data/lib/rubocop/ast/node/array_node.rb +13 -0
  9. data/lib/rubocop/ast/node/block_node.rb +5 -1
  10. data/lib/rubocop/ast/node/case_match_node.rb +56 -0
  11. data/lib/rubocop/ast/node/def_node.rb +11 -0
  12. data/lib/rubocop/ast/node/forward_args_node.rb +18 -0
  13. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +8 -0
  14. data/lib/rubocop/ast/node/regexp_node.rb +2 -4
  15. data/lib/rubocop/ast/traversal.rb +29 -10
  16. data/lib/rubocop/cli.rb +10 -4
  17. data/lib/rubocop/cli/command/show_cops.rb +11 -4
  18. data/lib/rubocop/comment_config.rb +6 -1
  19. data/lib/rubocop/config.rb +36 -10
  20. data/lib/rubocop/config_loader.rb +42 -33
  21. data/lib/rubocop/config_loader_resolver.rb +1 -1
  22. data/lib/rubocop/config_obsoletion.rb +4 -1
  23. data/lib/rubocop/config_validator.rb +66 -92
  24. data/lib/rubocop/cop/autocorrect_logic.rb +6 -3
  25. data/lib/rubocop/cop/badge.rb +5 -5
  26. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +1 -1
  27. data/lib/rubocop/cop/corrector.rb +48 -24
  28. data/lib/rubocop/cop/correctors/alignment_corrector.rb +2 -2
  29. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -2
  30. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  31. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +3 -3
  32. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -2
  33. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  34. data/lib/rubocop/cop/correctors/string_literal_corrector.rb +2 -2
  35. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  36. data/lib/rubocop/cop/generator.rb +3 -4
  37. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  38. data/lib/rubocop/cop/internal_affairs/offense_location_keyword.rb +1 -1
  39. data/lib/rubocop/cop/layout/array_alignment.rb +53 -10
  40. data/lib/rubocop/cop/layout/block_end_newline.rb +5 -3
  41. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  42. data/lib/rubocop/cop/layout/else_alignment.rb +8 -0
  43. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -1
  44. data/lib/rubocop/cop/layout/end_of_line.rb +2 -2
  45. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +16 -10
  46. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
  47. data/lib/rubocop/cop/layout/hash_alignment.rb +8 -4
  48. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  49. data/lib/rubocop/cop/layout/heredoc_indentation.rb +2 -2
  50. data/lib/rubocop/cop/layout/{tab.rb → indentation_style.rb} +48 -6
  51. data/lib/rubocop/cop/layout/leading_comment_space.rb +34 -3
  52. data/lib/rubocop/cop/layout/line_length.rb +32 -3
  53. data/lib/rubocop/cop/layout/multiline_block_layout.rb +15 -6
  54. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +0 -4
  55. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
  56. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +3 -3
  57. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +133 -0
  58. data/lib/rubocop/cop/layout/space_around_operators.rb +19 -1
  59. data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -2
  60. data/lib/rubocop/cop/layout/space_before_first_arg.rb +8 -0
  61. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -1
  62. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +2 -2
  63. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -9
  64. data/lib/rubocop/cop/layout/space_inside_range_literal.rb +2 -2
  65. data/lib/rubocop/cop/lint/boolean_symbol.rb +12 -0
  66. data/lib/rubocop/cop/lint/debugger.rb +2 -2
  67. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  68. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
  69. data/lib/rubocop/cop/lint/inherit_exception.rb +1 -1
  70. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -1
  71. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  72. data/lib/rubocop/cop/lint/loop.rb +6 -4
  73. data/lib/rubocop/cop/lint/multiple_comparison.rb +1 -1
  74. data/lib/rubocop/cop/lint/nested_method_definition.rb +2 -2
  75. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +2 -2
  76. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  77. data/lib/rubocop/cop/lint/percent_string_array.rb +2 -2
  78. data/lib/rubocop/cop/lint/raise_exception.rb +75 -0
  79. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +1 -1
  80. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +12 -7
  81. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +1 -1
  82. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -1
  83. data/lib/rubocop/cop/lint/struct_new_override.rb +58 -0
  84. data/lib/rubocop/cop/lint/suppressed_exception.rb +12 -22
  85. data/lib/rubocop/cop/lint/unified_integer.rb +0 -2
  86. data/lib/rubocop/cop/lint/unused_method_argument.rb +32 -6
  87. data/lib/rubocop/cop/lint/uri_regexp.rb +4 -4
  88. data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -0
  89. data/lib/rubocop/cop/migration/department_name.rb +47 -6
  90. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +4 -0
  91. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +6 -1
  92. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +7 -7
  93. data/lib/rubocop/cop/mixin/hash_transform_method.rb +171 -0
  94. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  95. data/lib/rubocop/cop/mixin/method_complexity.rb +5 -0
  96. data/lib/rubocop/cop/mixin/statement_modifier.rb +4 -3
  97. data/lib/rubocop/cop/mixin/trailing_comma.rb +3 -10
  98. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +1 -1
  99. data/lib/rubocop/cop/naming/constant_name.rb +2 -1
  100. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +1 -1
  101. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  102. data/lib/rubocop/cop/naming/method_name.rb +30 -0
  103. data/lib/rubocop/cop/naming/method_parameter_name.rb +1 -1
  104. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  105. data/lib/rubocop/cop/registry.rb +15 -3
  106. data/lib/rubocop/cop/style/access_modifier_declarations.rb +26 -6
  107. data/lib/rubocop/cop/style/alias.rb +4 -4
  108. data/lib/rubocop/cop/style/and_or.rb +5 -6
  109. data/lib/rubocop/cop/style/array_join.rb +1 -1
  110. data/lib/rubocop/cop/style/block_delimiters.rb +60 -1
  111. data/lib/rubocop/cop/style/case_equality.rb +24 -1
  112. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  113. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  114. data/lib/rubocop/cop/style/conditional_assignment.rb +8 -8
  115. data/lib/rubocop/cop/style/copyright.rb +1 -1
  116. data/lib/rubocop/cop/style/dir.rb +1 -1
  117. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -0
  118. data/lib/rubocop/cop/style/documentation.rb +43 -5
  119. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +1 -1
  120. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  121. data/lib/rubocop/cop/style/each_with_object.rb +3 -3
  122. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  123. data/lib/rubocop/cop/style/end_block.rb +6 -0
  124. data/lib/rubocop/cop/style/even_odd.rb +1 -1
  125. data/lib/rubocop/cop/style/expand_path_arguments.rb +3 -3
  126. data/lib/rubocop/cop/style/exponential_notation.rb +119 -0
  127. data/lib/rubocop/cop/style/format_string.rb +2 -2
  128. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +89 -11
  129. data/lib/rubocop/cop/style/hash_each_methods.rb +89 -0
  130. data/lib/rubocop/cop/style/hash_syntax.rb +3 -5
  131. data/lib/rubocop/cop/style/hash_transform_keys.rb +83 -0
  132. data/lib/rubocop/cop/style/hash_transform_values.rb +80 -0
  133. data/lib/rubocop/cop/style/if_unless_modifier.rb +23 -3
  134. data/lib/rubocop/cop/style/inverse_methods.rb +9 -5
  135. data/lib/rubocop/cop/style/lambda.rb +3 -2
  136. data/lib/rubocop/cop/style/lambda_call.rb +2 -2
  137. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -205
  138. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +169 -0
  139. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +54 -0
  140. data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
  141. data/lib/rubocop/cop/style/module_function.rb +58 -12
  142. data/lib/rubocop/cop/style/multiline_if_modifier.rb +1 -1
  143. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  144. data/lib/rubocop/cop/style/multiline_when_then.rb +5 -1
  145. data/lib/rubocop/cop/style/mutable_constant.rb +2 -4
  146. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -4
  147. data/lib/rubocop/cop/style/next.rb +2 -2
  148. data/lib/rubocop/cop/style/nil_comparison.rb +1 -1
  149. data/lib/rubocop/cop/style/non_nil_check.rb +4 -4
  150. data/lib/rubocop/cop/style/not.rb +1 -1
  151. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  152. data/lib/rubocop/cop/style/numeric_literals.rb +1 -1
  153. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -4
  154. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
  155. data/lib/rubocop/cop/style/or_assignment.rb +4 -3
  156. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +7 -7
  157. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  158. data/lib/rubocop/cop/style/perl_backrefs.rb +2 -2
  159. data/lib/rubocop/cop/style/proc.rb +1 -1
  160. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  161. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  162. data/lib/rubocop/cop/style/redundant_condition.rb +18 -6
  163. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -1
  164. data/lib/rubocop/cop/style/redundant_exception.rb +3 -3
  165. data/lib/rubocop/cop/style/redundant_interpolation.rb +2 -2
  166. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -2
  167. data/lib/rubocop/cop/style/redundant_return.rb +5 -7
  168. data/lib/rubocop/cop/style/redundant_self.rb +1 -1
  169. data/lib/rubocop/cop/style/redundant_sort.rb +2 -2
  170. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  171. data/lib/rubocop/cop/style/return_nil.rb +1 -1
  172. data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
  173. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  174. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  175. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -4
  176. data/lib/rubocop/cop/style/string_hash_keys.rb +1 -1
  177. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  178. data/lib/rubocop/cop/style/symbol_literal.rb +2 -2
  179. data/lib/rubocop/cop/style/ternary_parentheses.rb +2 -3
  180. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +35 -22
  181. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +41 -0
  182. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +88 -0
  183. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +44 -0
  184. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  185. data/lib/rubocop/cop/style/unpack_first.rb +0 -4
  186. data/lib/rubocop/cop/style/variable_interpolation.rb +1 -1
  187. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  188. data/lib/rubocop/cop/style/word_array.rb +1 -1
  189. data/lib/rubocop/cop/style/yoda_condition.rb +16 -1
  190. data/lib/rubocop/cop/style/zero_length_predicate.rb +1 -1
  191. data/lib/rubocop/cop/variable_force.rb +4 -1
  192. data/lib/rubocop/formatter/clang_style_formatter.rb +1 -1
  193. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  194. data/lib/rubocop/formatter/junit_formatter.rb +74 -0
  195. data/lib/rubocop/formatter/pacman_formatter.rb +1 -1
  196. data/lib/rubocop/formatter/tap_formatter.rb +1 -1
  197. data/lib/rubocop/node_pattern.rb +96 -10
  198. data/lib/rubocop/options.rb +7 -1
  199. data/lib/rubocop/processed_source.rb +1 -4
  200. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  201. data/lib/rubocop/rspec/expect_offense.rb +1 -1
  202. data/lib/rubocop/rspec/shared_contexts.rb +5 -4
  203. data/lib/rubocop/runner.rb +1 -1
  204. data/lib/rubocop/target_ruby.rb +151 -0
  205. data/lib/rubocop/version.rb +1 -1
  206. metadata +39 -12
  207. data/lib/rubocop/cop/lint/end_in_method.rb +0 -40
  208. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +0 -209
  209. data/lib/rubocop/formatter/disabled_lines_formatter.rb +0 -57
@@ -121,6 +121,14 @@ module RuboCop
121
121
  loc.respond_to?(:dot) && loc.dot && loc.dot.is?('::')
122
122
  end
123
123
 
124
+ # Checks whether the dispatched method uses a safe navigation operator to
125
+ # connect the receiver and the method name.
126
+ #
127
+ # @return [Boolean] whether the method was called with a connecting dot
128
+ def safe_navigation?
129
+ loc.respond_to?(:dot) && loc.dot && loc.dot.is?('&.')
130
+ end
131
+
124
132
  # Checks whether the *explicit* receiver of this method dispatch is
125
133
  # `self`.
126
134
  #
@@ -21,14 +21,12 @@ module RuboCop
21
21
 
22
22
  # @return [RuboCop::AST::Node] a regopt node
23
23
  def regopt
24
- first, second = *self
25
- first.regopt_type? ? first : second
24
+ children.last
26
25
  end
27
26
 
28
27
  # @return [String] a string of regexp content
29
28
  def content
30
- str = children.first
31
- str.str_content || ''
29
+ children.select(&:str_type?).map(&:str_content).join
32
30
  end
33
31
  end
34
32
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/ModuleLength
3
4
  module RuboCop
4
5
  module AST
5
6
  # Provides methods for traversing an AST.
@@ -18,15 +19,21 @@ module RuboCop
18
19
  rational str sym regopt self lvar
19
20
  ivar cvar gvar nth_ref back_ref cbase
20
21
  arg restarg blockarg shadowarg
21
- kwrestarg zsuper lambda redo retry].freeze
22
+ kwrestarg zsuper lambda redo retry
23
+ forward_args forwarded_args
24
+ match_var match_nil_pattern empty_else].freeze
22
25
  ONE_CHILD_NODE = %i[splat kwsplat block_pass not break next
23
26
  preexe postexe match_current_line defined?
24
- arg_expr].freeze
27
+ arg_expr pin match_rest if_guard unless_guard
28
+ match_with_trailing_comma].freeze
25
29
  MANY_CHILD_NODES = %i[dstr dsym xstr regexp array hash pair
26
30
  mlhs masgn or_asgn and_asgn
27
31
  undef alias args super yield or and
28
32
  while_post until_post iflipflop eflipflop
29
- match_with_lvasgn begin kwbegin return].freeze
33
+ match_with_lvasgn begin kwbegin return
34
+ in_match match_alt
35
+ match_as array_pattern array_pattern_with_tail
36
+ hash_pattern const_pattern].freeze
30
37
  SECOND_CHILD_ONLY = %i[lvasgn ivasgn cvasgn gvasgn optarg kwarg
31
38
  kwoptarg].freeze
32
39
 
@@ -171,13 +178,25 @@ module RuboCop
171
178
  nil
172
179
  end
173
180
 
174
- alias on_rescue on_case
175
- alias on_resbody on_case
176
- alias on_ensure on_case
177
- alias on_for on_case
178
- alias on_when on_case
179
- alias on_irange on_case
180
- alias on_erange on_case
181
+ alias on_rescue on_case
182
+ alias on_resbody on_case
183
+ alias on_ensure on_case
184
+ alias on_for on_case
185
+ alias on_when on_case
186
+ alias on_case_match on_case
187
+ alias on_in_pattern on_case
188
+ alias on_irange on_case
189
+ alias on_erange on_case
190
+
191
+ def on_numblock(node)
192
+ children = node.children
193
+ child = children[0]
194
+ send(:"on_#{child.type}", child)
195
+ return unless (child = children[2])
196
+
197
+ send(:"on_#{child.type}", child)
198
+ end
181
199
  end
182
200
  end
183
201
  end
202
+ # rubocop:enable Metrics/ModuleLength
@@ -83,10 +83,7 @@ module RuboCop
83
83
  end
84
84
 
85
85
  def act_on_options
86
- ConfigLoader.debug = @options[:debug]
87
- ConfigLoader.auto_gen_config = @options[:auto_gen_config]
88
- ConfigLoader.ignore_parent_exclusion = @options[:ignore_parent_exclusion]
89
- ConfigLoader.options_config = @options[:config]
86
+ set_options_to_config_loader
90
87
 
91
88
  @config_store.options_config = @options[:config] if @options[:config]
92
89
  @config_store.force_default_config! if @options[:force_default_config]
@@ -102,6 +99,15 @@ module RuboCop
102
99
  end
103
100
  end
104
101
 
102
+ def set_options_to_config_loader
103
+ ConfigLoader.debug = @options[:debug]
104
+ ConfigLoader.auto_gen_config = @options[:auto_gen_config]
105
+ ConfigLoader.disable_pending_cops = @options[:disable_pending_cops]
106
+ ConfigLoader.enable_pending_cops = @options[:enable_pending_cops]
107
+ ConfigLoader.ignore_parent_exclusion = @options[:ignore_parent_exclusion]
108
+ ConfigLoader.options_config = @options[:config]
109
+ end
110
+
105
111
  def handle_exiting_options
106
112
  return unless Options::EXITING_OPTIONS.any? { |o| @options.key? o }
107
113
 
@@ -8,6 +8,13 @@ module RuboCop
8
8
  class ShowCops < Base
9
9
  self.command_name = :show_cops
10
10
 
11
+ def initialize(env)
12
+ super
13
+
14
+ # Load the configs so the require()s are done for custom cops
15
+ @config = @config_store.for(Dir.pwd)
16
+ end
17
+
11
18
  def run
12
19
  print_available_cops
13
20
  end
@@ -15,8 +22,6 @@ module RuboCop
15
22
  private
16
23
 
17
24
  def print_available_cops
18
- # Load the configs so the require()s are done for custom cops
19
- @config_store.for(Dir.pwd)
20
25
  registry = Cop::Cop.registry
21
26
  show_all = @options[:show_cops].empty?
22
27
 
@@ -46,7 +51,9 @@ module RuboCop
46
51
 
47
52
  def print_cop_details(cops)
48
53
  cops.each do |cop|
49
- puts '# Supports --auto-correct' if cop.new.support_autocorrect?
54
+ if cop.new(@config).support_autocorrect?
55
+ puts '# Supports --auto-correct'
56
+ end
50
57
  puts "#{cop.cop_name}:"
51
58
  puts config_lines(cop)
52
59
  puts
@@ -64,7 +71,7 @@ module RuboCop
64
71
  end
65
72
 
66
73
  def config_lines(cop)
67
- cnf = @config_store.for(Dir.pwd).for_cop(cop)
74
+ cnf = @config.for_cop(cop)
68
75
  cnf.to_yaml.lines.to_a.drop(1).map { |line| ' ' + line }
69
76
  end
70
77
  end
@@ -113,7 +113,8 @@ module RuboCop
113
113
  def each_mentioned_cop
114
114
  each_directive do |comment, cop_names, disabled|
115
115
  comment_line_number = comment.loc.expression.line
116
- single_line = !comment_only_line?(comment_line_number)
116
+ single_line = !comment_only_line?(comment_line_number) ||
117
+ directive_on_comment_line?(comment)
117
118
 
118
119
  cop_names.each do |cop_name|
119
120
  yield qualified_cop_name(cop_name), disabled, comment_line_number,
@@ -122,6 +123,10 @@ module RuboCop
122
123
  end
123
124
  end
124
125
 
126
+ def directive_on_comment_line?(comment)
127
+ comment.text[1..-1].match(COMMENT_DIRECTIVE_REGEXP)
128
+ end
129
+
125
130
  def each_directive
126
131
  return if processed_source.comments.nil?
127
132
 
@@ -2,6 +2,9 @@
2
2
 
3
3
  require 'pathname'
4
4
 
5
+ # FIXME: Moving Rails department code to RuboCop Rails will remove
6
+ # the following rubocop:disable comment.
7
+ # rubocop:disable Metrics/ClassLength
5
8
  module RuboCop
6
9
  # This class represents the configuration of the RuboCop application
7
10
  # and all its cops. A Config is associated with a YAML configuration
@@ -13,6 +16,8 @@ module RuboCop
13
16
  include FileFinder
14
17
  extend Forwardable
15
18
 
19
+ CopConfig = Struct.new(:name, :metadata)
20
+
16
21
  DEFAULT_RAILS_VERSION = 5.0
17
22
  attr_reader :loaded_path
18
23
 
@@ -42,7 +47,7 @@ module RuboCop
42
47
  end
43
48
 
44
49
  def_delegators :@hash, :[], :[]=, :delete, :each, :key?, :keys, :each_key,
45
- :map, :merge, :to_h, :to_hash
50
+ :map, :merge, :to_h, :to_hash, :transform_values
46
51
  def_delegators :@validator, :validate, :target_ruby_version
47
52
 
48
53
  def to_s
@@ -111,6 +116,14 @@ module RuboCop
111
116
  @for_all_cops ||= self['AllCops'] || {}
112
117
  end
113
118
 
119
+ def disabled_new_cops?
120
+ for_all_cops['NewCops'] == 'disable'
121
+ end
122
+
123
+ def enabled_new_cops?
124
+ for_all_cops['NewCops'] == 'enable'
125
+ end
126
+
114
127
  def file_to_include?(file)
115
128
  relative_file_path = path_relative_to_config(file)
116
129
 
@@ -215,6 +228,18 @@ module RuboCop
215
228
  nil
216
229
  end
217
230
 
231
+ def pending_cops
232
+ keys.each_with_object([]) do |qualified_cop_name, pending_cops|
233
+ department = department_of(qualified_cop_name)
234
+ next if department && department['Enabled'] == false
235
+
236
+ cop_metadata = self[qualified_cop_name]
237
+ next unless cop_metadata['Enabled'] == 'pending'
238
+
239
+ pending_cops << CopConfig.new(qualified_cop_name, cop_metadata)
240
+ end
241
+ end
242
+
218
243
  private
219
244
 
220
245
  def target_rails_version_from_bundler_lock_file
@@ -235,17 +260,18 @@ module RuboCop
235
260
  end
236
261
 
237
262
  def enable_cop?(qualified_cop_name, cop_options)
238
- cop_department, cop_name = qualified_cop_name.split('/')
239
- department = cop_name.nil?
240
-
241
- unless department
242
- department_options = self[cop_department]
243
- if department_options && department_options['Enabled'] == false
244
- return false
245
- end
246
- end
263
+ department = department_of(qualified_cop_name)
264
+ return false if department && department['Enabled'] == false
247
265
 
248
266
  cop_options.fetch('Enabled') { !for_all_cops['DisabledByDefault'] }
249
267
  end
268
+
269
+ def department_of(qualified_cop_name)
270
+ cop_department, cop_name = qualified_cop_name.split('/')
271
+ return nil if cop_name.nil?
272
+
273
+ self[cop_department]
274
+ end
250
275
  end
251
276
  end
277
+ # rubocop:enable Metrics/ClassLength
@@ -24,7 +24,7 @@ module RuboCop
24
24
  include FileFinder
25
25
 
26
26
  attr_accessor :debug, :auto_gen_config, :ignore_parent_exclusion,
27
- :options_config
27
+ :options_config, :disable_pending_cops, :enable_pending_cops
28
28
  attr_writer :default_configuration
29
29
 
30
30
  alias debug? debug
@@ -55,7 +55,10 @@ module RuboCop
55
55
  end
56
56
 
57
57
  def add_missing_namespaces(path, hash)
58
- hash.keys.each do |key|
58
+ # Using `hash.each_key` will cause the
59
+ # `can't add a new key into hash during iteration` error
60
+ hash_keys = hash.keys
61
+ hash_keys.each do |key|
59
62
  q = Cop::Cop.qualified_cop_name(key, path)
60
63
  next if q == key
61
64
 
@@ -76,28 +79,35 @@ module RuboCop
76
79
  # user's home directory is checked. If there's no .rubocop.yml
77
80
  # there either, the path to the default file is returned.
78
81
  def configuration_file_for(target_dir)
79
- find_project_dotfile(target_dir) ||
80
- find_user_dotfile ||
81
- find_user_xdg_config ||
82
- DEFAULT_FILE
82
+ find_project_dotfile(target_dir) || find_user_dotfile ||
83
+ find_user_xdg_config || DEFAULT_FILE
83
84
  end
84
85
 
85
86
  def configuration_from_file(config_file)
86
- config = load_file(config_file)
87
- return config if config_file == DEFAULT_FILE
87
+ return ConfigLoader.default_configuration if config_file == DEFAULT_FILE
88
88
 
89
+ config = load_file(config_file)
89
90
  if ignore_parent_exclusion?
90
91
  print 'Ignoring AllCops/Exclude from parent folders' if debug?
91
92
  else
92
93
  add_excludes_from_files(config, config_file)
93
94
  end
94
- merge_with_default(config, config_file)
95
+
96
+ merge_with_default(config, config_file).tap do |merged_config|
97
+ unless possible_new_cops?(config)
98
+ warn_on_pending_cops(merged_config.pending_cops)
99
+ end
100
+ end
101
+ end
102
+
103
+ def possible_new_cops?(config)
104
+ disable_pending_cops || enable_pending_cops ||
105
+ config.disabled_new_cops? || config.enabled_new_cops?
95
106
  end
96
107
 
97
108
  def add_excludes_from_files(config, config_file)
98
- found_files =
99
- find_files_upwards(DOTFILE, config_file) +
100
- [find_user_dotfile, find_user_xdg_config].compact
109
+ found_files = find_files_upwards(DOTFILE, config_file) +
110
+ [find_user_dotfile, find_user_xdg_config].compact
101
111
 
102
112
  return if found_files.empty?
103
113
  return if PathUtil.relative_path(found_files.last) ==
@@ -114,6 +124,22 @@ module RuboCop
114
124
  end
115
125
  end
116
126
 
127
+ def warn_on_pending_cops(pending_cops)
128
+ return if pending_cops.empty?
129
+
130
+ warn Rainbow('The following cops were added to RuboCop, but are not ' \
131
+ 'configured. Please set Enabled to either `true` or ' \
132
+ '`false` in your `.rubocop.yml` file:').yellow
133
+
134
+ pending_cops.each do |cop|
135
+ warn Rainbow(
136
+ " - #{cop.name} (#{cop.metadata['VersionAdded']})"
137
+ ).yellow
138
+ end
139
+
140
+ warn Rainbow('For more information: https://docs.rubocop.org/en/latest/versioning/').yellow
141
+ end
142
+
117
143
  # Merges the given configuration with the default one. If
118
144
  # AllCops:DisabledByDefault is true, it changes the Enabled params so
119
145
  # that only cops from user configuration are enabled.
@@ -180,7 +206,9 @@ module RuboCop
180
206
  def write_config_file(file_name, file_string, rubocop_yml_contents)
181
207
  File.open(file_name, 'w') do |f|
182
208
  f.write "inherit_from:#{file_string}\n"
183
- f.write "\n#{rubocop_yml_contents}" if rubocop_yml_contents =~ /\S/
209
+ if /\S/.match?(rubocop_yml_contents)
210
+ f.write "\n#{rubocop_yml_contents}"
211
+ end
184
212
  end
185
213
  end
186
214
 
@@ -199,8 +227,6 @@ module RuboCop
199
227
  raise(TypeError, "Malformed configuration in #{absolute_path}")
200
228
  end
201
229
 
202
- check_cop_config_value(hash)
203
-
204
230
  hash
205
231
  end
206
232
 
@@ -222,22 +248,6 @@ module RuboCop
222
248
  end
223
249
  end
224
250
 
225
- def check_cop_config_value(hash, parent = nil)
226
- hash.each do |key, value|
227
- check_cop_config_value(value, key) if value.is_a?(Hash)
228
-
229
- next unless %w[Enabled
230
- Safe
231
- SafeAutoCorrect
232
- AutoCorrect].include?(key) && value.is_a?(String)
233
-
234
- abort(
235
- "Property #{Rainbow(key).yellow} of cop #{Rainbow(parent).yellow}" \
236
- " is supposed to be a boolean and #{Rainbow(value).yellow} is not."
237
- )
238
- end
239
- end
240
-
241
251
  # Read the specified file, or exit with a friendly, concise message on
242
252
  # stderr. Care is taken to use the standard OS exit code for a "file not
243
253
  # found" error.
@@ -250,8 +260,7 @@ module RuboCop
250
260
 
251
261
  def yaml_safe_load(yaml_code, filename)
252
262
  if defined?(SafeYAML) && SafeYAML.respond_to?(:load)
253
- SafeYAML.load(yaml_code, filename,
254
- whitelisted_tags: %w[!ruby/regexp])
263
+ SafeYAML.load(yaml_code, filename, whitelisted_tags: %w[!ruby/regexp])
255
264
  # Ruby 2.6+
256
265
  elsif Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0')
257
266
  YAML.safe_load(
@@ -181,7 +181,7 @@ module RuboCop
181
181
  end
182
182
 
183
183
  def transform(config)
184
- Hash[config.map { |cop, params| [cop, yield(params)] }]
184
+ config.transform_values { |params| yield(params) }
185
185
  end
186
186
 
187
187
  def gem_config_path(gem_name, relative_config_path)
@@ -17,8 +17,10 @@ module RuboCop
17
17
  'Layout/IndentHash' => 'Layout/FirstHashElementIndentation',
18
18
  'Layout/IndentHeredoc' => 'Layout/HeredocIndentation',
19
19
  'Layout/LeadingBlankLines' => 'Layout/LeadingEmptyLines',
20
+ 'Layout/Tab' => 'Layout/IndentationStyle',
20
21
  'Layout/TrailingBlankLines' => 'Layout/TrailingEmptyLines',
21
22
  'Lint/DuplicatedKey' => 'Lint/DuplicateHashKey',
23
+ 'Lint/EndInMethod' => 'Style/EndBlock',
22
24
  'Lint/HandleExceptions' => 'Lint/SuppressedException',
23
25
  'Lint/MultipleCompare' => 'Lint/MultipleComparison',
24
26
  'Lint/StringConversionInInterpolation' => 'Lint/RedundantStringCoercion',
@@ -69,7 +71,8 @@ module RuboCop
69
71
  'Style/TrailingCommaInHashLiteral',
70
72
  'Style/TrailingCommaInLiteral' => 'Style/TrailingCommaInArrayLiteral ' \
71
73
  'and/or ' \
72
- 'Style/TrailingCommaInHashLiteral'
74
+ 'Style/TrailingCommaInHashLiteral',
75
+ 'Style/BracesAroundHashParameters' => nil
73
76
  }.map do |old_name, other_cops|
74
77
  if other_cops
75
78
  more = ". Please use #{other_cops} instead".gsub(%r{[A-Z]\w+/\w+},
@@ -13,26 +13,22 @@ module RuboCop
13
13
  INTERNAL_PARAMS = %w[Description StyleGuide
14
14
  VersionAdded VersionChanged VersionRemoved
15
15
  Reference Safe SafeAutoCorrect].freeze
16
+ NEW_COPS_VALUES = %w[pending disable enable].freeze
16
17
 
17
- # 2.3 is the oldest officially supported Ruby version.
18
- DEFAULT_RUBY_VERSION = 2.3
19
- KNOWN_RUBIES = [2.3, 2.4, 2.5, 2.6, 2.7].freeze
20
- OBSOLETE_RUBIES = {
21
- 1.9 => '0.50', 2.0 => '0.50', 2.1 => '0.58', 2.2 => '0.69'
22
- }.freeze
23
- RUBY_VERSION_FILENAME = '.ruby-version'
24
-
25
- def_delegators :@config,
26
- :smart_loaded_path, :for_all_cops, :find_file_upwards,
27
- :base_dir_for_path_parameters, :bundler_lock_file_path
18
+ def_delegators :@config, :smart_loaded_path, :for_all_cops
28
19
 
29
20
  def initialize(config)
30
21
  @config = config
31
22
  @config_obsoletion = ConfigObsoletion.new(config)
23
+ @target_ruby = TargetRuby.new(config)
32
24
  end
33
25
 
26
+ # rubocop:disable Metrics/AbcSize
34
27
  def validate
35
- # Don't validate RuboCop's own files. Avoids infinite recursion.
28
+ check_cop_config_value(@config)
29
+ reject_conflicting_safe_settings
30
+
31
+ # Don't validate RuboCop's own files further. Avoids infinite recursion.
36
32
  return if @config.internal?
37
33
 
38
34
  valid_cop_names, invalid_cop_names = @config.keys.partition do |key|
@@ -43,30 +39,16 @@ module RuboCop
43
39
 
44
40
  alert_about_unrecognized_cops(invalid_cop_names)
45
41
  check_target_ruby
42
+ validate_new_cops_parameter
46
43
  validate_parameter_names(valid_cop_names)
47
44
  validate_enforced_styles(valid_cop_names)
48
45
  validate_syntax_cop
49
46
  reject_mutually_exclusive_defaults
50
47
  end
48
+ # rubocop:enable Metrics/AbcSize
51
49
 
52
50
  def target_ruby_version
53
- @target_ruby_version ||= begin
54
- if for_all_cops['TargetRubyVersion']
55
- @target_ruby_version_source = :rubocop_yml
56
-
57
- for_all_cops['TargetRubyVersion'].to_f
58
- elsif target_ruby_version_from_version_file
59
- @target_ruby_version_source = :ruby_version_file
60
-
61
- target_ruby_version_from_version_file
62
- elsif target_ruby_version_from_bundler_lock_file
63
- @target_ruby_version_source = :bundler_lock_file
64
-
65
- target_ruby_version_from_bundler_lock_file
66
- else
67
- DEFAULT_RUBY_VERSION
68
- end
69
- end
51
+ target_ruby.version
70
52
  end
71
53
 
72
54
  def validate_section_presence(name)
@@ -78,20 +60,24 @@ module RuboCop
78
60
 
79
61
  private
80
62
 
63
+ attr_reader :target_ruby
64
+
81
65
  def check_target_ruby
82
- return if KNOWN_RUBIES.include?(target_ruby_version)
66
+ return if target_ruby.supported?
67
+
68
+ source = target_ruby.source
69
+ last_version = target_ruby.rubocop_version_with_support
83
70
 
84
- msg = if OBSOLETE_RUBIES.include?(target_ruby_version)
71
+ msg = if last_version
85
72
  "RuboCop found unsupported Ruby version #{target_ruby_version} " \
86
- "in #{target_ruby_source}. #{target_ruby_version}-compatible " \
87
- 'analysis was dropped after version ' \
88
- "#{OBSOLETE_RUBIES[target_ruby_version]}."
73
+ "in #{source}. #{target_ruby_version}-compatible " \
74
+ "analysis was dropped after version #{last_version}."
89
75
  else
90
76
  'RuboCop found unknown Ruby version ' \
91
- "#{target_ruby_version.inspect} in #{target_ruby_source}."
77
+ "#{target_ruby_version.inspect} in #{source}."
92
78
  end
93
79
 
94
- msg += "\nSupported versions: #{KNOWN_RUBIES.join(', ')}"
80
+ msg += "\nSupported versions: #{TargetRuby.supported_versions.join(', ')}"
95
81
 
96
82
  raise ValidationError, msg
97
83
  end
@@ -125,6 +111,18 @@ module RuboCop
125
111
  'It\'s not possible to disable this cop.'
126
112
  end
127
113
 
114
+ def validate_new_cops_parameter
115
+ new_cop_parameter = @config.for_all_cops['NewCops']
116
+ return if new_cop_parameter.nil? ||
117
+ NEW_COPS_VALUES.include?(new_cop_parameter)
118
+
119
+ message = "invalid #{new_cop_parameter} for `NewCops` found in" \
120
+ "#{smart_loaded_path}\n" \
121
+ "Valid choices are: #{NEW_COPS_VALUES.join(', ')}"
122
+
123
+ raise ValidationError, message
124
+ end
125
+
128
126
  def validate_parameter_names(valid_cop_names)
129
127
  valid_cop_names.each do |name|
130
128
  validate_section_presence(name)
@@ -178,71 +176,47 @@ module RuboCop
178
176
  formats.all? { |format| valid.include?(format) }
179
177
  end
180
178
 
181
- def target_ruby_source
182
- case @target_ruby_version_source
183
- when :ruby_version_file
184
- "`#{RUBY_VERSION_FILENAME}`"
185
- when :bundler_lock_file
186
- "`#{bundler_lock_file_path}`"
187
- when :rubocop_yml
188
- "`TargetRubyVersion` parameter (in #{smart_loaded_path})"
189
- end
190
- end
179
+ def reject_mutually_exclusive_defaults
180
+ disabled_by_default = for_all_cops['DisabledByDefault']
181
+ enabled_by_default = for_all_cops['EnabledByDefault']
182
+ return unless disabled_by_default && enabled_by_default
191
183
 
192
- def ruby_version_file
193
- @ruby_version_file ||=
194
- find_file_upwards(RUBY_VERSION_FILENAME, base_dir_for_path_parameters)
184
+ msg = 'Cops cannot be both enabled by default and disabled by default'
185
+ raise ValidationError, msg
195
186
  end
196
187
 
197
- def target_ruby_version_from_version_file
198
- file = ruby_version_file
199
- return unless file && File.file?(file)
188
+ def reject_conflicting_safe_settings
189
+ @config.each do |name, cop_config|
190
+ next unless cop_config.is_a?(Hash)
191
+ next unless cop_config['Safe'] == false &&
192
+ cop_config['SafeAutoCorrect'] == true
200
193
 
201
- @target_ruby_version_from_version_file ||=
202
- File.read(file).match(/\A(ruby-)?(?<version>\d+\.\d+)/) do |md|
203
- md[:version].to_f
204
- end
194
+ msg = 'Unsafe cops cannot have a safe auto-correction ' \
195
+ "(section #{name} in #{smart_loaded_path})"
196
+ raise ValidationError, msg
197
+ end
205
198
  end
206
199
 
207
- def target_ruby_version_from_bundler_lock_file
208
- @target_ruby_version_from_bundler_lock_file ||=
209
- read_ruby_version_from_bundler_lock_file
210
- end
211
-
212
- def read_ruby_version_from_bundler_lock_file
213
- lock_file_path = bundler_lock_file_path
214
- return nil unless lock_file_path
215
-
216
- in_ruby_section = false
217
- File.foreach(lock_file_path) do |line|
218
- # If ruby is in Gemfile.lock or gems.lock, there should be two lines
219
- # towards the bottom of the file that look like:
220
- # RUBY VERSION
221
- # ruby W.X.YpZ
222
- # We ultimately want to match the "ruby W.X.Y.pZ" line, but there's
223
- # extra logic to make sure we only start looking once we've seen the
224
- # "RUBY VERSION" line.
225
- in_ruby_section ||= line.match(/^\s*RUBY\s*VERSION\s*$/)
226
- next unless in_ruby_section
227
-
228
- # We currently only allow this feature to work with MRI ruby. If jruby
229
- # (or something else) is used by the project, it's lock file will have a
230
- # line that looks like:
231
- # RUBY VERSION
232
- # ruby W.X.YpZ (jruby x.x.x.x)
233
- # The regex won't match in this situation.
234
- result = line.match(/^\s*ruby\s+(\d+\.\d+)[p.\d]*\s*$/)
235
- return result.captures.first.to_f if result
200
+ def check_cop_config_value(hash, parent = nil)
201
+ hash.each do |key, value|
202
+ check_cop_config_value(value, key) if value.is_a?(Hash)
203
+
204
+ next unless %w[Enabled
205
+ Safe
206
+ SafeAutoCorrect
207
+ AutoCorrect].include?(key) && value.is_a?(String)
208
+
209
+ next if key == 'Enabled' && value == 'pending'
210
+
211
+ raise ValidationError, msg_not_boolean(parent, key, value)
236
212
  end
237
213
  end
238
214
 
239
- def reject_mutually_exclusive_defaults
240
- disabled_by_default = for_all_cops['DisabledByDefault']
241
- enabled_by_default = for_all_cops['EnabledByDefault']
242
- return unless disabled_by_default && enabled_by_default
243
-
244
- msg = 'Cops cannot be both enabled by default and disabled by default'
245
- raise ValidationError, msg
215
+ # FIXME: Handling colors in exception messages like this is ugly.
216
+ def msg_not_boolean(parent, key, value)
217
+ "#{Rainbow('').reset}" \
218
+ "Property #{Rainbow(key).yellow} of cop #{Rainbow(parent).yellow}" \
219
+ " is supposed to be a boolean and #{Rainbow(value).yellow} is not."
246
220
  end
247
221
  end
248
222
  end