rubocop 1.40.0 → 1.42.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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +44 -1
  5. data/lib/rubocop/cli.rb +1 -1
  6. data/lib/rubocop/config.rb +34 -11
  7. data/lib/rubocop/config_loader.rb +9 -0
  8. data/lib/rubocop/config_loader_resolver.rb +5 -1
  9. data/lib/rubocop/config_validator.rb +1 -1
  10. data/lib/rubocop/cop/badge.rb +9 -4
  11. data/lib/rubocop/cop/base.rb +83 -66
  12. data/lib/rubocop/cop/commissioner.rb +8 -3
  13. data/lib/rubocop/cop/cop.rb +29 -29
  14. data/lib/rubocop/cop/corrector.rb +23 -11
  15. data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
  16. data/lib/rubocop/cop/internal_affairs/cop_description.rb +3 -1
  17. data/lib/rubocop/cop/layout/class_structure.rb +32 -11
  18. data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
  19. data/lib/rubocop/cop/layout/empty_lines.rb +2 -0
  20. data/lib/rubocop/cop/layout/extra_spacing.rb +10 -6
  21. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +38 -2
  22. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +49 -2
  23. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +61 -2
  24. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +52 -2
  25. data/lib/rubocop/cop/layout/indentation_style.rb +7 -2
  26. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +5 -0
  27. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +11 -5
  28. data/lib/rubocop/cop/layout/line_length.rb +2 -0
  29. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +51 -2
  30. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  31. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +49 -2
  32. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +53 -2
  33. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +58 -2
  34. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -2
  35. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  36. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +1 -1
  37. data/lib/rubocop/cop/layout/trailing_whitespace.rb +11 -4
  38. data/lib/rubocop/cop/lint/constant_resolution.rb +4 -0
  39. data/lib/rubocop/cop/lint/debugger.rb +3 -1
  40. data/lib/rubocop/cop/lint/duplicate_branch.rb +0 -2
  41. data/lib/rubocop/cop/lint/duplicate_methods.rb +19 -8
  42. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -5
  43. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
  44. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -1
  45. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  46. data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
  47. data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
  48. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +8 -19
  49. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
  50. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +5 -3
  51. data/lib/rubocop/cop/metrics/class_length.rb +1 -1
  52. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  53. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  54. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -4
  55. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
  56. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  57. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +2 -2
  58. data/lib/rubocop/cop/mixin/annotation_comment.rb +13 -6
  59. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +21 -9
  60. data/lib/rubocop/cop/mixin/first_element_line_break.rb +11 -7
  61. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +5 -1
  62. data/lib/rubocop/cop/mixin/line_length_help.rb +8 -1
  63. data/lib/rubocop/cop/mixin/method_complexity.rb +5 -3
  64. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +5 -3
  65. data/lib/rubocop/cop/mixin/percent_array.rb +3 -5
  66. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  67. data/lib/rubocop/cop/mixin/require_library.rb +2 -0
  68. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -3
  69. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  70. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  71. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +2 -0
  72. data/lib/rubocop/cop/naming/inclusive_language.rb +4 -1
  73. data/lib/rubocop/cop/registry.rb +28 -25
  74. data/lib/rubocop/cop/security/compound_hash.rb +2 -1
  75. data/lib/rubocop/cop/style/alias.rb +9 -1
  76. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  77. data/lib/rubocop/cop/style/concat_array_literals.rb +86 -0
  78. data/lib/rubocop/cop/style/documentation.rb +11 -5
  79. data/lib/rubocop/cop/style/guard_clause.rb +17 -9
  80. data/lib/rubocop/cop/style/hash_syntax.rb +10 -7
  81. data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
  82. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -3
  83. data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
  84. data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -1
  85. data/lib/rubocop/cop/style/map_to_set.rb +61 -0
  86. data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
  87. data/lib/rubocop/cop/style/min_max_comparison.rb +73 -0
  88. data/lib/rubocop/cop/style/redundant_constant_base.rb +13 -0
  89. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +39 -0
  90. data/lib/rubocop/cop/style/redundant_string_escape.rb +6 -3
  91. data/lib/rubocop/cop/style/require_order.rb +63 -9
  92. data/lib/rubocop/cop/style/select_by_regexp.rb +6 -2
  93. data/lib/rubocop/cop/style/semicolon.rb +2 -1
  94. data/lib/rubocop/cop/style/signal_exception.rb +8 -6
  95. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
  96. data/lib/rubocop/cop/style/word_array.rb +41 -0
  97. data/lib/rubocop/cop/style/yoda_expression.rb +74 -0
  98. data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
  99. data/lib/rubocop/cop/team.rb +29 -29
  100. data/lib/rubocop/cop/util.rb +31 -4
  101. data/lib/rubocop/cop/variable_force.rb +0 -3
  102. data/lib/rubocop/cops_documentation_generator.rb +33 -11
  103. data/lib/rubocop/directive_comment.rb +1 -1
  104. data/lib/rubocop/file_patterns.rb +43 -0
  105. data/lib/rubocop/formatter.rb +2 -0
  106. data/lib/rubocop/options.rb +1 -1
  107. data/lib/rubocop/path_util.rb +26 -15
  108. data/lib/rubocop/result_cache.rb +1 -1
  109. data/lib/rubocop/runner.rb +10 -3
  110. data/lib/rubocop/target_finder.rb +1 -1
  111. data/lib/rubocop/target_ruby.rb +0 -1
  112. data/lib/rubocop/version.rb +1 -1
  113. data/lib/rubocop.rb +6 -1
  114. metadata +15 -10
  115. data/lib/rubocop/optimized_patterns.rb +0 -38
@@ -17,13 +17,19 @@ module RuboCop
17
17
  # do_something
18
18
  # end
19
19
  class RegexpAsCondition < Base
20
+ include IgnoredNode
20
21
  extend AutoCorrector
21
22
 
22
23
  MSG = 'Do not use regexp literal as a condition. ' \
23
24
  'The regexp literal matches `$_` implicitly.'
24
25
 
25
26
  def on_match_current_line(node)
27
+ return if node.ancestors.none?(&:conditional?)
28
+ return if part_of_ignored_node?(node)
29
+
26
30
  add_offense(node) { |corrector| corrector.replace(node, "#{node.source} =~ $_") }
31
+
32
+ ignore_node(node)
27
33
  end
28
34
  end
29
35
  end
@@ -46,7 +46,9 @@ module RuboCop
46
46
  private
47
47
 
48
48
  def check_ternary(ternary, node)
49
- return if node.method?(:[]) || !ternary.condition.operator_keyword?
49
+ if node.method?(:[]) || node.assignment_method? || !ternary.condition.operator_keyword?
50
+ return
51
+ end
50
52
 
51
53
  range = range_between(node.source_range.begin_pos, ternary.condition.source_range.end_pos)
52
54
 
@@ -45,13 +45,12 @@ module RuboCop
45
45
  bad_method?(node) do |safe_nav, method|
46
46
  return if nil_methods.include?(method) || PLUS_MINUS_METHODS.include?(node.method_name)
47
47
 
48
- method_chain = method_chain(node)
49
48
  location =
50
49
  Parser::Source::Range.new(node.source_range.source_buffer,
51
50
  safe_nav.source_range.end_pos,
52
- method_chain.source_range.end_pos)
51
+ node.source_range.end_pos)
53
52
  add_offense(location) do |corrector|
54
- autocorrect(corrector, offense_range: location, send_node: method_chain)
53
+ autocorrect(corrector, offense_range: location, send_node: node)
55
54
  end
56
55
  end
57
56
  end
@@ -63,12 +62,12 @@ module RuboCop
63
62
  # @return [String]
64
63
  def add_safe_navigation_operator(offense_range:, send_node:)
65
64
  source =
66
- if (brackets = find_brackets(send_node))
65
+ if brackets?(send_node)
67
66
  format(
68
67
  '%<method_name>s(%<arguments>s)%<method_chain>s',
69
- arguments: brackets.arguments.map(&:source).join(', '),
70
- method_name: brackets.method_name,
71
- method_chain: brackets.source_range.end.join(send_node.source_range.end).source
68
+ arguments: send_node.arguments.map(&:source).join(', '),
69
+ method_name: send_node.method_name,
70
+ method_chain: send_node.source_range.end.join(send_node.source_range.end).source
72
71
  )
73
72
  else
74
73
  offense_range.source
@@ -90,18 +89,8 @@ module RuboCop
90
89
  )
91
90
  end
92
91
 
93
- def method_chain(node)
94
- chain = node
95
- chain = chain.parent if chain.send_type? && chain.parent&.call_type?
96
- chain
97
- end
98
-
99
- def find_brackets(send_node)
100
- return send_node if send_node.method?(:[]) || send_node.method?(:[]=)
101
-
102
- send_node.descendants.detect do |node|
103
- node.send_type? && (node.method?(:[]) || node.method?(:[]=))
104
- end
92
+ def brackets?(send_node)
93
+ send_node.method?(:[]) || send_node.method?(:[]=)
105
94
  end
106
95
  end
107
96
  end
@@ -100,7 +100,8 @@ module RuboCop
100
100
 
101
101
  unless variable.keyword_argument?
102
102
  message << " If it's necessary, use `_` or `_#{variable.name}` " \
103
- "as an argument name to indicate that it won't be used."
103
+ "as an argument name to indicate that it won't be used. " \
104
+ "If it's unnecessary, remove it."
104
105
  end
105
106
 
106
107
  scope = variable.scope
@@ -77,10 +77,12 @@ module RuboCop
77
77
  PATTERN
78
78
 
79
79
  def on_send(node)
80
- if node.first_argument.def_type?
81
- inspect_def(node, node.first_argument)
80
+ return unless (first_argument = node.first_argument)
81
+
82
+ if first_argument.def_type?
83
+ inspect_def(node, first_argument)
82
84
  elsif node.first_argument.sym_type?
83
- inspect_sym(node, node.first_argument)
85
+ inspect_sym(node, first_argument)
84
86
  end
85
87
  end
86
88
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Metrics
6
- # Checks if the length a class exceeds some maximum value.
6
+ # Checks if the length of a class exceeds some maximum value.
7
7
  # Comment lines can optionally be ignored.
8
8
  # The maximum allowed length is configurable.
9
9
  #
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Metrics
6
- # Checks if the length a module exceeds some maximum value.
6
+ # Checks if the length of a module exceeds some maximum value.
7
7
  # Comment lines can optionally be ignored.
8
8
  # The maximum allowed length is configurable.
9
9
  #
@@ -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
+ ((nb_branches * 0.2) + 0.8).round
49
49
  end
50
50
  when :if
51
51
  node.else? && !node.elsif? ? 2 : 1
@@ -25,15 +25,15 @@ module RuboCop
25
25
  # > http://c2.com/cgi/wiki?AbcMetric
26
26
  CONDITION_NODES = CyclomaticComplexity::COUNTED_NODES.freeze
27
27
 
28
- def self.calculate(node, discount_repeated_attributes: false)
29
- new(node, discount_repeated_attributes: discount_repeated_attributes).calculate
30
- end
31
-
32
28
  # TODO: move to rubocop-ast
33
29
  ARGUMENT_TYPES = %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg].freeze
34
30
 
35
31
  private_constant :BRANCH_NODES, :CONDITION_NODES, :ARGUMENT_TYPES
36
32
 
33
+ def self.calculate(node, discount_repeated_attributes: false)
34
+ new(node, discount_repeated_attributes: discount_repeated_attributes).calculate
35
+ end
36
+
37
37
  def initialize(node)
38
38
  @assignment = 0
39
39
  @branch = 0
@@ -58,8 +58,8 @@ module RuboCop
58
58
  end
59
59
 
60
60
  def normalize_foldable_types(types)
61
- types.concat(%i[str dstr]) if types.delete(:heredoc)
62
- types.concat(%i[send csend]) if types.delete(:method_call)
61
+ types.push(:str, :dstr) if types.delete(:heredoc)
62
+ types.push(:send, :csend) if types.delete(:method_call)
63
63
  types
64
64
  end
65
65
 
@@ -28,7 +28,7 @@ module RuboCop
28
28
 
29
29
  each_bad_alignment(items, base_column) do |current|
30
30
  expr = current.source_range
31
- if @current_offenses.any? { |o| within?(expr, o.location) }
31
+ if @current_offenses&.any? { |o| within?(expr, o.location) }
32
32
  # If this offense is within a line range that is already being
33
33
  # realigned by autocorrect, we report the offense without
34
34
  # autocorrecting it. Two rewrites in the same area by the same
@@ -7,11 +7,11 @@ module RuboCop
7
7
  SIGILS = '@$' # if a variable starts with a sigil it will be removed
8
8
 
9
9
  def allowed_identifier?(name)
10
- allowed_identifiers.include?(name.to_s.delete(SIGILS))
10
+ !allowed_identifiers.empty? && allowed_identifiers.include?(name.to_s.delete(SIGILS))
11
11
  end
12
12
 
13
13
  def allowed_identifiers
14
- cop_config.fetch('AllowedIdentifiers', [])
14
+ cop_config.fetch('AllowedIdentifiers') { [] }
15
15
  end
16
16
  end
17
17
  end
@@ -41,18 +41,25 @@ module RuboCop
41
41
  def split_comment(comment)
42
42
  # Sort keywords by reverse length so that if a keyword is in a phrase
43
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
44
  match = comment.text.match(regex)
51
45
  return false unless match
52
46
 
53
47
  match.captures
54
48
  end
55
49
 
50
+ KEYWORDS_REGEX_CACHE = {} # rubocop:disable Style/MutableConstant
51
+ private_constant :KEYWORDS_REGEX_CACHE
52
+
53
+ def regex
54
+ KEYWORDS_REGEX_CACHE[keywords] ||= begin
55
+ keywords_regex = Regexp.new(
56
+ Regexp.union(keywords.sort_by { |w| -w.length }).source,
57
+ Regexp::IGNORECASE
58
+ )
59
+ /^(# ?)(\b#{keywords_regex}\b)(\s*:)?(\s+)?(\S+)?/i
60
+ end
61
+ end
62
+
56
63
  def keyword_appearance?
57
64
  keyword && (colon || space)
58
65
  end
@@ -20,19 +20,30 @@ module RuboCop
20
20
  style_detected(possibilities)
21
21
  end
22
22
 
23
+ SYMBOL_TO_STRING_CACHE = Hash.new do |hash, key|
24
+ hash[key] = key.to_s if key.is_a?(Symbol)
25
+ end
26
+ private_constant :SYMBOL_TO_STRING_CACHE
27
+
28
+ # rubocop:disable Metrics
23
29
  def style_detected(detected)
24
30
  return if no_acceptable_style?
25
31
 
26
- # `detected` can be a single style or an Array of possible styles
27
- # (if there is more than one which matches the observed code)
28
- detected_as_strings = Array(detected).map(&:to_s)
32
+ # This logic is more complex than it needs to be
33
+ # to avoid allocating Arrays in the hot code path.
34
+ updated_list =
35
+ if detected_style
36
+ if detected_style.size == 1 && detected_style.include?(SYMBOL_TO_STRING_CACHE[detected])
37
+ detected_style
38
+ else
39
+ detected_as_strings = SYMBOL_TO_STRING_CACHE.values_at(*detected)
40
+ detected_style & detected_as_strings
41
+ end
42
+ else
43
+ # We haven't observed any specific style yet.
44
+ SYMBOL_TO_STRING_CACHE.values_at(*detected)
45
+ end
29
46
 
30
- updated_list = if detected_style
31
- detected_style & detected_as_strings
32
- else
33
- # We haven't observed any specific style yet.
34
- detected_as_strings
35
- end
36
47
  if updated_list.empty?
37
48
  no_acceptable_style!
38
49
  else
@@ -40,6 +51,7 @@ module RuboCop
40
51
  config_to_allow_offenses[style_parameter_name] = updated_list.first
41
52
  end
42
53
  end
54
+ # rubocop:enable Metrics
43
55
 
44
56
  def no_acceptable_style?
45
57
  config_to_allow_offenses['Enabled'] == false
@@ -7,12 +7,12 @@ module RuboCop
7
7
  module FirstElementLineBreak
8
8
  private
9
9
 
10
- def check_method_line_break(node, children)
10
+ def check_method_line_break(node, children, ignore_last: false)
11
11
  return if children.empty?
12
12
 
13
13
  return unless method_uses_parens?(node, children.first)
14
14
 
15
- check_children_line_break(node, children)
15
+ check_children_line_break(node, children, ignore_last: ignore_last)
16
16
  end
17
17
 
18
18
  def method_uses_parens?(node, limit)
@@ -20,7 +20,7 @@ module RuboCop
20
20
  /\s*\(\s*$/.match?(source)
21
21
  end
22
22
 
23
- def check_children_line_break(node, children, start = node)
23
+ def check_children_line_break(node, children, start = node, ignore_last: false)
24
24
  return if children.empty?
25
25
 
26
26
  line = start.first_line
@@ -28,8 +28,8 @@ module RuboCop
28
28
  min = first_by_line(children)
29
29
  return if line != min.first_line
30
30
 
31
- max = last_by_line(children)
32
- return if line == max.last_line
31
+ max_line = last_line(children, ignore_last: ignore_last)
32
+ return if line == max_line
33
33
 
34
34
  add_offense(min) { |corrector| EmptyLineCorrector.insert_before(corrector, min) }
35
35
  end
@@ -38,8 +38,12 @@ module RuboCop
38
38
  nodes.min_by(&:first_line)
39
39
  end
40
40
 
41
- def last_by_line(nodes)
42
- nodes.max_by(&:last_line)
41
+ def last_line(nodes, ignore_last:)
42
+ if ignore_last
43
+ nodes.map(&:first_line)
44
+ else
45
+ nodes.map(&:last_line)
46
+ end.max
43
47
  end
44
48
  end
45
49
  end
@@ -45,17 +45,21 @@ module RuboCop
45
45
 
46
46
  private
47
47
 
48
+ # rubocop:disable Metrics/AbcSize
48
49
  def register_offense(node, message, replacement)
49
50
  add_offense(node.value, message: message) do |corrector|
50
51
  if (def_node = def_node_that_require_parentheses(node))
51
52
  white_spaces = range_between(def_node.loc.selector.end_pos,
52
53
  def_node.first_argument.source_range.begin_pos)
53
54
  corrector.replace(white_spaces, '(')
54
- corrector.insert_after(def_node.arguments.last, ')')
55
+
56
+ last_argument = def_node.arguments.last
57
+ corrector.insert_after(last_argument, ')') if node == last_argument.pairs.last
55
58
  end
56
59
  corrector.replace(node, replacement)
57
60
  end
58
61
  end
62
+ # rubocop:enable Metrics/AbcSize
59
63
 
60
64
  def ignore_mixed_hash_shorthand_syntax?(hash_node)
61
65
  target_ruby_version <= 3.0 || enforced_shorthand_syntax != 'consistent' ||
@@ -57,7 +57,14 @@ module RuboCop
57
57
  def indentation_difference(line)
58
58
  return 0 unless tab_indentation_width
59
59
 
60
- (line.index(/[^\t]/) || 0) * (tab_indentation_width - 1)
60
+ index =
61
+ if line.match?(/^[^\t]/)
62
+ 0
63
+ else
64
+ line.index(/[^\t]/) || 0
65
+ end
66
+
67
+ index * (tab_indentation_width - 1)
61
68
  end
62
69
 
63
70
  def extend_uri_end_position(line, end_position)
@@ -59,13 +59,15 @@ module RuboCop
59
59
  end
60
60
 
61
61
  def complexity(body)
62
- body.each_node(:lvasgn, *self.class::COUNTED_NODES).reduce(1) do |score, node|
62
+ score = 1
63
+ body.each_node(:lvasgn, *self.class::COUNTED_NODES) do |node|
63
64
  if node.lvasgn_type?
64
65
  reset_on_lvasgn(node)
65
- next score
66
+ else
67
+ score += complexity_score_for(node)
66
68
  end
67
- score + complexity_score_for(node)
68
69
  end
70
+ score
69
71
  end
70
72
  end
71
73
  end
@@ -10,8 +10,8 @@ module RuboCop
10
10
  module MultilineElementLineBreaks
11
11
  private
12
12
 
13
- def check_line_breaks(_node, children)
14
- return if all_on_same_line?(children)
13
+ def check_line_breaks(_node, children, ignore_last: false)
14
+ return if all_on_same_line?(children, ignore_last: ignore_last)
15
15
 
16
16
  last_seen_line = -1
17
17
  children.each do |child|
@@ -23,9 +23,11 @@ module RuboCop
23
23
  end
24
24
  end
25
25
 
26
- def all_on_same_line?(nodes)
26
+ def all_on_same_line?(nodes, ignore_last: false)
27
27
  return true if nodes.empty?
28
28
 
29
+ return same_line?(nodes.first, nodes.last) if ignore_last
30
+
29
31
  nodes.first.first_line == nodes.last.last_line
30
32
  end
31
33
  end
@@ -97,9 +97,7 @@ module RuboCop
97
97
  # @return [String]
98
98
  def whitespace_between(node)
99
99
  if node.children.length >= 2
100
- node.source[
101
- node.children[0].loc.expression.end_pos...node.children[1].loc.expression.begin_pos
102
- ]
100
+ node.children[0].source_range.end.join(node.children[1].source_range.begin).source
103
101
  else
104
102
  ' '
105
103
  end
@@ -111,7 +109,7 @@ module RuboCop
111
109
  # @param [RuboCop::AST::ArrayNode] node
112
110
  # @return [String]
113
111
  def whitespace_leading(node)
114
- node.source[node.loc.begin.end_pos...node.children[0].loc.expression.begin_pos]
112
+ node.loc.begin.end.join(node.children[0].source_range.begin).source
115
113
  end
116
114
 
117
115
  # Provides trailing whitespace for building a bracketed array.
@@ -120,7 +118,7 @@ module RuboCop
120
118
  # @param [RuboCop::AST::ArrayNode] node
121
119
  # @return [String]
122
120
  def whitespace_trailing(node)
123
- node.source[node.children[-1].loc.expression.end_pos...node.loc.end.begin_pos]
121
+ node.children[-1].source_range.end.join(node.loc.end.begin).source
124
122
  end
125
123
  end
126
124
  end
@@ -175,7 +175,7 @@ module RuboCop
175
175
 
176
176
  def remove_optarg_equals(asgn_tokens, processed_source)
177
177
  optargs = processed_source.ast.each_node(:optarg)
178
- optarg_eql = optargs.map { |o| o.loc.operator.begin_pos }.to_set
178
+ optarg_eql = optargs.to_set { |o| o.loc.operator.begin_pos }
179
179
  asgn_tokens.reject { |t| optarg_eql.include?(t.begin_pos) }
180
180
  end
181
181
  end
@@ -7,6 +7,8 @@ module RuboCop
7
7
  module RequireLibrary
8
8
  extend NodePattern::Macros
9
9
 
10
+ RESTRICT_ON_SEND = [:require].freeze
11
+
10
12
  def ensure_required(corrector, node, library_name)
11
13
  node = node.parent while node.parent&.parent?
12
14
 
@@ -4,8 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  # Common functionality for checking `rescue` nodes.
6
6
  module RescueNode
7
- def on_new_investigation
8
- @modifier_locations = processed_source.tokens.select(&:rescue_modifier?).map(&:pos)
7
+ def modifier_locations
8
+ @modifier_locations ||= processed_source.tokens.select(&:rescue_modifier?).map!(&:pos)
9
9
  end
10
10
 
11
11
  private
@@ -13,7 +13,7 @@ module RuboCop
13
13
  def rescue_modifier?(node)
14
14
  return false unless node.respond_to?(:resbody_type?)
15
15
 
16
- node.resbody_type? && @modifier_locations.include?(node.loc.keyword)
16
+ node.resbody_type? && modifier_locations.include?(node.loc.keyword)
17
17
  end
18
18
 
19
19
  # @deprecated Use ResbodyNode#exceptions instead
@@ -56,7 +56,7 @@ module RuboCop
56
56
 
57
57
  def if_body_source(if_body)
58
58
  if if_body.call_type? &&
59
- if_body.last_argument&.hash_type? && if_body.last_argument.pairs.last.value_omission?
59
+ if_body.last_argument&.hash_type? && if_body.last_argument.pairs.last&.value_omission?
60
60
  "#{method_source(if_body)}(#{if_body.arguments.map(&:source).join(', ')})"
61
61
  else
62
62
  if_body.source
@@ -108,7 +108,7 @@ module RuboCop
108
108
  return if node.body.nil?
109
109
 
110
110
  node.body.each_descendant(:lvar, :lvasgn).any? do |lvar|
111
- !lvar.parent.block_pass_type? && lvar.source == last_argument
111
+ !lvar.parent.block_pass_type? && lvar.node_parts[0].to_s == last_argument
112
112
  end
113
113
  end
114
114
 
@@ -30,6 +30,8 @@ module RuboCop
30
30
  MSG = 'Use CamelCase for classes and modules.'
31
31
 
32
32
  def on_class(node)
33
+ return unless node.loc.name.source.include?('_')
34
+
33
35
  allowed = /#{cop_config['AllowedNames'].join('|')}/
34
36
  name = node.loc.name.source.gsub(allowed, '')
35
37
  return unless /_/.match?(name)
@@ -207,7 +207,10 @@ module RuboCop
207
207
  end
208
208
 
209
209
  def scan_for_words(input)
210
- mask_input(input).enum_for(:scan, @flagged_terms_regex).map do
210
+ masked_input = mask_input(input)
211
+ return EMPTY_ARRAY unless masked_input.match?(@flagged_terms_regex)
212
+
213
+ masked_input.enum_for(:scan, @flagged_terms_regex).map do
211
214
  match = Regexp.last_match
212
215
  WordLocation.new(match.to_s, match.offset(0).first)
213
216
  end
@@ -19,6 +19,28 @@ module RuboCop
19
19
  class Registry
20
20
  include Enumerable
21
21
 
22
+ def self.all
23
+ global.without_department(:Test).cops
24
+ end
25
+
26
+ def self.qualified_cop_name(name, origin)
27
+ global.qualified_cop_name(name, origin)
28
+ end
29
+
30
+ # Changes momentarily the global registry
31
+ # Intended for testing purposes
32
+ def self.with_temporary_global(temp_global = global.dup)
33
+ previous = @global
34
+ @global = temp_global
35
+ yield
36
+ ensure
37
+ @global = previous
38
+ end
39
+
40
+ def self.reset!
41
+ @global = new
42
+ end
43
+
22
44
  attr_reader :options
23
45
 
24
46
  def initialize(cops = [], options = {})
@@ -28,6 +50,9 @@ module RuboCop
28
50
 
29
51
  @enrollment_queue = cops
30
52
  @options = options
53
+
54
+ @enabled_cache = {}.compare_by_identity
55
+ @disabled_cache = {}.compare_by_identity
31
56
  end
32
57
 
33
58
  def enlist(cop)
@@ -61,7 +86,7 @@ module RuboCop
61
86
 
62
87
  # @return [Boolean] Checks if given name is department
63
88
  def department?(name)
64
- departments.include? name.to_sym
89
+ departments.include?(name.to_sym)
65
90
  end
66
91
 
67
92
  def contains_cop_matching?(names)
@@ -150,11 +175,11 @@ module RuboCop
150
175
  end
151
176
 
152
177
  def enabled(config)
153
- select { |cop| enabled?(cop, config) }
178
+ @enabled_cache[config] ||= select { |cop| enabled?(cop, config) }
154
179
  end
155
180
 
156
181
  def disabled(config)
157
- reject { |cop| enabled?(cop, config) }
182
+ @disabled_cache[config] ||= reject { |cop| enabled?(cop, config) }
158
183
  end
159
184
 
160
185
  def enabled?(cop, config)
@@ -235,28 +260,6 @@ module RuboCop
235
260
  attr_reader :global
236
261
  end
237
262
 
238
- def self.all
239
- global.without_department(:Test).cops
240
- end
241
-
242
- def self.qualified_cop_name(name, origin)
243
- global.qualified_cop_name(name, origin)
244
- end
245
-
246
- # Changes momentarily the global registry
247
- # Intended for testing purposes
248
- def self.with_temporary_global(temp_global = global.dup)
249
- previous = @global
250
- @global = temp_global
251
- yield
252
- ensure
253
- @global = previous
254
- end
255
-
256
- def self.reset!
257
- @global = new
258
- end
259
-
260
263
  private
261
264
 
262
265
  def initialize_copy(reg)
@@ -9,7 +9,8 @@ module RuboCop
9
9
  # Manually combining hashes is error prone and hard to follow, especially
10
10
  # when there are many values. Poor implementations may also introduce
11
11
  # performance or security concerns if they are prone to collisions.
12
- # Delegating to `Array#hash` is clearer, faster, and safer.
12
+ # Delegating to `Array#hash` is clearer and safer, although it might be slower
13
+ # depending on the use case.
13
14
  #
14
15
  # @safety
15
16
  # This cop may be unsafe if the application logic depends on the hash
@@ -7,6 +7,11 @@ module RuboCop
7
7
  # depending on configuration.
8
8
  # It also flags uses of `alias :symbol` rather than `alias bareword`.
9
9
  #
10
+ # However, it will always enforce `method_alias` when used `alias`
11
+ # in an instance method definition and in a singleton method definition.
12
+ # If used in a block, always enforce `alias_method`
13
+ # unless it is an `instance_eval` block.
14
+ #
10
15
  # @example EnforcedStyle: prefer_alias (default)
11
16
  # # bad
12
17
  # alias_method :bar, :foo
@@ -22,6 +27,7 @@ module RuboCop
22
27
  #
23
28
  # # good
24
29
  # alias_method :bar, :foo
30
+ #
25
31
  class Alias < Base
26
32
  include ConfigurableEnforcedStyle
27
33
  extend AutoCorrector
@@ -71,7 +77,9 @@ module RuboCop
71
77
  end
72
78
 
73
79
  def alias_method_possible?(node)
74
- scope_type(node) != :instance_eval && node.children.none?(&:gvar_type?)
80
+ scope_type(node) != :instance_eval &&
81
+ node.children.none?(&:gvar_type?) &&
82
+ node&.parent&.type != :def
75
83
  end
76
84
 
77
85
  def add_offense_for_args(node, &block)