rubocop 1.36.0 → 1.37.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +21 -1
  4. data/lib/rubocop/arguments_env.rb +17 -0
  5. data/lib/rubocop/arguments_file.rb +17 -0
  6. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  7. data/lib/rubocop/cli/command/suggest_extensions.rb +8 -1
  8. data/lib/rubocop/cop/generator.rb +1 -2
  9. data/lib/rubocop/cop/internal_affairs/create_empty_file.rb +37 -0
  10. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +111 -0
  11. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  12. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  13. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  14. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  15. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +28 -3
  16. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  17. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  18. data/lib/rubocop/cop/lint/duplicate_methods.rb +11 -1
  19. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  20. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  21. data/lib/rubocop/cop/lint/empty_conditional_body.rb +19 -7
  22. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  23. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  24. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  25. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  26. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +12 -1
  27. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  28. data/lib/rubocop/cop/lint/redundant_require_statement.rb +29 -9
  29. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  30. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -2
  31. data/lib/rubocop/cop/lint/shadowed_exception.rb +0 -10
  32. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +3 -0
  33. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  34. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  35. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  36. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  37. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +6 -3
  38. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  39. data/lib/rubocop/cop/mixin/surrounding_space.rb +6 -5
  40. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  41. data/lib/rubocop/cop/style/access_modifier_declarations.rb +24 -2
  42. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  43. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  44. data/lib/rubocop/cop/style/character_literal.rb +1 -1
  45. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  46. data/lib/rubocop/cop/style/collection_compact.rb +8 -1
  47. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  48. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  49. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  50. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  51. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  52. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  53. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  54. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  55. data/lib/rubocop/cop/style/operator_method_call.rb +40 -0
  56. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  57. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  58. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  59. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  60. data/lib/rubocop/cop/style/redundant_string_escape.rb +181 -0
  61. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  62. data/lib/rubocop/cop/style/static_class.rb +32 -1
  63. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  64. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  65. data/lib/rubocop/cop/style/word_array.rb +2 -0
  66. data/lib/rubocop/formatter/disabled_config_formatter.rb +8 -2
  67. data/lib/rubocop/options.rb +13 -13
  68. data/lib/rubocop/rspec/shared_contexts.rb +13 -1
  69. data/lib/rubocop/server/cache.rb +5 -1
  70. data/lib/rubocop/server/cli.rb +9 -2
  71. data/lib/rubocop/server/client_command/exec.rb +5 -0
  72. data/lib/rubocop/server/core.rb +2 -1
  73. data/lib/rubocop/server/socket_reader.rb +5 -1
  74. data/lib/rubocop/server.rb +1 -1
  75. data/lib/rubocop/version.rb +8 -3
  76. data/lib/rubocop.rb +3 -0
  77. metadata +12 -5
@@ -50,6 +50,10 @@ module RuboCop
50
50
  (block $_ (args $...) (yield $...))
51
51
  PATTERN
52
52
 
53
+ def self.autocorrect_incompatible_with
54
+ [Lint::UnusedMethodArgument]
55
+ end
56
+
53
57
  def initialize(config = nil, options = nil)
54
58
  super
55
59
  @def_nodes = Set.new
@@ -77,7 +77,7 @@ module RuboCop
77
77
  # # bad
78
78
  # redirect('foo/%{bar_id}')
79
79
  #
80
- # @example AllowedPatterns: [/redirect/]
80
+ # @example AllowedPatterns: ['redirect']
81
81
  #
82
82
  # # good
83
83
  # redirect('foo/%{bar_id}')
@@ -28,7 +28,7 @@ module RuboCop
28
28
  # * always - forces use of the 3.1 syntax (e.g. {foo:})
29
29
  # * never - forces use of explicit hash literal value
30
30
  # * either - accepts both shorthand and explicit use of hash literal value
31
- # * consistent - like "always", but will avoid mixing styles in a single hash
31
+ # * consistent - like "either", but will avoid mixing styles in a single hash
32
32
  #
33
33
  # @example EnforcedStyle: ruby19 (default)
34
34
  # # bad
@@ -20,8 +20,8 @@ module RuboCop
20
20
  return if require_parentheses_for_hash_value_omission?(node)
21
21
  return if syntax_like_method_call?(node)
22
22
  return if super_call_without_arguments?(node)
23
- return if allowed_camel_case_method_call?(node)
24
23
  return if legitimate_call_with_parentheses?(node)
24
+ return if allowed_camel_case_method_call?(node)
25
25
  return if allowed_string_interpolation_method_call?(node)
26
26
 
27
27
  add_offense(offense_range(node), message: OMIT_MSG) do |corrector|
@@ -97,7 +97,8 @@ module RuboCop
97
97
  call_in_optional_arguments?(node) ||
98
98
  call_in_single_line_inheritance?(node) ||
99
99
  allowed_multiline_call_with_parentheses?(node) ||
100
- allowed_chained_call_with_parentheses?(node)
100
+ allowed_chained_call_with_parentheses?(node) ||
101
+ assignment_in_condition?(node)
101
102
  end
102
103
 
103
104
  def call_in_literals?(node)
@@ -202,6 +203,16 @@ module RuboCop
202
203
  def inside_string_interpolation?(node)
203
204
  node.ancestors.drop_while { |a| !a.begin_type? }.any?(&:dstr_type?)
204
205
  end
206
+
207
+ def assignment_in_condition?(node)
208
+ parent = node.parent
209
+ return false unless parent
210
+
211
+ grandparent = parent.parent
212
+ return false unless grandparent
213
+
214
+ parent.assignment? && (grandparent.conditional? || grandparent.when_type?)
215
+ end
205
216
  end
206
217
  # rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
207
218
  end
@@ -49,7 +49,8 @@ module RuboCop
49
49
  def on_if(node)
50
50
  return unless if_else?(node)
51
51
 
52
- condition = node.condition
52
+ condition = unwrap_begin_nodes(node.condition)
53
+
53
54
  return if double_negation?(condition) || !negated_condition?(condition)
54
55
 
55
56
  type = node.ternary? ? 'ternary' : 'if-else'
@@ -71,6 +72,11 @@ module RuboCop
71
72
  !node.elsif? && else_branch && (!else_branch.if_type? || !else_branch.elsif?)
72
73
  end
73
74
 
75
+ def unwrap_begin_nodes(node)
76
+ node = node.children.first while node.begin_type? || node.kwbegin_type?
77
+ node
78
+ end
79
+
74
80
  def negated_condition?(node)
75
81
  node.send_type? &&
76
82
  (node.negation_method? || NEGATED_EQUALITY_METHODS.include?(node.method_name))
@@ -66,7 +66,7 @@ module RuboCop
66
66
  # foo.negative?
67
67
  # bar.baz.positive?
68
68
  #
69
- # @example AllowedPatterns: [/zero/] with EnforcedStyle: predicate
69
+ # @example AllowedPatterns: ['zero'] with EnforcedStyle: predicate
70
70
  # # good
71
71
  # # bad
72
72
  # foo.zero?
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant dot before operator method call.
7
+ # The target operator methods are `|`, `^`, `&`, `<=>`, `==`, `===`, `=~`, `>`, `>=`, `<`,
8
+ # `<=`, `<<`, `>>`, `+`, `-`, `*`, `/`, `%`, `**`, `~`, `!`, `!=`, and `!~`.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # foo.+ bar
14
+ # foo.& bar
15
+ #
16
+ # # good
17
+ # foo + bar
18
+ # foo & bar
19
+ #
20
+ class OperatorMethodCall < Base
21
+ extend AutoCorrector
22
+
23
+ MSG = 'Redundant dot detected.'
24
+ RESTRICT_ON_SEND = %i[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ ! != !~].freeze
25
+
26
+ def on_send(node)
27
+ return unless (dot = node.loc.dot)
28
+ return if node.receiver.const_type?
29
+
30
+ _lhs, _op, rhs = *node
31
+ return if rhs.nil? || rhs.children.first
32
+
33
+ add_offense(dot) do |corrector|
34
+ corrector.replace(dot, ' ')
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -75,6 +75,7 @@ module RuboCop
75
75
 
76
76
  def on_def(node)
77
77
  return unless node.body&.kwbegin_type?
78
+ return if node.endless? && !node.body.children.one?
78
79
 
79
80
  register_offense(node.body)
80
81
  end
@@ -145,11 +145,14 @@ module RuboCop
145
145
 
146
146
  return false unless if_branch && else_branch
147
147
 
148
- if_branch.send_type? && if_branch.arguments.count == 1 &&
149
- else_branch.send_type? && else_branch.arguments.count == 1 &&
148
+ single_argument_method?(if_branch) && single_argument_method?(else_branch) &&
150
149
  same_method?(if_branch, else_branch)
151
150
  end
152
151
 
152
+ def single_argument_method?(node)
153
+ node.send_type? && !node.method?(:[]) && node.arguments.one?
154
+ end
155
+
153
156
  def same_method?(if_branch, else_branch)
154
157
  if_branch.method?(else_branch.method_name) && if_branch.receiver == else_branch.receiver
155
158
  end
@@ -140,7 +140,9 @@ module RuboCop
140
140
  end
141
141
 
142
142
  def allow_comments?(node)
143
- cop_config['AllowComments'] && contains_comments?(node)
143
+ return false unless cop_config['AllowComments']
144
+
145
+ contains_comments?(node) && !comments_contain_disables?(node, name)
144
146
  end
145
147
 
146
148
  def same_args?(super_node, args)
@@ -74,7 +74,7 @@ module RuboCop
74
74
 
75
75
  non_redundant =
76
76
  whitespace_in_free_space_mode?(node, class_elem) ||
77
- backslash_b?(class_elem) ||
77
+ backslash_b?(class_elem) || backslash_zero?(class_elem) ||
78
78
  requires_escape_outside_char_class?(class_elem)
79
79
 
80
80
  !non_redundant
@@ -104,6 +104,13 @@ module RuboCop
104
104
  elem == '\b'
105
105
  end
106
106
 
107
+ def backslash_zero?(elem)
108
+ # See https://github.com/rubocop/rubocop/issues/11067 for details - in short "\0" != "0" -
109
+ # the former means an Unicode code point `"\u0000"`, the latter a number character `"0"`.
110
+ # Similarly "\032" means "\u001A". Other numbers starting with "\0" can also be mentioned.
111
+ elem == '\0'
112
+ end
113
+
107
114
  def requires_escape_outside_char_class?(elem)
108
115
  REQUIRES_ESCAPE_OUTSIDE_CHAR_CLASS_CHARS.include?(elem)
109
116
  end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant escapes in string literals.
7
+ #
8
+ # @example
9
+ # # bad - no need to escape # without following {/$/@
10
+ # "\#foo"
11
+ #
12
+ # # bad - no need to escape single quotes inside double quoted string
13
+ # "\'foo\'"
14
+ #
15
+ # # bad - heredocs are also checked for unnecessary escapes
16
+ # <<~STR
17
+ # \#foo \"foo\"
18
+ # STR
19
+ #
20
+ # # good
21
+ # "#foo"
22
+ #
23
+ # # good
24
+ # "\#{no_interpolation}"
25
+ #
26
+ # # good
27
+ # "'foo'"
28
+ #
29
+ # # good
30
+ # "foo\
31
+ # bar"
32
+ #
33
+ # # good
34
+ # <<~STR
35
+ # #foo "foo"
36
+ # STR
37
+ class RedundantStringEscape < Base
38
+ include MatchRange
39
+ include RangeHelp
40
+ extend AutoCorrector
41
+
42
+ MSG = 'Redundant escape of %<char>s inside string literal.'
43
+
44
+ def on_str(node)
45
+ return if node.parent&.regexp_type? || node.parent&.xstr_type? || node.character_literal?
46
+
47
+ str_contents_range = str_contents_range(node)
48
+
49
+ each_match_range(str_contents_range, /(\\.)/) do |range|
50
+ next if allowed_escape?(node, range.resize(3))
51
+
52
+ add_offense(range) do |corrector|
53
+ corrector.remove_leading(range, 1)
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def message(range)
61
+ format(MSG, char: range.source.chars.last)
62
+ end
63
+
64
+ def str_contents_range(node)
65
+ if heredoc?(node)
66
+ node.loc.heredoc_body
67
+ elsif begin_loc_present?(node)
68
+ contents_range(node)
69
+ else
70
+ node.loc.expression
71
+ end
72
+ end
73
+
74
+ def begin_loc_present?(node)
75
+ # e.g. a __FILE__ literal has no begin loc so we can't query if it's nil
76
+ node.loc.to_hash.key?(:begin) && !node.loc.begin.nil?
77
+ end
78
+
79
+ def allowed_escape?(node, range)
80
+ escaped = range.source[(1..-1)]
81
+
82
+ # Inside a single-quoted string, escapes (except \\ and \') do not have special meaning,
83
+ # and so are not redundant, as they are a literal backslash.
84
+ return true if interpolation_not_enabled?(node)
85
+
86
+ # Strictly speaking a few single-letter chars are currently unnecessary to "escape", e.g.
87
+ # d, but enumerating them is rather difficult, and their behavior could change over time
88
+ # with different versions of Ruby so that e.g. /\d/ != /d/
89
+ return true if /[\n\\[[:alnum:]]]/.match?(escaped[0])
90
+
91
+ return true if escaped[0] == ' ' && percent_array_literal?(node)
92
+
93
+ return true if disabling_interpolation?(range)
94
+ return true if delimiter?(node, escaped[0])
95
+
96
+ false
97
+ end
98
+
99
+ def interpolation_not_enabled?(node)
100
+ single_quoted?(node) ||
101
+ percent_w_literal?(node) ||
102
+ percent_q_literal?(node) ||
103
+ heredoc_with_disabled_interpolation?(node)
104
+ end
105
+
106
+ def single_quoted?(node)
107
+ delimiter?(node, "'")
108
+ end
109
+
110
+ def percent_q_literal?(node)
111
+ if literal_in_interpolated_or_multiline_string?(node)
112
+ percent_q_literal?(node.parent)
113
+ else
114
+ node.source.start_with?('%q')
115
+ end
116
+ end
117
+
118
+ def array_literal?(node, prefix)
119
+ if literal_in_interpolated_or_multiline_string?(node)
120
+ array_literal?(node.parent, prefix)
121
+ else
122
+ node.parent&.array_type? && node.parent.source.start_with?(prefix)
123
+ end
124
+ end
125
+
126
+ def percent_w_literal?(node)
127
+ array_literal?(node, '%w')
128
+ end
129
+
130
+ def percent_w_upper_literal?(node)
131
+ array_literal?(node, '%W')
132
+ end
133
+
134
+ def percent_array_literal?(node)
135
+ (percent_w_literal?(node) || percent_w_upper_literal?(node))
136
+ end
137
+
138
+ def heredoc_with_disabled_interpolation?(node)
139
+ if heredoc?(node)
140
+ node.loc.expression.source.end_with?("'")
141
+ elsif node.parent&.dstr_type?
142
+ heredoc_with_disabled_interpolation?(node.parent)
143
+ else
144
+ false
145
+ end
146
+ end
147
+
148
+ def heredoc?(node)
149
+ (node.str_type? || node.dstr_type?) && node.heredoc?
150
+ end
151
+
152
+ def delimiter?(node, char)
153
+ return false if heredoc?(node)
154
+
155
+ if literal_in_interpolated_or_multiline_string?(node) || percent_array_literal?(node)
156
+ return delimiter?(node.parent, char)
157
+ end
158
+
159
+ delimiters = [node.loc.begin.source[-1], node.loc.end.source[0]]
160
+
161
+ delimiters.include?(char)
162
+ end
163
+
164
+ def literal_in_interpolated_or_multiline_string?(node)
165
+ node.str_type? && !begin_loc_present?(node) && node.parent&.dstr_type?
166
+ end
167
+
168
+ def disabling_interpolation?(range)
169
+ # Allow \#{foo}, \#$foo, \#@foo, and \#@@foo
170
+ # for escaping local, global, instance and class variable interpolations
171
+ return true if range.source.match?(/\A\\#[{$@]/)
172
+
173
+ # Also allow #\{foo}, #\$foo, #\@foo and #\@@foo
174
+ return true if range.adjust(begin_pos: -2).source.match?(/\A[^\\]#\\[{$@]/)
175
+
176
+ false
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -9,7 +9,7 @@ module RuboCop
9
9
  # reasons:
10
10
  #
11
11
  # * The syntax of modifier form `rescue` can be misleading because it
12
- # might led us to believe that `rescue` handles the given exception
12
+ # might lead us to believe that `rescue` handles the given exception
13
13
  # but it actually rescue all exceptions to return the given rescue
14
14
  # block. In this case, value returned by handle_error or
15
15
  # SomeException.
@@ -44,18 +44,49 @@ module RuboCop
44
44
  # end
45
45
  #
46
46
  class StaticClass < Base
47
+ include RangeHelp
47
48
  include VisibilityHelp
49
+ extend AutoCorrector
48
50
 
49
51
  MSG = 'Prefer modules to classes with only class methods.'
50
52
 
51
53
  def on_class(class_node)
52
54
  return if class_node.parent_class
55
+ return unless class_convertible_to_module?(class_node)
53
56
 
54
- add_offense(class_node) if class_convertible_to_module?(class_node)
57
+ add_offense(class_node) do |corrector|
58
+ autocorrect(corrector, class_node)
59
+ end
55
60
  end
56
61
 
57
62
  private
58
63
 
64
+ def autocorrect(corrector, class_node)
65
+ corrector.replace(class_node.loc.keyword, 'module')
66
+ corrector.insert_after(class_node.loc.name, "\nmodule_function\n")
67
+
68
+ class_elements(class_node).each do |node|
69
+ if node.defs_type?
70
+ autocorrect_def(corrector, node)
71
+ elsif node.sclass_type?
72
+ autocorrect_sclass(corrector, node)
73
+ end
74
+ end
75
+ end
76
+
77
+ def autocorrect_def(corrector, node)
78
+ corrector.remove(
79
+ range_between(node.receiver.source_range.begin_pos, node.loc.name.begin_pos)
80
+ )
81
+ end
82
+
83
+ def autocorrect_sclass(corrector, node)
84
+ corrector.remove(
85
+ range_between(node.loc.keyword.begin_pos, node.identifier.source_range.end_pos)
86
+ )
87
+ corrector.remove(node.loc.end)
88
+ end
89
+
59
90
  def class_convertible_to_module?(class_node)
60
91
  nodes = class_elements(class_node)
61
92
  return false if nodes.empty?
@@ -65,6 +65,8 @@ module RuboCop
65
65
  end
66
66
 
67
67
  def build_bracketed_array(node)
68
+ return '[]' if node.children.empty?
69
+
68
70
  syms = node.children.map do |c|
69
71
  if c.dsym_type?
70
72
  string_literal = to_string_literal(c.source)
@@ -82,7 +82,7 @@ module RuboCop
82
82
  # # bad
83
83
  # something.map { |s| s.upcase }
84
84
  #
85
- # @example AllowedPatterns: [/map/] (default)
85
+ # @example AllowedPatterns: ['map'] (default)
86
86
  # # good
87
87
  # something.map { |s| s.upcase }
88
88
  #
@@ -83,6 +83,8 @@ module RuboCop
83
83
  end
84
84
 
85
85
  def build_bracketed_array(node)
86
+ return '[]' if node.children.empty?
87
+
86
88
  words = node.children.map do |word|
87
89
  if word.dstr_type?
88
90
  string_literal = to_string_literal(word.source)
@@ -70,7 +70,9 @@ module RuboCop
70
70
 
71
71
  command += ' --auto-gen-only-exclude' if @options[:auto_gen_only_exclude]
72
72
 
73
- if @exclude_limit_option
73
+ if no_exclude_limit?
74
+ command += ' --no-exclude-limit'
75
+ elsif @exclude_limit_option
74
76
  command += format(' --exclude-limit %<limit>d', limit: Integer(@exclude_limit_option))
75
77
  end
76
78
  command += ' --no-offense-counts' unless show_offense_counts?
@@ -187,7 +189,7 @@ module RuboCop
187
189
  return unless cfg.empty?
188
190
 
189
191
  offending_files = @files_with_offenses[cop_name].sort
190
- if offending_files.count > @exclude_limit
192
+ if !no_exclude_limit? && offending_files.count > @exclude_limit
191
193
  output_buffer.puts ' Enabled: false'
192
194
  else
193
195
  output_exclude_list(output_buffer, offending_files, cop_name)
@@ -245,6 +247,10 @@ module RuboCop
245
247
  def safe_autocorrect?(config)
246
248
  config.fetch('Safe', true) && config.fetch('SafeAutoCorrect', true)
247
249
  end
250
+
251
+ def no_exclude_limit?
252
+ @options[:no_exclude_limit] == false
253
+ end
248
254
  end
249
255
  end
250
256
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'optparse'
4
- require 'shellwords'
4
+ require_relative 'arguments_env'
5
+ require_relative 'arguments_file'
5
6
 
6
7
  module RuboCop
7
8
  class IncorrectCopNameError < StandardError; end
@@ -24,7 +25,10 @@ module RuboCop
24
25
  end
25
26
 
26
27
  def parse(command_line_args)
28
+ args_from_file = ArgumentsFile.read_as_arguments
29
+ args_from_env = ArgumentsEnv.read_as_arguments
27
30
  args = args_from_file.concat(args_from_env).concat(command_line_args)
31
+
28
32
  define_options.parse!(args)
29
33
 
30
34
  @validator.validate_compatibility
@@ -45,18 +49,6 @@ module RuboCop
45
49
 
46
50
  private
47
51
 
48
- def args_from_file
49
- if File.exist?('.rubocop') && !File.directory?('.rubocop')
50
- File.read('.rubocop').shellsplit
51
- else
52
- []
53
- end
54
- end
55
-
56
- def args_from_env
57
- Shellwords.split(ENV.fetch('RUBOCOP_OPTS', ''))
58
- end
59
-
60
52
  def define_options
61
53
  OptionParser.new do |opts|
62
54
  opts.banner = rainbow.wrap('Usage: rubocop [options] [file1, file2, ...]').bright
@@ -169,6 +161,7 @@ module RuboCop
169
161
  end
170
162
 
171
163
  option(opts, '--exclude-limit COUNT') { @validator.validate_exclude_limit_option }
164
+ option(opts, '--no-exclude-limit')
172
165
 
173
166
  option(opts, '--[no-]offense-counts')
174
167
  option(opts, '--[no-]auto-gen-only-exclude')
@@ -400,6 +393,12 @@ module RuboCop
400
393
  end
401
394
 
402
395
  def validate_autocorrect
396
+ if @options.key?(:safe_autocorrect) && @options.key?(:autocorrect_all)
397
+ message = Rainbow(<<~MESSAGE).red
398
+ Error: Both safe and unsafe autocorrect options are specified, use only one.
399
+ MESSAGE
400
+ raise OptionArgumentError, message
401
+ end
403
402
  return if @options.key?(:autocorrect)
404
403
  return unless @options.key?(:disable_uncorrectable)
405
404
 
@@ -497,6 +496,7 @@ module RuboCop
497
496
  disable_uncorrectable: ['Used with --autocorrect to annotate any',
498
497
  'offenses that do not support autocorrect',
499
498
  'with `rubocop:todo` comments.'],
499
+ no_exclude_limit: ['Do not set the limit for how many files to exclude.'],
500
500
  force_exclusion: ['Any files excluded by `Exclude` in configuration',
501
501
  'files will be excluded, even if given explicitly',
502
502
  'as arguments.'],
@@ -2,7 +2,15 @@
2
2
 
3
3
  require 'tmpdir'
4
4
 
5
- RSpec.shared_context 'isolated environment' do
5
+ RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLength
6
+ before do
7
+ if RuboCop.const_defined?(:Server)
8
+ # Bust server cache to behave as an isolated environment
9
+ RuboCop::Server::Cache.cache_root_path = nil
10
+ RuboCop::Server::Cache.instance_variable_set(:@project_dir_cache_key, nil)
11
+ end
12
+ end
13
+
6
14
  around do |example|
7
15
  Dir.mktmpdir do |tmpdir|
8
16
  original_home = Dir.home
@@ -32,6 +40,10 @@ RSpec.shared_context 'isolated environment' do
32
40
  ENV['HOME'] = original_home
33
41
  ENV['XDG_CONFIG_HOME'] = original_xdg_config_home
34
42
 
43
+ if RuboCop.const_defined?(:Server)
44
+ RuboCop::Server::Cache.cache_root_path = nil
45
+ RuboCop::Server::Cache.instance_variable_set(:@project_dir_cache_key, nil)
46
+ end
35
47
  RuboCop::FileFinder.root_level = nil
36
48
  end
37
49
  end
@@ -103,9 +103,13 @@ module RuboCop
103
103
  dir.join('version')
104
104
  end
105
105
 
106
+ def stderr_path
107
+ dir.join('stderr')
108
+ end
109
+
106
110
  def pid_running?
107
111
  Process.kill(0, pid_path.read.to_i) == 1
108
- rescue Errno::ESRCH
112
+ rescue Errno::ESRCH, Errno::ENOENT
109
113
  false
110
114
  end
111
115
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rainbow'
4
+ require_relative '../arguments_env'
5
+ require_relative '../arguments_file'
4
6
 
5
7
  #
6
8
  # This code is based on https://github.com/fohte/rubocop-daemon.
@@ -29,7 +31,7 @@ module RuboCop
29
31
  @exit = false
30
32
  end
31
33
 
32
- # rubocop:disable Metrics/MethodLength
34
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
33
35
  def run(argv = ARGV)
34
36
  unless Server.support_server?
35
37
  return error('RuboCop server is not supported by this Ruby.') if use_server_option?(argv)
@@ -50,11 +52,16 @@ module RuboCop
50
52
  return error("#{server_command} cannot be combined with other options.")
51
53
  end
52
54
 
55
+ if server_command.nil?
56
+ server_command = ArgumentsEnv.read_as_arguments.delete('--server') ||
57
+ ArgumentsFile.read_as_arguments.delete('--server')
58
+ end
59
+
53
60
  run_command(server_command)
54
61
 
55
62
  STATUS_SUCCESS
56
63
  end
57
- # rubocop:enable Metrics/MethodLength
64
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
58
65
 
59
66
  def exit?
60
67
  @exit
@@ -23,6 +23,7 @@ module RuboCop
23
23
  args: ARGV.dup,
24
24
  body: $stdin.tty? ? '' : $stdin.read
25
25
  )
26
+ warn stderr unless stderr.empty?
26
27
  status
27
28
  end
28
29
 
@@ -43,6 +44,10 @@ module RuboCop
43
44
  RuboCop::Version::STRING != Cache.version_path.read
44
45
  end
45
46
 
47
+ def stderr
48
+ Cache.stderr_path.read
49
+ end
50
+
46
51
  def status
47
52
  unless Cache.status_path.file?
48
53
  raise "RuboCop server: Could not find status file at: #{Cache.status_path}"