rubocop 1.63.0 → 1.64.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +18 -3
  4. data/lib/rubocop/cached_data.rb +11 -3
  5. data/lib/rubocop/cli/command/show_docs_url.rb +2 -2
  6. data/lib/rubocop/cli.rb +4 -0
  7. data/lib/rubocop/config.rb +2 -3
  8. data/lib/rubocop/cop/base.rb +9 -14
  9. data/lib/rubocop/cop/bundler/gem_version.rb +3 -5
  10. data/lib/rubocop/cop/documentation.rb +16 -6
  11. data/lib/rubocop/cop/gemspec/dependency_version.rb +3 -5
  12. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  13. data/lib/rubocop/cop/layout/comment_indentation.rb +1 -1
  14. data/lib/rubocop/cop/layout/empty_comment.rb +3 -1
  15. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -0
  16. data/lib/rubocop/cop/lint/assignment_in_condition.rb +3 -1
  17. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  18. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  19. data/lib/rubocop/cop/lint/mixed_case_range.rb +9 -4
  20. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
  21. data/lib/rubocop/cop/lint/unreachable_code.rb +4 -2
  22. data/lib/rubocop/cop/lint/unreachable_loop.rb +8 -2
  23. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +5 -5
  24. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +9 -2
  25. data/lib/rubocop/cop/security/compound_hash.rb +2 -2
  26. data/lib/rubocop/cop/style/access_modifier_declarations.rb +50 -0
  27. data/lib/rubocop/cop/style/arguments_forwarding.rb +5 -2
  28. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  29. data/lib/rubocop/cop/style/copyright.rb +10 -8
  30. data/lib/rubocop/cop/style/documentation_method.rb +20 -0
  31. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  32. data/lib/rubocop/cop/style/hash_syntax.rb +18 -0
  33. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +5 -3
  34. data/lib/rubocop/cop/style/map_into_array.rb +3 -3
  35. data/lib/rubocop/cop/style/numeric_predicate.rb +10 -2
  36. data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
  37. data/lib/rubocop/cop/style/redundant_line_continuation.rb +3 -1
  38. data/lib/rubocop/cop/style/require_order.rb +1 -1
  39. data/lib/rubocop/cop/style/send.rb +4 -4
  40. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +83 -0
  41. data/lib/rubocop/cop/style/special_global_vars.rb +1 -2
  42. data/lib/rubocop/cop/style/super_arguments.rb +137 -0
  43. data/lib/rubocop/cop/style/symbol_proc.rb +32 -5
  44. data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -1
  45. data/lib/rubocop/formatter/disabled_config_formatter.rb +13 -9
  46. data/lib/rubocop/formatter/formatter_set.rb +7 -1
  47. data/lib/rubocop/lockfile.rb +25 -6
  48. data/lib/rubocop/lsp/routes.rb +9 -12
  49. data/lib/rubocop/lsp/server.rb +2 -0
  50. data/lib/rubocop/lsp.rb +9 -2
  51. data/lib/rubocop/options.rb +3 -3
  52. data/lib/rubocop/rake_task.rb +1 -1
  53. data/lib/rubocop/runner.rb +5 -4
  54. data/lib/rubocop/version.rb +4 -4
  55. data/lib/rubocop.rb +2 -0
  56. metadata +6 -4
@@ -61,7 +61,7 @@ module RuboCop
61
61
  raise Warning, AUTOCORRECT_EMPTY_WARNING if autocorrect_notice.empty?
62
62
 
63
63
  regex = Regexp.new(notice)
64
- return if autocorrect_notice&.match?(regex)
64
+ return if autocorrect_notice.gsub(/^# */, '').match?(regex)
65
65
 
66
66
  raise Warning, "AutocorrectNotice '#{autocorrect_notice}' must match Notice /#{notice}/"
67
67
  end
@@ -77,26 +77,28 @@ module RuboCop
77
77
  return false if token_index >= processed_source.tokens.size
78
78
 
79
79
  token = processed_source.tokens[token_index]
80
- token.comment? && /^#!.*$/.match?(token.text)
80
+ token.comment? && /\A#!.*\z/.match?(token.text)
81
81
  end
82
82
 
83
83
  def encoding_token?(processed_source, token_index)
84
84
  return false if token_index >= processed_source.tokens.size
85
85
 
86
86
  token = processed_source.tokens[token_index]
87
- token.comment? && /^#.*coding\s?[:=]\s?(?:UTF|utf)-8/.match?(token.text)
87
+ token.comment? && /\A#.*coding\s?[:=]\s?(?:UTF|utf)-8/.match?(token.text)
88
88
  end
89
89
 
90
90
  def notice_found?(processed_source)
91
- notice_found = false
92
- notice_regexp = Regexp.new(notice)
91
+ notice_regexp = Regexp.new(notice.lines.map(&:strip).join)
92
+ multiline_notice = +''
93
93
  processed_source.tokens.each do |token|
94
94
  break unless token.comment?
95
95
 
96
- notice_found = notice_regexp.match?(token.text)
97
- break if notice_found
96
+ multiline_notice << token.text.sub(/\A# */, '')
97
+
98
+ break if notice_regexp.match?(token.text)
98
99
  end
99
- notice_found
100
+
101
+ multiline_notice.match?(notice_regexp)
100
102
  end
101
103
  end
102
104
  end
@@ -95,6 +95,17 @@ module RuboCop
95
95
  # end
96
96
  # end
97
97
  #
98
+ # @example AllowedMethods: ['method_missing', 'respond_to_missing?']
99
+ #
100
+ # # good
101
+ # class Foo
102
+ # def method_missing(name, *args)
103
+ # end
104
+ #
105
+ # def respond_to_missing?(symbol, include_private)
106
+ # end
107
+ # end
108
+ #
98
109
  class DocumentationMethod < Base
99
110
  include DocumentationComment
100
111
  include DefNode
@@ -119,6 +130,7 @@ module RuboCop
119
130
  def check(node)
120
131
  return if non_public?(node) && !require_for_non_public_methods?
121
132
  return if documentation_comment?(node)
133
+ return if method_allowed?(node)
122
134
 
123
135
  add_offense(node)
124
136
  end
@@ -126,6 +138,14 @@ module RuboCop
126
138
  def require_for_non_public_methods?
127
139
  cop_config['RequireForNonPublicMethods']
128
140
  end
141
+
142
+ def method_allowed?(node)
143
+ allowed_methods.include?(node.method_name)
144
+ end
145
+
146
+ def allowed_methods
147
+ @allowed_methods ||= cop_config.fetch('AllowedMethods', []).map(&:to_sym)
148
+ end
129
149
  end
130
150
  end
131
151
  end
@@ -141,7 +141,7 @@ module RuboCop
141
141
  end
142
142
 
143
143
  def check_file(node, file_node)
144
- return true if special_file_keyword?(file_node)
144
+ return if special_file_keyword?(file_node)
145
145
 
146
146
  message = format(MSG_INCORRECT_FILE,
147
147
  method_name: node.method_name,
@@ -29,6 +29,8 @@ module RuboCop
29
29
  # * never - forces use of explicit hash literal value
30
30
  # * either - accepts both shorthand and explicit use of hash literal value
31
31
  # * consistent - forces use of the 3.1 syntax only if all values can be omitted in the hash
32
+ # * either_consistent - accepts both shorthand and explicit use of hash literal value,
33
+ # but they must be consistent
32
34
  #
33
35
  # @example EnforcedStyle: ruby19 (default)
34
36
  # # bad
@@ -110,6 +112,22 @@ module RuboCop
110
112
  # # good - can't omit `baz`
111
113
  # {foo: foo, bar: baz}
112
114
  #
115
+ # @example EnforcedShorthandSyntax: either_consistent
116
+ #
117
+ # # good - `foo` and `bar` values can be omitted, but they are consistent, so it's accepted
118
+ # {foo: foo, bar: bar}
119
+ #
120
+ # # bad - `bar` value can be omitted
121
+ # {foo:, bar: bar}
122
+ #
123
+ # # bad - mixed syntaxes
124
+ # {foo:, bar: baz}
125
+ #
126
+ # # good
127
+ # {foo:, bar:}
128
+ #
129
+ # # good - can't omit `baz`
130
+ # {foo: foo, bar: baz}
113
131
  class HashSyntax < Base
114
132
  include ConfigurableEnforcedStyle
115
133
  include HashShorthandSyntax
@@ -112,9 +112,11 @@ module RuboCop
112
112
  end
113
113
 
114
114
  def message(node, keyword)
115
- message_template = node.elsif? ? MSG_FOR_ELSIF : MSG
116
-
117
- format(message_template, keyword: keyword)
115
+ if node.elsif?
116
+ MSG_FOR_ELSIF
117
+ else
118
+ format(MSG, keyword: keyword)
119
+ end
118
120
  end
119
121
 
120
122
  def return_boolean_value?(condition)
@@ -20,8 +20,8 @@ module RuboCop
20
20
  #
21
21
  # [source,ruby]
22
22
  # ----
23
- # @dest = []
24
- # src.each { |e| @dest << e * 2 } # `src` method may mutate `@dest`
23
+ # ret = []
24
+ # src.each { |e| ret << e * 2 } # `<<` method may mutate `ret`
25
25
  #
26
26
  # dest = []
27
27
  # src.each { |e| dest << transform(e, dest) } # `transform` method may mutate `dest`
@@ -57,7 +57,7 @@ module RuboCop
57
57
  def_node_matcher :each_block_with_push?, <<-PATTERN
58
58
  [
59
59
  ^({begin kwbegin} ...)
60
- ({block numblock} (send _ :each) _
60
+ ({block numblock} (send !{nil? self} :each) _
61
61
  (send (lvar _) {:<< :push :append} _))
62
62
  ]
63
63
  PATTERN
@@ -118,12 +118,14 @@ module RuboCop
118
118
 
119
119
  return unless numeric && operator && replacement_supported?(operator)
120
120
 
121
- [numeric, replacement(numeric, operator)]
121
+ [numeric, replacement(node, numeric, operator)]
122
122
  end
123
123
 
124
- def replacement(numeric, operation)
124
+ def replacement(node, numeric, operation)
125
125
  if style == :predicate
126
126
  [parenthesized_source(numeric), REPLACEMENTS.invert[operation.to_s]].join('.')
127
+ elsif negated?(node)
128
+ "(#{numeric.source} #{REPLACEMENTS[operation.to_s]} 0)"
127
129
  else
128
130
  [numeric.source, REPLACEMENTS[operation.to_s], 0].join(' ')
129
131
  end
@@ -157,6 +159,12 @@ module RuboCop
157
159
  end
158
160
  end
159
161
 
162
+ def negated?(node)
163
+ return false unless (parent = node.parent)
164
+
165
+ parent.send_type? && parent.method?(:!)
166
+ end
167
+
160
168
  # @!method predicate(node)
161
169
  def_node_matcher :predicate, <<~PATTERN
162
170
  (send $(...) ${:zero? :positive? :negative?})
@@ -75,7 +75,7 @@ module RuboCop
75
75
  end
76
76
 
77
77
  def always_multiline?
78
- @config.for_cop('Style/OneLineConditional')['AlwaysCorrectToMultiline']
78
+ cop_config['AlwaysCorrectToMultiline']
79
79
  end
80
80
 
81
81
  def cannot_replace_to_ternary?(node)
@@ -137,7 +137,9 @@ module RuboCop
137
137
  # do_something \
138
138
  # argument
139
139
  def method_with_argument?(current_token, next_token)
140
- current_token.type == :tIDENTIFIER && ARGUMENT_TYPES.include?(next_token.type)
140
+ return false if current_token.type != :tIDENTIFIER && current_token.type != :kRETURN
141
+
142
+ ARGUMENT_TYPES.include?(next_token.type)
141
143
  end
142
144
 
143
145
  # rubocop:disable Metrics/AbcSize
@@ -131,7 +131,7 @@ module RuboCop
131
131
  end
132
132
 
133
133
  def in_same_section?(node1, node2)
134
- !node1.source_range.with(end_pos: node2.source_range.end_pos).source.include?("\n\n")
134
+ !node1.source_range.join(node2.source_range.end).source.include?("\n\n")
135
135
  end
136
136
  end
137
137
  end
@@ -7,12 +7,12 @@ module RuboCop
7
7
  #
8
8
  # @example
9
9
  # # bad
10
- # Foo.send(:bar)
11
- # quuz.send(:fred)
10
+ # Foo.send(bar)
11
+ # quuz.send(fred)
12
12
  #
13
13
  # # good
14
- # Foo.__send__(:bar)
15
- # quuz.public_send(:fred)
14
+ # Foo.__send__(bar)
15
+ # quuz.public_send(fred)
16
16
  class Send < Base
17
17
  MSG = 'Prefer `Object#__send__` or `Object#public_send` to `send`.'
18
18
  RESTRICT_ON_SEND = %i[send].freeze
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Detects the use of the `public_send` method with a literal method name argument.
7
+ # Since the `send` method can be used to call private methods, by default,
8
+ # only the `public_send` method is detected.
9
+ #
10
+ # @safety
11
+ # This cop is not safe because it can incorrectly detect based on the receiver.
12
+ # Additionally, when `AllowSend` is set to `true`, it cannot determine whether
13
+ # the `send` method being detected is calling a private method.
14
+ #
15
+ # @example
16
+ # # bad
17
+ # obj.public_send(:method_name)
18
+ # obj.public_send('method_name')
19
+ #
20
+ # # good
21
+ # obj.method_name
22
+ #
23
+ # @example AllowSend: true (default)
24
+ # # good
25
+ # obj.send(:method_name)
26
+ # obj.send('method_name')
27
+ # obj.__send__(:method_name)
28
+ # obj.__send__('method_name')
29
+ #
30
+ # @example AllowSend: false
31
+ # # bad
32
+ # obj.send(:method_name)
33
+ # obj.send('method_name')
34
+ # obj.__send__(:method_name)
35
+ # obj.__send__('method_name')
36
+ #
37
+ # # good
38
+ # obj.method_name
39
+ #
40
+ class SendWithLiteralMethodName < Base
41
+ extend AutoCorrector
42
+
43
+ MSG = 'Use `%<method_name>s` method call directly instead.'
44
+ RESTRICT_ON_SEND = %i[public_send send __send__].freeze
45
+ STATIC_METHOD_NAME_NODE_TYPES = %i[sym str].freeze
46
+
47
+ # rubocop:disable Metrics/AbcSize
48
+ def on_send(node)
49
+ return if allow_send? && !node.method?(:public_send)
50
+ return unless (first_argument = node.first_argument)
51
+ return unless STATIC_METHOD_NAME_NODE_TYPES.include?(first_argument.type)
52
+
53
+ offense_range = offense_range(node)
54
+ method_name = first_argument.value
55
+
56
+ add_offense(offense_range, message: format(MSG, method_name: method_name)) do |corrector|
57
+ if node.arguments.one?
58
+ corrector.replace(offense_range, method_name)
59
+ else
60
+ corrector.replace(node.loc.selector, method_name)
61
+ corrector.remove(removal_argument_range(first_argument, node.arguments[1]))
62
+ end
63
+ end
64
+ end
65
+ # rubocop:enable Metrics/AbcSize
66
+
67
+ private
68
+
69
+ def allow_send?
70
+ !!cop_config['AllowSend']
71
+ end
72
+
73
+ def offense_range(node)
74
+ node.loc.selector.join(node.source_range.end)
75
+ end
76
+
77
+ def removal_argument_range(first_argument, second_argument)
78
+ first_argument.source_range.begin.join(second_argument.source_range.begin)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -58,9 +58,8 @@ module RuboCop
58
58
  #
59
59
  # @example EnforcedStyle: use_builtin_english_names
60
60
  #
61
- # Like `use_perl_names` but allows builtin global vars.
62
- #
63
61
  # # good
62
+ # # Like `use_perl_names` but allows builtin global vars.
64
63
  # puts $LOAD_PATH
65
64
  # puts $LOADED_FEATURES
66
65
  # puts $PROGRAM_NAME
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant argument forwarding when calling super
7
+ # with arguments identical to the method definition.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # def method(*args, **kwargs)
12
+ # super(*args, **kwargs)
13
+ # end
14
+ #
15
+ # # good - implicitly passing all arguments
16
+ # def method(*args, **kwargs)
17
+ # super
18
+ # end
19
+ #
20
+ # # good - forwarding a subset of the arguments
21
+ # def method(*args, **kwargs)
22
+ # super(*args)
23
+ # end
24
+ #
25
+ # # good - forwarding no arguments
26
+ # def method(*args, **kwargs)
27
+ # super()
28
+ # end
29
+ class SuperArguments < Base
30
+ extend AutoCorrector
31
+
32
+ DEF_TYPES = %i[def defs].freeze
33
+
34
+ MSG = 'Call `super` without arguments and parentheses when the signature is identical.'
35
+
36
+ def on_super(super_node)
37
+ def_node = super_node.ancestors.find do |node|
38
+ # You can't implicitly call super when dynamically defining methods
39
+ break if define_method?(node)
40
+
41
+ break node if DEF_TYPES.include?(node.type)
42
+ end
43
+ return unless def_node
44
+ return unless arguments_identical?(def_node.arguments.argument_list, super_node.arguments)
45
+
46
+ add_offense(super_node) { |corrector| corrector.replace(super_node, 'super') }
47
+ end
48
+
49
+ private
50
+
51
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
52
+ def arguments_identical?(def_args, super_args)
53
+ super_args = preprocess_super_args(super_args)
54
+ return false if def_args.size != super_args.size
55
+
56
+ def_args.zip(super_args).each do |def_arg, super_arg|
57
+ next if positional_arg_same?(def_arg, super_arg)
58
+ next if positional_rest_arg_same(def_arg, super_arg)
59
+ next if keyword_arg_same?(def_arg, super_arg)
60
+ next if keyword_rest_arg_same?(def_arg, super_arg)
61
+ next if block_arg_same?(def_arg, super_arg)
62
+ next if forward_arg_same?(def_arg, super_arg)
63
+
64
+ return false
65
+ end
66
+ true
67
+ end
68
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
69
+
70
+ def positional_arg_same?(def_arg, super_arg)
71
+ return false unless def_arg.arg_type? || def_arg.optarg_type?
72
+ return false unless super_arg.lvar_type?
73
+
74
+ def_arg.name == super_arg.children.first
75
+ end
76
+
77
+ def positional_rest_arg_same(def_arg, super_arg)
78
+ return false unless def_arg.restarg_type?
79
+ # anonymous forwarding
80
+ return true if def_arg.name.nil? && super_arg.forwarded_restarg_type?
81
+ return false unless super_arg.splat_type?
82
+ return false unless (lvar_node = super_arg.children.first).lvar_type?
83
+
84
+ def_arg.name == lvar_node.children.first
85
+ end
86
+
87
+ def keyword_arg_same?(def_arg, super_arg)
88
+ return false unless def_arg.kwarg_type? || def_arg.kwoptarg_type?
89
+ return false unless (pair_node = super_arg).pair_type?
90
+ return false unless (sym_node = pair_node.key).sym_type?
91
+ return false unless (lvar_node = pair_node.value).lvar_type?
92
+ return false unless sym_node.source == lvar_node.source
93
+
94
+ def_arg.name == sym_node.value
95
+ end
96
+
97
+ def keyword_rest_arg_same?(def_arg, super_arg)
98
+ return false unless def_arg.kwrestarg_type?
99
+ # anonymous forwarding
100
+ return true if def_arg.name.nil? && super_arg.forwarded_kwrestarg_type?
101
+ return false unless super_arg.kwsplat_type?
102
+ return false unless (lvar_node = super_arg.children.first).lvar_type?
103
+
104
+ def_arg.name == lvar_node.children.first
105
+ end
106
+
107
+ def block_arg_same?(def_arg, super_arg)
108
+ return false unless def_arg.blockarg_type? && super_arg.block_pass_type?
109
+ # anonymous forwarding
110
+ return true if (block_pass_child = super_arg.children.first).nil? && def_arg.name.nil?
111
+
112
+ def_arg.name == block_pass_child.children.first
113
+ end
114
+
115
+ def forward_arg_same?(def_arg, super_arg)
116
+ def_arg.forward_arg_type? && super_arg.forwarded_args_type?
117
+ end
118
+
119
+ def define_method?(node)
120
+ return false unless node.block_type?
121
+
122
+ node.method?(:define_method) || node.method?(:define_singleton_method)
123
+ end
124
+
125
+ def preprocess_super_args(super_args)
126
+ super_args.flat_map do |node|
127
+ if node.hash_type? && !node.braces?
128
+ node.children
129
+ else
130
+ node
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -120,6 +120,23 @@ module RuboCop
120
120
  # # good
121
121
  # something.map { |s| s.upcase }
122
122
  #
123
+ # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
124
+ # # bad
125
+ # ->(x) { x.foo }
126
+ # proc { |x| x.foo }
127
+ # Proc.new { |x| x.foo }
128
+ #
129
+ # # good
130
+ # lambda(&:foo)
131
+ # proc(&:foo)
132
+ # Proc.new(&:foo)
133
+ #
134
+ # @example AllCops:ActiveSupportExtensionsEnabled: true
135
+ # # good
136
+ # ->(x) { x.foo }
137
+ # proc { |x| x.foo }
138
+ # Proc.new { |x| x.foo }
139
+ #
123
140
  class SymbolProc < Base
124
141
  include CommentsHelp
125
142
  include RangeHelp
@@ -129,6 +146,7 @@ module RuboCop
129
146
 
130
147
  MSG = 'Pass `&:%<method>s` as an argument to `%<block_method>s` instead of a block.'
131
148
  SUPER_TYPES = %i[super zsuper].freeze
149
+ LAMBDA_OR_PROC = %i[lambda proc].freeze
132
150
 
133
151
  # @!method proc_node?(node)
134
152
  def_node_matcher :proc_node?, '(send (const {nil? cbase} :Proc) :new)'
@@ -151,13 +169,12 @@ module RuboCop
151
169
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
152
170
  def on_block(node)
153
171
  symbol_proc?(node) do |dispatch_node, arguments_node, method_name|
154
- # TODO: Rails-specific handling that we should probably make
155
- # configurable - https://github.com/rubocop/rubocop/issues/1485
156
- # we should allow lambdas & procs
157
- return if proc_node?(dispatch_node)
172
+ if active_support_extensions_enabled?
173
+ return if proc_node?(dispatch_node)
174
+ return if LAMBDA_OR_PROC.include?(dispatch_node.method_name)
175
+ end
158
176
  return if unsafe_hash_usage?(dispatch_node)
159
177
  return if unsafe_array_usage?(dispatch_node)
160
- return if %i[lambda proc].include?(dispatch_node.method_name)
161
178
  return if allowed_method_name?(dispatch_node.method_name)
162
179
  return if allow_if_method_has_argument?(node.send_node)
163
180
  return if node.block_type? && destructuring_block_argument?(arguments_node)
@@ -206,6 +223,8 @@ module RuboCop
206
223
  end
207
224
 
208
225
  def autocorrect_without_args(corrector, node)
226
+ autocorrect_lambda_block(corrector, node) if node.send_node.lambda_literal?
227
+
209
228
  corrector.replace(block_range_with_space(node), "(&:#{node.body.method_name})")
210
229
  end
211
230
 
@@ -218,6 +237,14 @@ module RuboCop
218
237
  corrector.remove(block_range_with_space(node))
219
238
  end
220
239
 
240
+ def autocorrect_lambda_block(corrector, node)
241
+ send_node_loc = node.send_node.loc
242
+ corrector.replace(send_node_loc.selector, 'lambda')
243
+
244
+ range = range_between(send_node_loc.selector.end_pos, node.loc.begin.end_pos - 2)
245
+ corrector.remove(range)
246
+ end
247
+
221
248
  def block_range_with_space(node)
222
249
  block_range = range_between(begin_pos_for_replacement(node), node.loc.end.end_pos)
223
250
  range_with_surrounding_space(block_range, side: :left)
@@ -77,7 +77,7 @@ module RuboCop
77
77
 
78
78
  # @!method define_method_block?(node)
79
79
  def_node_matcher :define_method_block?, <<~PATTERN
80
- ({block numblock} (send _ {:define_method} _) ...)
80
+ ({block numblock} (send _ :define_method _) ...)
81
81
  PATTERN
82
82
  end
83
83
  end
@@ -116,20 +116,24 @@ module RuboCop
116
116
  def set_max(cfg, cop_name)
117
117
  return unless cfg[:exclude_limit]
118
118
 
119
- max_set_in_user_config =
120
- @config_for_pwd.for_cop(cop_name)['Max'] != default_config(cop_name)['Max']
121
- if !max_set_in_user_config &&
122
- # In case auto_gen_only_exclude is set, only modify the maximum if the files are not
123
- # excluded one by one.
124
- (!@options[:auto_gen_only_exclude] ||
125
- @files_with_offenses[cop_name].size > @exclude_limit)
126
- cfg.merge!(cfg[:exclude_limit])
127
- end
119
+ cfg.merge!(cfg[:exclude_limit]) if should_set_max?(cop_name)
128
120
 
129
121
  # Remove already used exclude_limit.
130
122
  cfg.reject! { |key| key == :exclude_limit }
131
123
  end
132
124
 
125
+ def should_set_max?(cop_name)
126
+ max_set_in_user_config =
127
+ @config_for_pwd.for_cop(cop_name)['Max'] != default_config(cop_name)['Max']
128
+
129
+ max_allowed = !max_set_in_user_config && !no_exclude_limit?
130
+ return false unless max_allowed
131
+
132
+ # In case auto_gen_only_exclude is set, only modify the maximum if the files are not
133
+ # excluded one by one.
134
+ !@options[:auto_gen_only_exclude] || @files_with_offenses[cop_name].size > @exclude_limit
135
+ end
136
+
133
137
  def output_cop_comments(output_buffer, cfg, cop_name, offense_count)
134
138
  output_buffer.puts "# Offense count: #{offense_count}" if show_offense_counts?
135
139
 
@@ -27,6 +27,7 @@ module RuboCop
27
27
  '[t]ap' => 'TapFormatter',
28
28
  '[w]orst' => 'WorstOffendersFormatter'
29
29
  }.freeze
30
+ BUILTIN_FORMATTER_NAMES = BUILTIN_FORMATTERS_FOR_KEYS.keys.map { |key| key.delete('[]') }
30
31
 
31
32
  FORMATTER_APIS = %i[started finished].freeze
32
33
 
@@ -88,7 +89,12 @@ module RuboCop
88
89
  /^\[#{specified_key}\]/.match?(key) || specified_key == key.delete('[]')
89
90
  end
90
91
 
91
- raise %(No formatter for "#{specified_key}") if matching_keys.empty?
92
+ if matching_keys.empty?
93
+ similar_name = NameSimilarity.find_similar_name(specified_key, BUILTIN_FORMATTER_NAMES)
94
+ suggestion = %( Did you mean? "#{similar_name}") if similar_name
95
+
96
+ raise Rainbow(%(Formatter "#{specified_key}" not found.#{suggestion})).red
97
+ end
92
98
 
93
99
  raise %(Cannot determine formatter for "#{specified_key}") if matching_keys.size > 1
94
100
 
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ begin
4
+ # We might not be running with `bundle exec`, so we need to pull in Bundler ourselves,
5
+ # in order to use `Bundler::LockfileParser`.
6
+ require 'bundler'
7
+ rescue LoadError
8
+ nil
9
+ end
10
+
3
11
  module RuboCop
4
12
  # Encapsulation of a lockfile for use when checking for gems.
5
13
  # Does not actually resolve gems, just parses the lockfile.
@@ -7,7 +15,11 @@ module RuboCop
7
15
  class Lockfile
8
16
  # @param [String, Pathname, nil] lockfile_path
9
17
  def initialize(lockfile_path = nil)
10
- lockfile_path ||= defined?(Bundler) ? Bundler.default_lockfile : nil
18
+ lockfile_path ||= begin
19
+ ::Bundler.default_lockfile if bundler_lock_parser_defined?
20
+ rescue ::Bundler::GemfileNotFound
21
+ nil # We might not be a folder with a Gemfile, but that's okay.
22
+ end
11
23
 
12
24
  @lockfile_path = lockfile_path
13
25
  end
@@ -59,12 +71,19 @@ module RuboCop
59
71
  # @return [Bundler::LockfileParser, nil]
60
72
  def parser
61
73
  return @parser if defined?(@parser)
62
- return unless @lockfile_path
63
74
 
64
- lockfile = Bundler.read_file(@lockfile_path)
65
- @parser = lockfile ? Bundler::LockfileParser.new(lockfile) : nil
66
- rescue Bundler::BundlerError
67
- nil
75
+ @parser = if @lockfile_path && File.exist?(@lockfile_path) && bundler_lock_parser_defined?
76
+ begin
77
+ lockfile = ::Bundler.read_file(@lockfile_path)
78
+ ::Bundler::LockfileParser.new(lockfile) if lockfile
79
+ rescue ::Bundler::BundlerError
80
+ nil
81
+ end
82
+ end
83
+ end
84
+
85
+ def bundler_lock_parser_defined?
86
+ Object.const_defined?(:Bundler) && Bundler.const_defined?(:LockfileParser)
68
87
  end
69
88
  end
70
89
  end