rubocop 1.19.0 → 1.23.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 (236) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +129 -21
  4. data/lib/rubocop/config.rb +5 -0
  5. data/lib/rubocop/config_loader.rb +5 -3
  6. data/lib/rubocop/config_validator.rb +9 -1
  7. data/lib/rubocop/cop/base.rb +3 -3
  8. data/lib/rubocop/cop/bundler/gem_comment.rb +3 -3
  9. data/lib/rubocop/cop/bundler/gem_filename.rb +103 -0
  10. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +45 -21
  11. data/lib/rubocop/cop/bundler/ordered_gems.rb +3 -12
  12. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
  13. data/lib/rubocop/cop/correctors/line_break_corrector.rb +1 -1
  14. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +11 -10
  15. data/lib/rubocop/cop/documentation.rb +1 -1
  16. data/lib/rubocop/cop/gemspec/date_assignment.rb +2 -10
  17. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -10
  18. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +3 -12
  19. data/lib/rubocop/cop/gemspec/require_mfa.rb +146 -0
  20. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +31 -24
  21. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -10
  22. data/lib/rubocop/cop/generator.rb +14 -8
  23. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +60 -0
  24. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  25. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  26. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  27. data/lib/rubocop/cop/layout/assignment_indentation.rb +1 -1
  28. data/lib/rubocop/cop/layout/block_alignment.rb +3 -3
  29. data/lib/rubocop/cop/layout/class_structure.rb +2 -1
  30. data/lib/rubocop/cop/layout/dot_position.rb +34 -5
  31. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  32. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +22 -1
  33. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +7 -4
  34. data/lib/rubocop/cop/layout/end_alignment.rb +2 -3
  35. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  36. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
  37. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  38. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  39. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  40. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  41. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  42. data/lib/rubocop/cop/layout/line_length.rb +9 -7
  43. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  44. data/lib/rubocop/cop/layout/multiline_block_layout.rb +3 -3
  45. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +3 -0
  46. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
  47. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -0
  48. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +6 -5
  49. data/lib/rubocop/cop/layout/single_line_block_chain.rb +15 -4
  50. data/lib/rubocop/cop/layout/space_after_not.rb +1 -0
  51. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -1
  52. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  53. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -0
  54. data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
  55. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +11 -5
  56. data/lib/rubocop/cop/layout/space_inside_parens.rb +74 -28
  57. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
  58. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +111 -0
  59. data/lib/rubocop/cop/lint/ambiguous_range.rb +11 -11
  60. data/lib/rubocop/cop/lint/assignment_in_condition.rb +7 -5
  61. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +18 -5
  62. data/lib/rubocop/cop/lint/boolean_symbol.rb +5 -0
  63. data/lib/rubocop/cop/lint/debugger.rb +2 -4
  64. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  65. data/lib/rubocop/cop/lint/deprecated_constants.rb +3 -2
  66. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +24 -1
  67. data/lib/rubocop/cop/lint/else_layout.rb +10 -6
  68. data/lib/rubocop/cop/lint/empty_in_pattern.rb +1 -1
  69. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  70. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  71. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +12 -3
  72. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +67 -0
  73. data/lib/rubocop/cop/lint/interpolation_check.rb +5 -0
  74. data/lib/rubocop/cop/lint/loop.rb +4 -3
  75. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +5 -1
  76. data/lib/rubocop/cop/lint/number_conversion.rb +16 -2
  77. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  78. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +4 -2
  79. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +17 -0
  80. data/lib/rubocop/cop/lint/percent_string_array.rb +10 -0
  81. data/lib/rubocop/cop/lint/raise_exception.rb +4 -0
  82. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +5 -4
  83. data/lib/rubocop/cop/lint/require_relative_self_path.rb +50 -0
  84. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -1
  85. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  86. data/lib/rubocop/cop/lint/triple_quotes.rb +1 -1
  87. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +8 -3
  88. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -3
  89. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -2
  90. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +117 -0
  91. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  92. data/lib/rubocop/cop/lint/useless_times.rb +4 -3
  93. data/lib/rubocop/cop/metrics/abc_size.rb +6 -0
  94. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  95. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  96. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  97. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  98. data/lib/rubocop/cop/mixin/annotation_comment.rb +57 -34
  99. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  100. data/lib/rubocop/cop/mixin/documentation_comment.rb +5 -2
  101. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -2
  102. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +23 -1
  103. data/lib/rubocop/cop/mixin/gemspec_help.rb +30 -0
  104. data/lib/rubocop/cop/mixin/hash_transform_method.rb +3 -3
  105. data/lib/rubocop/cop/mixin/heredoc.rb +1 -3
  106. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  107. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  108. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  109. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +9 -1
  110. data/lib/rubocop/cop/mixin/percent_array.rb +11 -3
  111. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +9 -1
  112. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  113. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  114. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  115. data/lib/rubocop/cop/mixin/trailing_body.rb +1 -1
  116. data/lib/rubocop/cop/naming/ascii_identifiers.rb +0 -3
  117. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  118. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  119. data/lib/rubocop/cop/naming/file_name.rb +37 -4
  120. data/lib/rubocop/cop/naming/inclusive_language.rb +9 -9
  121. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +5 -4
  122. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +7 -0
  123. data/lib/rubocop/cop/security/io_methods.rb +49 -0
  124. data/lib/rubocop/cop/security/json_load.rb +8 -7
  125. data/lib/rubocop/cop/security/open.rb +4 -0
  126. data/lib/rubocop/cop/security/yaml_load.rb +4 -0
  127. data/lib/rubocop/cop/style/accessor_grouping.rb +2 -2
  128. data/lib/rubocop/cop/style/and_or.rb +5 -0
  129. data/lib/rubocop/cop/style/arguments_forwarding.rb +13 -2
  130. data/lib/rubocop/cop/style/array_coercion.rb +21 -3
  131. data/lib/rubocop/cop/style/ascii_comments.rb +0 -3
  132. data/lib/rubocop/cop/style/block_delimiters.rb +23 -6
  133. data/lib/rubocop/cop/style/case_equality.rb +6 -9
  134. data/lib/rubocop/cop/style/case_like_if.rb +5 -0
  135. data/lib/rubocop/cop/style/class_and_module_children.rb +9 -0
  136. data/lib/rubocop/cop/style/collection_compact.rb +7 -5
  137. data/lib/rubocop/cop/style/collection_methods.rb +8 -6
  138. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  139. data/lib/rubocop/cop/style/comment_annotation.rb +25 -39
  140. data/lib/rubocop/cop/style/commented_keyword.rb +9 -4
  141. data/lib/rubocop/cop/style/date_time.rb +5 -0
  142. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  143. data/lib/rubocop/cop/style/documentation.rb +23 -8
  144. data/lib/rubocop/cop/style/double_negation.rb +27 -6
  145. data/lib/rubocop/cop/style/empty_method.rb +2 -2
  146. data/lib/rubocop/cop/style/encoding.rb +26 -15
  147. data/lib/rubocop/cop/style/explicit_block_argument.rb +21 -11
  148. data/lib/rubocop/cop/style/float_division.rb +10 -2
  149. data/lib/rubocop/cop/style/format_string_token.rb +2 -1
  150. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +7 -2
  151. data/lib/rubocop/cop/style/global_std_stream.rb +4 -0
  152. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +11 -0
  153. data/lib/rubocop/cop/style/hash_each_methods.rb +5 -0
  154. data/lib/rubocop/cop/style/hash_except.rb +4 -3
  155. data/lib/rubocop/cop/style/hash_transform_keys.rb +4 -6
  156. data/lib/rubocop/cop/style/hash_transform_values.rb +4 -6
  157. data/lib/rubocop/cop/style/identical_conditional_branches.rb +18 -16
  158. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +18 -4
  159. data/lib/rubocop/cop/style/infinite_loop.rb +4 -3
  160. data/lib/rubocop/cop/style/inverse_methods.rb +9 -2
  161. data/lib/rubocop/cop/style/lambda_call.rb +1 -1
  162. data/lib/rubocop/cop/style/line_end_concatenation.rb +14 -1
  163. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -6
  164. data/lib/rubocop/cop/style/module_function.rb +8 -9
  165. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  166. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -1
  167. data/lib/rubocop/cop/style/mutable_constant.rb +73 -6
  168. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  169. data/lib/rubocop/cop/style/negated_unless.rb +1 -1
  170. data/lib/rubocop/cop/style/non_nil_check.rb +2 -2
  171. data/lib/rubocop/cop/style/not.rb +2 -2
  172. data/lib/rubocop/cop/style/numbered_parameters.rb +46 -0
  173. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +50 -0
  174. data/lib/rubocop/cop/style/numeric_literals.rb +7 -8
  175. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -0
  176. data/lib/rubocop/cop/style/open_struct_use.rb +69 -0
  177. data/lib/rubocop/cop/style/optional_arguments.rb +4 -0
  178. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +14 -4
  179. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  180. data/lib/rubocop/cop/style/parentheses_around_condition.rb +12 -2
  181. data/lib/rubocop/cop/style/percent_q_literals.rb +2 -2
  182. data/lib/rubocop/cop/style/preferred_hash_methods.rb +9 -4
  183. data/lib/rubocop/cop/style/quoted_symbols.rb +21 -7
  184. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  185. data/lib/rubocop/cop/style/redundant_argument.rb +19 -9
  186. data/lib/rubocop/cop/style/redundant_begin.rb +25 -0
  187. data/lib/rubocop/cop/style/redundant_condition.rb +2 -3
  188. data/lib/rubocop/cop/style/redundant_fetch_block.rb +4 -0
  189. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +12 -3
  190. data/lib/rubocop/cop/style/redundant_freeze.rb +4 -4
  191. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  192. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -3
  193. data/lib/rubocop/cop/style/redundant_self.rb +10 -0
  194. data/lib/rubocop/cop/style/redundant_self_assignment.rb +4 -3
  195. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +23 -28
  196. data/lib/rubocop/cop/style/redundant_sort.rb +51 -18
  197. data/lib/rubocop/cop/style/regexp_literal.rb +3 -3
  198. data/lib/rubocop/cop/style/return_nil.rb +2 -1
  199. data/lib/rubocop/cop/style/safe_navigation.rb +13 -2
  200. data/lib/rubocop/cop/style/select_by_regexp.rb +139 -0
  201. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -0
  202. data/lib/rubocop/cop/style/slicing_with_range.rb +13 -0
  203. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -0
  204. data/lib/rubocop/cop/style/special_global_vars.rb +4 -0
  205. data/lib/rubocop/cop/style/static_class.rb +5 -5
  206. data/lib/rubocop/cop/style/string_chars.rb +4 -2
  207. data/lib/rubocop/cop/style/string_concatenation.rb +5 -1
  208. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -0
  209. data/lib/rubocop/cop/style/struct_inheritance.rb +4 -0
  210. data/lib/rubocop/cop/style/swap_values.rb +4 -2
  211. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  212. data/lib/rubocop/cop/style/symbol_proc.rb +26 -0
  213. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +19 -0
  214. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  215. data/lib/rubocop/cop/style/word_array.rb +3 -3
  216. data/lib/rubocop/cop/style/yoda_condition.rb +24 -7
  217. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -0
  218. data/lib/rubocop/cop/util.rb +15 -4
  219. data/lib/rubocop/cops_documentation_generator.rb +17 -5
  220. data/lib/rubocop/formatter/html_formatter.rb +5 -2
  221. data/lib/rubocop/formatter/json_formatter.rb +4 -1
  222. data/lib/rubocop/magic_comment.rb +44 -15
  223. data/lib/rubocop/options.rb +126 -112
  224. data/lib/rubocop/rake_task.rb +1 -1
  225. data/lib/rubocop/remote_config.rb +1 -1
  226. data/lib/rubocop/result_cache.rb +3 -3
  227. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  228. data/lib/rubocop/rspec/expect_offense.rb +6 -2
  229. data/lib/rubocop/rspec/parallel_formatter.rb +90 -0
  230. data/lib/rubocop/rspec/support.rb +1 -0
  231. data/lib/rubocop/runner.rb +2 -3
  232. data/lib/rubocop/target_finder.rb +1 -1
  233. data/lib/rubocop/version.rb +1 -1
  234. data/lib/rubocop/yaml_duplication_checker.rb +1 -1
  235. data/lib/rubocop.rb +14 -2
  236. metadata +20 -5
@@ -5,6 +5,10 @@ module RuboCop
5
5
  module Style
6
6
  # This cop checks for inheritance from Struct.new.
7
7
  #
8
+ # @safety
9
+ # Auto-correction is unsafe because it will change the inheritance
10
+ # tree (e.g. return value of `Module#ancestors`) of the constant.
11
+ #
8
12
  # @example
9
13
  # # bad
10
14
  # class Person < Struct.new(:first_name, :last_name)
@@ -4,8 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # This cop enforces the use of shorthand-style swapping of 2 variables.
7
- # Its autocorrection is marked as unsafe, because it can erroneously remove
8
- # the temporary variable which is used later.
7
+ #
8
+ # @safety
9
+ # Autocorrection is unsafe, because the temporary variable used to
10
+ # swap variables will be removed, but may be referred to elsewhere.
9
11
  #
10
12
  # @example
11
13
  # # bad
@@ -35,7 +35,7 @@ module RuboCop
35
35
  extend AutoCorrector
36
36
 
37
37
  PERCENT_MSG = 'Use `%i` or `%I` for an array of symbols.'
38
- ARRAY_MSG = 'Use `[]` for an array of symbols.'
38
+ ARRAY_MSG = 'Use `%<prefer>s` for an array of symbols.'
39
39
 
40
40
  class << self
41
41
  attr_accessor :largest_brackets
@@ -60,7 +60,7 @@ module RuboCop
60
60
  end
61
61
  end
62
62
 
63
- def correct_bracketed(corrector, node)
63
+ def build_bracketed_array(node)
64
64
  syms = node.children.map do |c|
65
65
  if c.dsym_type?
66
66
  string_literal = to_string_literal(c.source)
@@ -71,7 +71,7 @@ module RuboCop
71
71
  end
72
72
  end
73
73
 
74
- corrector.replace(node, "[#{syms.join(', ')}]")
74
+ "[#{syms.join(', ')}]"
75
75
  end
76
76
 
77
77
  def to_symbol_literal(string)
@@ -8,6 +8,32 @@ module RuboCop
8
8
  # If you prefer a style that allows block for method with arguments,
9
9
  # please set `true` to `AllowMethodsWithArguments`.
10
10
  #
11
+ # @safety
12
+ # This cop is unsafe because `proc`s and blocks work differently
13
+ # when additional arguments are passed in. A block will silently
14
+ # ignore additional arguments, but a `proc` will raise
15
+ # an `ArgumentError`.
16
+ #
17
+ # For example:
18
+ #
19
+ # [source,ruby]
20
+ # ----
21
+ # class Foo
22
+ # def bar
23
+ # :bar
24
+ # end
25
+ # end
26
+ #
27
+ # def call(options = {}, &block)
28
+ # block.call(Foo.new, options)
29
+ # end
30
+ #
31
+ # call { |x| x.bar }
32
+ # #=> :bar
33
+ # call(&:bar)
34
+ # # ArgumentError: wrong number of arguments (given 1, expected 0)
35
+ # ----
36
+ #
11
37
  # @example
12
38
  # # bad
13
39
  # something.map { |s| s.upcase }
@@ -8,6 +8,25 @@ module RuboCop
8
8
  # that comma to be present. Blocks with more than one argument never
9
9
  # require a trailing comma.
10
10
  #
11
+ # @safety
12
+ # This cop is unsafe because a trailing comma can indicate there are
13
+ # more parameters that are not used.
14
+ #
15
+ # For example:
16
+ # [source,ruby]
17
+ # ----
18
+ # # with a trailing comma
19
+ # {foo: 1, bar: 2, baz: 3}.map {|key,| key }
20
+ # #=> [:foo, :bar, :baz]
21
+ #
22
+ # # without a trailing comma
23
+ # {foo: 1, bar: 2, baz: 3}.map {|key| key }
24
+ # #=> [[:foo, 1], [:bar, 2], [:baz, 3]]
25
+ # ----
26
+ #
27
+ # This can be fixed by replacing the trailing comma with a placeholder
28
+ # argument (such as `|key, _value|`).
29
+ #
11
30
  # @example
12
31
  # # bad
13
32
  # add { |foo, bar,| foo + bar }
@@ -192,7 +192,7 @@ module RuboCop
192
192
 
193
193
  def allowed_method_name?(node)
194
194
  allowed_method_names.include?(node.method_name) ||
195
- exact_name_match? && !names_match?(node)
195
+ (exact_name_match? && !names_match?(node))
196
196
  end
197
197
 
198
198
  def allowed_writer?(method_name)
@@ -44,7 +44,7 @@ module RuboCop
44
44
  extend AutoCorrector
45
45
 
46
46
  PERCENT_MSG = 'Use `%w` or `%W` for an array of words.'
47
- ARRAY_MSG = 'Use `[]` for an array of words.'
47
+ ARRAY_MSG = 'Use `%<prefer>s` for an array of words.'
48
48
 
49
49
  class << self
50
50
  attr_accessor :largest_brackets
@@ -82,7 +82,7 @@ module RuboCop
82
82
  Regexp.new(cop_config['WordRegex'])
83
83
  end
84
84
 
85
- def correct_bracketed(corrector, node)
85
+ def build_bracketed_array(node)
86
86
  words = node.children.map do |word|
87
87
  if word.dstr_type?
88
88
  string_literal = to_string_literal(word.source)
@@ -93,7 +93,7 @@ module RuboCop
93
93
  end
94
94
  end
95
95
 
96
- corrector.replace(node, "[#{words.join(', ')}]")
96
+ "[#{words.join(', ')}]"
97
97
  end
98
98
  end
99
99
  end
@@ -7,6 +7,26 @@ module RuboCop
7
7
  # i.e. comparison operations where the order of expression is reversed.
8
8
  # eg. `5 == x`
9
9
  #
10
+ # @safety
11
+ # This cop is unsafe because comparison operators can be defined
12
+ # differently on different classes, and are not guaranteed to
13
+ # have the same result if reversed.
14
+ #
15
+ # For example:
16
+ #
17
+ # [source,ruby]
18
+ # ----
19
+ # class MyKlass
20
+ # def ==(other)
21
+ # true
22
+ # end
23
+ # end
24
+ #
25
+ # obj = MyKlass.new
26
+ # obj == 'string' #=> true
27
+ # 'string' == obj #=> false
28
+ # ----
29
+ #
10
30
  # @example EnforcedStyle: forbid_for_all_comparison_operators (default)
11
31
  # # bad
12
32
  # 99 == foo
@@ -58,14 +78,11 @@ module RuboCop
58
78
  extend AutoCorrector
59
79
 
60
80
  MSG = 'Reverse the order of the operands `%<source>s`.'
61
-
62
81
  REVERSE_COMPARISON = { '<' => '>', '<=' => '>=', '>' => '<', '>=' => '<=' }.freeze
63
-
64
82
  EQUALITY_OPERATORS = %i[== !=].freeze
65
-
66
83
  NONCOMMUTATIVE_OPERATORS = %i[===].freeze
67
-
68
84
  PROGRAM_NAMES = %i[$0 $PROGRAM_NAME].freeze
85
+ RESTRICT_ON_SEND = RuboCop::AST::Node::COMPARISON_OPERATORS
69
86
 
70
87
  # @!method file_constant_equal_program_name?(node)
71
88
  def_node_matcher :file_constant_equal_program_name?, <<~PATTERN
@@ -74,7 +91,7 @@ module RuboCop
74
91
 
75
92
  def on_send(node)
76
93
  return unless yoda_compatible_condition?(node)
77
- return if equality_only? && non_equality_operator?(node) ||
94
+ return if (equality_only? && non_equality_operator?(node)) ||
78
95
  file_constant_equal_program_name?(node)
79
96
 
80
97
  valid_yoda?(node) || add_offense(node) do |corrector|
@@ -102,8 +119,8 @@ module RuboCop
102
119
  lhs = node.receiver
103
120
  rhs = node.first_argument
104
121
 
105
- return true if lhs.literal? && rhs.literal? ||
106
- !lhs.literal? && !rhs.literal? ||
122
+ return true if (lhs.literal? && rhs.literal?) ||
123
+ (!lhs.literal? && !rhs.literal?) ||
107
124
  interpolation?(lhs)
108
125
 
109
126
  enforce_yoda? ? lhs.literal? : rhs.literal?
@@ -9,6 +9,12 @@ module RuboCop
9
9
  # receiver.length < 1 and receiver.size == 0 that can be
10
10
  # replaced by receiver.empty? and !receiver.empty?.
11
11
  #
12
+ # @safety
13
+ # This cop is unsafe because it cannot be guaranteed that the receiver
14
+ # has an `empty?` method that is defined in terms of `length`. If there
15
+ # is a non-standard class that redefines `length` or `empty?`, the cop
16
+ # may register a false positive.
17
+ #
12
18
  # @example
13
19
  # # bad
14
20
  # [1, 2, 3].length == 0
@@ -113,7 +113,8 @@ module RuboCop
113
113
  if needs_escaping?(string) && compatible_external_encoding_for?(string)
114
114
  string.inspect
115
115
  else
116
- "'#{string.gsub('\\') { '\\\\' }}'"
116
+ # In a single-quoted strings, double quotes don't need to be escaped
117
+ "'#{string.gsub('\"', '"').gsub('\\') { '\\\\' }}'"
117
118
  end
118
119
  end
119
120
 
@@ -125,12 +126,22 @@ module RuboCop
125
126
  StringInterpreter.interpret(string)
126
127
  end
127
128
 
129
+ def line(node_or_range)
130
+ if node_or_range.respond_to?(:line)
131
+ node_or_range.line
132
+ elsif node_or_range.respond_to?(:loc)
133
+ node_or_range.loc.line
134
+ end
135
+ end
136
+
128
137
  def same_line?(node1, node2)
129
- node1.respond_to?(:loc) && node2.respond_to?(:loc) && node1.loc.line == node2.loc.line
138
+ line1 = line(node1)
139
+ line2 = line(node2)
140
+ line1 && line2 && line1 == line2
130
141
  end
131
142
 
132
- def indent(node)
133
- ' ' * node.loc.column
143
+ def indent(node, offset: 0)
144
+ ' ' * (node.loc.column + offset)
134
145
  end
135
146
 
136
147
  def to_supported_styles(enforced_style)
@@ -33,11 +33,12 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
33
33
  cops.with_department(department).sort!
34
34
  end
35
35
 
36
- def cops_body(cop, description, examples_objects, pars)
36
+ def cops_body(cop, description, examples_objects, safety_objects, pars) # rubocop:disable Metrics/AbcSize
37
37
  content = h2(cop.cop_name)
38
38
  content << required_ruby_version(cop)
39
39
  content << properties(cop)
40
40
  content << "#{description}\n"
41
+ content << safety_object(safety_objects) if safety_objects.any? { |s| !s.text.blank? }
41
42
  content << examples(examples_objects) if examples_objects.count.positive?
42
43
  content << configurations(pars)
43
44
  content << references(cop)
@@ -52,6 +53,16 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
52
53
  end
53
54
  end
54
55
 
56
+ def safety_object(safety_object_objects)
57
+ safety_object_objects.each_with_object(h3('Safety').dup) do |safety_object, content|
58
+ next if safety_object.text.blank?
59
+
60
+ content << "\n" unless content.end_with?("\n\n")
61
+ content << safety_object.text
62
+ content << "\n"
63
+ end
64
+ end
65
+
55
66
  def required_ruby_version(cop)
56
67
  return '' unless cop.respond_to?(:required_minimum_ruby_version)
57
68
 
@@ -61,8 +72,8 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
61
72
  # rubocop:disable Metrics/MethodLength
62
73
  def properties(cop)
63
74
  header = [
64
- 'Enabled by default', 'Safe', 'Supports autocorrection', 'VersionAdded',
65
- 'VersionChanged'
75
+ 'Enabled by default', 'Safe', 'Supports autocorrection', 'Version Added',
76
+ 'Version Changed'
66
77
  ]
67
78
  autocorrect = if cop.support_autocorrect?
68
79
  "Yes#{' (Unsafe)' unless cop.new(config).safe_autocorrect?}"
@@ -217,12 +228,13 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
217
228
  ]
218
229
  pars = cop_config.reject { |k| non_display_keys.include? k }
219
230
  description = 'No documentation'
220
- examples_object = []
231
+ examples_object = safety_object = []
221
232
  cop_code(cop) do |code_object|
222
233
  description = code_object.docstring unless code_object.docstring.blank?
223
234
  examples_object = code_object.tags('example')
235
+ safety_object = code_object.tags('safety')
224
236
  end
225
- cops_body(cop, description, examples_object, pars)
237
+ cops_body(cop, description, examples_object, safety_object, pars)
226
238
  end
227
239
 
228
240
  def cop_code(cop)
@@ -23,12 +23,15 @@ module RuboCop
23
23
  end
24
24
  end
25
25
 
26
+ Summary = Struct.new(:offense_count, :inspected_files, :target_files, keyword_init: true)
27
+ FileOffenses = Struct.new(:path, :offenses, keyword_init: true)
28
+
26
29
  attr_reader :files, :summary
27
30
 
28
31
  def initialize(output, options = {})
29
32
  super
30
33
  @files = []
31
- @summary = OpenStruct.new(offense_count: 0)
34
+ @summary = Summary.new(offense_count: 0)
32
35
  end
33
36
 
34
37
  def started(target_files)
@@ -36,7 +39,7 @@ module RuboCop
36
39
  end
37
40
 
38
41
  def file_finished(file, offenses)
39
- files << OpenStruct.new(path: file, offenses: offenses)
42
+ files << FileOffenses.new(path: file, offenses: offenses)
40
43
  summary.offense_count += offenses.count
41
44
  end
42
45
 
@@ -59,12 +59,15 @@ module RuboCop
59
59
  end
60
60
 
61
61
  # TODO: Consider better solution for Offense#real_column.
62
+ # The minimum value of `start_column: real_column` is 1.
63
+ # So, the minimum value of `last_column` should be 1.
64
+ # And non-zero value of `last_column` should be used as is.
62
65
  def hash_for_location(offense)
63
66
  {
64
67
  start_line: offense.line,
65
68
  start_column: offense.real_column,
66
69
  last_line: offense.last_line,
67
- last_column: offense.last_column,
70
+ last_column: offense.last_column.zero? ? 1 : offense.last_column,
68
71
  length: offense.location.length,
69
72
  # `line` and `column` exist for compatibility.
70
73
  # Use `start_line` and `start_column` instead.
@@ -7,6 +7,11 @@ module RuboCop
7
7
  class MagicComment
8
8
  # @see https://git.io/vMC1C IRB's pattern for matching magic comment tokens
9
9
  TOKEN = /[[:alnum:]\-_]+/.freeze
10
+ KEYWORDS = {
11
+ encoding: '(?:en)?coding',
12
+ frozen_string_literal: 'frozen[_-]string[_-]literal',
13
+ shareable_constant_value: 'shareable[_-]constant[_-]value'
14
+ }.freeze
10
15
 
11
16
  # Detect magic comment format and pass it to the appropriate wrapper.
12
17
  #
@@ -15,8 +20,8 @@ module RuboCop
15
20
  # @return [RuboCop::MagicComment]
16
21
  def self.parse(comment)
17
22
  case comment
18
- when EmacsComment::FORMAT then EmacsComment.new(comment)
19
- when VimComment::FORMAT then VimComment.new(comment)
23
+ when EmacsComment::REGEXP then EmacsComment.new(comment)
24
+ when VimComment::REGEXP then VimComment.new(comment)
20
25
  else
21
26
  SimpleComment.new(comment)
22
27
  end
@@ -30,6 +35,10 @@ module RuboCop
30
35
  frozen_string_literal_specified? || encoding_specified? || shareable_constant_value_specified?
31
36
  end
32
37
 
38
+ def valid?
39
+ @comment.start_with?('#') && any?
40
+ end
41
+
33
42
  # Does the magic comment enable the frozen string literal feature.
34
43
  #
35
44
  # Test whether the frozen string literal value is `true`. Cannot
@@ -111,6 +120,18 @@ module RuboCop
111
120
  #
112
121
  # @abstract
113
122
  class EditorComment < MagicComment
123
+ def encoding
124
+ match(self.class::KEYWORDS[:encoding])
125
+ end
126
+
127
+ # Rewrite the comment without a given token type
128
+ def without(type)
129
+ remaining = tokens.grep_v(/\A#{self.class::KEYWORDS[type.to_sym]}/)
130
+ return '' if remaining.empty?
131
+
132
+ self.class::FORMAT % remaining.join(self.class::SEPARATOR)
133
+ end
134
+
114
135
  private
115
136
 
116
137
  # Find a token starting with the provided keyword and extract its value.
@@ -135,7 +156,7 @@ module RuboCop
135
156
  #
136
157
  # @return [Array<String>]
137
158
  def tokens
138
- extract(self.class::FORMAT).split(self.class::SEPARATOR).map(&:strip)
159
+ extract(self.class::REGEXP).split(self.class::SEPARATOR).map(&:strip)
139
160
  end
140
161
  end
141
162
 
@@ -151,22 +172,19 @@ module RuboCop
151
172
  # @see https://www.gnu.org/software/emacs/manual/html_node/emacs/Specify-Coding.html
152
173
  # @see https://git.io/vMCXh Emacs handling in Ruby's parse.y
153
174
  class EmacsComment < EditorComment
154
- FORMAT = /-\*-(.+)-\*-/.freeze
175
+ REGEXP = /-\*-(.+)-\*-/.freeze
176
+ FORMAT = '# -*- %s -*-'
155
177
  SEPARATOR = ';'
156
178
  OPERATOR = ':'
157
179
 
158
- def encoding
159
- match('(?:en)?coding')
160
- end
161
-
162
180
  private
163
181
 
164
182
  def extract_frozen_string_literal
165
- match('frozen[_-]string[_-]literal')
183
+ match(KEYWORDS[:frozen_string_literal])
166
184
  end
167
185
 
168
186
  def extract_shareable_constant_value
169
- match('shareable[_-]constant[_-]values')
187
+ match(KEYWORDS[:shareable_constant_value])
170
188
  end
171
189
  end
172
190
 
@@ -179,9 +197,11 @@ module RuboCop
179
197
  #
180
198
  # comment.encoding # => 'ascii-8bit'
181
199
  class VimComment < EditorComment
182
- FORMAT = /#\s*vim:\s*(.+)/.freeze
200
+ REGEXP = /#\s*vim:\s*(.+)/.freeze
201
+ FORMAT = '# vim: %s'
183
202
  SEPARATOR = ', '
184
203
  OPERATOR = '='
204
+ KEYWORDS = MagicComment::KEYWORDS.merge(encoding: 'fileencoding').freeze
185
205
 
186
206
  # For some reason the fileencoding keyword only works if there
187
207
  # is at least one other token included in the string. For example
@@ -193,7 +213,7 @@ module RuboCop
193
213
  # # vim: foo=bar, fileencoding=ascii-8bit
194
214
  #
195
215
  def encoding
196
- match('fileencoding') if tokens.size > 1
216
+ super if tokens.size > 1
197
217
  end
198
218
 
199
219
  # Vim comments cannot specify frozen string literal behavior.
@@ -219,7 +239,16 @@ module RuboCop
219
239
  class SimpleComment < MagicComment
220
240
  # Match `encoding` or `coding`
221
241
  def encoding
222
- extract(/\A\s*\#.*\b(?:en)?coding: (#{TOKEN})/io)
242
+ extract(/\A\s*\#.*\b#{KEYWORDS[:encoding]}: (#{TOKEN})/io)
243
+ end
244
+
245
+ # Rewrite the comment without a given token type
246
+ def without(type)
247
+ if @comment.match?(/\A#\s*#{self.class::KEYWORDS[type.to_sym]}/)
248
+ ''
249
+ else
250
+ @comment
251
+ end
223
252
  end
224
253
 
225
254
  private
@@ -232,11 +261,11 @@ module RuboCop
232
261
  # Case-insensitive and dashes/underscores are acceptable.
233
262
  # @see https://git.io/vM7Mg
234
263
  def extract_frozen_string_literal
235
- extract(/\A\s*#\s*frozen[_-]string[_-]literal:\s*(#{TOKEN})\s*\z/io)
264
+ extract(/\A\s*#\s*#{KEYWORDS[:frozen_string_literal]}:\s*(#{TOKEN})\s*\z/io)
236
265
  end
237
266
 
238
267
  def extract_shareable_constant_value
239
- extract(/\A\s*#\s*shareable[_-]constant[_-]value:\s*(#{TOKEN})\s*\z/io)
268
+ extract(/\A\s*#\s*#{KEYWORDS[:shareable_constant_value]}:\s*(#{TOKEN})\s*\z/io)
240
269
  end
241
270
  end
242
271
  end