rubocop 1.19.0 → 1.23.0

Sign up to get free protection for your applications and to get access to all the features.
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