rubocop 1.19.1 → 1.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +86 -14
  4. data/lib/rubocop/config.rb +5 -0
  5. data/lib/rubocop/config_loader.rb +4 -2
  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_filename.rb +103 -0
  9. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +45 -21
  10. data/lib/rubocop/cop/bundler/ordered_gems.rb +3 -12
  11. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
  12. data/lib/rubocop/cop/correctors/line_break_corrector.rb +1 -1
  13. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +11 -10
  14. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +3 -12
  15. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +1 -1
  16. data/lib/rubocop/cop/generator.rb +14 -8
  17. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  18. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  19. data/lib/rubocop/cop/layout/class_structure.rb +2 -1
  20. data/lib/rubocop/cop/layout/dot_position.rb +31 -4
  21. data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
  22. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  23. data/lib/rubocop/cop/layout/line_length.rb +8 -6
  24. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  25. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +3 -0
  26. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -0
  27. data/lib/rubocop/cop/layout/single_line_block_chain.rb +15 -4
  28. data/lib/rubocop/cop/layout/space_after_not.rb +1 -0
  29. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -1
  30. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  31. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -0
  32. data/lib/rubocop/cop/layout/space_inside_parens.rb +74 -24
  33. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
  34. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +111 -0
  35. data/lib/rubocop/cop/lint/ambiguous_range.rb +8 -8
  36. data/lib/rubocop/cop/lint/assignment_in_condition.rb +7 -5
  37. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +18 -5
  38. data/lib/rubocop/cop/lint/boolean_symbol.rb +5 -0
  39. data/lib/rubocop/cop/lint/debugger.rb +2 -4
  40. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  41. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +24 -1
  42. data/lib/rubocop/cop/lint/else_layout.rb +9 -5
  43. data/lib/rubocop/cop/lint/empty_in_pattern.rb +1 -1
  44. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  45. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  46. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +12 -3
  47. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +67 -0
  48. data/lib/rubocop/cop/lint/interpolation_check.rb +5 -0
  49. data/lib/rubocop/cop/lint/loop.rb +4 -3
  50. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +5 -1
  51. data/lib/rubocop/cop/lint/number_conversion.rb +12 -1
  52. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  53. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +4 -2
  54. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +17 -0
  55. data/lib/rubocop/cop/lint/percent_string_array.rb +10 -0
  56. data/lib/rubocop/cop/lint/raise_exception.rb +4 -0
  57. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +5 -4
  58. data/lib/rubocop/cop/lint/require_relative_self_path.rb +50 -0
  59. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -1
  60. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  61. data/lib/rubocop/cop/lint/triple_quotes.rb +1 -1
  62. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +8 -3
  63. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -3
  64. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -2
  65. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  66. data/lib/rubocop/cop/lint/useless_times.rb +4 -3
  67. data/lib/rubocop/cop/metrics/abc_size.rb +6 -0
  68. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  69. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  70. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  71. data/lib/rubocop/cop/mixin/annotation_comment.rb +57 -34
  72. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  73. data/lib/rubocop/cop/mixin/documentation_comment.rb +5 -2
  74. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +23 -1
  75. data/lib/rubocop/cop/mixin/heredoc.rb +1 -3
  76. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  77. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +9 -1
  78. data/lib/rubocop/cop/mixin/percent_array.rb +6 -1
  79. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +9 -1
  80. data/lib/rubocop/cop/mixin/string_literals_help.rb +5 -1
  81. data/lib/rubocop/cop/naming/ascii_identifiers.rb +0 -3
  82. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  83. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  84. data/lib/rubocop/cop/naming/inclusive_language.rb +9 -9
  85. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +5 -4
  86. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +7 -0
  87. data/lib/rubocop/cop/security/io_methods.rb +49 -0
  88. data/lib/rubocop/cop/security/json_load.rb +8 -7
  89. data/lib/rubocop/cop/security/open.rb +4 -0
  90. data/lib/rubocop/cop/security/yaml_load.rb +4 -0
  91. data/lib/rubocop/cop/style/accessor_grouping.rb +2 -2
  92. data/lib/rubocop/cop/style/and_or.rb +5 -0
  93. data/lib/rubocop/cop/style/arguments_forwarding.rb +13 -2
  94. data/lib/rubocop/cop/style/array_coercion.rb +21 -3
  95. data/lib/rubocop/cop/style/ascii_comments.rb +0 -3
  96. data/lib/rubocop/cop/style/block_delimiters.rb +23 -6
  97. data/lib/rubocop/cop/style/case_equality.rb +6 -9
  98. data/lib/rubocop/cop/style/case_like_if.rb +5 -0
  99. data/lib/rubocop/cop/style/class_and_module_children.rb +9 -0
  100. data/lib/rubocop/cop/style/collection_compact.rb +7 -5
  101. data/lib/rubocop/cop/style/collection_methods.rb +8 -6
  102. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  103. data/lib/rubocop/cop/style/comment_annotation.rb +25 -39
  104. data/lib/rubocop/cop/style/commented_keyword.rb +4 -1
  105. data/lib/rubocop/cop/style/date_time.rb +5 -0
  106. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  107. data/lib/rubocop/cop/style/documentation.rb +23 -8
  108. data/lib/rubocop/cop/style/double_negation.rb +15 -5
  109. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  110. data/lib/rubocop/cop/style/explicit_block_argument.rb +21 -11
  111. data/lib/rubocop/cop/style/float_division.rb +10 -2
  112. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +7 -2
  113. data/lib/rubocop/cop/style/global_std_stream.rb +4 -0
  114. data/lib/rubocop/cop/style/hash_each_methods.rb +5 -0
  115. data/lib/rubocop/cop/style/hash_except.rb +4 -3
  116. data/lib/rubocop/cop/style/hash_transform_keys.rb +4 -6
  117. data/lib/rubocop/cop/style/hash_transform_values.rb +4 -6
  118. data/lib/rubocop/cop/style/identical_conditional_branches.rb +18 -16
  119. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +18 -4
  120. data/lib/rubocop/cop/style/infinite_loop.rb +4 -3
  121. data/lib/rubocop/cop/style/inverse_methods.rb +9 -2
  122. data/lib/rubocop/cop/style/lambda_call.rb +1 -1
  123. data/lib/rubocop/cop/style/line_end_concatenation.rb +13 -0
  124. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -6
  125. data/lib/rubocop/cop/style/module_function.rb +8 -9
  126. data/lib/rubocop/cop/style/mutable_constant.rb +73 -6
  127. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  128. data/lib/rubocop/cop/style/negated_unless.rb +1 -1
  129. data/lib/rubocop/cop/style/non_nil_check.rb +2 -2
  130. data/lib/rubocop/cop/style/not.rb +2 -2
  131. data/lib/rubocop/cop/style/numbered_parameters.rb +46 -0
  132. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +50 -0
  133. data/lib/rubocop/cop/style/numeric_literals.rb +7 -8
  134. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -0
  135. data/lib/rubocop/cop/style/optional_arguments.rb +4 -0
  136. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +14 -4
  137. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  138. data/lib/rubocop/cop/style/percent_q_literals.rb +2 -2
  139. data/lib/rubocop/cop/style/preferred_hash_methods.rb +9 -4
  140. data/lib/rubocop/cop/style/quoted_symbols.rb +10 -6
  141. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  142. data/lib/rubocop/cop/style/redundant_argument.rb +19 -9
  143. data/lib/rubocop/cop/style/redundant_condition.rb +2 -3
  144. data/lib/rubocop/cop/style/redundant_fetch_block.rb +4 -0
  145. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +12 -3
  146. data/lib/rubocop/cop/style/redundant_freeze.rb +4 -4
  147. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  148. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -3
  149. data/lib/rubocop/cop/style/redundant_self.rb +10 -0
  150. data/lib/rubocop/cop/style/redundant_self_assignment.rb +4 -3
  151. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +1 -1
  152. data/lib/rubocop/cop/style/redundant_sort.rb +51 -18
  153. data/lib/rubocop/cop/style/regexp_literal.rb +3 -3
  154. data/lib/rubocop/cop/style/return_nil.rb +2 -1
  155. data/lib/rubocop/cop/style/safe_navigation.rb +13 -2
  156. data/lib/rubocop/cop/style/select_by_regexp.rb +133 -0
  157. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -0
  158. data/lib/rubocop/cop/style/slicing_with_range.rb +13 -0
  159. data/lib/rubocop/cop/style/special_global_vars.rb +4 -0
  160. data/lib/rubocop/cop/style/static_class.rb +5 -5
  161. data/lib/rubocop/cop/style/string_chars.rb +4 -2
  162. data/lib/rubocop/cop/style/string_concatenation.rb +5 -1
  163. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -0
  164. data/lib/rubocop/cop/style/struct_inheritance.rb +4 -0
  165. data/lib/rubocop/cop/style/swap_values.rb +4 -2
  166. data/lib/rubocop/cop/style/symbol_proc.rb +26 -0
  167. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +19 -0
  168. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  169. data/lib/rubocop/cop/style/yoda_condition.rb +24 -7
  170. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -0
  171. data/lib/rubocop/cop/util.rb +4 -3
  172. data/lib/rubocop/cops_documentation_generator.rb +17 -5
  173. data/lib/rubocop/options.rb +126 -112
  174. data/lib/rubocop/rake_task.rb +1 -1
  175. data/lib/rubocop/result_cache.rb +3 -3
  176. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  177. data/lib/rubocop/rspec/expect_offense.rb +6 -2
  178. data/lib/rubocop/runner.rb +2 -3
  179. data/lib/rubocop/version.rb +1 -1
  180. data/lib/rubocop.rb +10 -2
  181. metadata +17 -9
@@ -5,11 +5,14 @@ module RuboCop
5
5
  module Lint
6
6
  # This cop checks for setter call to local variable as the final
7
7
  # expression of a function definition.
8
- # Its auto-correction is marked as unsafe because return value will be changed.
9
8
  #
10
- # NOTE: There are edge cases in which the local variable references a
11
- # value that is also accessible outside the local scope. This is not
12
- # detected by the cop, and it can yield a false positive.
9
+ # @safety
10
+ # There are edge cases in which the local variable references a
11
+ # value that is also accessible outside the local scope. This is not
12
+ # detected by the cop, and it can yield a false positive.
13
+ #
14
+ # As well, auto-correction is unsafe because the method's
15
+ # return value will be changed.
13
16
  #
14
17
  # @example
15
18
  #
@@ -7,8 +7,9 @@ module RuboCop
7
7
  # (when the integer <= 0) or that will only ever yield once
8
8
  # (`1.times`).
9
9
  #
10
- # This cop is marked as unsafe as `times` returns its receiver, which
11
- # is *usually* OK, but might change behavior.
10
+ # @safety
11
+ # This cop is unsafe as `times` returns its receiver, which is
12
+ # *usually* OK, but might change behavior.
12
13
  #
13
14
  # @example
14
15
  # # bad
@@ -65,7 +66,7 @@ module RuboCop
65
66
  private
66
67
 
67
68
  def never_process?(count, node)
68
- count < 1 || node.block_type? && node.body.nil?
69
+ count < 1 || (node.block_type? && node.body.nil?)
69
70
  end
70
71
 
71
72
  def remove_node(corrector, node)
@@ -8,6 +8,12 @@ module RuboCop
8
8
  # (method calls), and conditions. See http://c2.com/cgi/wiki?AbcMetric
9
9
  # and https://en.wikipedia.org/wiki/ABC_Software_Metric.
10
10
  #
11
+ # Interpreting ABC size:
12
+ #
13
+ # * <= 17 satisfactory
14
+ # * 18..30 unsatisfactory
15
+ # * > 30 dangerous
16
+ #
11
17
  # You can have repeated "attributes" calls count as a single "branch".
12
18
  # For this purpose, attributes are any method with no argument; no attempt
13
19
  # is meant to distinguish actual `attr_reader` from other methods.
@@ -45,7 +45,7 @@ module RuboCop
45
45
  else
46
46
  # Otherwise, the case node gets 0.8 complexity points and each
47
47
  # when gets 0.2.
48
- (0.8 + 0.2 * nb_branches).round
48
+ (0.8 + (0.2 * nb_branches)).round
49
49
  end
50
50
  when :if
51
51
  node.else? && !node.elsif? ? 2 : 1
@@ -46,7 +46,7 @@ module RuboCop
46
46
  visit_depth_last(@node) { |child| calculate_node(child) }
47
47
 
48
48
  [
49
- Math.sqrt(@assignment**2 + @branch**2 + @condition**2).round(2),
49
+ Math.sqrt((@assignment**2) + (@branch**2) + (@condition**2)).round(2),
50
50
  "<#{@assignment}, #{@branch}, #{@condition}>"
51
51
  ]
52
52
  end
@@ -147,7 +147,7 @@ module RuboCop
147
147
 
148
148
  # Returns true for lines that shall not be included in the count.
149
149
  def irrelevant_line?(source_line)
150
- source_line.blank? || !count_comments? && comment_line?(source_line)
150
+ source_line.blank? || (!count_comments? && comment_line?(source_line))
151
151
  end
152
152
 
153
153
  def count_comments?
@@ -2,40 +2,63 @@
2
2
 
3
3
  module RuboCop
4
4
  module Cop
5
- module Style
6
- # Common functionality related to annotation comments.
7
- module AnnotationComment
8
- private
9
-
10
- # @api public
11
- def annotation?(comment)
12
- _margin, first_word, colon, space, note = split_comment(comment)
13
- keyword_appearance?(first_word, colon, space) &&
14
- !just_first_word_of_sentence?(first_word, colon, space, note)
15
- end
16
-
17
- # @api public
18
- def split_comment(comment)
19
- match = comment.text.match(/^(# ?)([A-Za-z]+)(\s*:)?(\s+)?(\S+)?/)
20
- return false unless match
21
-
22
- match.captures
23
- end
24
-
25
- # @api public
26
- def keyword_appearance?(first_word, colon, space)
27
- first_word && keyword?(first_word.upcase) && (colon || space)
28
- end
29
-
30
- # @api private
31
- def just_first_word_of_sentence?(first_word, colon, space, note)
32
- first_word == first_word.capitalize && !colon && space && note
33
- end
34
-
35
- # @api public
36
- def keyword?(word)
37
- config.for_cop('Style/CommentAnnotation')['Keywords'].include?(word)
38
- end
5
+ # Representation of an annotation comment in source code (eg. `# TODO: blah blah blah`).
6
+ class AnnotationComment
7
+ extend Forwardable
8
+
9
+ attr_reader :comment, :margin, :keyword, :colon, :space, :note
10
+
11
+ # @param [Parser::Source::Comment] comment
12
+ # @param [Array<String>] keywords
13
+ def initialize(comment, keywords)
14
+ @comment = comment
15
+ @keywords = keywords
16
+ @margin, @keyword, @colon, @space, @note = split_comment(comment)
17
+ end
18
+
19
+ def annotation?
20
+ keyword_appearance? && !just_keyword_of_sentence?
21
+ end
22
+
23
+ def correct?(colon:)
24
+ return false unless keyword && space && note
25
+ return false unless keyword == keyword.upcase
26
+
27
+ self.colon.nil? == !colon
28
+ end
29
+
30
+ # Returns the range bounds for just the annotation
31
+ def bounds
32
+ start = comment.loc.expression.begin_pos + margin.length
33
+ length = [keyword, colon, space].reduce(0) { |len, elem| len + elem.to_s.length }
34
+ [start, start + length]
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :keywords
40
+
41
+ def split_comment(comment)
42
+ # Sort keywords by reverse length so that if a keyword is in a phrase
43
+ # but also on its own, both will match properly.
44
+ keywords_regex = Regexp.new(
45
+ Regexp.union(keywords.sort_by { |w| -w.length }).source,
46
+ Regexp::IGNORECASE
47
+ )
48
+ regex = /^(# ?)(\b#{keywords_regex}\b)(\s*:)?(\s+)?(\S+)?/i
49
+
50
+ match = comment.text.match(regex)
51
+ return false unless match
52
+
53
+ match.captures
54
+ end
55
+
56
+ def keyword_appearance?
57
+ keyword && (colon || space)
58
+ end
59
+
60
+ def just_keyword_of_sentence?
61
+ keyword == keyword.capitalize && !colon && space && note
39
62
  end
40
63
  end
41
64
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
 
44
44
  # Returns true for lines that shall not be included in the count.
45
45
  def irrelevant_line(source_line)
46
- source_line.blank? || !count_comments? && comment_line?(source_line)
46
+ source_line.blank? || (!count_comments? && comment_line?(source_line))
47
47
  end
48
48
 
49
49
  def build_code_length_calculator(node)
@@ -5,7 +5,6 @@ module RuboCop
5
5
  # Common functionality for checking documentation.
6
6
  module DocumentationComment
7
7
  extend NodePattern::Macros
8
- include Style::AnnotationComment
9
8
 
10
9
  private
11
10
 
@@ -15,7 +14,7 @@ module RuboCop
15
14
  return false unless preceding_comment?(node, preceding_lines.last)
16
15
 
17
16
  preceding_lines.any? do |comment|
18
- !annotation?(comment) &&
17
+ !AnnotationComment.new(comment, annotation_keywords).annotation? &&
19
18
  !interpreter_directive_comment?(comment) &&
20
19
  !rubocop_directive_comment?(comment)
21
20
  end
@@ -44,6 +43,10 @@ module RuboCop
44
43
  def rubocop_directive_comment?(comment)
45
44
  !!DirectiveComment.new(comment).match_captures
46
45
  end
46
+
47
+ def annotation_keywords
48
+ config.for_cop('Style/CommentAnnotation')['Keywords']
49
+ end
47
50
  end
48
51
  end
49
52
  end
@@ -8,7 +8,9 @@ module RuboCop
8
8
 
9
9
  FROZEN_STRING_LITERAL = '# frozen_string_literal:'
10
10
  FROZEN_STRING_LITERAL_ENABLED = '# frozen_string_literal: true'
11
- FROZEN_STRING_LITERAL_TYPES = %i[str dstr].freeze
11
+ FROZEN_STRING_LITERAL_TYPES_RUBY27 = %i[str dstr].freeze
12
+
13
+ private_constant :FROZEN_STRING_LITERAL_TYPES_RUBY27
12
14
 
13
15
  def frozen_string_literal_comment_exists?
14
16
  leading_comment_lines.any? { |line| MagicComment.parse(line).valid_literal_value? }
@@ -16,6 +18,26 @@ module RuboCop
16
18
 
17
19
  private
18
20
 
21
+ def frozen_string_literal?(node)
22
+ frozen_string = if target_ruby_version >= 3.0
23
+ uninterpolated_string?(node) || frozen_heredoc?(node)
24
+ else
25
+ FROZEN_STRING_LITERAL_TYPES_RUBY27.include?(node.type)
26
+ end
27
+
28
+ frozen_string && frozen_string_literals_enabled?
29
+ end
30
+
31
+ def uninterpolated_string?(node)
32
+ node.str_type? || (node.dstr_type? && node.each_descendant(:begin).none?)
33
+ end
34
+
35
+ def frozen_heredoc?(node)
36
+ return false unless node.dstr_type? && node.heredoc?
37
+
38
+ node.children.all?(&:str_type?)
39
+ end
40
+
19
41
  def frozen_string_literals_enabled?
20
42
  ruby_version = processed_source.ruby_version
21
43
  return false unless ruby_version
@@ -21,9 +21,7 @@ module RuboCop
21
21
  private
22
22
 
23
23
  def indent_level(str)
24
- indentations = str.lines
25
- .map { |line| line[/^\s*/] }
26
- .reject { |line| line.end_with?("\n") }
24
+ indentations = str.lines.map { |line| line[/^\s*/] }.reject { |line| line.end_with?("\n") }
27
25
  indentations.empty? ? 0 : indentations.min_by(&:size).size
28
26
  end
29
27
 
@@ -134,7 +134,7 @@ module RuboCop
134
134
 
135
135
  next if a.setter_method?
136
136
  next unless kind == :with_or_without_parentheses ||
137
- kind == :with_parentheses && parentheses?(a)
137
+ (kind == :with_parentheses && parentheses?(a))
138
138
 
139
139
  a.arguments.any? { |arg| within_node?(node, arg) }
140
140
  end
@@ -156,7 +156,7 @@ module RuboCop
156
156
 
157
157
  def disqualified_rhs?(candidate, ancestor)
158
158
  UNALIGNED_RHS_TYPES.include?(ancestor.type) ||
159
- ancestor.block_type? && part_of_block_body?(candidate, ancestor)
159
+ (ancestor.block_type? && part_of_block_body?(candidate, ancestor))
160
160
  end
161
161
 
162
162
  def valid_rhs?(candidate, ancestor)
@@ -35,7 +35,15 @@ module RuboCop
35
35
  previous: gem_name(current),
36
36
  current: gem_name(previous)
37
37
  )
38
- add_offense(current, message: message)
38
+
39
+ add_offense(current, message: message) do |corrector|
40
+ OrderedGemCorrector.correct(
41
+ processed_source,
42
+ current,
43
+ previous_declaration(current),
44
+ treat_comments_as_separators
45
+ ).call(corrector)
46
+ end
39
47
  end
40
48
 
41
49
  def gem_name(declaration_node)
@@ -36,7 +36,12 @@ module RuboCop
36
36
  def check_percent_array(node)
37
37
  array_style_detected(:percent, node.values.size)
38
38
 
39
- return unless style == :brackets || invalid_percent_array_contents?(node)
39
+ brackets_required = invalid_percent_array_contents?(node)
40
+ return unless style == :brackets || brackets_required
41
+
42
+ # If in percent style but brackets are required due to
43
+ # string content, the file should be excluded in auto-gen-config
44
+ no_acceptable_style! if brackets_required
40
45
 
41
46
  bracketed_array = build_bracketed_array(node)
42
47
  message = format(self.class::ARRAY_MSG, prefer: bracketed_array)
@@ -93,7 +93,15 @@ module RuboCop
93
93
  end
94
94
 
95
95
  def aligned_assignment?(range, line)
96
- range.source[-1] == '=' && line[range.last_column - 1] == '='
96
+ (range.source[-1] == '=' && line[range.last_column - 1] == '=') ||
97
+ aligned_with_append_operator?(range, line)
98
+ end
99
+
100
+ def aligned_with_append_operator?(range, line)
101
+ last_column = range.last_column
102
+
103
+ (range.source == '<<' && line[last_column - 1] == '=') ||
104
+ (range.source[-1] == '=' && line[(last_column - 2)..(last_column - 1)] == '<<')
97
105
  end
98
106
 
99
107
  def aligned_identical?(range, line)
@@ -13,7 +13,11 @@ module RuboCop
13
13
  if style == :single_quotes
14
14
  !double_quotes_required?(src)
15
15
  else
16
- !/" | \\[^'\\] | \#[@{$]/x.match?(src)
16
+ # The string needs single quotes if:
17
+ # 1. It contains a double quote
18
+ # 2. It contains text that would become an escape sequence with double quotes
19
+ # 3. It contains text that would become an interpolation with double quotes
20
+ !/" | (?<!\\)\\[abcefMnrtuUx0-7] | \#[@{$]/x.match?(src)
17
21
  end
18
22
  end
19
23
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Style/AsciiComments
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Naming
@@ -90,4 +88,3 @@ module RuboCop
90
88
  end
91
89
  end
92
90
  end
93
- # rubocop:enable Style/AsciiComments
@@ -24,7 +24,7 @@ module RuboCop
24
24
  # # With `AllowNamesEndingInNumbers` set to false
25
25
  # foo { |num1, num2| num1 * num2 }
26
26
  #
27
- # # With `MinParamNameLength` set to number greater than 1
27
+ # # With `MinNameLength` set to number greater than 1
28
28
  # baz { |a, b, c| do_stuff(a, b, c) }
29
29
  #
30
30
  # # good
@@ -54,7 +54,7 @@ module RuboCop
54
54
  private
55
55
 
56
56
  def allowed_assignment?(value)
57
- value && %i[block const casgn].include?(value.type) ||
57
+ (value && %i[block const casgn].include?(value.type)) ||
58
58
  allowed_method_call_on_rhs?(value) ||
59
59
  class_or_struct_return_method?(value) ||
60
60
  allowed_conditional_expression_on_rhs?(value)
@@ -70,6 +70,8 @@ module RuboCop
70
70
  include RangeHelp
71
71
 
72
72
  EMPTY_ARRAY = [].freeze
73
+ MSG = "Consider replacing '%<term>s'%<suffix>s."
74
+ MSG_FOR_FILE_PATH = "Consider replacing '%<term>s' in file path%<suffix>s."
73
75
 
74
76
  WordLocation = Struct.new(:word, :position)
75
77
 
@@ -197,12 +199,11 @@ module RuboCop
197
199
  end
198
200
 
199
201
  def create_single_word_message_for_file(word)
200
- create_message(word).sub(/\.$/, ' in file path.')
202
+ create_message(word, MSG_FOR_FILE_PATH)
201
203
  end
202
204
 
203
205
  def create_multiple_word_message_for_file(words)
204
- quoted_words = words.map { |word| "'#{word}'" }
205
- "Consider replacing problematic terms #{quoted_words.join(', ')} in file path."
206
+ format(MSG_FOR_FILE_PATH, term: words.join("', '"), suffix: ' with other terms')
206
207
  end
207
208
 
208
209
  def scan_for_words(input)
@@ -223,9 +224,12 @@ module RuboCop
223
224
  safe_str.gsub(@allowed_regex) { |match| '*' * match.size }
224
225
  end
225
226
 
226
- def create_message(word)
227
+ def create_message(word, message = MSG)
227
228
  flagged_term = find_flagged_term(word)
228
- "Consider replacing problematic term '#{word}'#{flagged_term['SuggestionString']}."
229
+ suggestions = flagged_term['SuggestionString']
230
+ suggestions = ' with another term' if suggestions.blank?
231
+
232
+ format(message, term: word, suffix: suggestions)
229
233
  end
230
234
 
231
235
  def find_flagged_term(word)
@@ -235,10 +239,6 @@ module RuboCop
235
239
  flagged_term
236
240
  end
237
241
 
238
- def create_message_for_file(word)
239
- create_message(word).sub(/\.$/, ' in file path.')
240
- end
241
-
242
242
  def preprocess_suggestions(suggestions)
243
243
  return '' if suggestions.nil? ||
244
244
  (suggestions.is_a?(String) && suggestions.strip.empty?) || suggestions.empty?
@@ -14,6 +14,11 @@ module RuboCop
14
14
  # convention that is used to implicitly indicate that an ivar should not
15
15
  # be set or referenced outside of the memoization method.
16
16
  #
17
+ # @safety
18
+ # This cop relies on the pattern `@instance_var ||= ...`,
19
+ # but this is sometimes used for other purposes than memoization
20
+ # so this cop is considered unsafe.
21
+ #
17
22
  # @example EnforcedStyleForLeadingUnderscores: disallowed (default)
18
23
  # # bad
19
24
  # # Method foo is memoized using an instance variable that is
@@ -139,10 +144,6 @@ module RuboCop
139
144
  # define_method(:foo) do
140
145
  # @_foo ||= calculate_expensive_thing
141
146
  # end
142
- #
143
- # This cop relies on the pattern `@instance_var ||= ...`,
144
- # but this is sometimes used for other purposes than memoization
145
- # so this cop is considered unsafe.
146
147
  class MemoizedInstanceVariableName < Base
147
148
  include ConfigurableEnforcedStyle
148
149
 
@@ -75,6 +75,9 @@ module RuboCop
75
75
  preferred_name = preferred_name(offending_name)
76
76
  return if preferred_name.to_sym == offending_name
77
77
 
78
+ # check variable shadowing for exception variable
79
+ return if shadowed_variable_name?(node)
80
+
78
81
  range = offense_range(node)
79
82
  message = message(node)
80
83
 
@@ -150,6 +153,10 @@ module RuboCop
150
153
  preferred_name = preferred_name(offending_name)
151
154
  format(MSG, preferred: preferred_name, bad: offending_name)
152
155
  end
156
+
157
+ def shadowed_variable_name?(node)
158
+ node.each_descendant(:lvar).any? { |n| n.children.first.to_s == preferred_name(n) }
159
+ end
153
160
  end
154
161
  end
155
162
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Security
6
+ # Checks for the first argument to `IO.read`, `IO.binread`, `IO.write`, `IO.binwrite`,
7
+ # `IO.foreach`, and `IO.readlines`.
8
+ #
9
+ # If argument starts with a pipe character (`'|'`) and the receiver is the `IO` class,
10
+ # a subprocess is created in the same way as `Kernel#open`, and its output is returned.
11
+ # `Kernel#open` may allow unintentional command injection, which is the reason these
12
+ # `IO` methods are a security risk.
13
+ # Consider to use `File.read` to disable the behavior of subprocess invocation.
14
+ #
15
+ # @safety
16
+ # This cop is unsafe because false positive will occur if the variable passed as
17
+ # the first argument is a command that is not a file path.
18
+ #
19
+ # @example
20
+ #
21
+ # # bad
22
+ # IO.read(path)
23
+ # IO.read('path')
24
+ #
25
+ # # good
26
+ # File.read(path)
27
+ # File.read('path')
28
+ # IO.read('| command') # Allow intentional command invocation.
29
+ #
30
+ class IoMethods < Base
31
+ extend AutoCorrector
32
+
33
+ MSG = '`File.%<method_name>s` is safer than `IO.%<method_name>s`.'
34
+ RESTRICT_ON_SEND = %i[read binread write binwrite foreach readlines].freeze
35
+
36
+ def on_send(node)
37
+ return unless (receiver = node.receiver) && receiver.source == 'IO'
38
+
39
+ argument = node.first_argument
40
+ return if argument.respond_to?(:value) && argument.value.strip.start_with?('|')
41
+
42
+ add_offense(node, message: format(MSG, method_name: node.method_name)) do |corrector|
43
+ corrector.replace(receiver, 'File')
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -6,13 +6,14 @@ module RuboCop
6
6
  # This cop checks for the use of JSON class methods which have potential
7
7
  # security issues.
8
8
  #
9
- # Autocorrect is disabled by default because it's potentially dangerous.
10
- # If using a stream, like `JSON.load(open('file'))`, it will need to call
11
- # `#read` manually, like `JSON.parse(open('file').read)`.
12
- # If reading single values (rather than proper JSON objects), like
13
- # `JSON.load('false')`, it will need to pass the `quirks_mode: true`
14
- # option, like `JSON.parse('false', quirks_mode: true)`.
15
- # Other similar issues may apply.
9
+ # @safety
10
+ # Autocorrect is disabled by default because it's potentially dangerous.
11
+ # If using a stream, like `JSON.load(open('file'))`, it will need to call
12
+ # `#read` manually, like `JSON.parse(open('file').read)`.
13
+ # If reading single values (rather than proper JSON objects), like
14
+ # `JSON.load('false')`, it will need to pass the `quirks_mode: true`
15
+ # option, like `JSON.parse('false', quirks_mode: true)`.
16
+ # Other similar issues may apply.
16
17
  #
17
18
  # @example
18
19
  # # bad
@@ -11,6 +11,10 @@ module RuboCop
11
11
  # the argument of `Kernel#open` and `URI.open`. It would be better to use
12
12
  # `File.open`, `IO.popen` or `URI.parse#open` explicitly.
13
13
  #
14
+ # @safety
15
+ # This cop could register false positives if `open` is redefined
16
+ # in a class and then used without a receiver in that class.
17
+ #
14
18
  # @example
15
19
  # # bad
16
20
  # open(something)
@@ -7,6 +7,10 @@ module RuboCop
7
7
  # potential security issues leading to remote code execution when
8
8
  # loading from an untrusted source.
9
9
  #
10
+ # @safety
11
+ # The behaviour of the code might change depending on what was
12
+ # in the YAML payload, since `YAML.safe_load` is more restrictive.
13
+ #
10
14
  # @example
11
15
  # # bad
12
16
  # YAML.load("--- foo")
@@ -59,8 +59,8 @@ module RuboCop
59
59
 
60
60
  def check(send_node)
61
61
  return if previous_line_comment?(send_node)
62
- return unless grouped_style? && sibling_accessors(send_node).size > 1 ||
63
- separated_style? && send_node.arguments.size > 1
62
+ return unless (grouped_style? && sibling_accessors(send_node).size > 1) ||
63
+ (separated_style? && send_node.arguments.size > 1)
64
64
 
65
65
  message = message(send_node)
66
66
  add_offense(send_node, message: message) do |corrector|
@@ -7,6 +7,11 @@ module RuboCop
7
7
  # `||` instead. It can be configured to check only in conditions or in
8
8
  # all contexts.
9
9
  #
10
+ # @safety
11
+ # Auto-correction is unsafe because there is a different operator precedence
12
+ # between logical operators (`&&` and `||`) and semantic operators (`and` and `or`),
13
+ # and that might change the behaviour.
14
+ #
10
15
  # @example EnforcedStyle: always
11
16
  # # bad
12
17
  # foo.save and return
@@ -30,6 +30,10 @@ module RuboCop
30
30
  # bar(*args)
31
31
  # end
32
32
  #
33
+ # def foo(**kwargs)
34
+ # bar(**kwargs)
35
+ # end
36
+ #
33
37
  # @example AllowOnlyRestArgument: false
34
38
  # # bad
35
39
  # # The following code can replace the arguments with `...`,
@@ -38,6 +42,10 @@ module RuboCop
38
42
  # bar(*args)
39
43
  # end
40
44
  #
45
+ # def foo(**kwargs)
46
+ # bar(**kwargs)
47
+ # end
48
+ #
41
49
  class ArgumentsForwarding < Base
42
50
  include RangeHelp
43
51
  extend AutoCorrector
@@ -49,12 +57,15 @@ module RuboCop
49
57
 
50
58
  # @!method use_rest_arguments?(node)
51
59
  def_node_matcher :use_rest_arguments?, <<~PATTERN
52
- (args (restarg $_) $...)
60
+ (args ({restarg kwrestarg} $_) $...)
53
61
  PATTERN
54
62
 
55
63
  # @!method only_rest_arguments?(node, name)
56
64
  def_node_matcher :only_rest_arguments?, <<~PATTERN
57
- (send _ _ (splat (lvar %1)))
65
+ {
66
+ (send _ _ (splat (lvar %1)))
67
+ (send _ _ (hash (kwsplat (lvar %1))))
68
+ }
58
69
  PATTERN
59
70
 
60
71
  # @!method forwarding_method_arguments?(node, rest_name, block_name, kwargs_name)