rubocop 1.31.2 → 1.34.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +74 -16
  4. data/config/obsoletion.yml +23 -1
  5. data/lib/rubocop/cache_config.rb +29 -0
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +2 -2
  7. data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
  8. data/lib/rubocop/cli/command/suggest_extensions.rb +53 -15
  9. data/lib/rubocop/cli.rb +1 -0
  10. data/lib/rubocop/config.rb +1 -1
  11. data/lib/rubocop/config_finder.rb +68 -0
  12. data/lib/rubocop/config_loader.rb +12 -40
  13. data/lib/rubocop/config_loader_resolver.rb +1 -5
  14. data/lib/rubocop/config_obsoletion/changed_parameter.rb +5 -0
  15. data/lib/rubocop/config_obsoletion/parameter_rule.rb +4 -0
  16. data/lib/rubocop/config_obsoletion.rb +7 -2
  17. data/lib/rubocop/cop/cop.rb +1 -1
  18. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +28 -0
  19. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +61 -0
  20. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +7 -1
  21. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  22. data/lib/rubocop/cop/layout/block_end_newline.rb +33 -5
  23. data/lib/rubocop/cop/layout/first_argument_indentation.rb +6 -1
  24. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +57 -13
  25. data/lib/rubocop/cop/layout/line_length.rb +2 -0
  26. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  27. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +4 -1
  28. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +45 -0
  29. data/lib/rubocop/cop/legacy/corrections_proxy.rb +1 -1
  30. data/lib/rubocop/cop/legacy/corrector.rb +1 -1
  31. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +26 -6
  32. data/lib/rubocop/cop/lint/debugger.rb +26 -16
  33. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +10 -4
  34. data/lib/rubocop/cop/lint/empty_conditional_body.rb +65 -1
  35. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +55 -24
  36. data/lib/rubocop/cop/lint/number_conversion.rb +28 -6
  37. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +7 -0
  38. data/lib/rubocop/cop/lint/require_range_parentheses.rb +57 -0
  39. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +35 -1
  40. data/lib/rubocop/cop/lint/shadowed_exception.rb +15 -0
  41. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +9 -1
  42. data/lib/rubocop/cop/metrics/abc_size.rb +3 -1
  43. data/lib/rubocop/cop/metrics/block_length.rb +6 -6
  44. data/lib/rubocop/cop/metrics/method_length.rb +8 -7
  45. data/lib/rubocop/cop/mixin/allowed_methods.rb +15 -1
  46. data/lib/rubocop/cop/mixin/allowed_pattern.rb +9 -1
  47. data/lib/rubocop/cop/mixin/check_line_breakable.rb +4 -0
  48. data/lib/rubocop/cop/mixin/comments_help.rb +5 -1
  49. data/lib/rubocop/cop/mixin/enforce_superclass.rb +2 -1
  50. data/lib/rubocop/cop/mixin/method_complexity.rb +4 -9
  51. data/lib/rubocop/cop/mixin/percent_array.rb +60 -1
  52. data/lib/rubocop/cop/mixin/range_help.rb +2 -2
  53. data/lib/rubocop/cop/naming/predicate_name.rb +30 -1
  54. data/lib/rubocop/cop/style/block_delimiters.rb +26 -7
  55. data/lib/rubocop/cop/style/class_and_module_children.rb +4 -4
  56. data/lib/rubocop/cop/style/class_equality_comparison.rb +50 -3
  57. data/lib/rubocop/cop/style/double_negation.rb +2 -0
  58. data/lib/rubocop/cop/style/empty_else.rb +37 -0
  59. data/lib/rubocop/cop/style/empty_heredoc.rb +73 -0
  60. data/lib/rubocop/cop/style/fetch_env_var.rb +10 -177
  61. data/lib/rubocop/cop/style/format_string_token.rb +25 -6
  62. data/lib/rubocop/cop/style/hash_except.rb +0 -4
  63. data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -1
  64. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +2 -0
  65. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -1
  66. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -7
  67. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +19 -2
  68. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +9 -0
  69. data/lib/rubocop/cop/style/numeric_literals.rb +16 -1
  70. data/lib/rubocop/cop/style/numeric_predicate.rb +43 -9
  71. data/lib/rubocop/cop/style/redundant_condition.rb +19 -4
  72. data/lib/rubocop/cop/style/redundant_parentheses.rb +15 -22
  73. data/lib/rubocop/cop/style/redundant_sort.rb +21 -6
  74. data/lib/rubocop/cop/style/semicolon.rb +27 -3
  75. data/lib/rubocop/cop/style/sole_nested_conditional.rb +14 -3
  76. data/lib/rubocop/cop/style/symbol_array.rb +2 -3
  77. data/lib/rubocop/cop/style/symbol_proc.rb +40 -7
  78. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -13
  79. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -0
  80. data/lib/rubocop/cop/style/trivial_accessors.rb +3 -0
  81. data/lib/rubocop/cop/style/word_array.rb +2 -3
  82. data/lib/rubocop/ext/range.rb +15 -0
  83. data/lib/rubocop/feature_loader.rb +88 -0
  84. data/lib/rubocop/formatter/clang_style_formatter.rb +1 -1
  85. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  86. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  87. data/lib/rubocop/formatter/markdown_formatter.rb +1 -1
  88. data/lib/rubocop/formatter/tap_formatter.rb +1 -1
  89. data/lib/rubocop/options.rb +3 -6
  90. data/lib/rubocop/result_cache.rb +22 -20
  91. data/lib/rubocop/rspec/shared_contexts.rb +14 -14
  92. data/lib/rubocop/rspec/support.rb +14 -0
  93. data/lib/rubocop/runner.rb +4 -0
  94. data/lib/rubocop/server/cache.rb +33 -1
  95. data/lib/rubocop/server/cli.rb +19 -2
  96. data/lib/rubocop/server/client_command/base.rb +1 -1
  97. data/lib/rubocop/version.rb +1 -1
  98. data/lib/rubocop.rb +5 -1
  99. metadata +16 -9
  100. data/lib/rubocop/cop/mixin/ignored_methods.rb +0 -52
@@ -57,8 +57,8 @@ module RuboCop
57
57
  def allowed_expression?(node)
58
58
  allowed_ancestor?(node) ||
59
59
  allowed_method_call?(node) ||
60
- allowed_array_or_hash_element?(node) ||
61
- allowed_multiple_expression?(node)
60
+ allowed_multiple_expression?(node) ||
61
+ allowed_ternary?(node)
62
62
  end
63
63
 
64
64
  def allowed_ancestor?(node)
@@ -80,6 +80,19 @@ module RuboCop
80
80
  !ancestor.begin_type? && !ancestor.def_type? && !ancestor.block_type?
81
81
  end
82
82
 
83
+ def allowed_ternary?(node)
84
+ return unless node&.parent&.if_type?
85
+
86
+ node.parent.ternary? && ternary_parentheses_required?
87
+ end
88
+
89
+ def ternary_parentheses_required?
90
+ config = @config.for_cop('Style/TernaryParentheses')
91
+ allowed_styles = %w[require_parentheses require_parentheses_when_complex]
92
+
93
+ config.fetch('Enabled') && allowed_styles.include?(config['EnforcedStyle'])
94
+ end
95
+
83
96
  def like_method_argument_parentheses?(node)
84
97
  node.send_type? && node.arguments.one? &&
85
98
  !node.arithmetic_operation? && node.first_argument.begin_type?
@@ -153,26 +166,6 @@ module RuboCop
153
166
  node.parent&.keyword?
154
167
  end
155
168
 
156
- def allowed_array_or_hash_element?(node)
157
- # Don't flag
158
- # ```
159
- # { a: (1
160
- # ), }
161
- # ```
162
- hash_or_array_element?(node) && only_closing_paren_before_comma?(node)
163
- end
164
-
165
- def hash_or_array_element?(node)
166
- node.each_ancestor(:array, :hash).any?
167
- end
168
-
169
- def only_closing_paren_before_comma?(node)
170
- source_buffer = node.source_range.source_buffer
171
- line_range = source_buffer.line_range(node.loc.end.line)
172
-
173
- /^\s*\)\s*,/.match?(line_range.source)
174
- end
175
-
176
169
  def disallowed_literal?(begin_node, node)
177
170
  node.literal? && !node.range_type? && !raised_to_power_negative_numeric?(begin_node, node)
178
171
  end
@@ -123,13 +123,8 @@ module RuboCop
123
123
 
124
124
  def register_offense(node, sort_node, sorter, accessor)
125
125
  message = message(node, sorter, accessor)
126
-
127
126
  add_offense(offense_range(sort_node, node), message: message) do |corrector|
128
- # Remove accessor, e.g. `first` or `[-1]`.
129
- corrector.remove(range_between(accessor_start(node), node.loc.expression.end_pos))
130
-
131
- # Replace "sort" or "sort_by" with the appropriate min/max method.
132
- corrector.replace(sort_node.loc.selector, suggestion(sorter, accessor, arg_value(node)))
127
+ autocorrect(corrector, node, sort_node, sorter, accessor)
133
128
  end
134
129
  end
135
130
 
@@ -149,6 +144,20 @@ module RuboCop
149
144
  accessor_source: accessor_source)
150
145
  end
151
146
 
147
+ def autocorrect(corrector, node, sort_node, sorter, accessor)
148
+ # Remove accessor, e.g. `first` or `[-1]`.
149
+ corrector.remove(range_between(accessor_start(node), node.loc.expression.end_pos))
150
+ # Replace "sort" or "sort_by" with the appropriate min/max method.
151
+ corrector.replace(sort_node.loc.selector, suggestion(sorter, accessor, arg_value(node)))
152
+ # Replace to avoid syntax errors when followed by a logical operator.
153
+ replace_with_logical_operator(corrector, node) if with_logical_operator?(node)
154
+ end
155
+
156
+ def replace_with_logical_operator(corrector, node)
157
+ corrector.insert_after(node.child_nodes.first, " #{node.parent.loc.operator.source}")
158
+ corrector.remove(node.parent.loc.operator)
159
+ end
160
+
152
161
  def suggestion(sorter, accessor, arg)
153
162
  base(accessor, arg) + suffix(sorter)
154
163
  end
@@ -187,6 +196,12 @@ module RuboCop
187
196
  node.loc.selector.begin_pos
188
197
  end
189
198
  end
199
+
200
+ def with_logical_operator?(node)
201
+ return unless (parent = node.parent)
202
+
203
+ parent.or_type? || parent.and_type?
204
+ end
190
205
  end
191
206
  end
192
207
  end
@@ -64,12 +64,14 @@ module RuboCop
64
64
  # Make the obvious check first
65
65
  return unless processed_source.raw_source.include?(';')
66
66
 
67
- each_semicolon { |line, column| register_semicolon(line, column, false) }
67
+ each_semicolon do |line, column, token_before_semicolon|
68
+ register_semicolon(line, column, false, token_before_semicolon)
69
+ end
68
70
  end
69
71
 
70
72
  def each_semicolon
71
73
  tokens_for_lines.each do |line, tokens|
72
- yield line, tokens.last.column if tokens.last.semicolon?
74
+ yield line, tokens.last.column, tokens[-2] if tokens.last.semicolon?
73
75
  yield line, tokens.first.column if tokens.first.semicolon?
74
76
  end
75
77
  end
@@ -78,13 +80,21 @@ module RuboCop
78
80
  processed_source.tokens.group_by(&:line)
79
81
  end
80
82
 
81
- def register_semicolon(line, column, after_expression)
83
+ def register_semicolon(line, column, after_expression, token_before_semicolon = nil)
82
84
  range = source_range(processed_source.buffer, line, column)
83
85
 
84
86
  add_offense(range) do |corrector|
85
87
  if after_expression
86
88
  corrector.replace(range, "\n")
87
89
  else
90
+ # Prevents becoming one range instance with subsequent line when endless range
91
+ # without parentheses.
92
+ # See: https://github.com/rubocop/rubocop/issues/10791
93
+ if token_before_semicolon&.regexp_dots?
94
+ range_node = find_range_node(token_before_semicolon)
95
+ corrector.wrap(range_node, '(', ')') if range_node
96
+ end
97
+
88
98
  corrector.remove(range)
89
99
  end
90
100
  end
@@ -103,6 +113,20 @@ module RuboCop
103
113
  yield Regexp.last_match.begin(0)
104
114
  end
105
115
  end
116
+
117
+ def find_range_node(token_before_semicolon)
118
+ range_nodes.detect do |range_node|
119
+ range_node.source_range.contains?(token_before_semicolon.pos)
120
+ end
121
+ end
122
+
123
+ def range_nodes
124
+ return @range_nodes if instance_variable_defined?(:@range_nodes)
125
+
126
+ ast = processed_source.ast
127
+ @range_nodes = ast.range_type? ? [ast] : []
128
+ @range_nodes.concat(ast.each_descendant(:irange, :erange).to_a)
129
+ end
106
130
  end
107
131
  end
108
132
  end
@@ -148,7 +148,18 @@ module RuboCop
148
148
  )
149
149
  corrector.replace(range, and_operator)
150
150
  corrector.remove(range_by_whole_lines(node.loc.end, include_final_newline: true))
151
- corrector.wrap(if_branch.condition, '(', ')') if wrap_condition?(if_branch.condition)
151
+
152
+ wrap_condition(corrector, if_branch.condition)
153
+ end
154
+
155
+ def wrap_condition(corrector, condition)
156
+ # Handle `send` and `block` nodes that need to be wrapped in parens
157
+ # FIXME: autocorrection prevents syntax errors by wrapping the entire node in parens,
158
+ # but wrapping the argument list would be a more ergonomic correction.
159
+ node_to_check = condition&.block_type? ? condition.send_node : condition
160
+ return unless wrap_condition?(node_to_check)
161
+
162
+ corrector.wrap(condition, '(', ')')
152
163
  end
153
164
 
154
165
  def correct_for_outer_condition_modify_form_style(corrector, node, if_branch)
@@ -207,7 +218,7 @@ module RuboCop
207
218
  end
208
219
 
209
220
  def require_parentheses?(condition)
210
- condition.send_type? && !condition.arguments.empty? && !condition.parenthesized? &&
221
+ condition.call_type? && !condition.arguments.empty? && !condition.parenthesized? &&
211
222
  !condition.comparison_method?
212
223
  end
213
224
 
@@ -219,7 +230,7 @@ module RuboCop
219
230
 
220
231
  def wrap_condition?(node)
221
232
  node.and_type? || node.or_type? ||
222
- (node.send_type? && node.arguments.any? && !node.parenthesized?)
233
+ (node.call_type? && node.arguments.any? && !node.parenthesized?)
223
234
  end
224
235
 
225
236
  def replace_condition(condition)
@@ -39,7 +39,7 @@ module RuboCop
39
39
  minimum_target_ruby_version 2.0
40
40
 
41
41
  PERCENT_MSG = 'Use `%i` or `%I` for an array of symbols.'
42
- ARRAY_MSG = 'Use `%<prefer>s` for an array of symbols.'
42
+ ARRAY_MSG = 'Use %<prefer>s for an array of symbols.'
43
43
 
44
44
  class << self
45
45
  attr_accessor :largest_brackets
@@ -74,8 +74,7 @@ module RuboCop
74
74
  to_symbol_literal(c.value.to_s)
75
75
  end
76
76
  end
77
-
78
- "[#{syms.join(', ')}]"
77
+ build_bracketed_array_with_appropriate_whitespace(elements: syms, node: node)
79
78
  end
80
79
 
81
80
  def to_symbol_literal(string)
@@ -7,11 +7,13 @@ module RuboCop
7
7
  #
8
8
  # If you prefer a style that allows block for method with arguments,
9
9
  # please set `true` to `AllowMethodsWithArguments`.
10
+ # respond_to , and `define_method?` methods are allowed by default.
11
+ # These are customizable with `AllowedMethods` option.
10
12
  #
11
13
  # @safety
12
14
  # This cop is unsafe because `proc`s and blocks work differently
13
15
  # when additional arguments are passed in. A block will silently
14
- # ignore additional arguments, but a `proc` will raise
16
+ # allow additional arguments, but a `proc` will raise
15
17
  # an `ArgumentError`.
16
18
  #
17
19
  # For example:
@@ -68,10 +70,26 @@ module RuboCop
68
70
  # s.upcase # some comment
69
71
  # # some comment
70
72
  # end
73
+ #
74
+ # @example AllowedMethods: [respond_to, define_method] (default)
75
+ # # good
76
+ # respond_to { |foo| foo.bar }
77
+ # define_method(:foo) { |foo| foo.bar }
78
+ #
79
+ #
80
+ # @example AllowedPatterns: [] (default)
81
+ # # bad
82
+ # something.map { |s| s.upcase }
83
+ #
84
+ # @example AllowedPatterns: [/map/] (default)
85
+ # # good
86
+ # something.map { |s| s.upcase }
87
+ #
71
88
  class SymbolProc < Base
72
89
  include CommentsHelp
73
90
  include RangeHelp
74
- include IgnoredMethods
91
+ include AllowedMethods
92
+ include AllowedPattern
75
93
  extend AutoCorrector
76
94
 
77
95
  MSG = 'Pass `&:%<method>s` as an argument to `%<block_method>s` instead of a block.'
@@ -81,7 +99,7 @@ module RuboCop
81
99
  def_node_matcher :proc_node?, '(send (const {nil? cbase} :Proc) :new)'
82
100
 
83
101
  # @!method symbol_proc_receiver?(node)
84
- def_node_matcher :symbol_proc_receiver?, '{(send ...) (super ...) zsuper}'
102
+ def_node_matcher :symbol_proc_receiver?, '{(call ...) (super ...) zsuper}'
85
103
 
86
104
  # @!method symbol_proc?(node)
87
105
  def_node_matcher :symbol_proc?, <<~PATTERN
@@ -95,15 +113,17 @@ module RuboCop
95
113
  [Layout::SpaceBeforeBlockBraces]
96
114
  end
97
115
 
98
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
116
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
99
117
  def on_block(node)
100
118
  symbol_proc?(node) do |dispatch_node, arguments_node, method_name|
101
119
  # TODO: Rails-specific handling that we should probably make
102
120
  # configurable - https://github.com/rubocop/rubocop/issues/1485
103
- # we should ignore lambdas & procs
121
+ # we should allow lambdas & procs
104
122
  return if proc_node?(dispatch_node)
123
+ return if unsafe_hash_usage?(dispatch_node)
124
+ return if unsafe_array_usage?(dispatch_node)
105
125
  return if %i[lambda proc].include?(dispatch_node.method_name)
106
- return if ignored_method?(dispatch_node.method_name)
126
+ return if allowed_method_name?(dispatch_node.method_name)
107
127
  return if allow_if_method_has_argument?(node.send_node)
108
128
  return if node.block_type? && destructuring_block_argument?(arguments_node)
109
129
  return if allow_comments? && contains_comments?(node)
@@ -111,7 +131,7 @@ module RuboCop
111
131
  register_offense(node, method_name, dispatch_node.method_name)
112
132
  end
113
133
  end
114
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
134
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
115
135
  alias on_numblock on_block
116
136
 
117
137
  def destructuring_block_argument?(argument_node)
@@ -120,6 +140,19 @@ module RuboCop
120
140
 
121
141
  private
122
142
 
143
+ # See: https://github.com/rubocop/rubocop/issues/10864
144
+ def unsafe_hash_usage?(node)
145
+ node.receiver&.hash_type? && %i[reject select].include?(node.method_name)
146
+ end
147
+
148
+ def unsafe_array_usage?(node)
149
+ node.receiver&.array_type? && %i[min max].include?(node.method_name)
150
+ end
151
+
152
+ def allowed_method_name?(name)
153
+ allowed_method?(name) || matches_allowed_pattern?(name)
154
+ end
155
+
123
156
  def register_offense(node, method_name, block_method_name)
124
157
  block_start = node.loc.begin.begin_pos
125
158
  block_end = node.loc.end.end_pos
@@ -71,7 +71,7 @@ module RuboCop
71
71
 
72
72
  return if only_closing_parenthesis_is_last_line?(condition)
73
73
  return if condition_as_parenthesized_one_line_pattern_matching?(condition)
74
- return unless node.ternary? && !infinite_loop? && offense?(node)
74
+ return unless node.ternary? && offense?(node)
75
75
 
76
76
  message = message(node)
77
77
 
@@ -166,22 +166,10 @@ module RuboCop
166
166
  style == :require_parentheses_when_complex
167
167
  end
168
168
 
169
- def redundant_parentheses_enabled?
170
- @config.for_cop('Style/RedundantParentheses').fetch('Enabled')
171
- end
172
-
173
169
  def parenthesized?(node)
174
170
  node.begin_type?
175
171
  end
176
172
 
177
- # When this cop is configured to enforce parentheses and the
178
- # `RedundantParentheses` cop is enabled, it will cause an infinite loop
179
- # as they compete to add and remove the parentheses respectively.
180
- def infinite_loop?
181
- (require_parentheses? || require_parentheses_when_complex?) &&
182
- redundant_parentheses_enabled?
183
- end
184
-
185
173
  def unsafe_autocorrect?(condition)
186
174
  condition.children.any? do |child|
187
175
  unparenthesized_method_call?(child) || below_ternary_precedence?(child)
@@ -47,6 +47,8 @@ module RuboCop
47
47
  class TopLevelMethodDefinition < Base
48
48
  MSG = 'Do not define methods at the top-level.'
49
49
 
50
+ RESTRICT_ON_SEND = %i[define_method].freeze
51
+
50
52
  def on_def(node)
51
53
  return unless top_level_method_definition?(node)
52
54
 
@@ -5,6 +5,9 @@ module RuboCop
5
5
  module Style
6
6
  # Looks for trivial reader/writer methods, that could
7
7
  # have been created with the attr_* family of functions automatically.
8
+ # `to_ary`, `to_a`, `to_c`, `to_enum`, `to_h`, `to_hash`, `to_i`, `to_int`, `to_io`,
9
+ # `to_open`, `to_path`, `to_proc`, `to_r`, `to_regexp`, `to_str`, `to_s`, and `to_sym` methods
10
+ # are allowed by default. These are customizable with `AllowedMethods` option.
8
11
  #
9
12
  # @example
10
13
  # # bad
@@ -44,7 +44,7 @@ module RuboCop
44
44
  extend AutoCorrector
45
45
 
46
46
  PERCENT_MSG = 'Use `%w` or `%W` for an array of words.'
47
- ARRAY_MSG = 'Use `%<prefer>s` for an array of words.'
47
+ ARRAY_MSG = 'Use %<prefer>s for an array of words.'
48
48
 
49
49
  class << self
50
50
  attr_accessor :largest_brackets
@@ -92,8 +92,7 @@ module RuboCop
92
92
  to_string_literal(word.children[0])
93
93
  end
94
94
  end
95
-
96
- "[#{words.join(', ')}]"
95
+ build_bracketed_array_with_appropriate_whitespace(elements: words, node: node)
97
96
  end
98
97
  end
99
98
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Ext
5
+ # Extensions to Parser::Source::Range
6
+ module Range
7
+ # Adds `Range#single_line?` to parallel `Node#single_line?`
8
+ def single_line?
9
+ first_line == last_line
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ Parser::Source::Range.include RuboCop::Ext::Range
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This class handles loading files (a.k.a. features in Ruby) specified
5
+ # by `--require` command line option and `require` directive in the config.
6
+ #
7
+ # Normally, the given string is directly passed to `require`. If a string
8
+ # beginning with `.` is given, it is assumed to be relative to the given
9
+ # directory.
10
+ #
11
+ # If a string containing `-` is given, it will be used as is, but if we
12
+ # cannot find the file to load, we will replace `-` with `/` and try it
13
+ # again as when Bundler loads gems.
14
+ #
15
+ # @api private
16
+ class FeatureLoader
17
+ class << self
18
+ # @param [String] config_directory_path
19
+ # @param [String] feature
20
+ def load(config_directory_path:, feature:)
21
+ new(config_directory_path: config_directory_path, feature: feature).load
22
+ end
23
+ end
24
+
25
+ # @param [String] config_directory_path
26
+ # @param [String] feature
27
+ def initialize(config_directory_path:, feature:)
28
+ @config_directory_path = config_directory_path
29
+ @feature = feature
30
+ end
31
+
32
+ def load
33
+ ::Kernel.require(target)
34
+ rescue ::LoadError => e
35
+ raise if e.path != target
36
+
37
+ begin
38
+ ::Kernel.require(namespaced_target)
39
+ rescue ::LoadError => error_for_namespaced_target
40
+ raise e if error_for_namespaced_target.path == namespaced_target
41
+
42
+ raise error_for_namespaced_target
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ # @return [String]
49
+ def namespaced_feature
50
+ @feature.tr('-', '/')
51
+ end
52
+
53
+ # @return [String]
54
+ def namespaced_target
55
+ if relative?
56
+ relative(namespaced_feature)
57
+ else
58
+ namespaced_feature
59
+ end
60
+ end
61
+
62
+ # @param [String]
63
+ # @return [String]
64
+ def relative(feature)
65
+ ::File.join(@config_directory_path, feature)
66
+ end
67
+
68
+ # @return [Boolean]
69
+ def relative?
70
+ @feature.start_with?('.')
71
+ end
72
+
73
+ # @param [LoadError] error
74
+ # @return [Boolean]
75
+ def seems_cannot_load_such_file_error?(error)
76
+ error.path == target
77
+ end
78
+
79
+ # @return [String]
80
+ def target
81
+ if relative?
82
+ relative(@feature)
83
+ else
84
+ @feature
85
+ end
86
+ end
87
+ end
88
+ end
@@ -41,7 +41,7 @@ module RuboCop
41
41
  def report_line(location)
42
42
  source_line = location.source_line
43
43
 
44
- if location.first_line == location.last_line
44
+ if location.single_line?
45
45
  output.puts(source_line)
46
46
  else
47
47
  output.puts("#{source_line} #{yellow(ELLIPSES)}")
@@ -161,7 +161,7 @@ module RuboCop
161
161
  next unless value.is_a?(Array)
162
162
  next if value.empty?
163
163
 
164
- output_buffer.puts "# #{param}: #{value.join(', ')}"
164
+ output_buffer.puts "# #{param}: #{value.uniq.join(', ')}"
165
165
  end
166
166
  end
167
167
 
@@ -115,7 +115,7 @@ module RuboCop
115
115
  end
116
116
 
117
117
  def possible_ellipses(location)
118
- location.first_line == location.last_line ? '' : " #{ELLIPSES}"
118
+ location.single_line? ? '' : " #{ELLIPSES}"
119
119
  end
120
120
 
121
121
  def escape(string)
@@ -71,7 +71,7 @@ module RuboCop
71
71
  end
72
72
 
73
73
  def possible_ellipses(location)
74
- location.first_line == location.last_line ? '' : ' ...'
74
+ location.single_line? ? '' : ' ...'
75
75
  end
76
76
  end
77
77
  end
@@ -29,7 +29,7 @@ module RuboCop
29
29
  def report_line(location)
30
30
  source_line = location.source_line
31
31
 
32
- if location.first_line == location.last_line
32
+ if location.single_line?
33
33
  output.puts("# #{source_line}")
34
34
  else
35
35
  output.puts("# #{source_line} #{yellow(ELLIPSES)}")
@@ -421,12 +421,9 @@ module RuboCop
421
421
  end
422
422
 
423
423
  def invalid_arguments_for_parallel
424
- [('--auto-gen-config' if @options.key?(:auto_gen_config)),
425
- ('-F/--fail-fast' if @options.key?(:fail_fast)),
426
- ('-x/--fix-layout' if @options.key?(:fix_layout)),
427
- ('-a/--autocorrect' if @options.key?(:safe_autocorrect)),
428
- ('-A/--autocorrect-all' if @options.key?(:autocorrect_all)),
429
- ('--cache false' if @options > { cache: 'false' })].compact
424
+ [('--auto-gen-config' if @options.key?(:auto_gen_config)),
425
+ ('-F/--fail-fast' if @options.key?(:fail_fast)),
426
+ ('--cache false' if @options > { cache: 'false' })].compact
430
427
  end
431
428
 
432
429
  def only_includes_redundant_disable?
@@ -4,6 +4,7 @@ require 'digest/sha1'
4
4
  require 'find'
5
5
  require 'etc'
6
6
  require 'zlib'
7
+ require_relative 'cache_config'
7
8
 
8
9
  module RuboCop
9
10
  # Provides functionality for caching RuboCop runs.
@@ -13,6 +14,12 @@ module RuboCop
13
14
  fix_layout autocorrect safe_autocorrect autocorrect_all
14
15
  cache fail_fast stdin parallel].freeze
15
16
 
17
+ DL_EXTENSIONS = ::RbConfig::CONFIG
18
+ .values_at('DLEXT', 'DLEXT2')
19
+ .reject { |ext| !ext || ext.empty? }
20
+ .map { |ext| ".#{ext}" }
21
+ .freeze
22
+
16
23
  # Remove old files so that the cache doesn't grow too big. When the
17
24
  # threshold MaxFilesInCache has been exceeded, the oldest 50% of all the
18
25
  # files in the cache are removed. The reason for removing so much is that
@@ -67,24 +74,9 @@ module RuboCop
67
74
  end
68
75
 
69
76
  def self.cache_root(config_store)
70
- root = ENV.fetch('RUBOCOP_CACHE_ROOT', nil)
71
- root ||= config_store.for_pwd.for_all_cops['CacheRootDirectory']
72
- root ||= if ENV.key?('XDG_CACHE_HOME')
73
- # Include user ID in the path to make sure the user has write
74
- # access.
75
- File.join(ENV.fetch('XDG_CACHE_HOME'), Process.uid.to_s)
76
- else
77
- # On FreeBSD, the /home path is a symbolic link to /usr/home
78
- # and the $HOME environment variable returns the /home path.
79
- #
80
- # As $HOME is a built-in environment variable, FreeBSD users
81
- # always get a warning message.
82
- #
83
- # To avoid raising warn log messages on FreeBSD, we retrieve
84
- # the real path of the home folder.
85
- File.join(File.realpath(Dir.home), '.cache')
86
- end
87
- File.join(root, 'rubocop_cache')
77
+ CacheConfig.root_dir do
78
+ config_store.for_pwd.for_all_cops['CacheRootDirectory']
79
+ end
88
80
  end
89
81
 
90
82
  def self.allow_symlinks_in_cache_location?(config_store)
@@ -188,14 +180,24 @@ module RuboCop
188
180
  .select { |path| File.file?(path) }
189
181
  .sort!
190
182
  .each do |path|
191
- content = File.binread(path)
192
- digest << Zlib.crc32(content).to_s # mtime not reliable
183
+ digest << digest(path)
193
184
  end
194
185
  digest << RuboCop::Version::STRING << RuboCop::AST::Version::STRING
195
186
  digest.hexdigest
196
187
  end
197
188
  end
198
189
 
190
+ def digest(path)
191
+ content = if path.end_with?(*DL_EXTENSIONS)
192
+ # Shared libraries often contain timestamps of when
193
+ # they were compiled and other non-stable data.
194
+ File.basename(path)
195
+ else
196
+ File.binread(path) # mtime not reliable
197
+ end
198
+ Zlib.crc32(content).to_s
199
+ end
200
+
199
201
  def rubocop_extra_features
200
202
  lib_root = File.join(File.dirname(__FILE__), '..')
201
203
  exe_root = File.join(lib_root, '..', 'exe')