rubocop 1.63.0 → 1.64.0
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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +18 -3
- data/lib/rubocop/cached_data.rb +11 -3
- data/lib/rubocop/cli/command/show_docs_url.rb +2 -2
- data/lib/rubocop/cli.rb +4 -0
- data/lib/rubocop/config.rb +2 -3
- data/lib/rubocop/cop/base.rb +9 -14
- data/lib/rubocop/cop/bundler/gem_version.rb +3 -5
- data/lib/rubocop/cop/documentation.rb +16 -6
- data/lib/rubocop/cop/gemspec/dependency_version.rb +3 -5
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
- data/lib/rubocop/cop/layout/comment_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/empty_comment.rb +3 -1
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -0
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +3 -1
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
- data/lib/rubocop/cop/lint/mixed_case_range.rb +9 -4
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
- data/lib/rubocop/cop/lint/unreachable_code.rb +4 -2
- data/lib/rubocop/cop/lint/unreachable_loop.rb +8 -2
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +5 -5
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +9 -2
- data/lib/rubocop/cop/security/compound_hash.rb +2 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +50 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +5 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/copyright.rb +10 -8
- data/lib/rubocop/cop/style/documentation_method.rb +20 -0
- data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +18 -0
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +5 -3
- data/lib/rubocop/cop/style/map_into_array.rb +3 -3
- data/lib/rubocop/cop/style/numeric_predicate.rb +10 -2
- data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +3 -1
- data/lib/rubocop/cop/style/require_order.rb +1 -1
- data/lib/rubocop/cop/style/send.rb +4 -4
- data/lib/rubocop/cop/style/send_with_literal_method_name.rb +83 -0
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -2
- data/lib/rubocop/cop/style/super_arguments.rb +137 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +32 -5
- data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +13 -9
- data/lib/rubocop/formatter/formatter_set.rb +7 -1
- data/lib/rubocop/lockfile.rb +25 -6
- data/lib/rubocop/lsp/routes.rb +9 -12
- data/lib/rubocop/lsp/server.rb +2 -0
- data/lib/rubocop/lsp.rb +9 -2
- data/lib/rubocop/options.rb +3 -3
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/runner.rb +5 -4
- data/lib/rubocop/version.rb +4 -4
- data/lib/rubocop.rb +2 -0
- 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
|
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? &&
|
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? &&
|
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
|
-
|
92
|
-
|
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
|
-
|
97
|
-
|
96
|
+
multiline_notice << token.text.sub(/\A# */, '')
|
97
|
+
|
98
|
+
break if notice_regexp.match?(token.text)
|
98
99
|
end
|
99
|
-
|
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
|
@@ -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
|
-
|
116
|
-
|
117
|
-
|
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
|
-
#
|
24
|
-
# src.each { |e|
|
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
|
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?})
|
@@ -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
|
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.
|
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(
|
11
|
-
# quuz.send(
|
10
|
+
# Foo.send(bar)
|
11
|
+
# quuz.send(fred)
|
12
12
|
#
|
13
13
|
# # good
|
14
|
-
# Foo.__send__(
|
15
|
-
# quuz.public_send(
|
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
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
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)
|
@@ -116,20 +116,24 @@ module RuboCop
|
|
116
116
|
def set_max(cfg, cop_name)
|
117
117
|
return unless cfg[:exclude_limit]
|
118
118
|
|
119
|
-
|
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
|
-
|
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
|
|
data/lib/rubocop/lockfile.rb
CHANGED
@@ -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 ||=
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|