rubocop 1.42.0 → 1.45.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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +73 -31
  4. data/lib/rubocop/cli.rb +54 -8
  5. data/lib/rubocop/config_loader.rb +12 -15
  6. data/lib/rubocop/config_loader_resolver.rb +3 -4
  7. data/lib/rubocop/cop/base.rb +27 -9
  8. data/lib/rubocop/cop/commissioner.rb +8 -2
  9. data/lib/rubocop/cop/cop.rb +23 -3
  10. data/lib/rubocop/cop/corrector.rb +10 -2
  11. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +1 -6
  12. data/lib/rubocop/cop/gemspec/development_dependencies.rb +107 -0
  13. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +11 -3
  14. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  15. data/lib/rubocop/cop/layout/block_end_newline.rb +7 -1
  16. data/lib/rubocop/cop/layout/class_structure.rb +2 -16
  17. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +2 -6
  18. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  19. data/lib/rubocop/cop/layout/heredoc_indentation.rb +6 -9
  20. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  21. data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
  22. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +11 -13
  23. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +4 -4
  24. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +5 -4
  25. data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -0
  26. data/lib/rubocop/cop/lint/debugger.rb +8 -27
  27. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +62 -112
  28. data/lib/rubocop/cop/lint/else_layout.rb +2 -6
  29. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +14 -7
  30. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +15 -17
  31. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
  32. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  33. data/lib/rubocop/cop/lint/nested_method_definition.rb +8 -5
  34. data/lib/rubocop/cop/lint/redundant_require_statement.rb +11 -1
  35. data/lib/rubocop/cop/lint/useless_access_modifier.rb +7 -4
  36. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -3
  37. data/lib/rubocop/cop/lint/useless_rescue.rb +85 -0
  38. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +9 -1
  39. data/lib/rubocop/cop/lint/void.rb +19 -10
  40. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  41. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  42. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
  43. data/lib/rubocop/cop/metrics/parameter_lists.rb +27 -0
  44. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +2 -5
  45. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  46. data/lib/rubocop/cop/mixin/allowed_methods.rb +3 -1
  47. data/lib/rubocop/cop/mixin/comments_help.rb +5 -3
  48. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +57 -23
  49. data/lib/rubocop/cop/mixin/line_length_help.rb +3 -1
  50. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -0
  51. data/lib/rubocop/cop/mixin/surrounding_space.rb +3 -3
  52. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  53. data/lib/rubocop/cop/naming/block_forwarding.rb +4 -0
  54. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +1 -1
  55. data/lib/rubocop/cop/registry.rb +12 -7
  56. data/lib/rubocop/cop/style/access_modifier_declarations.rb +26 -11
  57. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -0
  58. data/lib/rubocop/cop/style/block_delimiters.rb +8 -2
  59. data/lib/rubocop/cop/style/class_and_module_children.rb +3 -10
  60. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  61. data/lib/rubocop/cop/style/comparable_clamp.rb +125 -0
  62. data/lib/rubocop/cop/style/conditional_assignment.rb +0 -6
  63. data/lib/rubocop/cop/style/documentation.rb +1 -1
  64. data/lib/rubocop/cop/style/documentation_method.rb +6 -0
  65. data/lib/rubocop/cop/style/hash_each_methods.rb +13 -1
  66. data/lib/rubocop/cop/style/hash_syntax.rb +1 -0
  67. data/lib/rubocop/cop/style/infinite_loop.rb +2 -5
  68. data/lib/rubocop/cop/style/invertible_unless_condition.rb +114 -0
  69. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +23 -14
  70. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -0
  71. data/lib/rubocop/cop/style/min_max_comparison.rb +11 -1
  72. data/lib/rubocop/cop/style/missing_else.rb +13 -1
  73. data/lib/rubocop/cop/style/multiline_if_modifier.rb +0 -4
  74. data/lib/rubocop/cop/style/multiline_memoization.rb +2 -2
  75. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +18 -3
  76. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -5
  77. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +11 -3
  78. data/lib/rubocop/cop/style/one_line_conditional.rb +3 -6
  79. data/lib/rubocop/cop/style/operator_method_call.rb +16 -2
  80. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -1
  81. data/lib/rubocop/cop/style/redundant_condition.rb +16 -1
  82. data/lib/rubocop/cop/style/redundant_conditional.rb +0 -4
  83. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +16 -10
  84. data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +58 -0
  85. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
  86. data/lib/rubocop/cop/style/require_order.rb +2 -9
  87. data/lib/rubocop/cop/style/self_assignment.rb +2 -2
  88. data/lib/rubocop/cop/style/semicolon.rb +24 -2
  89. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -1
  90. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  91. data/lib/rubocop/cop/style/word_array.rb +1 -1
  92. data/lib/rubocop/cop/style/yoda_condition.rb +12 -5
  93. data/lib/rubocop/cop/style/yoda_expression.rb +18 -2
  94. data/lib/rubocop/cop/team.rb +19 -14
  95. data/lib/rubocop/cop/variable_force/scope.rb +3 -3
  96. data/lib/rubocop/cop/variable_force/variable_table.rb +3 -1
  97. data/lib/rubocop/cop/variable_force.rb +1 -1
  98. data/lib/rubocop/formatter.rb +0 -1
  99. data/lib/rubocop/options.rb +22 -1
  100. data/lib/rubocop/path_util.rb +11 -6
  101. data/lib/rubocop/rspec/expect_offense.rb +6 -4
  102. data/lib/rubocop/runner.rb +40 -4
  103. data/lib/rubocop/server/cache.rb +10 -3
  104. data/lib/rubocop/server/cli.rb +37 -18
  105. data/lib/rubocop/server/client_command/exec.rb +1 -1
  106. data/lib/rubocop/server/client_command/start.rb +6 -1
  107. data/lib/rubocop/server/core.rb +23 -8
  108. data/lib/rubocop/version.rb +1 -1
  109. data/lib/rubocop.rb +5 -0
  110. metadata +12 -27
@@ -18,21 +18,27 @@ module RuboCop
18
18
 
19
19
  MSG = 'Remove the redundant double splat and braces, use keyword arguments directly.'
20
20
 
21
- # @!method double_splat_hash_braces?(node)
22
- def_node_matcher :double_splat_hash_braces?, <<~PATTERN
23
- (hash (kwsplat (hash ...)))
24
- PATTERN
25
-
26
21
  def on_hash(node)
27
22
  return if node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
23
+ return unless (parent = node.parent)
24
+ return unless parent.kwsplat_type?
28
25
 
29
- grandparent = node.parent&.parent
30
- return unless double_splat_hash_braces?(grandparent)
31
-
32
- add_offense(grandparent) do |corrector|
33
- corrector.replace(grandparent, node.pairs.map(&:source).join(', '))
26
+ add_offense(parent) do |corrector|
27
+ corrector.remove(parent.loc.operator)
28
+ corrector.remove(opening_brace(node))
29
+ corrector.remove(closing_brace(node))
34
30
  end
35
31
  end
32
+
33
+ private
34
+
35
+ def opening_brace(node)
36
+ node.loc.begin.join(node.children.first.loc.expression.begin)
37
+ end
38
+
39
+ def closing_brace(node)
40
+ node.children.last.loc.expression.end.join(node.loc.end)
41
+ end
36
42
  end
37
43
  end
38
44
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant heredoc delimiter quotes.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # do_something(<<~'EOS')
12
+ # no string interpolation style text
13
+ # EOS
14
+ #
15
+ # # good
16
+ # do_something(<<~EOS)
17
+ # no string interpolation style text
18
+ # EOS
19
+ #
20
+ # do_something(<<~'EOS')
21
+ # #{string_interpolation_style_text_not_evaluated}
22
+ # EOS
23
+ #
24
+ # do_something(<<~'EOS')
25
+ # Preserve \
26
+ # newlines
27
+ # EOS
28
+ #
29
+ class RedundantHeredocDelimiterQuotes < Base
30
+ include Heredoc
31
+ extend AutoCorrector
32
+
33
+ MSG = 'Remove the redundant heredoc delimiter quotes, use `%<replacement>s` instead.'
34
+ STRING_INTERPOLATION_OR_ESCAPED_CHARACTER_PATTERN = /#(\{|@|\$)|\\/.freeze
35
+
36
+ def on_heredoc(node)
37
+ return if need_heredoc_delimiter_quotes?(node)
38
+
39
+ replacement = "#{heredoc_type(node)}#{delimiter_string(node)}"
40
+
41
+ add_offense(node, message: format(MSG, replacement: replacement)) do |corrector|
42
+ corrector.replace(node, replacement)
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def need_heredoc_delimiter_quotes?(node)
49
+ heredoc_delimiter = node.source.delete(heredoc_type(node))
50
+ return true unless heredoc_delimiter.start_with?("'", '"')
51
+
52
+ node.loc.heredoc_end.source.strip.match?(/\W/) ||
53
+ node.loc.heredoc_body.source.match?(STRING_INTERPOLATION_OR_ESCAPED_CHARACTER_PATTERN)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -77,7 +77,8 @@ module RuboCop
77
77
  # but it's not necessry to escape hyphen if it's the first or last character
78
78
  # within the character class. This method checks if that's the case.
79
79
  # e.g. "[0-9\\-]" or "[\\-0-9]" would return true
80
- node.source[index] == '[' || node.source[index + 3] == ']'
80
+ contents_range(node).source[index - 1] == '[' ||
81
+ contents_range(node).source[index + 2] == ']'
81
82
  end
82
83
 
83
84
  def delimiter?(node, char)
@@ -88,10 +88,9 @@ module RuboCop
88
88
  return unless previous_older_sibling
89
89
 
90
90
  add_offense(node, message: format(MSG, name: node.method_name)) do |corrector|
91
- swap(
91
+ corrector.swap(
92
92
  range_with_comments_and_lines(previous_older_sibling),
93
- range_with_comments_and_lines(node.parent.if_type? ? node.parent : node),
94
- corrector: corrector
93
+ range_with_comments_and_lines(node.parent.if_type? ? node.parent : node)
95
94
  )
96
95
  end
97
96
  end
@@ -130,12 +129,6 @@ module RuboCop
130
129
  end_pos: node2.location.expression.end_pos
131
130
  ).source.include?("\n\n")
132
131
  end
133
-
134
- def swap(range1, range2, corrector:)
135
- inserted = range2.source
136
- corrector.insert_before(range1, inserted)
137
- corrector.remove(range2)
138
- end
139
132
  end
140
133
  end
141
134
  end
@@ -42,7 +42,7 @@ module RuboCop
42
42
 
43
43
  if rhs.send_type?
44
44
  check_send_node(node, rhs, var_name, var_type)
45
- elsif %i[and or].include?(rhs.type)
45
+ elsif rhs.operator_keyword?
46
46
  check_boolean_node(node, rhs, var_name, var_type)
47
47
  end
48
48
  end
@@ -76,7 +76,7 @@ module RuboCop
76
76
 
77
77
  if rhs.send_type?
78
78
  autocorrect_send_node(corrector, node, rhs)
79
- elsif %i[and or].include?(rhs.type)
79
+ elsif rhs.operator_keyword?
80
80
  autocorrect_boolean_node(corrector, node, rhs)
81
81
  end
82
82
  end
@@ -72,8 +72,10 @@ module RuboCop
72
72
 
73
73
  def each_semicolon
74
74
  tokens_for_lines.each do |line, tokens|
75
- yield line, tokens.last.column, tokens[-2] if tokens.last.semicolon?
76
- yield line, tokens.first.column if tokens.first.semicolon?
75
+ semicolon_pos = semicolon_position(tokens)
76
+ after_expr_pos = semicolon_pos == -1 ? -2 : semicolon_pos
77
+
78
+ yield line, tokens[semicolon_pos].column, tokens[after_expr_pos] if semicolon_pos
77
79
  end
78
80
  end
79
81
 
@@ -81,6 +83,26 @@ module RuboCop
81
83
  processed_source.tokens.group_by(&:line)
82
84
  end
83
85
 
86
+ def semicolon_position(tokens)
87
+ if tokens.last.semicolon?
88
+ -1
89
+ elsif tokens.first.semicolon?
90
+ 0
91
+ elsif exist_semicolon_before_right_curly_brace?(tokens)
92
+ -3
93
+ elsif exist_semicolon_after_left_curly_brace?(tokens)
94
+ 2
95
+ end
96
+ end
97
+
98
+ def exist_semicolon_before_right_curly_brace?(tokens)
99
+ tokens[-2]&.right_curly_brace? && tokens[-3]&.semicolon?
100
+ end
101
+
102
+ def exist_semicolon_after_left_curly_brace?(tokens)
103
+ tokens[1]&.left_curly_brace? && tokens[2]&.semicolon?
104
+ end
105
+
84
106
  def register_semicolon(line, column, after_expression, token_before_semicolon = nil)
85
107
  range = source_range(processed_source.buffer, line, column)
86
108
 
@@ -41,10 +41,13 @@ module RuboCop
41
41
 
42
42
  def on_pair(node)
43
43
  return unless string_hash_key?(node)
44
+
45
+ key_content = node.key.str_content
46
+ return unless key_content.valid_encoding?
44
47
  return if receive_environments_method?(node)
45
48
 
46
49
  add_offense(node.key) do |corrector|
47
- symbol_content = node.key.str_content.to_sym.inspect
50
+ symbol_content = key_content.to_sym.inspect
48
51
 
49
52
  corrector.replace(node.key, symbol_content)
50
53
  end
@@ -60,7 +60,7 @@ module RuboCop
60
60
  def symbols_contain_spaces?(node)
61
61
  node.children.any? do |sym|
62
62
  content, = *sym
63
- / /.match?(content)
63
+ content.to_s.include?(' ')
64
64
  end
65
65
  end
66
66
 
@@ -110,7 +110,7 @@ module RuboCop
110
110
  string = s.str_content.dup.force_encoding(::Encoding::UTF_8)
111
111
  !string.valid_encoding? ||
112
112
  (complex_regex && !complex_regex.match?(string)) ||
113
- / /.match?(string)
113
+ string.include?(' ')
114
114
  end
115
115
  end
116
116
 
@@ -33,12 +33,14 @@ module RuboCop
33
33
  # "bar" != foo
34
34
  # 42 >= foo
35
35
  # 10 < bar
36
+ # 99 == CONST
36
37
  #
37
38
  # # good
38
39
  # foo == 99
39
40
  # foo == "bar"
40
41
  # foo <= 42
41
42
  # bar > 10
43
+ # CONST == 99
42
44
  # "#{interpolation}" == foo
43
45
  # /#{interpolation}/ == foo
44
46
  #
@@ -92,9 +94,10 @@ module RuboCop
92
94
  def on_send(node)
93
95
  return unless yoda_compatible_condition?(node)
94
96
  return if (equality_only? && non_equality_operator?(node)) ||
95
- file_constant_equal_program_name?(node)
97
+ file_constant_equal_program_name?(node) ||
98
+ valid_yoda?(node)
96
99
 
97
- valid_yoda?(node) || add_offense(node) do |corrector|
100
+ add_offense(node) do |corrector|
98
101
  corrector.replace(actual_code_range(node), corrected_code(node))
99
102
  end
100
103
  end
@@ -119,11 +122,11 @@ module RuboCop
119
122
  lhs = node.receiver
120
123
  rhs = node.first_argument
121
124
 
122
- return true if (lhs.literal? && rhs.literal?) ||
123
- (!lhs.literal? && !rhs.literal?) ||
125
+ return true if (constant_portion?(lhs) && constant_portion?(rhs)) ||
126
+ (!constant_portion?(lhs) && !constant_portion?(rhs)) ||
124
127
  interpolation?(lhs)
125
128
 
126
- enforce_yoda? ? lhs.literal? : rhs.literal?
129
+ enforce_yoda? ? constant_portion?(lhs) : constant_portion?(rhs)
127
130
  end
128
131
 
129
132
  def message(node)
@@ -137,6 +140,10 @@ module RuboCop
137
140
  "#{rhs.source} #{reverse_comparison(node.method_name)} #{lhs.source}"
138
141
  end
139
142
 
143
+ def constant_portion?(node)
144
+ node.literal? || node.const_type?
145
+ end
146
+
140
147
  def actual_code_range(node)
141
148
  range_between(node.loc.expression.begin_pos, node.loc.expression.end_pos)
142
149
  end
@@ -7,6 +7,13 @@ module RuboCop
7
7
  # and `^` operators) where the order of expression is reversed, eg. `1 + x`.
8
8
  # This cop complements `Style/YodaCondition` cop, which has a similar purpose.
9
9
  #
10
+ # This cop is disabled by default to respect user intentions such as:
11
+ #
12
+ # [source,ruby]
13
+ # ----
14
+ # config.server_port = 9000 + ENV["TEST_ENV_NUMBER"].to_i
15
+ # ----
16
+ #
10
17
  # @safety
11
18
  # This cop is unsafe because binary operators can be defined
12
19
  # differently on different classes, and are not guaranteed to
@@ -17,12 +24,14 @@ module RuboCop
17
24
  # 1 + x
18
25
  # 10 * y
19
26
  # 1 & z
27
+ # 1 + CONST
20
28
  #
21
29
  # # good
22
30
  # 60 * 24
23
31
  # x + 1
24
32
  # y * 10
25
33
  # z & 1
34
+ # CONST + 1
26
35
  #
27
36
  # # good
28
37
  # 1 | x
@@ -43,8 +52,7 @@ module RuboCop
43
52
 
44
53
  lhs = node.receiver
45
54
  rhs = node.first_argument
46
- return if !lhs.numeric_type? || rhs.numeric_type?
47
-
55
+ return unless yoda_expression_constant?(lhs, rhs)
48
56
  return if offended_ancestor?(node)
49
57
 
50
58
  message = format(MSG, source: rhs.source)
@@ -57,6 +65,14 @@ module RuboCop
57
65
 
58
66
  private
59
67
 
68
+ def yoda_expression_constant?(lhs, rhs)
69
+ constant_portion?(lhs) && !constant_portion?(rhs)
70
+ end
71
+
72
+ def constant_portion?(node)
73
+ node.numeric_type? || node.const_type?
74
+ end
75
+
60
76
  def supported_operators
61
77
  Array(cop_config['SupportedOperators'])
62
78
  end
@@ -77,7 +77,7 @@ module RuboCop
77
77
  end
78
78
 
79
79
  # @return [Commissioner::InvestigationReport]
80
- def investigate(processed_source)
80
+ def investigate(processed_source, offset: 0, original: processed_source)
81
81
  be_ready
82
82
 
83
83
  # The autocorrection process may have to be repeated multiple times
@@ -87,14 +87,15 @@ module RuboCop
87
87
  on_duty = roundup_relevant_cops(processed_source.file_path)
88
88
 
89
89
  autocorrect_cops, other_cops = on_duty.partition(&:autocorrect?)
90
+ report = investigate_partial(autocorrect_cops, processed_source,
91
+ offset: offset, original: original)
90
92
 
91
- report = investigate_partial(autocorrect_cops, processed_source)
92
-
93
- unless autocorrect(processed_source, report)
93
+ unless autocorrect(processed_source, report, offset: offset, original: original)
94
94
  # If we corrected some errors, another round of inspection will be
95
95
  # done, and any other offenses will be caught then, so only need
96
96
  # to check other_cops if no correction was done
97
- report = report.merge(investigate_partial(other_cops, processed_source))
97
+ report = report.merge(investigate_partial(other_cops, processed_source,
98
+ offset: offset, original: original))
98
99
  end
99
100
 
100
101
  process_errors(processed_source.path, report.errors)
@@ -116,12 +117,12 @@ module RuboCop
116
117
 
117
118
  private
118
119
 
119
- def autocorrect(processed_source, report)
120
+ def autocorrect(processed_source, report, original:, offset:)
120
121
  @updated_source_file = false
121
122
  return unless autocorrect?
122
123
  return if report.processed_source.parser_error
123
124
 
124
- new_source = autocorrect_report(report)
125
+ new_source = autocorrect_report(report, original: original, offset: offset)
125
126
 
126
127
  return unless new_source
127
128
 
@@ -149,9 +150,9 @@ module RuboCop
149
150
  end
150
151
 
151
152
  # @return [Commissioner::InvestigationReport]
152
- def investigate_partial(cops, processed_source)
153
+ def investigate_partial(cops, processed_source, offset:, original:)
153
154
  commissioner = Commissioner.new(cops, self.class.forces_for(cops), @options)
154
- commissioner.investigate(processed_source)
155
+ commissioner.investigate(processed_source, offset: offset, original: original)
155
156
  end
156
157
 
157
158
  # @return [Array<cop>]
@@ -175,18 +176,22 @@ module RuboCop
175
176
  cop.class.support_target_rails_version?(cop.target_rails_version)
176
177
  end
177
178
 
178
- def autocorrect_report(report)
179
- corrector = collate_corrections(report)
179
+ def autocorrect_report(report, offset:, original:)
180
+ corrector = collate_corrections(report, offset: offset, original: original)
180
181
 
181
182
  corrector.rewrite unless corrector.empty?
182
183
  end
183
184
 
184
- def collate_corrections(report)
185
- corrector = Corrector.new(report.processed_source)
185
+ def collate_corrections(report, offset:, original:)
186
+ corrector = Corrector.new(original)
186
187
 
187
188
  each_corrector(report) do |to_merge|
188
189
  suppress_clobbering do
189
- corrector.merge!(to_merge)
190
+ if offset.positive?
191
+ corrector.import!(to_merge, offset: offset)
192
+ else
193
+ corrector.merge!(to_merge)
194
+ end
190
195
  end
191
196
  end
192
197
 
@@ -45,9 +45,9 @@ module RuboCop
45
45
  node
46
46
  else
47
47
  child_index = case node.type
48
- when :module, :sclass then 1
49
- when :def, :class, :block then 2
50
- when :defs then 3
48
+ when :module, :sclass then 1
49
+ when :def, :class, :block, :numblock then 2
50
+ when :defs then 3
51
51
  end
52
52
 
53
53
  node.children[child_index]
@@ -97,8 +97,10 @@ module RuboCop
97
97
  scope_stack.reverse_each do |scope|
98
98
  variable = scope.variables[name]
99
99
  return variable if variable
100
+
100
101
  # Only block scope allows referencing outer scope variables.
101
- return nil unless scope.node.block_type?
102
+ node = scope.node
103
+ return nil unless node.block_type? || node.numblock_type?
102
104
  end
103
105
 
104
106
  nil
@@ -50,7 +50,7 @@ module RuboCop
50
50
 
51
51
  ZERO_ARITY_SUPER_TYPE = :zsuper
52
52
 
53
- TWISTED_SCOPE_TYPES = %i[block class sclass defs module].freeze
53
+ TWISTED_SCOPE_TYPES = %i[block numblock class sclass defs module].freeze
54
54
  SCOPE_TYPES = (TWISTED_SCOPE_TYPES + [:def]).freeze
55
55
 
56
56
  SEND_TYPE = :send
@@ -2,7 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  # The bootstrap module for formatter.
5
- # @api private
6
5
  module Formatter
7
6
  require_relative 'formatter/text_util'
8
7
 
@@ -61,6 +61,9 @@ module RuboCop
61
61
  add_config_generation_options(opts)
62
62
  add_additional_modes(opts)
63
63
  add_general_options(opts)
64
+
65
+ # `stackprof` is not supported on JRuby and Windows.
66
+ add_profile_options(opts) if RUBY_ENGINE == 'ruby' && !Platform.windows?
64
67
  end
65
68
  end
66
69
 
@@ -206,6 +209,7 @@ module RuboCop
206
209
  option(opts, '--start-server')
207
210
  option(opts, '--stop-server')
208
211
  option(opts, '--server-status')
212
+ option(opts, '--no-detach')
209
213
  end
210
214
  end
211
215
 
@@ -233,6 +237,16 @@ module RuboCop
233
237
  end
234
238
  end
235
239
 
240
+ def add_profile_options(opts)
241
+ section(opts, 'Profiling Options') do
242
+ option(opts, '--profile') do
243
+ @options[:profile] = true
244
+ @options[:cache] = 'false' unless @options.key?(:cache)
245
+ end
246
+ option(opts, '--memory')
247
+ end
248
+ end
249
+
236
250
  def handle_deprecated_option(old_option, new_option)
237
251
  warn rainbow.wrap("#{old_option} is deprecated; use #{new_option} instead.").yellow
238
252
  @options[long_opt_symbol([new_option])] = @options.delete(long_opt_symbol([old_option]))
@@ -424,6 +438,8 @@ module RuboCop
424
438
  def invalid_arguments_for_parallel
425
439
  [('--auto-gen-config' if @options.key?(:auto_gen_config)),
426
440
  ('-F/--fail-fast' if @options.key?(:fail_fast)),
441
+ ('--profile' if @options[:profile]),
442
+ ('--memory' if @options[:memory]),
427
443
  ('--cache false' if @options > { cache: 'false' })].compact
428
444
  end
429
445
 
@@ -465,6 +481,7 @@ module RuboCop
465
481
 
466
482
  # This module contains help texts for command line options.
467
483
  # @api private
484
+ # rubocop:disable Metrics/ModuleLength
468
485
  module OptionsHelp
469
486
  MAX_EXCL = RuboCop::Options::DEFAULT_MAXIMUM_EXCLUSION_ITEMS.to_s
470
487
  FORMATTER_OPTION_LIST = RuboCop::Formatter::FormatterSet::BUILTIN_FORMATTERS_FOR_KEYS.keys
@@ -599,9 +616,13 @@ module RuboCop
599
616
  start_server: 'Start server process.',
600
617
  stop_server: 'Stop server process.',
601
618
  server_status: 'Show server status.',
619
+ no_detach: 'Run the server process in the foreground.',
602
620
  raise_cop_error: ['Raise cop-related errors with cause and location.',
603
621
  'This is used to prevent cops from failing silently.',
604
- 'Default is false.']
622
+ 'Default is false.'],
623
+ profile: 'Profile rubocop',
624
+ memory: 'Profile rubocop memory usage'
605
625
  }.freeze
606
626
  end
627
+ # rubocop:enable Metrics/ModuleLength
607
628
  end
@@ -28,14 +28,19 @@ module RuboCop
28
28
  end
29
29
  end
30
30
 
31
+ SMART_PATH_CACHE = {} # rubocop:disable Style/MutableConstant
32
+ private_constant :SMART_PATH_CACHE
33
+
31
34
  def smart_path(path)
32
- # Ideally, we calculate this relative to the project root.
33
- base_dir = Dir.pwd
35
+ SMART_PATH_CACHE[path] ||= begin
36
+ # Ideally, we calculate this relative to the project root.
37
+ base_dir = Dir.pwd
34
38
 
35
- if path.start_with? base_dir
36
- relative_path(path, base_dir)
37
- else
38
- path
39
+ if path.start_with? base_dir
40
+ relative_path(path, base_dir)
41
+ else
42
+ path
43
+ end
39
44
  end
40
45
  end
41
46
 
@@ -6,7 +6,7 @@ module RuboCop
6
6
  #
7
7
  # This mixin makes it easier to specify strict offense expectations
8
8
  # in a declarative and visual fashion. Just type out the code that
9
- # should generate a offense, annotate code by writing '^'s
9
+ # should generate an offense, annotate code by writing '^'s
10
10
  # underneath each character that should be highlighted, and follow
11
11
  # the carets with a string (separated by a space) that is the
12
12
  # message of the offense. You can include multiple offenses in
@@ -126,7 +126,7 @@ module RuboCop
126
126
  @offenses
127
127
  end
128
128
 
129
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
129
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
130
130
  def expect_correction(correction, loop: true, source: nil)
131
131
  if source
132
132
  expected_annotations = parse_annotations(source, raise_error: false)
@@ -138,6 +138,8 @@ module RuboCop
138
138
 
139
139
  source = @processed_source.raw_source
140
140
 
141
+ raise 'Use `expect_no_corrections` if the code will not change' if correction == source
142
+
141
143
  iteration = 0
142
144
  new_source = loop do
143
145
  iteration += 1
@@ -157,11 +159,11 @@ module RuboCop
157
159
  _investigate(cop, @processed_source)
158
160
  end
159
161
 
160
- raise 'Use `expect_no_corrections` if the code will not change' if new_source == source
162
+ raise 'Expected correction but no corrections were made' if new_source == source
161
163
 
162
164
  expect(new_source).to eq(correction)
163
165
  end
164
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
166
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
165
167
 
166
168
  def expect_no_corrections
167
169
  raise '`expect_no_corrections` must follow `expect_offense`' unless @processed_source
@@ -24,6 +24,27 @@ module RuboCop
24
24
  end
25
25
  end
26
26
 
27
+ class << self
28
+ # @return [Array<#call>]
29
+ def ruby_extractors
30
+ @ruby_extractors ||= [default_ruby_extractor]
31
+ end
32
+
33
+ private
34
+
35
+ # @return [#call]
36
+ def default_ruby_extractor
37
+ lambda do |processed_source|
38
+ [
39
+ {
40
+ offset: 0,
41
+ processed_source: processed_source
42
+ }
43
+ ]
44
+ end
45
+ end
46
+ end
47
+
27
48
  # @api private
28
49
  MAX_ITERATIONS = 200
29
50
 
@@ -319,10 +340,25 @@ module RuboCop
319
340
  end
320
341
 
321
342
  def inspect_file(processed_source, team = mobilize_team(processed_source))
322
- report = team.investigate(processed_source)
323
- @errors.concat(team.errors)
324
- @warnings.concat(team.warnings)
325
- [report.offenses, team.updated_source_file?]
343
+ extracted_ruby_sources = extract_ruby_sources(processed_source)
344
+ offenses = extracted_ruby_sources.flat_map do |extracted_ruby_source|
345
+ report = team.investigate(
346
+ extracted_ruby_source[:processed_source],
347
+ offset: extracted_ruby_source[:offset],
348
+ original: processed_source
349
+ )
350
+ @errors.concat(team.errors)
351
+ @warnings.concat(team.warnings)
352
+ report.offenses
353
+ end
354
+ [offenses, team.updated_source_file?]
355
+ end
356
+
357
+ def extract_ruby_sources(processed_source)
358
+ self.class.ruby_extractors.find do |ruby_extractor|
359
+ result = ruby_extractor.call(processed_source)
360
+ break result if result
361
+ end
326
362
  end
327
363
 
328
364
  def mobilize_team(processed_source)