rubocop 0.85.0 → 0.88.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 (215) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -17
  3. data/bin/rubocop-profile +31 -0
  4. data/config/default.yml +132 -11
  5. data/lib/rubocop.rb +17 -1
  6. data/lib/rubocop/cli.rb +2 -4
  7. data/lib/rubocop/cli/command/auto_genenerate_config.rb +42 -7
  8. data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
  9. data/lib/rubocop/cli/command/show_cops.rb +1 -1
  10. data/lib/rubocop/config.rb +1 -1
  11. data/lib/rubocop/config_loader.rb +39 -67
  12. data/lib/rubocop/config_loader_resolver.rb +1 -1
  13. data/lib/rubocop/config_obsoletion.rb +0 -1
  14. data/lib/rubocop/config_store.rb +4 -0
  15. data/lib/rubocop/cop/autocorrect_logic.rb +14 -24
  16. data/lib/rubocop/cop/badge.rb +1 -1
  17. data/lib/rubocop/cop/base.rb +407 -0
  18. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +10 -20
  19. data/lib/rubocop/cop/commissioner.rb +48 -50
  20. data/lib/rubocop/cop/cop.rb +91 -235
  21. data/lib/rubocop/cop/corrector.rb +38 -115
  22. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +26 -0
  23. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +7 -2
  24. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
  25. data/lib/rubocop/cop/generator.rb +1 -1
  26. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  27. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +11 -14
  28. data/lib/rubocop/cop/layout/case_indentation.rb +18 -19
  29. data/lib/rubocop/cop/layout/class_structure.rb +2 -37
  30. data/lib/rubocop/cop/layout/comment_indentation.rb +3 -3
  31. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  32. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  33. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +3 -8
  34. data/lib/rubocop/cop/layout/end_alignment.rb +3 -2
  35. data/lib/rubocop/cop/layout/end_of_line.rb +1 -1
  36. data/lib/rubocop/cop/layout/first_argument_indentation.rb +5 -1
  37. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +1 -1
  38. data/lib/rubocop/cop/layout/hash_alignment.rb +2 -3
  39. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  40. data/lib/rubocop/cop/layout/multiline_block_layout.rb +17 -7
  41. data/lib/rubocop/cop/layout/space_after_colon.rb +1 -1
  42. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +22 -27
  43. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  44. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +27 -68
  45. data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
  46. data/lib/rubocop/cop/layout/space_before_block_braces.rb +14 -0
  47. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +4 -3
  48. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -1
  49. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -2
  50. data/lib/rubocop/cop/legacy/corrections_proxy.rb +49 -0
  51. data/lib/rubocop/cop/legacy/corrector.rb +29 -0
  52. data/lib/rubocop/cop/lint/constant_resolution.rb +89 -0
  53. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -4
  54. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +8 -2
  55. data/lib/rubocop/cop/lint/duplicate_elsif_condition.rb +39 -0
  56. data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -2
  57. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  58. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +38 -2
  59. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +3 -2
  60. data/lib/rubocop/cop/lint/interpolation_check.rb +13 -0
  61. data/lib/rubocop/cop/lint/literal_as_condition.rb +11 -1
  62. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +8 -1
  63. data/lib/rubocop/cop/lint/nested_method_definition.rb +14 -20
  64. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +69 -2
  65. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +8 -3
  66. data/lib/rubocop/cop/lint/percent_string_array.rb +1 -1
  67. data/lib/rubocop/cop/lint/percent_symbol_array.rb +1 -1
  68. data/lib/rubocop/cop/lint/raise_exception.rb +12 -4
  69. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  70. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +31 -25
  71. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +2 -2
  72. data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
  73. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +9 -1
  74. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  75. data/lib/rubocop/cop/lint/syntax.rb +11 -26
  76. data/lib/rubocop/cop/lint/unused_method_argument.rb +1 -1
  77. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -1
  78. data/lib/rubocop/cop/metrics/block_length.rb +22 -0
  79. data/lib/rubocop/cop/metrics/class_length.rb +25 -2
  80. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +35 -3
  81. data/lib/rubocop/cop/metrics/method_length.rb +23 -0
  82. data/lib/rubocop/cop/metrics/module_length.rb +25 -2
  83. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  84. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +129 -0
  85. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +61 -0
  86. data/lib/rubocop/cop/mixin/allowed_methods.rb +19 -0
  87. data/lib/rubocop/cop/mixin/auto_corrector.rb +12 -0
  88. data/lib/rubocop/cop/mixin/code_length.rb +4 -0
  89. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -1
  90. data/lib/rubocop/cop/mixin/configurable_naming.rb +1 -1
  91. data/lib/rubocop/cop/mixin/documentation_comment.rb +2 -2
  92. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -1
  93. data/lib/rubocop/cop/mixin/enforce_superclass.rb +3 -1
  94. data/lib/rubocop/cop/mixin/first_element_line_break.rb +1 -1
  95. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  96. data/lib/rubocop/cop/mixin/nil_methods.rb +3 -5
  97. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +6 -1
  98. data/lib/rubocop/cop/mixin/parentheses.rb +1 -2
  99. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  100. data/lib/rubocop/cop/mixin/range_help.rb +1 -1
  101. data/lib/rubocop/cop/mixin/regexp_literal_help.rb +27 -0
  102. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -3
  103. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
  104. data/lib/rubocop/cop/mixin/surrounding_space.rb +10 -5
  105. data/lib/rubocop/cop/mixin/too_many_lines.rb +3 -13
  106. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  107. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +6 -4
  108. data/lib/rubocop/cop/mixin/visibility_help.rb +50 -0
  109. data/lib/rubocop/cop/naming/ascii_identifiers.rb +27 -4
  110. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +3 -3
  111. data/lib/rubocop/cop/naming/file_name.rb +1 -3
  112. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +1 -1
  113. data/lib/rubocop/cop/naming/method_name.rb +1 -1
  114. data/lib/rubocop/cop/naming/method_parameter_name.rb +1 -1
  115. data/lib/rubocop/cop/naming/predicate_name.rb +3 -5
  116. data/lib/rubocop/cop/naming/variable_name.rb +1 -1
  117. data/lib/rubocop/cop/naming/variable_number.rb +1 -1
  118. data/lib/rubocop/cop/offense.rb +16 -2
  119. data/lib/rubocop/cop/registry.rb +62 -7
  120. data/lib/rubocop/cop/style/accessor_grouping.rb +147 -0
  121. data/lib/rubocop/cop/style/array_coercion.rb +63 -0
  122. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +3 -2
  123. data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -1
  124. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +146 -0
  125. data/lib/rubocop/cop/style/block_delimiters.rb +2 -4
  126. data/lib/rubocop/cop/style/case_like_if.rb +217 -0
  127. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  128. data/lib/rubocop/cop/style/class_vars.rb +21 -0
  129. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  130. data/lib/rubocop/cop/style/commented_keyword.rb +5 -2
  131. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  132. data/lib/rubocop/cop/style/copyright.rb +3 -3
  133. data/lib/rubocop/cop/style/date_time.rb +1 -1
  134. data/lib/rubocop/cop/style/dir.rb +2 -2
  135. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
  136. data/lib/rubocop/cop/style/documentation.rb +2 -2
  137. data/lib/rubocop/cop/style/empty_case_condition.rb +8 -6
  138. data/lib/rubocop/cop/style/empty_literal.rb +5 -5
  139. data/lib/rubocop/cop/style/encoding.rb +1 -1
  140. data/lib/rubocop/cop/style/expand_path_arguments.rb +2 -2
  141. data/lib/rubocop/cop/style/exponential_notation.rb +8 -10
  142. data/lib/rubocop/cop/style/float_division.rb +7 -10
  143. data/lib/rubocop/cop/style/format_string_token.rb +5 -5
  144. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  145. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +62 -0
  146. data/lib/rubocop/cop/style/hash_like_case.rb +76 -0
  147. data/lib/rubocop/cop/style/identical_conditional_branches.rb +1 -1
  148. data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
  149. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -11
  150. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -0
  151. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  152. data/lib/rubocop/cop/style/ip_addresses.rb +1 -1
  153. data/lib/rubocop/cop/style/missing_else.rb +1 -11
  154. data/lib/rubocop/cop/style/multiline_block_chain.rb +10 -1
  155. data/lib/rubocop/cop/style/multiline_if_then.rb +1 -1
  156. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +17 -6
  157. data/lib/rubocop/cop/style/mutable_constant.rb +4 -4
  158. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +2 -5
  159. data/lib/rubocop/cop/style/nested_ternary_operator.rb +27 -0
  160. data/lib/rubocop/cop/style/next.rb +2 -2
  161. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +2 -2
  162. data/lib/rubocop/cop/style/numeric_predicate.rb +3 -4
  163. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -3
  164. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  165. data/lib/rubocop/cop/style/proc.rb +1 -1
  166. data/lib/rubocop/cop/style/random_with_offset.rb +4 -10
  167. data/lib/rubocop/cop/style/redundant_assignment.rb +117 -0
  168. data/lib/rubocop/cop/style/redundant_conditional.rb +4 -3
  169. data/lib/rubocop/cop/style/redundant_exception.rb +14 -10
  170. data/lib/rubocop/cop/style/redundant_fetch_block.rb +122 -0
  171. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +50 -0
  172. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  173. data/lib/rubocop/cop/style/redundant_parentheses.rb +8 -2
  174. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -2
  175. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +4 -3
  176. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +14 -23
  177. data/lib/rubocop/cop/style/redundant_self.rb +6 -9
  178. data/lib/rubocop/cop/style/redundant_sort.rb +3 -2
  179. data/lib/rubocop/cop/style/rescue_standard_error.rb +1 -1
  180. data/lib/rubocop/cop/style/sample.rb +1 -1
  181. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  182. data/lib/rubocop/cop/style/signal_exception.rb +1 -1
  183. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +3 -2
  184. data/lib/rubocop/cop/style/stderr_puts.rb +1 -1
  185. data/lib/rubocop/cop/style/struct_inheritance.rb +23 -2
  186. data/lib/rubocop/cop/style/symbol_array.rb +5 -5
  187. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  188. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  189. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +9 -32
  190. data/lib/rubocop/cop/style/trivial_accessors.rb +8 -7
  191. data/lib/rubocop/cop/style/word_array.rb +1 -1
  192. data/lib/rubocop/cop/style/yoda_condition.rb +18 -1
  193. data/lib/rubocop/cop/style/zero_length_predicate.rb +2 -2
  194. data/lib/rubocop/cop/team.rb +105 -81
  195. data/lib/rubocop/cop/util.rb +2 -2
  196. data/lib/rubocop/cop/utils/format_string.rb +19 -2
  197. data/lib/rubocop/cop/variable_force/variable.rb +5 -3
  198. data/lib/rubocop/file_finder.rb +12 -12
  199. data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -2
  200. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  201. data/lib/rubocop/name_similarity.rb +7 -3
  202. data/lib/rubocop/options.rb +15 -8
  203. data/lib/rubocop/path_util.rb +4 -19
  204. data/lib/rubocop/platform.rb +1 -1
  205. data/lib/rubocop/rake_task.rb +6 -9
  206. data/lib/rubocop/result_cache.rb +12 -8
  207. data/lib/rubocop/rspec/cop_helper.rb +4 -4
  208. data/lib/rubocop/rspec/expect_offense.rb +65 -21
  209. data/lib/rubocop/rspec/shared_contexts.rb +19 -16
  210. data/lib/rubocop/runner.rb +34 -33
  211. data/lib/rubocop/target_finder.rb +3 -3
  212. data/lib/rubocop/target_ruby.rb +2 -2
  213. data/lib/rubocop/version.rb +1 -1
  214. metadata +34 -9
  215. data/lib/rubocop/cop/mixin/classish_length.rb +0 -37
@@ -14,7 +14,7 @@ module RuboCop
14
14
  module_function
15
15
 
16
16
  def comment_line?(line_source)
17
- line_source =~ /^\s*#/
17
+ /^\s*#/.match?(line_source)
18
18
  end
19
19
 
20
20
  def comment_lines?(node)
@@ -88,7 +88,7 @@ module RuboCop
88
88
 
89
89
  # Regex matches IF there is a ' or there is a \\ in the string that is
90
90
  # not preceded/followed by another \\ (e.g. "\\x34") but not "\\\\".
91
- string =~ /'|(?<! \\) \\{2}* \\ (?![\\"])/x
91
+ /'|(?<! \\) \\{2}* \\ (?![\\"])/x.match?(string)
92
92
  end
93
93
 
94
94
  def needs_escaping?(string)
@@ -41,8 +41,7 @@ module RuboCop
41
41
  #
42
42
  # @see https://ruby-doc.org/core-2.6.3/Kernel.html#method-i-format
43
43
  class FormatSequence
44
- attr_reader :begin_pos, :end_pos
45
- attr_reader :flags, :width, :precision, :name, :type
44
+ attr_reader :begin_pos, :end_pos, :flags, :width, :precision, :name, :type
46
45
 
47
46
  def initialize(match)
48
47
  @source = match[0]
@@ -97,6 +96,10 @@ module RuboCop
97
96
  @format_sequences ||= parse
98
97
  end
99
98
 
99
+ def valid?
100
+ !mixed_formats?
101
+ end
102
+
100
103
  def named_interpolation?
101
104
  format_sequences.any?(&:name)
102
105
  end
@@ -114,6 +117,20 @@ module RuboCop
114
117
  )
115
118
  end
116
119
  end
120
+
121
+ def mixed_formats?
122
+ formats = format_sequences.reject(&:percent?).map do |seq|
123
+ if seq.name
124
+ :named
125
+ elsif seq.max_digit_dollar_num
126
+ :numbered
127
+ else
128
+ :unnumbered
129
+ end
130
+ end
131
+
132
+ formats.uniq.size > 1
133
+ end
117
134
  end
118
135
  end
119
136
  end
@@ -42,10 +42,10 @@ module RuboCop
42
42
  def reference!(node)
43
43
  reference = Reference.new(node, @scope)
44
44
  @references << reference
45
- consumed_branches = Set.new
45
+ consumed_branches = nil
46
46
 
47
47
  @assignments.reverse_each do |assignment|
48
- next if consumed_branches.include?(assignment.branch)
48
+ next if consumed_branches&.include?(assignment.branch)
49
49
 
50
50
  assignment.reference!(node) unless assignment.run_exclusively_with?(reference)
51
51
 
@@ -58,7 +58,9 @@ module RuboCop
58
58
 
59
59
  break if !assignment.branch || assignment.branch == reference.branch
60
60
 
61
- consumed_branches << assignment.branch unless assignment.branch.may_run_incompletely?
61
+ unless assignment.branch.may_run_incompletely?
62
+ (consumed_branches ||= Set.new) << assignment.branch
63
+ end
62
64
  end
63
65
  end
64
66
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
@@ -9,33 +9,33 @@ module RuboCop
9
9
  @root_level = level
10
10
  end
11
11
 
12
- def self.root_level?(path)
13
- @root_level == path.to_s
12
+ def self.root_level?(path, stop_dir)
13
+ (@root_level || stop_dir) == path.to_s
14
14
  end
15
15
 
16
- def find_file_upwards(filename, start_dir)
17
- traverse_files_upwards(filename, start_dir) do |file|
16
+ def find_file_upwards(filename, start_dir, stop_dir = nil)
17
+ traverse_files_upwards(filename, start_dir, stop_dir) do |file|
18
18
  # minimize iteration for performance
19
19
  return file if file
20
20
  end
21
21
  end
22
22
 
23
- def find_files_upwards(filename, start_dir)
24
- files = []
25
- traverse_files_upwards(filename, start_dir) do |file|
26
- files << file
23
+ def find_last_file_upwards(filename, start_dir, stop_dir = nil)
24
+ last_file = nil
25
+ traverse_files_upwards(filename, start_dir, stop_dir) do |file|
26
+ last_file = file
27
27
  end
28
- files
28
+ last_file
29
29
  end
30
30
 
31
31
  private
32
32
 
33
- def traverse_files_upwards(filename, start_dir)
33
+ def traverse_files_upwards(filename, start_dir, stop_dir)
34
34
  Pathname.new(start_dir).expand_path.ascend do |dir|
35
- break if FileFinder.root_level?(dir)
36
-
37
35
  file = dir + filename
38
36
  yield(file.to_s) if file.exist?
37
+
38
+ break if FileFinder.root_level?(dir, stop_dir)
39
39
  end
40
40
  end
41
41
  end
@@ -74,7 +74,7 @@ module RuboCop
74
74
  end
75
75
 
76
76
  def timestamp
77
- @options[:no_auto_gen_timestamp] ? '' : "on #{Time.now} "
77
+ @options[:no_auto_gen_timestamp] ? '' : "on #{Time.now.utc} "
78
78
  end
79
79
 
80
80
  def output_offenses
@@ -115,7 +115,7 @@ module RuboCop
115
115
  output_buffer.puts "# Offense count: #{offense_count}" if @show_offense_counts
116
116
 
117
117
  cop_class = Cop::Cop.registry.find_by_cop_name(cop_name)
118
- output_buffer.puts '# Cop supports --auto-correct.' if cop_class&.new&.support_autocorrect?
118
+ output_buffer.puts '# Cop supports --auto-correct.' if cop_class&.support_autocorrect?
119
119
 
120
120
  default_cfg = default_config(cop_name)
121
121
  return unless default_cfg
@@ -82,7 +82,7 @@ module RuboCop
82
82
 
83
83
  def builtin_formatter_class(specified_key)
84
84
  matching_keys = BUILTIN_FORMATTERS_FOR_KEYS.keys.select do |key|
85
- key =~ /^\[#{specified_key}\]/ || specified_key == key.delete('[]')
85
+ /^\[#{specified_key}\]/.match?(key) || specified_key == key.delete('[]')
86
86
  end
87
87
 
88
88
  raise %(No formatter for "#{specified_key}") if matching_keys.empty?
@@ -12,13 +12,17 @@ module RuboCop
12
12
  end
13
13
 
14
14
  def find_similar_names(target_name, names)
15
+ # DidYouMean::SpellChecker is not available in all versions of Ruby, and
16
+ # even on versions where it *is* available (>= 2.3), it is not always
17
+ # required correctly. So we do a feature check first.
18
+ # See: https://github.com/rubocop-hq/rubocop/issues/7979
19
+ return [] unless defined?(DidYouMean::SpellChecker)
20
+
15
21
  names = names.dup
16
22
  names.delete(target_name)
17
23
 
18
24
  spell_checker = DidYouMean::SpellChecker.new(dictionary: names)
19
- similar_names = spell_checker.correct(target_name)
20
-
21
- similar_names
25
+ spell_checker.correct(target_name)
22
26
  end
23
27
  end
24
28
  end
@@ -163,7 +163,7 @@ module RuboCop
163
163
  end
164
164
  end
165
165
 
166
- # rubocop:disable Metrics/MethodLength
166
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
167
167
  def add_boolean_flags(opts)
168
168
  option(opts, '-F', '--fail-fast')
169
169
  option(opts, '-C', '--cache FLAG')
@@ -171,7 +171,16 @@ module RuboCop
171
171
  option(opts, '-D', '--[no-]display-cop-names')
172
172
  option(opts, '-E', '--extra-details')
173
173
  option(opts, '-S', '--display-style-guide')
174
- option(opts, '-a', '--auto-correct')
174
+ option(opts, '-a', '--auto-correct') do
175
+ @options[:safe_auto_correct] = true
176
+ end
177
+ option(opts, '--safe-auto-correct') do
178
+ warn '--safe-auto-correct is deprecated; use --auto-correct'
179
+ @options[:safe_auto_correct] = @options[:auto_correct] = true
180
+ end
181
+ option(opts, '-A', '--auto-correct-all') do
182
+ @options[:auto_correct] = true
183
+ end
175
184
  option(opts, '--disable-pending-cops')
176
185
  option(opts, '--enable-pending-cops')
177
186
  option(opts, '--ignore-disable-comments')
@@ -184,7 +193,7 @@ module RuboCop
184
193
  option(opts, '-V', '--verbose-version')
185
194
  option(opts, '-P', '--parallel')
186
195
  end
187
- # rubocop:enable Metrics/MethodLength
196
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
188
197
 
189
198
  def add_aliases(opts)
190
199
  option(opts, '-l', '--lint') do
@@ -196,9 +205,6 @@ module RuboCop
196
205
  @options[:only] << 'Layout'
197
206
  @options[:auto_correct] = true
198
207
  end
199
- option(opts, '--safe-auto-correct') do
200
- @options[:auto_correct] = true
201
- end
202
208
  end
203
209
 
204
210
  def add_list_options(opts)
@@ -465,8 +471,9 @@ module RuboCop
465
471
  lint: 'Run only lint cops.',
466
472
  safe: 'Run only safe cops.',
467
473
  list_target_files: 'List all files RuboCop will inspect.',
468
- auto_correct: 'Auto-correct offenses.',
469
- safe_auto_correct: 'Run auto-correct only when it\'s safe.',
474
+ auto_correct: 'Auto-correct offenses (only when it\'s safe).',
475
+ safe_auto_correct: '(same, deprecated)',
476
+ auto_correct_all: 'Auto-correct offenses (safe and unsafe)',
470
477
  fix_layout: 'Run only layout cops, with auto-correct on.',
471
478
  color: 'Force color output on or off.',
472
479
  version: 'Display version.',
@@ -5,7 +5,7 @@ module RuboCop
5
5
  module PathUtil
6
6
  module_function
7
7
 
8
- def relative_path(path, base_dir = PathUtil.pwd)
8
+ def relative_path(path, base_dir = Dir.pwd)
9
9
  # Optimization for the common case where path begins with the base
10
10
  # dir. Just cut off the first part.
11
11
  if path.start_with?(base_dir)
@@ -24,7 +24,7 @@ module RuboCop
24
24
 
25
25
  def smart_path(path)
26
26
  # Ideally, we calculate this relative to the project root.
27
- base_dir = PathUtil.pwd
27
+ base_dir = Dir.pwd
28
28
 
29
29
  if path.start_with? base_dir
30
30
  relative_path(path, base_dir)
@@ -40,7 +40,7 @@ module RuboCop
40
40
  hidden_file_in_not_hidden_dir?(pattern, path)
41
41
  when Regexp
42
42
  begin
43
- path =~ pattern
43
+ pattern.match?(path)
44
44
  rescue ArgumentError => e
45
45
  return false if e.message.start_with?('invalid byte sequence')
46
46
 
@@ -51,22 +51,7 @@ module RuboCop
51
51
 
52
52
  # Returns true for an absolute Unix or Windows path.
53
53
  def absolute?(path)
54
- path =~ %r{\A([A-Z]:)?/}i
55
- end
56
-
57
- def self.pwd
58
- @pwd ||= Dir.pwd
59
- end
60
-
61
- def self.reset_pwd
62
- @pwd = nil
63
- end
64
-
65
- def self.chdir(dir, &block)
66
- reset_pwd
67
- Dir.chdir(dir, &block)
68
- ensure
69
- reset_pwd
54
+ %r{\A([A-Z]:)?/}i.match?(path)
70
55
  end
71
56
 
72
57
  def hidden_file_in_not_hidden_dir?(pattern, path)
@@ -5,7 +5,7 @@ module RuboCop
5
5
  # on.
6
6
  module Platform
7
7
  def self.windows?
8
- RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/
8
+ /cygwin|mswin|mingw|bccwin|wince|emx/.match?(RbConfig::CONFIG['host_os'])
9
9
  end
10
10
  end
11
11
  end
@@ -8,14 +8,11 @@ module RuboCop
8
8
  #
9
9
  # require 'rubocop/rake_task'
10
10
  # RuboCop::RakeTask.new
11
- class RakeTask < Rake::TaskLib
12
- attr_accessor :name
13
- attr_accessor :verbose
14
- attr_accessor :fail_on_error
15
- attr_accessor :patterns
16
- attr_accessor :formatters
17
- attr_accessor :requires
18
- attr_accessor :options
11
+ #
12
+ # Use global Rake namespace here to avoid namespace issues with custom
13
+ # rubocop-rake tasks
14
+ class RakeTask < ::Rake::TaskLib
15
+ attr_accessor :name, :verbose, :fail_on_error, :patterns, :formatters, :requires, :options
19
16
 
20
17
  def initialize(name = :rubocop, *args, &task_block)
21
18
  setup_ivars(name)
@@ -69,7 +66,7 @@ module RuboCop
69
66
  task(:auto_correct, *args) do |_, task_args|
70
67
  RakeFileUtils.verbose(verbose) do
71
68
  yield(*[self, task_args].slice(0, task_block.arity)) if block_given?
72
- options = full_options.unshift('--auto-correct')
69
+ options = full_options.unshift('--auto-correct-all')
73
70
  options.delete('--parallel')
74
71
  run_cli(verbose, options)
75
72
  end
@@ -33,7 +33,7 @@ module RuboCop
33
33
 
34
34
  def requires_file_removal?(file_count, config_store)
35
35
  file_count > 1 &&
36
- file_count > config_store.for_dir('.').for_all_cops['MaxFilesInCache']
36
+ file_count > config_store.for_pwd.for_all_cops['MaxFilesInCache']
37
37
  end
38
38
 
39
39
  def remove_oldest_files(files, dirs, cache_root, verbose)
@@ -60,7 +60,7 @@ module RuboCop
60
60
  end
61
61
 
62
62
  def self.cache_root(config_store)
63
- root = config_store.for_dir('.').for_all_cops['CacheRootDirectory']
63
+ root = config_store.for_pwd.for_all_cops['CacheRootDirectory']
64
64
  root ||= if ENV.key?('XDG_CACHE_HOME')
65
65
  # Include user ID in the path to make sure the user has write
66
66
  # access.
@@ -72,7 +72,7 @@ module RuboCop
72
72
  end
73
73
 
74
74
  def self.allow_symlinks_in_cache_location?(config_store)
75
- config_store.for_dir('.').for_all_cops['AllowSymlinksInCacheRootDirectory']
75
+ config_store.for_pwd.for_all_cops['AllowSymlinksInCacheRootDirectory']
76
76
  end
77
77
 
78
78
  def initialize(file, team, options, config_store, cache_root = nil)
@@ -158,6 +158,7 @@ module RuboCop
158
158
  end
159
159
 
160
160
  # The checksum of the rubocop program running the inspection.
161
+ # rubocop:disable Metrics/AbcSize
161
162
  def rubocop_checksum
162
163
  ResultCache.source_checksum ||=
163
164
  begin
@@ -168,13 +169,16 @@ module RuboCop
168
169
  # exe directory. A change to any of them could affect the cop output
169
170
  # so we include them in the cache hash.
170
171
  source_files = $LOADED_FEATURES + Find.find(exe_root).to_a
171
- sources = source_files
172
- .select { |path| File.file?(path) }
173
- .sort
174
- .map { |path| IO.read(path, encoding: Encoding::UTF_8) }
175
- Digest::SHA1.hexdigest(sources.join)
172
+
173
+ digest = Digest::SHA1.new
174
+ source_files
175
+ .select { |path| File.file?(path) }
176
+ .sort!
177
+ .each { |path| digest << File.mtime(path).to_s }
178
+ digest.hexdigest
176
179
  end
177
180
  end
181
+ # rubocop:enable Metrics/AbcSize
178
182
 
179
183
  # Return a hash of the options given at invocation, minus the ones that have
180
184
  # no effect on which offenses and disabled line ranges are found, and thus
@@ -43,14 +43,14 @@ module CopHelper
43
43
  processed_source = parse_source(source, file)
44
44
  _investigate(cop, processed_source)
45
45
 
46
- corrector =
47
- RuboCop::Cop::Corrector.new(processed_source.buffer, cop.corrections)
48
- corrector.rewrite
46
+ @last_corrector.rewrite
49
47
  end
50
48
 
51
49
  def _investigate(cop, processed_source)
52
50
  team = RuboCop::Cop::Team.new([cop], nil, raise_error: true)
53
- team.inspect_file(processed_source)
51
+ report = team.investigate(processed_source)
52
+ @last_corrector = report.correctors.first || RuboCop::Cop::Corrector.new(processed_source)
53
+ report.offenses
54
54
  end
55
55
  end
56
56
 
@@ -72,25 +72,47 @@ module RuboCop
72
72
  #
73
73
  # expect_no_corrections
74
74
  #
75
- # If your code has variables of different lengths, you can use `%{foo}`
76
- # and `^{foo}` to format your template:
75
+ # If your code has variables of different lengths, you can use `%{foo}`,
76
+ # `^{foo}`, and `_{foo}` to format your template; you can also abbreviate
77
+ # offense messages with `[...]`:
77
78
  #
78
79
  # %w[raise fail].each do |keyword|
79
80
  # expect_offense(<<~RUBY, keyword: keyword)
80
81
  # %{keyword}(RuntimeError, msg)
81
- # ^{keyword}^^^^^^^^^^^^^^^^^^^ Redundant `RuntimeError` argument can be removed.
82
+ # ^{keyword}^^^^^^^^^^^^^^^^^^^ Redundant `RuntimeError` argument [...]
82
83
  # RUBY
84
+ #
85
+ # %w[has_one has_many].each do |type|
86
+ # expect_offense(<<~RUBY, type: type)
87
+ # class Book
88
+ # %{type} :chapter, foreign_key: 'book_id'
89
+ # _{type} ^^^^^^^^^^^^^^^^^^^^^^ Specifying the default [...]
90
+ # end
91
+ # RUBY
92
+ # end
93
+ #
94
+ # If you need to specify an offense on a blank line, use the empty `^{}` marker:
95
+ #
96
+ # @example `^{}` empty line offense
97
+ #
98
+ # expect_offense(<<~RUBY)
99
+ #
100
+ # ^{} Missing frozen string literal comment.
101
+ # puts 1
102
+ # RUBY
83
103
  module ExpectOffense
84
104
  def format_offense(source, **replacements)
85
105
  replacements.each do |keyword, value|
106
+ value = value.to_s
86
107
  source = source.gsub("%{#{keyword}}", value)
87
108
  .gsub("^{#{keyword}}", '^' * value.size)
109
+ .gsub("_{#{keyword}}", ' ' * value.size)
88
110
  end
89
111
  source
90
112
  end
91
113
 
92
114
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
93
- def expect_offense(source, file = nil, **replacements)
115
+ def expect_offense(source, file = nil, severity: nil, **replacements)
94
116
  source = format_offense(source, **replacements)
95
117
  RuboCop::Formatter::DisabledConfigFormatter
96
118
  .config_to_allow_offenses = {}
@@ -108,33 +130,32 @@ module RuboCop
108
130
 
109
131
  raise 'Error parsing example code' unless @processed_source.valid_syntax?
110
132
 
111
- _investigate(cop, @processed_source)
133
+ offenses = _investigate(cop, @processed_source)
112
134
  actual_annotations =
113
- expected_annotations.with_offense_annotations(cop.offenses)
135
+ expected_annotations.with_offense_annotations(offenses)
114
136
 
115
- expect(actual_annotations.to_s).to eq(expected_annotations.to_s)
137
+ expect(actual_annotations).to eq(expected_annotations)
138
+ expect(offenses.map(&:severity).uniq).to eq([severity]) if severity
116
139
  end
117
140
 
118
- def expect_correction(correction, loop: false)
141
+ def expect_correction(correction, loop: true)
119
142
  raise '`expect_correction` must follow `expect_offense`' unless @processed_source
120
143
 
121
144
  iteration = 0
122
145
  new_source = loop do
123
146
  iteration += 1
124
147
 
125
- corrector =
126
- RuboCop::Cop::Corrector.new(@processed_source.buffer, cop.corrections)
127
- corrected_source = corrector.rewrite
148
+ corrected_source = @last_corrector.rewrite
128
149
 
129
150
  break corrected_source unless loop
130
- break corrected_source if cop.corrections.empty?
151
+ break corrected_source if @last_corrector.empty?
152
+ break corrected_source if corrected_source == @processed_source.buffer.source
131
153
 
132
154
  if iteration > RuboCop::Runner::MAX_ITERATIONS
133
155
  raise RuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path, [])
134
156
  end
135
157
 
136
158
  # Prepare for next loop
137
- cop.instance_variable_set(:@corrections, [])
138
159
  @processed_source = parse_source(corrected_source,
139
160
  @processed_source.path)
140
161
  _investigate(cop, @processed_source)
@@ -147,30 +168,29 @@ module RuboCop
147
168
  def expect_no_corrections
148
169
  raise '`expect_no_corrections` must follow `expect_offense`' unless @processed_source
149
170
 
150
- return if cop.corrections.empty?
171
+ return if @last_corrector.empty?
151
172
 
152
173
  # In order to print a nice diff, e.g. what source got corrected to,
153
174
  # we need to run the actual corrections
154
175
 
155
- corrector =
156
- RuboCop::Cop::Corrector.new(@processed_source.buffer, cop.corrections)
157
- new_source = corrector.rewrite
176
+ new_source = @last_corrector.rewrite
158
177
 
159
178
  expect(new_source).to eq(@processed_source.buffer.source)
160
179
  end
161
180
 
162
181
  def expect_no_offenses(source, file = nil)
163
- inspect_source(source, file)
182
+ offenses = inspect_source(source, file)
164
183
 
165
184
  expected_annotations = AnnotatedSource.parse(source)
166
185
  actual_annotations =
167
- expected_annotations.with_offense_annotations(cop.offenses)
186
+ expected_annotations.with_offense_annotations(offenses)
168
187
  expect(actual_annotations.to_s).to eq(source)
169
188
  end
170
189
 
171
190
  # Parsed representation of code annotated with the `^^^ Message` style
172
191
  class AnnotatedSource
173
- ANNOTATION_PATTERN = /\A\s*\^+ /.freeze
192
+ ANNOTATION_PATTERN = /\A\s*(\^+|\^{}) /.freeze
193
+ ABBREV = "[...]\n"
174
194
 
175
195
  # @param annotated_source [String] string passed to the matchers
176
196
  #
@@ -189,6 +209,7 @@ module RuboCop
189
209
  source << source_line
190
210
  end
191
211
  end
212
+ annotations.each { |a| a[0] = 1 } if source.empty?
192
213
 
193
214
  new(source, annotations)
194
215
  end
@@ -204,6 +225,27 @@ module RuboCop
204
225
  @annotations = annotations.sort.freeze
205
226
  end
206
227
 
228
+ def ==(other)
229
+ other.is_a?(self.class) &&
230
+ other.lines == lines &&
231
+ match_annotations?(other)
232
+ end
233
+
234
+ # Dirty hack: expectations with [...] are rewritten when they match
235
+ # This way the diff is clean.
236
+ def match_annotations?(other)
237
+ annotations.zip(other.annotations) do |(_actual_line, actual_annotation),
238
+ (_expected_line, expected_annotation)|
239
+ if expected_annotation&.end_with?(ABBREV)
240
+ if actual_annotation.start_with?(expected_annotation[0...-ABBREV.length])
241
+ expected_annotation.replace(actual_annotation)
242
+ end
243
+ end
244
+ end
245
+
246
+ annotations == other.annotations
247
+ end
248
+
207
249
  # Construct annotated source string (like what we parse)
208
250
  #
209
251
  # Reconstruct a deterministic annotated source string. This is
@@ -236,6 +278,7 @@ module RuboCop
236
278
 
237
279
  reconstructed.join
238
280
  end
281
+ alias inspect to_s
239
282
 
240
283
  # Return the plain source code without annotations
241
284
  #
@@ -254,6 +297,7 @@ module RuboCop
254
297
  offenses.map do |offense|
255
298
  indent = ' ' * offense.column
256
299
  carets = '^' * offense.column_length
300
+ carets = '^{}' if offense.column_length.zero?
257
301
 
258
302
  [offense.line, "#{indent}#{carets} #{offense.message}\n"]
259
303
  end
@@ -261,7 +305,7 @@ module RuboCop
261
305
  self.class.new(lines, offense_annotations)
262
306
  end
263
307
 
264
- private
308
+ protected
265
309
 
266
310
  attr_reader :lines, :annotations
267
311
  end