rubocop 1.39.0 → 1.44.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 (188) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +149 -11
  5. data/exe/rubocop +1 -1
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/comment_config.rb +5 -0
  8. data/lib/rubocop/config.rb +39 -15
  9. data/lib/rubocop/config_loader.rb +26 -20
  10. data/lib/rubocop/config_loader_resolver.rb +6 -2
  11. data/lib/rubocop/config_validator.rb +1 -1
  12. data/lib/rubocop/cop/badge.rb +9 -4
  13. data/lib/rubocop/cop/base.rb +84 -74
  14. data/lib/rubocop/cop/commissioner.rb +8 -3
  15. data/lib/rubocop/cop/cop.rb +29 -29
  16. data/lib/rubocop/cop/corrector.rb +30 -10
  17. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +22 -6
  18. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +1 -6
  19. data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
  20. data/lib/rubocop/cop/gemspec/development_dependencies.rb +107 -0
  21. data/lib/rubocop/cop/internal_affairs/cop_description.rb +3 -1
  22. data/lib/rubocop/cop/internal_affairs/lambda_or_proc.rb +46 -0
  23. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +11 -3
  24. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  25. data/lib/rubocop/cop/layout/block_end_newline.rb +7 -1
  26. data/lib/rubocop/cop/layout/class_structure.rb +32 -11
  27. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +2 -6
  28. data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
  29. data/lib/rubocop/cop/layout/empty_lines.rb +2 -0
  30. data/lib/rubocop/cop/layout/extra_spacing.rb +10 -6
  31. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +38 -2
  32. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +49 -2
  33. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +61 -2
  34. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +52 -2
  35. data/lib/rubocop/cop/layout/heredoc_indentation.rb +6 -9
  36. data/lib/rubocop/cop/layout/indentation_style.rb +7 -2
  37. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +5 -0
  38. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +11 -5
  39. data/lib/rubocop/cop/layout/line_length.rb +2 -0
  40. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +51 -2
  41. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  42. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +49 -2
  43. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +53 -2
  44. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +58 -2
  45. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -2
  46. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  47. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +0 -2
  48. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +1 -1
  49. data/lib/rubocop/cop/layout/trailing_whitespace.rb +11 -4
  50. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  51. data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -0
  52. data/lib/rubocop/cop/lint/assignment_in_condition.rb +11 -1
  53. data/lib/rubocop/cop/lint/constant_resolution.rb +4 -0
  54. data/lib/rubocop/cop/lint/debugger.rb +3 -1
  55. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +62 -112
  56. data/lib/rubocop/cop/lint/deprecated_constants.rb +8 -1
  57. data/lib/rubocop/cop/lint/duplicate_branch.rb +0 -2
  58. data/lib/rubocop/cop/lint/duplicate_methods.rb +19 -8
  59. data/lib/rubocop/cop/lint/else_layout.rb +2 -6
  60. data/lib/rubocop/cop/lint/empty_block.rb +1 -5
  61. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  62. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +11 -7
  63. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +15 -17
  64. data/lib/rubocop/cop/lint/interpolation_check.rb +4 -3
  65. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  66. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -5
  67. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
  68. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -0
  69. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +15 -3
  70. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  71. data/lib/rubocop/cop/lint/redundant_require_statement.rb +11 -1
  72. data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
  73. data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
  74. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +10 -12
  75. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +5 -4
  76. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +4 -3
  77. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
  78. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -3
  79. data/lib/rubocop/cop/lint/useless_rescue.rb +85 -0
  80. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +14 -4
  81. data/lib/rubocop/cop/lint/void.rb +25 -16
  82. data/lib/rubocop/cop/metrics/block_length.rb +9 -4
  83. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  84. data/lib/rubocop/cop/metrics/class_length.rb +10 -5
  85. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
  86. data/lib/rubocop/cop/metrics/method_length.rb +9 -4
  87. data/lib/rubocop/cop/metrics/module_length.rb +10 -5
  88. data/lib/rubocop/cop/metrics/parameter_lists.rb +27 -0
  89. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  90. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +3 -6
  91. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +6 -3
  92. data/lib/rubocop/cop/mixin/alignment.rb +2 -2
  93. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +2 -2
  94. data/lib/rubocop/cop/mixin/annotation_comment.rb +13 -6
  95. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +21 -9
  96. data/lib/rubocop/cop/mixin/first_element_line_break.rb +11 -7
  97. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +59 -6
  98. data/lib/rubocop/cop/mixin/line_length_help.rb +11 -2
  99. data/lib/rubocop/cop/mixin/method_complexity.rb +5 -3
  100. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +5 -3
  101. data/lib/rubocop/cop/mixin/percent_array.rb +3 -5
  102. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  103. data/lib/rubocop/cop/mixin/require_library.rb +2 -0
  104. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -3
  105. data/lib/rubocop/cop/mixin/statement_modifier.rb +16 -1
  106. data/lib/rubocop/cop/naming/block_forwarding.rb +5 -1
  107. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +2 -0
  108. data/lib/rubocop/cop/naming/inclusive_language.rb +4 -1
  109. data/lib/rubocop/cop/registry.rb +63 -43
  110. data/lib/rubocop/cop/security/compound_hash.rb +2 -1
  111. data/lib/rubocop/cop/style/access_modifier_declarations.rb +18 -10
  112. data/lib/rubocop/cop/style/alias.rb +9 -1
  113. data/lib/rubocop/cop/style/array_intersect.rb +111 -0
  114. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  115. data/lib/rubocop/cop/style/block_delimiters.rb +8 -2
  116. data/lib/rubocop/cop/style/class_and_module_children.rb +2 -9
  117. data/lib/rubocop/cop/style/comparable_clamp.rb +125 -0
  118. data/lib/rubocop/cop/style/concat_array_literals.rb +86 -0
  119. data/lib/rubocop/cop/style/conditional_assignment.rb +0 -6
  120. data/lib/rubocop/cop/style/documentation.rb +11 -5
  121. data/lib/rubocop/cop/style/guard_clause.rb +44 -9
  122. data/lib/rubocop/cop/style/hash_each_methods.rb +13 -1
  123. data/lib/rubocop/cop/style/hash_syntax.rb +11 -7
  124. data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
  125. data/lib/rubocop/cop/style/if_with_semicolon.rb +4 -4
  126. data/lib/rubocop/cop/style/infinite_loop.rb +2 -5
  127. data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
  128. data/lib/rubocop/cop/style/invertible_unless_condition.rb +114 -0
  129. data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -1
  130. data/lib/rubocop/cop/style/map_to_set.rb +61 -0
  131. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +23 -14
  132. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -0
  133. data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
  134. data/lib/rubocop/cop/style/min_max_comparison.rb +83 -0
  135. data/lib/rubocop/cop/style/missing_else.rb +13 -1
  136. data/lib/rubocop/cop/style/multiline_if_modifier.rb +0 -4
  137. data/lib/rubocop/cop/style/multiline_memoization.rb +2 -2
  138. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -5
  139. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  140. data/lib/rubocop/cop/style/one_line_conditional.rb +3 -6
  141. data/lib/rubocop/cop/style/operator_method_call.rb +15 -1
  142. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -1
  143. data/lib/rubocop/cop/style/redundant_argument.rb +3 -0
  144. data/lib/rubocop/cop/style/redundant_conditional.rb +0 -4
  145. data/lib/rubocop/cop/style/redundant_constant_base.rb +85 -0
  146. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +45 -0
  147. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
  148. data/lib/rubocop/cop/style/redundant_return.rb +7 -0
  149. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  150. data/lib/rubocop/cop/style/redundant_string_escape.rb +6 -3
  151. data/lib/rubocop/cop/style/require_order.rb +135 -0
  152. data/lib/rubocop/cop/style/safe_navigation.rb +35 -6
  153. data/lib/rubocop/cop/style/select_by_regexp.rb +13 -5
  154. data/lib/rubocop/cop/style/self_assignment.rb +2 -2
  155. data/lib/rubocop/cop/style/semicolon.rb +26 -3
  156. data/lib/rubocop/cop/style/signal_exception.rb +8 -6
  157. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -1
  158. data/lib/rubocop/cop/style/string_literals.rb +1 -5
  159. data/lib/rubocop/cop/style/symbol_proc.rb +2 -4
  160. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
  161. data/lib/rubocop/cop/style/word_array.rb +41 -0
  162. data/lib/rubocop/cop/style/yoda_expression.rb +81 -0
  163. data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
  164. data/lib/rubocop/cop/team.rb +30 -30
  165. data/lib/rubocop/cop/util.rb +32 -5
  166. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  167. data/lib/rubocop/cop/variable_force/variable_table.rb +3 -1
  168. data/lib/rubocop/cop/variable_force.rb +18 -30
  169. data/lib/rubocop/cops_documentation_generator.rb +33 -11
  170. data/lib/rubocop/directive_comment.rb +1 -1
  171. data/lib/rubocop/file_patterns.rb +43 -0
  172. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  173. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  174. data/lib/rubocop/formatter.rb +4 -1
  175. data/lib/rubocop/options.rb +8 -0
  176. data/lib/rubocop/path_util.rb +50 -22
  177. data/lib/rubocop/result_cache.rb +2 -2
  178. data/lib/rubocop/rspec/cop_helper.rb +4 -1
  179. data/lib/rubocop/rspec/expect_offense.rb +6 -4
  180. data/lib/rubocop/rspec/support.rb +2 -2
  181. data/lib/rubocop/runner.rb +10 -3
  182. data/lib/rubocop/server/cache.rb +3 -1
  183. data/lib/rubocop/server/core.rb +1 -1
  184. data/lib/rubocop/target_finder.rb +1 -1
  185. data/lib/rubocop/target_ruby.rb +1 -2
  186. data/lib/rubocop/version.rb +1 -1
  187. data/lib/rubocop.rb +23 -6
  188. metadata +23 -9
@@ -25,15 +25,12 @@ module RuboCop
25
25
  # > http://c2.com/cgi/wiki?AbcMetric
26
26
  CONDITION_NODES = CyclomaticComplexity::COUNTED_NODES.freeze
27
27
 
28
+ private_constant :BRANCH_NODES, :CONDITION_NODES
29
+
28
30
  def self.calculate(node, discount_repeated_attributes: false)
29
31
  new(node, discount_repeated_attributes: discount_repeated_attributes).calculate
30
32
  end
31
33
 
32
- # TODO: move to rubocop-ast
33
- ARGUMENT_TYPES = %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg].freeze
34
-
35
- private_constant :BRANCH_NODES, :CONDITION_NODES, :ARGUMENT_TYPES
36
-
37
34
  def initialize(node)
38
35
  @assignment = 0
39
36
  @branch = 0
@@ -129,7 +126,7 @@ module RuboCop
129
126
  end
130
127
 
131
128
  def argument?(node)
132
- ARGUMENT_TYPES.include?(node.type) && capturing_variable?(node.children.first)
129
+ node.argument_type? && capturing_variable?(node.children.first)
133
130
  end
134
131
 
135
132
  def condition?(node)
@@ -9,7 +9,7 @@ module RuboCop
9
9
  extend NodePattern::Macros
10
10
  include Util
11
11
 
12
- FOLDABLE_TYPES = %i[array hash heredoc].freeze
12
+ FOLDABLE_TYPES = %i[array hash heredoc send csend].freeze
13
13
  CLASSLIKE_TYPES = %i[class module].freeze
14
14
  private_constant :FOLDABLE_TYPES, :CLASSLIKE_TYPES
15
15
 
@@ -39,7 +39,7 @@ module RuboCop
39
39
 
40
40
  private
41
41
 
42
- def build_foldable_checks(types)
42
+ def build_foldable_checks(types) # rubocop:disable Metrics/MethodLength
43
43
  types.map do |type|
44
44
  case type
45
45
  when :array
@@ -48,6 +48,8 @@ module RuboCop
48
48
  ->(node) { node.hash_type? }
49
49
  when :heredoc
50
50
  ->(node) { heredoc_node?(node) }
51
+ when :method_call
52
+ ->(node) { node.call_type? }
51
53
  else
52
54
  raise ArgumentError, "Unknown foldable type: #{type.inspect}. " \
53
55
  "Valid foldable types are: #{FOLDABLE_TYPES.join(', ')}."
@@ -56,7 +58,8 @@ module RuboCop
56
58
  end
57
59
 
58
60
  def normalize_foldable_types(types)
59
- types.concat(%i[str dstr]) if types.delete(:heredoc)
61
+ types.push(:str, :dstr) if types.delete(:heredoc)
62
+ types.push(:send, :csend) if types.delete(:method_call)
60
63
  types
61
64
  end
62
65
 
@@ -12,7 +12,7 @@ module RuboCop
12
12
  attr_reader :column_delta
13
13
 
14
14
  def configured_indentation_width
15
- cop_config['IndentationWidth'] || config.for_cop('Layout/IndentationWidth')['Width']
15
+ cop_config['IndentationWidth'] || config.for_cop('Layout/IndentationWidth')['Width'] || 2
16
16
  end
17
17
 
18
18
  def indentation(node)
@@ -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
@@ -3,6 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  # This module checks for Ruby 3.1's hash value omission syntax.
6
+ # rubocop:disable Metrics/ModuleLength
6
7
  module HashShorthandSyntax
7
8
  OMIT_HASH_VALUE_MSG = 'Omit the hash value.'
8
9
  EXPLICIT_HASH_VALUE_MSG = 'Include the hash value.'
@@ -45,8 +46,19 @@ module RuboCop
45
46
 
46
47
  private
47
48
 
48
- def register_offense(node, message, replacement)
49
+ def register_offense(node, message, replacement) # rubocop:disable Metrics/AbcSize
49
50
  add_offense(node.value, message: message) do |corrector|
51
+ if (def_node = def_node_that_require_parentheses(node))
52
+ last_argument = def_node.last_argument
53
+ if last_argument.nil? || !last_argument.hash_type?
54
+ next corrector.replace(node, replacement)
55
+ end
56
+
57
+ white_spaces = range_between(def_node.selector.end_pos,
58
+ def_node.first_argument.source_range.begin_pos)
59
+ corrector.replace(white_spaces, '(')
60
+ corrector.insert_after(last_argument, ')') if node == last_argument.pairs.last
61
+ end
50
62
  corrector.replace(node, replacement)
51
63
  end
52
64
  end
@@ -76,12 +88,34 @@ module RuboCop
76
88
  end
77
89
 
78
90
  def require_hash_value_for_around_hash_literal?(node)
79
- return false unless (ancestor = node.parent.parent)
80
- return false if ancestor.send_type? && ancestor.method?(:[])
91
+ return false unless (method_dispatch_node = find_ancestor_method_dispatch_node(node))
92
+
93
+ !node.parent.braces? &&
94
+ !use_element_of_hash_literal_as_receiver?(method_dispatch_node, node.parent) &&
95
+ use_modifier_form_without_parenthesized_method_call?(method_dispatch_node)
96
+ end
97
+
98
+ def def_node_that_require_parentheses(node)
99
+ last_pair = node.parent.pairs.last
100
+ return unless last_pair.key.source == last_pair.value.source
101
+ return unless (method_dispatch_node = find_ancestor_method_dispatch_node(node))
102
+ return unless without_parentheses_call_expr_follows?(method_dispatch_node)
103
+
104
+ def_node = node.each_ancestor(:send, :csend, :super, :yield).first
105
+
106
+ DefNode.new(def_node) unless def_node && def_node.arguments.empty?
107
+ end
108
+
109
+ def find_ancestor_method_dispatch_node(node)
110
+ return unless (ancestor = node.parent.parent)
111
+ return unless ancestor.call_type? || ancestor.super_type? || ancestor.yield_type?
112
+ return if brackets?(ancestor)
113
+
114
+ ancestor
115
+ end
81
116
 
82
- !node.parent.braces? && !use_element_of_hash_literal_as_receiver?(ancestor, node.parent) &&
83
- (use_modifier_form_without_parenthesized_method_call?(ancestor) ||
84
- without_parentheses_call_expr_follows?(ancestor))
117
+ def brackets?(method_dispatch_node)
118
+ method_dispatch_node.method?(:[]) || method_dispatch_node.method?(:[]=)
85
119
  end
86
120
 
87
121
  def use_element_of_hash_literal_as_receiver?(ancestor, parent)
@@ -159,6 +193,25 @@ module RuboCop
159
193
  register_offense(pair_node, OMIT_HASH_VALUE_MSG, replacement)
160
194
  end
161
195
  end
196
+
197
+ DefNode = Struct.new(:node) do
198
+ def selector
199
+ if node.loc.respond_to?(:selector)
200
+ node.loc.selector
201
+ else
202
+ node.loc.keyword
203
+ end
204
+ end
205
+
206
+ def first_argument
207
+ node.first_argument
208
+ end
209
+
210
+ def last_argument
211
+ node.last_argument
212
+ end
213
+ end
162
214
  end
163
215
  end
216
+ # rubocop:enable Metrics/ModuleLength
164
217
  end
@@ -4,6 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  # Help methods for determining if a line is too long.
6
6
  module LineLengthHelp
7
+ include Alignment
8
+
7
9
  private
8
10
 
9
11
  def ignore_cop_directives?
@@ -57,7 +59,14 @@ module RuboCop
57
59
  def indentation_difference(line)
58
60
  return 0 unless tab_indentation_width
59
61
 
60
- (line.index(/[^\t]/) || 0) * (tab_indentation_width - 1)
62
+ index =
63
+ if line.match?(/^[^\t]/)
64
+ 0
65
+ else
66
+ line.index(/[^\t]/) || 0
67
+ end
68
+
69
+ index * (tab_indentation_width - 1)
61
70
  end
62
71
 
63
72
  def extend_uri_end_position(line, end_position)
@@ -78,7 +87,7 @@ module RuboCop
78
87
 
79
88
  def tab_indentation_width
80
89
  config.for_cop('Layout/IndentationStyle')['IndentationWidth'] ||
81
- config.for_cop('Layout/IndentationWidth')['Width']
90
+ configured_indentation_width
82
91
  end
83
92
 
84
93
  def uri_regexp
@@ -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
@@ -5,6 +5,7 @@ module RuboCop
5
5
  # Common functionality for modifier cops.
6
6
  module StatementModifier
7
7
  include LineLengthHelp
8
+ include RangeHelp
8
9
 
9
10
  private
10
11
 
@@ -48,11 +49,25 @@ module RuboCop
48
49
  end
49
50
 
50
51
  def to_modifier_form(node)
51
- expression = [node.body.source, node.keyword, node.condition.source].compact.join(' ')
52
+ body = if_body_source(node.body)
53
+ expression = [body, node.keyword, node.condition.source].compact.join(' ')
52
54
  parenthesized = parenthesize?(node) ? "(#{expression})" : expression
53
55
  [parenthesized, first_line_comment(node)].compact.join(' ')
54
56
  end
55
57
 
58
+ def if_body_source(if_body)
59
+ if if_body.call_type? &&
60
+ if_body.last_argument&.hash_type? && if_body.last_argument.pairs.last&.value_omission?
61
+ "#{method_source(if_body)}(#{if_body.arguments.map(&:source).join(', ')})"
62
+ else
63
+ if_body.source
64
+ end
65
+ end
66
+
67
+ def method_source(if_body)
68
+ range_between(if_body.loc.expression.begin_pos, if_body.loc.selector.end_pos).source
69
+ end
70
+
56
71
  def first_line_comment(node)
57
72
  comment = processed_source.find_comment { |c| same_line?(c, node) }
58
73
  return unless comment
@@ -47,6 +47,10 @@ module RuboCop
47
47
 
48
48
  MSG = 'Use %<style>s block forwarding.'
49
49
 
50
+ def self.autocorrect_incompatible_with
51
+ [Lint::AmbiguousOperator]
52
+ end
53
+
50
54
  def on_def(node)
51
55
  return if node.arguments.empty?
52
56
 
@@ -108,7 +112,7 @@ module RuboCop
108
112
  return if node.body.nil?
109
113
 
110
114
  node.body.each_descendant(:lvar, :lvasgn).any? do |lvar|
111
- !lvar.parent.block_pass_type? && lvar.source == last_argument
115
+ !lvar.parent.block_pass_type? && lvar.node_parts[0].to_s == last_argument
112
116
  end
113
117
  end
114
118
 
@@ -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