rubocop 0.77.0 → 0.81.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/LICENSE.txt +1 -1
- data/README.md +3 -3
- data/config/default.yml +136 -60
- data/lib/rubocop.rb +20 -4
- data/lib/rubocop/ast/builder.rb +45 -42
- data/lib/rubocop/ast/node.rb +11 -18
- data/lib/rubocop/ast/node/block_node.rb +5 -1
- data/lib/rubocop/ast/node/case_match_node.rb +56 -0
- data/lib/rubocop/ast/node/def_node.rb +11 -0
- data/lib/rubocop/ast/node/forward_args_node.rb +18 -0
- data/lib/rubocop/ast/node/regexp_node.rb +2 -4
- data/lib/rubocop/ast/traversal.rb +29 -10
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +7 -7
- data/lib/rubocop/cli/command/show_cops.rb +11 -4
- data/lib/rubocop/comment_config.rb +6 -1
- data/lib/rubocop/config.rb +28 -10
- data/lib/rubocop/config_loader.rb +19 -19
- data/lib/rubocop/config_obsoletion.rb +6 -4
- data/lib/rubocop/config_validator.rb +55 -95
- data/lib/rubocop/cop/autocorrect_logic.rb +7 -4
- data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +2 -2
- data/lib/rubocop/cop/cop.rb +3 -1
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
- data/lib/rubocop/cop/generator.rb +3 -4
- data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
- data/lib/rubocop/cop/layout/array_alignment.rb +53 -10
- data/lib/rubocop/cop/layout/block_end_newline.rb +5 -3
- data/lib/rubocop/cop/layout/else_alignment.rb +8 -0
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -1
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/hash_alignment.rb +8 -4
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +4 -4
- data/lib/rubocop/cop/layout/leading_comment_space.rb +33 -2
- data/lib/rubocop/cop/{metrics → layout}/line_length.rb +35 -79
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +14 -5
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +0 -4
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +49 -6
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +17 -0
- data/lib/rubocop/cop/layout/space_before_first_arg.rb +8 -0
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -9
- data/lib/rubocop/cop/lint/boolean_symbol.rb +12 -0
- data/lib/rubocop/cop/lint/debugger.rb +1 -1
- data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/loop.rb +6 -4
- data/lib/rubocop/cop/lint/nested_method_definition.rb +2 -2
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +89 -0
- data/lib/rubocop/cop/lint/raise_exception.rb +39 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -3
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +13 -8
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -1
- data/lib/rubocop/cop/lint/struct_new_override.rb +58 -0
- data/lib/rubocop/cop/lint/suppressed_exception.rb +12 -22
- data/lib/rubocop/cop/lint/unused_method_argument.rb +32 -6
- data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -0
- data/lib/rubocop/cop/migration/department_name.rb +47 -6
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +4 -0
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +6 -1
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +7 -7
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +171 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +88 -0
- data/lib/rubocop/cop/mixin/method_complexity.rb +5 -0
- data/lib/rubocop/cop/mixin/rational_literal.rb +18 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -2
- data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -12
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/naming/method_name.rb +30 -0
- data/lib/rubocop/cop/naming/method_parameter_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +11 -0
- data/lib/rubocop/cop/registry.rb +7 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +26 -6
- data/lib/rubocop/cop/style/attr.rb +8 -0
- data/lib/rubocop/cop/style/block_delimiters.rb +60 -1
- data/lib/rubocop/cop/style/collection_methods.rb +2 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
- data/lib/rubocop/cop/style/documentation.rb +43 -5
- data/lib/rubocop/cop/style/end_block.rb +6 -0
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +89 -11
- data/lib/rubocop/cop/style/guard_clause.rb +3 -2
- data/lib/rubocop/cop/style/hash_each_methods.rb +89 -0
- data/lib/rubocop/cop/style/hash_transform_keys.rb +83 -0
- data/lib/rubocop/cop/style/hash_transform_values.rb +83 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +38 -3
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +9 -5
- data/lib/rubocop/cop/style/lambda.rb +1 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -205
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +169 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +54 -0
- data/lib/rubocop/cop/style/module_function.rb +56 -10
- data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -1
- data/lib/rubocop/cop/style/multiline_when_then.rb +5 -1
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -4
- data/lib/rubocop/cop/style/numeric_predicate.rb +4 -3
- data/lib/rubocop/cop/style/one_line_conditional.rb +3 -2
- data/lib/rubocop/cop/style/or_assignment.rb +3 -2
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +7 -7
- data/lib/rubocop/cop/style/redundant_condition.rb +17 -4
- data/lib/rubocop/cop/style/redundant_sort.rb +2 -2
- data/lib/rubocop/cop/style/symbol_array.rb +2 -2
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +34 -22
- data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +41 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +85 -0
- data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +44 -0
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -1
- data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
- data/lib/rubocop/cop/style/yoda_condition.rb +16 -1
- data/lib/rubocop/cop/variable_force.rb +4 -1
- data/lib/rubocop/formatter/base_formatter.rb +2 -2
- data/lib/rubocop/formatter/clang_style_formatter.rb +1 -1
- data/lib/rubocop/formatter/formatter_set.rb +1 -0
- data/lib/rubocop/formatter/json_formatter.rb +6 -5
- data/lib/rubocop/formatter/junit_formatter.rb +74 -0
- data/lib/rubocop/formatter/tap_formatter.rb +1 -1
- data/lib/rubocop/node_pattern.rb +97 -11
- data/lib/rubocop/options.rb +8 -8
- data/lib/rubocop/processed_source.rb +1 -1
- data/lib/rubocop/result_cache.rb +2 -0
- data/lib/rubocop/rspec/shared_contexts.rb +5 -0
- data/lib/rubocop/runner.rb +5 -1
- data/lib/rubocop/target_ruby.rb +151 -0
- data/lib/rubocop/version.rb +1 -1
- metadata +38 -10
- data/lib/rubocop/cop/lint/end_in_method.rb +0 -40
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +0 -209
@@ -19,9 +19,9 @@ module RuboCop
|
|
19
19
|
#
|
20
20
|
# @example
|
21
21
|
# # bad
|
22
|
-
# # rubocop:disable
|
22
|
+
# # rubocop:disable Layout/LineLength
|
23
23
|
# x += 1
|
24
|
-
# # rubocop:enable
|
24
|
+
# # rubocop:enable Layout/LineLength
|
25
25
|
#
|
26
26
|
# # good
|
27
27
|
# x += 1
|
@@ -213,7 +213,7 @@ module RuboCop
|
|
213
213
|
end
|
214
214
|
|
215
215
|
def matching_range(haystack, needle)
|
216
|
-
offset =
|
216
|
+
offset = haystack.source.index(needle)
|
217
217
|
return unless offset
|
218
218
|
|
219
219
|
offset += haystack.begin_pos
|
@@ -1,9 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# The Lint/RedundantCopEnableDirective
|
4
|
-
# to be able to provide a (bad) example of an
|
3
|
+
# The Lint/RedundantCopEnableDirective and Lint/RedundantCopDisableDirective
|
4
|
+
# cops need to be disabled so as to be able to provide a (bad) example of an
|
5
|
+
# unneeded enable.
|
5
6
|
|
6
7
|
# rubocop:disable Lint/RedundantCopEnableDirective
|
8
|
+
# rubocop:disable Lint/RedundantCopDisableDirective
|
7
9
|
module RuboCop
|
8
10
|
module Cop
|
9
11
|
module Lint
|
@@ -15,21 +17,21 @@ module RuboCop
|
|
15
17
|
# @example
|
16
18
|
# # bad
|
17
19
|
# foo = 1
|
18
|
-
# # rubocop:enable
|
20
|
+
# # rubocop:enable Layout/LineLength
|
19
21
|
#
|
20
22
|
# # good
|
21
23
|
# foo = 1
|
22
24
|
# @example
|
23
25
|
# # bad
|
24
|
-
# # rubocop:disable
|
25
|
-
#
|
26
|
-
# # rubocop:enable
|
26
|
+
# # rubocop:disable Style/StringLiterals
|
27
|
+
# foo = "1"
|
28
|
+
# # rubocop:enable Style/StringLiterals
|
27
29
|
# baz
|
28
30
|
# # rubocop:enable all
|
29
31
|
#
|
30
32
|
# # good
|
31
|
-
# # rubocop:disable
|
32
|
-
#
|
33
|
+
# # rubocop:disable Style/StringLiterals
|
34
|
+
# foo = "1"
|
33
35
|
# # rubocop:enable all
|
34
36
|
# baz
|
35
37
|
class RedundantCopEnableDirective < Cop
|
@@ -112,3 +114,6 @@ module RuboCop
|
|
112
114
|
end
|
113
115
|
end
|
114
116
|
end
|
117
|
+
|
118
|
+
# rubocop:enable Lint/RedundantCopDisableDirective
|
119
|
+
# rubocop:enable Lint/RedundantCopEnableDirective
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks unexpected overrides of the `Struct` built-in methods
|
7
|
+
# via `Struct.new`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# Bad = Struct.new(:members, :clone, :count)
|
12
|
+
# b = Bad.new([], true, 1)
|
13
|
+
# b.members #=> [] (overriding `Struct#members`)
|
14
|
+
# b.clone #=> true (overriding `Object#clone`)
|
15
|
+
# b.count #=> 1 (overriding `Enumerable#count`)
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# Good = Struct.new(:id, :name)
|
19
|
+
# g = Good.new(1, "foo")
|
20
|
+
# g.members #=> [:id, :name]
|
21
|
+
# g.clone #=> #<struct Good id=1, name="foo">
|
22
|
+
# g.count #=> 2
|
23
|
+
#
|
24
|
+
class StructNewOverride < Cop
|
25
|
+
MSG = '`%<member_name>s` member overrides `Struct#%<method_name>s`' \
|
26
|
+
' and it may be unexpected.'
|
27
|
+
|
28
|
+
STRUCT_METHOD_NAMES = Struct.instance_methods
|
29
|
+
STRUCT_MEMBER_NAME_TYPES = %i[sym str].freeze
|
30
|
+
|
31
|
+
def_node_matcher :struct_new, <<~PATTERN
|
32
|
+
(send
|
33
|
+
(const ${nil? cbase} :Struct) :new ...)
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def on_send(node)
|
37
|
+
return unless struct_new(node) do
|
38
|
+
node.arguments.each_with_index do |arg, index|
|
39
|
+
# Ignore if the first argument is a class name
|
40
|
+
next if index.zero? && arg.str_type?
|
41
|
+
|
42
|
+
# Ignore if the argument is not a member name
|
43
|
+
next unless STRUCT_MEMBER_NAME_TYPES.include?(arg.type)
|
44
|
+
|
45
|
+
member_name = arg.value
|
46
|
+
|
47
|
+
next unless STRUCT_METHOD_NAMES.include?(member_name.to_sym)
|
48
|
+
|
49
|
+
message = format(MSG, member_name: member_name.inspect,
|
50
|
+
method_name: member_name.to_s)
|
51
|
+
add_offense(arg, message: message)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -5,7 +5,7 @@ module RuboCop
|
|
5
5
|
module Lint
|
6
6
|
# This cop checks for *rescue* blocks with no body.
|
7
7
|
#
|
8
|
-
# @example
|
8
|
+
# @example
|
9
9
|
#
|
10
10
|
# # bad
|
11
11
|
# def some_method
|
@@ -14,25 +14,11 @@ module RuboCop
|
|
14
14
|
# end
|
15
15
|
#
|
16
16
|
# # bad
|
17
|
-
# def some_method
|
18
|
-
# do_something
|
19
|
-
# rescue
|
20
|
-
# # do nothing
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# # bad
|
24
17
|
# begin
|
25
18
|
# do_something
|
26
19
|
# rescue
|
27
20
|
# end
|
28
21
|
#
|
29
|
-
# # bad
|
30
|
-
# begin
|
31
|
-
# do_something
|
32
|
-
# rescue
|
33
|
-
# # do nothing
|
34
|
-
# end
|
35
|
-
#
|
36
22
|
# # good
|
37
23
|
# def some_method
|
38
24
|
# do_something
|
@@ -47,32 +33,36 @@ module RuboCop
|
|
47
33
|
# handle_exception
|
48
34
|
# end
|
49
35
|
#
|
50
|
-
# @example AllowComments: true
|
36
|
+
# @example AllowComments: true (default)
|
51
37
|
#
|
52
|
-
# #
|
38
|
+
# # good
|
53
39
|
# def some_method
|
54
40
|
# do_something
|
55
41
|
# rescue
|
42
|
+
# # do nothing
|
56
43
|
# end
|
57
44
|
#
|
58
|
-
# #
|
45
|
+
# # good
|
59
46
|
# begin
|
60
47
|
# do_something
|
61
48
|
# rescue
|
49
|
+
# # do nothing
|
62
50
|
# end
|
63
51
|
#
|
64
|
-
#
|
52
|
+
# @example AllowComments: false
|
53
|
+
#
|
54
|
+
# # bad
|
65
55
|
# def some_method
|
66
56
|
# do_something
|
67
57
|
# rescue
|
68
|
-
# # do nothing
|
58
|
+
# # do nothing
|
69
59
|
# end
|
70
60
|
#
|
71
|
-
# #
|
61
|
+
# # bad
|
72
62
|
# begin
|
73
63
|
# do_something
|
74
64
|
# rescue
|
75
|
-
# # do nothing
|
65
|
+
# # do nothing
|
76
66
|
# end
|
77
67
|
class SuppressedException < Cop
|
78
68
|
MSG = 'Do not suppress exceptions.'
|
@@ -38,9 +38,34 @@ module RuboCop
|
|
38
38
|
# def do_something(unused)
|
39
39
|
# end
|
40
40
|
#
|
41
|
+
# @example IgnoreNotImplementedMethods: true (default)
|
42
|
+
# # good
|
43
|
+
# def do_something(unused)
|
44
|
+
# raise NotImplementedError
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# def do_something_else(unused)
|
48
|
+
# fail "TODO"
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# @example IgnoreNotImplementedMethods: false
|
52
|
+
# # bad
|
53
|
+
# def do_something(unused)
|
54
|
+
# raise NotImplementedError
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# def do_something_else(unused)
|
58
|
+
# fail "TODO"
|
59
|
+
# end
|
60
|
+
#
|
41
61
|
class UnusedMethodArgument < Cop
|
42
62
|
include UnusedArgument
|
43
63
|
|
64
|
+
def_node_matcher :not_implemented?, <<~PATTERN
|
65
|
+
{(send nil? :raise (const nil? :NotImplementedError))
|
66
|
+
(send nil? :fail ...)}
|
67
|
+
PATTERN
|
68
|
+
|
44
69
|
def autocorrect(node)
|
45
70
|
UnusedArgCorrector.correct(processed_source, node)
|
46
71
|
end
|
@@ -51,16 +76,17 @@ module RuboCop
|
|
51
76
|
return unless variable.method_argument?
|
52
77
|
return if variable.keyword_argument? &&
|
53
78
|
cop_config['AllowUnusedKeywordArguments']
|
54
|
-
|
55
|
-
if cop_config['IgnoreEmptyMethods']
|
56
|
-
body = variable.scope.node.body
|
57
|
-
|
58
|
-
return if body.nil?
|
59
|
-
end
|
79
|
+
return if ignored_method?(variable.scope.node.body)
|
60
80
|
|
61
81
|
super
|
62
82
|
end
|
63
83
|
|
84
|
+
def ignored_method?(body)
|
85
|
+
cop_config['IgnoreEmptyMethods'] && body.nil? ||
|
86
|
+
cop_config['IgnoreNotImplementedMethods'] &&
|
87
|
+
not_implemented?(body)
|
88
|
+
end
|
89
|
+
|
64
90
|
def message(variable)
|
65
91
|
message = +"Unused method argument - `#{variable.name}`."
|
66
92
|
|
@@ -6,6 +6,10 @@ module RuboCop
|
|
6
6
|
# This cop checks for setter call to local variable as the final
|
7
7
|
# expression of a function definition.
|
8
8
|
#
|
9
|
+
# Note: There are edge cases in which the local variable references a
|
10
|
+
# value that is also accessible outside the local scope. This is not
|
11
|
+
# detected by the cop, and it can yield a false positive.
|
12
|
+
#
|
9
13
|
# @example
|
10
14
|
#
|
11
15
|
# # bad
|
@@ -10,13 +10,29 @@ module RuboCop
|
|
10
10
|
|
11
11
|
MSG = 'Department name is missing.'
|
12
12
|
|
13
|
+
DISABLE_COMMENT_FORMAT =
|
14
|
+
/\A(# *rubocop *: *((dis|en)able|todo) +)(.*)/.freeze
|
15
|
+
|
16
|
+
# The token that makes up a disable comment.
|
17
|
+
# The allowed specification for comments after `# rubocop: disable` is
|
18
|
+
# `DepartmentName/CopName` or` all`.
|
19
|
+
DISABLING_COPS_CONTENT_TOKEN = %r{[A-z]+/[A-z]+|all}.freeze
|
20
|
+
|
13
21
|
def investigate(processed_source)
|
14
22
|
processed_source.each_comment do |comment|
|
15
|
-
next if comment.text !~
|
23
|
+
next if comment.text !~ DISABLE_COMMENT_FORMAT
|
16
24
|
|
17
25
|
offset = Regexp.last_match(1).length
|
18
|
-
|
19
|
-
|
26
|
+
|
27
|
+
Regexp.last_match(4).scan(/[^,]+|[\W]+/) do |name|
|
28
|
+
trimmed_name = name.strip
|
29
|
+
|
30
|
+
break if contain_plain_comment?(trimmed_name)
|
31
|
+
|
32
|
+
unless valid_content_token?(trimmed_name)
|
33
|
+
check_cop_name(trimmed_name, comment, offset)
|
34
|
+
end
|
35
|
+
|
20
36
|
offset += name.length
|
21
37
|
end
|
22
38
|
end
|
@@ -24,20 +40,45 @@ module RuboCop
|
|
24
40
|
|
25
41
|
def autocorrect(range)
|
26
42
|
shall_warn = false
|
27
|
-
|
43
|
+
cop_name = range.source
|
44
|
+
qualified_cop_name = Cop.registry.qualified_cop_name(cop_name,
|
28
45
|
nil, shall_warn)
|
46
|
+
unless qualified_cop_name.include?('/')
|
47
|
+
qualified_cop_name = qualified_legacy_cop_name(cop_name)
|
48
|
+
end
|
49
|
+
|
29
50
|
->(corrector) { corrector.replace(range, qualified_cop_name) }
|
30
51
|
end
|
31
52
|
|
32
53
|
private
|
33
54
|
|
34
|
-
def
|
35
|
-
|
55
|
+
def disable_comment_offset
|
56
|
+
Regexp.last_match(1).length
|
57
|
+
end
|
36
58
|
|
59
|
+
def check_cop_name(name, comment, offset)
|
37
60
|
start = comment.location.expression.begin_pos + offset
|
38
61
|
range = range_between(start, start + name.length)
|
62
|
+
|
39
63
|
add_offense(range, location: range)
|
40
64
|
end
|
65
|
+
|
66
|
+
def valid_content_token?(content_token)
|
67
|
+
!/\W+/.match(content_token).nil? ||
|
68
|
+
!DISABLING_COPS_CONTENT_TOKEN.match(content_token).nil?
|
69
|
+
end
|
70
|
+
|
71
|
+
def contain_plain_comment?(name)
|
72
|
+
name == '#'
|
73
|
+
end
|
74
|
+
|
75
|
+
def qualified_legacy_cop_name(cop_name)
|
76
|
+
legacy_cop_names = RuboCop::ConfigObsoletion::OBSOLETE_COPS.keys
|
77
|
+
|
78
|
+
legacy_cop_names.detect do |legacy_cop_name|
|
79
|
+
legacy_cop_name.split('/')[1] == cop_name
|
80
|
+
end
|
81
|
+
end
|
41
82
|
end
|
42
83
|
end
|
43
84
|
end
|
@@ -35,7 +35,7 @@ module RuboCop
|
|
35
35
|
# If this offense is within a line range that is already being
|
36
36
|
# realigned by autocorrect, we report the offense without
|
37
37
|
# autocorrecting it. Two rewrites in the same area by the same
|
38
|
-
# cop
|
38
|
+
# cop cannot be handled. The next iteration will find the
|
39
39
|
# offense again and correct it.
|
40
40
|
add_offense(nil, location: expr)
|
41
41
|
else
|
@@ -60,6 +60,10 @@ module RuboCop
|
|
60
60
|
alias conflicting_styles_detected no_acceptable_style!
|
61
61
|
alias unrecognized_style_detected no_acceptable_style!
|
62
62
|
|
63
|
+
def style_configured?
|
64
|
+
cop_config.key?(style_parameter_name)
|
65
|
+
end
|
66
|
+
|
63
67
|
def style
|
64
68
|
@style ||= begin
|
65
69
|
s = cop_config[style_parameter_name].to_sym
|
@@ -20,7 +20,7 @@ module RuboCop
|
|
20
20
|
return if ignored_node?(node)
|
21
21
|
|
22
22
|
end_loc = node.loc.end
|
23
|
-
return
|
23
|
+
return if accept_end_kw_alignment?(end_loc)
|
24
24
|
|
25
25
|
matching = matching_ranges(end_loc, align_ranges)
|
26
26
|
|
@@ -49,6 +49,11 @@ module RuboCop
|
|
49
49
|
add_offense(node, location: end_loc, message: msg)
|
50
50
|
end
|
51
51
|
|
52
|
+
def accept_end_kw_alignment?(end_loc)
|
53
|
+
end_loc.nil? || # Discard modifier forms of if/while/until.
|
54
|
+
processed_source.lines[end_loc.line - 1] !~ /\A[ \t]*end/
|
55
|
+
end
|
56
|
+
|
52
57
|
def style_parameter_name
|
53
58
|
'EnforcedStyleAlignWith'
|
54
59
|
end
|
@@ -39,15 +39,15 @@ module RuboCop
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
comments.each_with_object([]) do |comment, leading_comments|
|
46
|
-
next if comment.loc.line > 3
|
47
|
-
|
48
|
-
leading_comments << comment.text
|
42
|
+
def frozen_string_literal_specified?
|
43
|
+
leading_comment_lines.any? do |line|
|
44
|
+
MagicComment.parse(line).frozen_string_literal_specified?
|
49
45
|
end
|
50
46
|
end
|
47
|
+
|
48
|
+
def leading_comment_lines
|
49
|
+
processed_source.comments.first(3).map(&:text)
|
50
|
+
end
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Common functionality for Style/HashTransformKeys and
|
6
|
+
# Style/HashTransformValues
|
7
|
+
module HashTransformMethod
|
8
|
+
def on_block(node)
|
9
|
+
on_bad_each_with_object(node) do |*match|
|
10
|
+
handle_possible_offense(node, match, 'each_with_object')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_send(node)
|
15
|
+
on_bad_hash_brackets_map(node) do |*match|
|
16
|
+
handle_possible_offense(node, match, 'Hash[_.map {...}]')
|
17
|
+
end
|
18
|
+
on_bad_map_to_h(node) do |*match|
|
19
|
+
handle_possible_offense(node, match, 'map {...}.to_h')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_csend(node)
|
24
|
+
on_bad_map_to_h(node) do |*match|
|
25
|
+
handle_possible_offense(node, match, 'map {...}.to_h')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def autocorrect(node)
|
30
|
+
lambda do |corrector|
|
31
|
+
correction = prepare_correction(node)
|
32
|
+
execute_correction(corrector, node, correction)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# @abstract Implemented with `def_node_matcher`
|
39
|
+
def on_bad_each_with_object(_node)
|
40
|
+
raise NotImplementedError
|
41
|
+
end
|
42
|
+
|
43
|
+
# @abstract Implemented with `def_node_matcher`
|
44
|
+
def on_bad_hash_brackets_map(_node)
|
45
|
+
raise NotImplementedError
|
46
|
+
end
|
47
|
+
|
48
|
+
# @abstract Implemented with `def_node_matcher`
|
49
|
+
def on_bad_map_to_h(_node)
|
50
|
+
raise NotImplementedError
|
51
|
+
end
|
52
|
+
|
53
|
+
def handle_possible_offense(node, match, match_desc)
|
54
|
+
captures = extract_captures(match)
|
55
|
+
|
56
|
+
# If key didn't actually change either, this is most likely a false
|
57
|
+
# positive (receiver isn't a hash).
|
58
|
+
return if captures.noop_transformation?
|
59
|
+
|
60
|
+
# Can't `transform_keys` if key transformation uses value, or
|
61
|
+
# `transform_values` if value transformation uses key.
|
62
|
+
return if captures.transformation_uses_both_args?
|
63
|
+
|
64
|
+
add_offense(
|
65
|
+
node,
|
66
|
+
message: "Prefer `#{new_method_name}` over `#{match_desc}`."
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @abstract
|
71
|
+
#
|
72
|
+
# @return [Captures]
|
73
|
+
def extract_captures(_match)
|
74
|
+
raise NotImplementedError
|
75
|
+
end
|
76
|
+
|
77
|
+
# @abstract
|
78
|
+
#
|
79
|
+
# @return [String]
|
80
|
+
def new_method_name
|
81
|
+
raise NotImplementedError
|
82
|
+
end
|
83
|
+
|
84
|
+
def prepare_correction(node)
|
85
|
+
if (match = on_bad_each_with_object(node))
|
86
|
+
Autocorrection.from_each_with_object(node, match)
|
87
|
+
elsif (match = on_bad_hash_brackets_map(node))
|
88
|
+
Autocorrection.from_hash_brackets_map(node, match)
|
89
|
+
elsif (match = on_bad_map_to_h(node))
|
90
|
+
Autocorrection.from_map_to_h(node, match)
|
91
|
+
else
|
92
|
+
raise 'unreachable'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def execute_correction(corrector, node, correction)
|
97
|
+
correction.strip_prefix_and_suffix(node, corrector)
|
98
|
+
correction.set_new_method_name(new_method_name, corrector)
|
99
|
+
|
100
|
+
captures = extract_captures(correction.match)
|
101
|
+
correction.set_new_arg_name(captures.transformed_argname, corrector)
|
102
|
+
correction.set_new_body_expression(
|
103
|
+
captures.transforming_body_expr,
|
104
|
+
corrector
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Internal helper class to hold match data
|
109
|
+
Captures = Struct.new(
|
110
|
+
:transformed_argname,
|
111
|
+
:transforming_body_expr,
|
112
|
+
:unchanged_body_expr
|
113
|
+
) do
|
114
|
+
def noop_transformation?
|
115
|
+
transforming_body_expr.lvar_type? &&
|
116
|
+
transforming_body_expr.children == [transformed_argname]
|
117
|
+
end
|
118
|
+
|
119
|
+
def transformation_uses_both_args?
|
120
|
+
transforming_body_expr.descendants.include?(unchanged_body_expr)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Internal helper class to hold autocorrect data
|
125
|
+
Autocorrection = Struct.new(:match, :block_node, :leading, :trailing) do # rubocop:disable Metrics/BlockLength
|
126
|
+
def self.from_each_with_object(node, match)
|
127
|
+
new(match, node, 0, 0)
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.from_hash_brackets_map(node, match)
|
131
|
+
new(match, node.children.last, 'Hash['.length, ']'.length)
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.from_map_to_h(node, match)
|
135
|
+
strip_trailing_chars = node.parent&.block_type? ? 0 : '.to_h'.length
|
136
|
+
new(match, node.children.first, 0, strip_trailing_chars)
|
137
|
+
end
|
138
|
+
|
139
|
+
def strip_prefix_and_suffix(node, corrector)
|
140
|
+
expression = node.loc.expression
|
141
|
+
corrector.remove_leading(expression, leading)
|
142
|
+
corrector.remove_trailing(expression, trailing)
|
143
|
+
end
|
144
|
+
|
145
|
+
def set_new_method_name(new_method_name, corrector)
|
146
|
+
range = block_node.send_node.loc.selector
|
147
|
+
if (send_end = block_node.send_node.loc.end)
|
148
|
+
# If there are arguments (only true in the `each_with_object`
|
149
|
+
# case)
|
150
|
+
range = range.begin.join(send_end)
|
151
|
+
end
|
152
|
+
corrector.replace(range, new_method_name)
|
153
|
+
end
|
154
|
+
|
155
|
+
def set_new_arg_name(transformed_argname, corrector)
|
156
|
+
corrector.replace(
|
157
|
+
block_node.arguments.loc.expression,
|
158
|
+
"|#{transformed_argname}|"
|
159
|
+
)
|
160
|
+
end
|
161
|
+
|
162
|
+
def set_new_body_expression(transforming_body_expr, corrector)
|
163
|
+
corrector.replace(
|
164
|
+
block_node.body.loc.expression,
|
165
|
+
transforming_body_expr.loc.expression.source
|
166
|
+
)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|