rubocop 1.22.1 → 1.24.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +88 -8
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
- data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
- data/lib/rubocop/cli/command/show_docs_url.rb +48 -0
- data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
- data/lib/rubocop/cli.rb +1 -0
- data/lib/rubocop/config_loader_resolver.rb +1 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
- data/lib/rubocop/cop/bundler/gem_comment.rb +3 -3
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/if_then_corrector.rb +55 -0
- data/lib/rubocop/cop/documentation.rb +19 -2
- data/lib/rubocop/cop/gemspec/date_assignment.rb +2 -10
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -10
- data/lib/rubocop/cop/gemspec/require_mfa.rb +146 -0
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +30 -23
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -10
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +60 -0
- data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +46 -0
- data/lib/rubocop/cop/internal_affairs/undefined_config.rb +3 -1
- data/lib/rubocop/cop/internal_affairs.rb +2 -0
- data/lib/rubocop/cop/layout/assignment_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +3 -3
- data/lib/rubocop/cop/layout/comment_indentation.rb +31 -2
- data/lib/rubocop/cop/layout/dot_position.rb +13 -7
- data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +22 -1
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +7 -4
- data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -2
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/space_after_colon.rb +1 -1
- data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
- data/lib/rubocop/cop/layout/space_before_first_arg.rb +4 -0
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +11 -5
- data/lib/rubocop/cop/layout/space_inside_parens.rb +0 -4
- data/lib/rubocop/cop/lint/ambiguous_range.rb +3 -3
- data/lib/rubocop/cop/lint/constant_definition_in_block.rb +1 -1
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +16 -4
- data/lib/rubocop/cop/lint/deprecated_constants.rb +3 -2
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +6 -0
- data/lib/rubocop/cop/lint/else_layout.rb +1 -1
- data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +4 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +5 -2
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +117 -0
- data/lib/rubocop/cop/metrics/block_length.rb +1 -0
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -9
- data/lib/rubocop/cop/metrics/method_length.rb +1 -0
- data/lib/rubocop/cop/metrics/module_length.rb +1 -1
- data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -2
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +5 -0
- data/lib/rubocop/cop/mixin/gemspec_help.rb +30 -0
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +4 -3
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +56 -0
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +3 -3
- data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
- data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -5
- data/lib/rubocop/cop/mixin/trailing_body.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +102 -0
- data/lib/rubocop/cop/naming/file_name.rb +37 -4
- data/lib/rubocop/cop/security/json_load.rb +1 -1
- data/lib/rubocop/cop/security/open.rb +11 -1
- data/lib/rubocop/cop/style/character_literal.rb +8 -1
- data/lib/rubocop/cop/style/collection_compact.rb +31 -13
- data/lib/rubocop/cop/style/combinable_loops.rb +2 -2
- data/lib/rubocop/cop/style/commented_keyword.rb +5 -3
- data/lib/rubocop/cop/style/documentation.rb +1 -1
- data/lib/rubocop/cop/style/empty_case_condition.rb +10 -0
- data/lib/rubocop/cop/style/empty_method.rb +1 -1
- data/lib/rubocop/cop/style/file_read.rb +112 -0
- data/lib/rubocop/cop/style/file_write.rb +98 -0
- data/lib/rubocop/cop/style/format_string_token.rb +2 -1
- data/lib/rubocop/cop/style/hash_conversion.rb +2 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +22 -0
- data/lib/rubocop/cop/style/if_inside_else.rb +15 -0
- data/lib/rubocop/cop/style/line_end_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +68 -0
- data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
- data/lib/rubocop/cop/style/multiline_when_then.rb +1 -1
- data/lib/rubocop/cop/style/numeric_literals.rb +10 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +18 -39
- data/lib/rubocop/cop/style/open_struct_use.rb +69 -0
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +12 -2
- data/lib/rubocop/cop/style/quoted_symbols.rb +11 -1
- data/lib/rubocop/cop/style/redundant_interpolation.rb +17 -3
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +5 -1
- data/lib/rubocop/cop/style/redundant_self.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -5
- data/lib/rubocop/cop/style/select_by_regexp.rb +9 -3
- data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
- data/lib/rubocop/cop/team.rb +1 -1
- data/lib/rubocop/cop/util.rb +11 -1
- data/lib/rubocop/formatter/html_formatter.rb +5 -2
- data/lib/rubocop/formatter/json_formatter.rb +4 -1
- data/lib/rubocop/options.rb +6 -1
- data/lib/rubocop/remote_config.rb +2 -4
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/rspec/parallel_formatter.rb +90 -0
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/yaml_duplication_checker.rb +1 -1
- data/lib/rubocop.rb +11 -0
- metadata +24 -9
@@ -19,6 +19,10 @@ module RuboCop
|
|
19
19
|
# * ruby19_no_mixed_keys - forces use of ruby 1.9 syntax and forbids mixed
|
20
20
|
# syntax hashes
|
21
21
|
#
|
22
|
+
# This cop has `EnforcedShorthandSyntax` option.
|
23
|
+
# It can enforce either the use of the explicit hash value syntax or
|
24
|
+
# the use of Ruby 3.1's hash value shorthand syntax.
|
25
|
+
#
|
22
26
|
# @example EnforcedStyle: ruby19 (default)
|
23
27
|
# # bad
|
24
28
|
# {:a => 2}
|
@@ -54,8 +58,26 @@ module RuboCop
|
|
54
58
|
# # good
|
55
59
|
# {a: 1, b: 2}
|
56
60
|
# {:c => 3, 'd' => 4}
|
61
|
+
#
|
62
|
+
# @example EnforcedShorthandSyntax: always (default)
|
63
|
+
#
|
64
|
+
# # bad
|
65
|
+
# {foo: foo, bar: bar}
|
66
|
+
#
|
67
|
+
# # good
|
68
|
+
# {foo:, bar:}
|
69
|
+
#
|
70
|
+
# @example EnforcedShorthandSyntax: never
|
71
|
+
#
|
72
|
+
# # bad
|
73
|
+
# {foo:, bar:}
|
74
|
+
#
|
75
|
+
# # good
|
76
|
+
# {foo: foo, bar: bar}
|
77
|
+
#
|
57
78
|
class HashSyntax < Base
|
58
79
|
include ConfigurableEnforcedStyle
|
80
|
+
include HashShorthandSyntax
|
59
81
|
include RangeHelp
|
60
82
|
extend AutoCorrector
|
61
83
|
|
@@ -80,11 +80,19 @@ module RuboCop
|
|
80
80
|
private
|
81
81
|
|
82
82
|
def autocorrect(corrector, node)
|
83
|
+
if then?(node)
|
84
|
+
# If the nested `if` is a then node, correct it first,
|
85
|
+
# then the next pass will use `correct_to_elsif_from_if_inside_else_form`
|
86
|
+
IfThenCorrector.new(node, indentation: 0).call(corrector)
|
87
|
+
return
|
88
|
+
end
|
89
|
+
|
83
90
|
if node.modifier_form?
|
84
91
|
correct_to_elsif_from_modifier_form(corrector, node)
|
85
92
|
else
|
86
93
|
correct_to_elsif_from_if_inside_else_form(corrector, node, node.condition)
|
87
94
|
end
|
95
|
+
|
88
96
|
corrector.remove(range_by_whole_lines(find_end_range(node), include_final_newline: true))
|
89
97
|
return unless (if_branch = node.if_branch)
|
90
98
|
|
@@ -102,15 +110,22 @@ module RuboCop
|
|
102
110
|
|
103
111
|
def correct_to_elsif_from_if_inside_else_form(corrector, node, condition)
|
104
112
|
corrector.replace(node.parent.loc.else, "elsif #{condition.source}")
|
113
|
+
|
105
114
|
if_condition_range = if_condition_range(node, condition)
|
115
|
+
|
106
116
|
if (if_branch = node.if_branch)
|
107
117
|
corrector.replace(if_condition_range, if_branch.source)
|
108
118
|
else
|
109
119
|
corrector.remove(range_by_whole_lines(if_condition_range, include_final_newline: true))
|
110
120
|
end
|
121
|
+
|
111
122
|
corrector.remove(condition)
|
112
123
|
end
|
113
124
|
|
125
|
+
def then?(node)
|
126
|
+
node.loc.begin&.source == 'then'
|
127
|
+
end
|
128
|
+
|
114
129
|
def find_end_range(node)
|
115
130
|
end_range = node.loc.end
|
116
131
|
return end_range if end_range
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop looks for uses of `map.to_h` or `collect.to_h` that could be
|
7
|
+
# written with just `to_h` in Ruby >= 2.6.
|
8
|
+
#
|
9
|
+
# NOTE: `Style/HashTransformKeys` and `Style/HashTransformValues` will
|
10
|
+
# also change this pattern if only hash keys or hash values are being
|
11
|
+
# transformed.
|
12
|
+
#
|
13
|
+
# @safety
|
14
|
+
# This cop is unsafe, as it can produce false positives if the receiver
|
15
|
+
# is not an `Enumerable`.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# # bad
|
19
|
+
# something.map { |v| [v, v * 2] }.to_h
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# something.to_h { |v| [v, v * 2] }
|
23
|
+
#
|
24
|
+
# # bad
|
25
|
+
# {foo: bar}.collect { |k, v| [k.to_s, v.do_something] }.to_h
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# {foo: bar}.to_h { |k, v| [k.to_s, v.do_something] }
|
29
|
+
#
|
30
|
+
class MapToHash < Base
|
31
|
+
extend AutoCorrector
|
32
|
+
extend TargetRubyVersion
|
33
|
+
include RangeHelp
|
34
|
+
|
35
|
+
minimum_target_ruby_version 2.6
|
36
|
+
|
37
|
+
MSG = 'Pass a block to `to_h` instead of calling `%<method>s.to_h`.'
|
38
|
+
RESTRICT_ON_SEND = %i[to_h].freeze
|
39
|
+
|
40
|
+
# @!method map_to_h?(node)
|
41
|
+
def_node_matcher :map_to_h?, <<~PATTERN
|
42
|
+
$(send (block $(send _ {:map :collect}) ...) :to_h)
|
43
|
+
PATTERN
|
44
|
+
|
45
|
+
def on_send(node)
|
46
|
+
return unless (to_h_node, map_node = map_to_h?(node))
|
47
|
+
|
48
|
+
message = format(MSG, method: map_node.loc.selector.source)
|
49
|
+
add_offense(map_node.loc.selector, message: message) do |corrector|
|
50
|
+
# If the `to_h` call already has a block, do not auto-correct.
|
51
|
+
next if to_h_node.block_node
|
52
|
+
|
53
|
+
autocorrect(corrector, to_h_node, map_node)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def autocorrect(corrector, to_h, map)
|
60
|
+
removal_range = range_between(to_h.loc.dot.begin_pos, to_h.loc.selector.end_pos)
|
61
|
+
|
62
|
+
corrector.remove(removal_range)
|
63
|
+
corrector.replace(map.loc.selector, 'to_h')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -54,7 +54,7 @@ module RuboCop
|
|
54
54
|
return true if in_pattern_node.pattern.first_line != in_pattern_node.pattern.last_line
|
55
55
|
return false unless in_pattern_node.body
|
56
56
|
|
57
|
-
in_pattern_node
|
57
|
+
same_line?(in_pattern_node, in_pattern_node.body)
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
@@ -27,6 +27,11 @@ module RuboCop
|
|
27
27
|
# # bad
|
28
28
|
# 10_000_00 # typical representation of $10,000 in cents
|
29
29
|
#
|
30
|
+
# @example AllowedNumbers: [3000]
|
31
|
+
#
|
32
|
+
# # good
|
33
|
+
# 3000 # You can specify allowed numbers. (e.g. port number)
|
34
|
+
#
|
30
35
|
class NumericLiterals < Base
|
31
36
|
include IntegerNode
|
32
37
|
extend AutoCorrector
|
@@ -51,9 +56,9 @@ module RuboCop
|
|
51
56
|
|
52
57
|
def check(node)
|
53
58
|
int = integer_part(node)
|
54
|
-
|
55
59
|
# TODO: handle non-decimal literals as well
|
56
60
|
return if int.start_with?('0')
|
61
|
+
return if allowed_numbers.include?(int)
|
57
62
|
return unless int.size >= min_digits
|
58
63
|
|
59
64
|
case int
|
@@ -99,6 +104,10 @@ module RuboCop
|
|
99
104
|
def min_digits
|
100
105
|
cop_config['MinDigits']
|
101
106
|
end
|
107
|
+
|
108
|
+
def allowed_numbers
|
109
|
+
cop_config.fetch('AllowedNumbers', []).map(&:to_s)
|
110
|
+
end
|
102
111
|
end
|
103
112
|
end
|
104
113
|
end
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
45
45
|
|
46
46
|
message = message(node)
|
47
47
|
add_offense(node, message: message) do |corrector|
|
48
|
-
corrector
|
48
|
+
autocorrect(corrector, node)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -55,55 +55,30 @@ module RuboCop
|
|
55
55
|
format(MSG, keyword: node.keyword)
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
58
|
+
def autocorrect(corrector, node)
|
59
59
|
if always_multiline? || cannot_replace_to_ternary?(node)
|
60
|
-
|
60
|
+
IfThenCorrector.new(node, indentation: indentation_width).call(corrector)
|
61
61
|
else
|
62
|
-
|
63
|
-
return replaced_node unless node.parent
|
64
|
-
return "(#{replaced_node})" if %i[and or].include?(node.parent.type)
|
65
|
-
return "(#{replaced_node})" if node.parent.send_type? && node.parent.operator_method?
|
66
|
-
|
67
|
-
replaced_node
|
62
|
+
corrector.replace(node, ternary_correction(node))
|
68
63
|
end
|
69
64
|
end
|
70
65
|
|
71
|
-
def
|
72
|
-
|
73
|
-
end
|
66
|
+
def ternary_correction(node)
|
67
|
+
replaced_node = ternary_replacement(node)
|
74
68
|
|
75
|
-
|
76
|
-
node.
|
77
|
-
|
69
|
+
return replaced_node unless node.parent
|
70
|
+
return "(#{replaced_node})" if %i[and or].include?(node.parent.type)
|
71
|
+
return "(#{replaced_node})" if node.parent.send_type? && node.parent.operator_method?
|
78
72
|
|
79
|
-
|
80
|
-
indentation = ' ' * node.source_range.column if indentation.nil?
|
81
|
-
if_branch_source = node.if_branch&.source || 'nil'
|
82
|
-
elsif_indentation = indentation if node.respond_to?(:elsif?) && node.elsif?
|
83
|
-
if_branch = <<~RUBY
|
84
|
-
#{elsif_indentation}#{node.keyword} #{node.condition.source}
|
85
|
-
#{indentation}#{branch_body_indentation}#{if_branch_source}
|
86
|
-
RUBY
|
87
|
-
else_branch = else_branch_to_multiline(node.else_branch, indentation)
|
88
|
-
if_branch + else_branch
|
73
|
+
replaced_node
|
89
74
|
end
|
90
75
|
|
91
|
-
def
|
92
|
-
|
93
|
-
'end'
|
94
|
-
elsif else_branch.if_type? && else_branch.elsif?
|
95
|
-
multiline_replacement(else_branch, indentation)
|
96
|
-
else
|
97
|
-
<<~RUBY.chomp
|
98
|
-
#{indentation}else
|
99
|
-
#{indentation}#{branch_body_indentation}#{else_branch.source}
|
100
|
-
#{indentation}end
|
101
|
-
RUBY
|
102
|
-
end
|
76
|
+
def always_multiline?
|
77
|
+
@config.for_cop('Style/OneLineConditional')['AlwaysCorrectToMultiline']
|
103
78
|
end
|
104
79
|
|
105
|
-
def
|
106
|
-
|
80
|
+
def cannot_replace_to_ternary?(node)
|
81
|
+
node.elsif_conditional?
|
107
82
|
end
|
108
83
|
|
109
84
|
def ternary_replacement(node)
|
@@ -141,6 +116,10 @@ module RuboCop
|
|
141
116
|
|
142
117
|
node.respond_to?(:arguments?) && node.arguments? && !node.parenthesized_call?
|
143
118
|
end
|
119
|
+
|
120
|
+
def indentation_width
|
121
|
+
@config.for_cop('Layout/IndentationWidth')['Width']
|
122
|
+
end
|
144
123
|
end
|
145
124
|
end
|
146
125
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop flags uses of OpenStruct, as it is now officially discouraged
|
7
|
+
# to be used for performance, version compatibility, and potential security issues.
|
8
|
+
#
|
9
|
+
# @safety
|
10
|
+
#
|
11
|
+
# Note that this cop may flag false positives; for instance, the following legal
|
12
|
+
# use of a hand-rolled `OpenStruct` type would be considered an offense:
|
13
|
+
#
|
14
|
+
# ```
|
15
|
+
# module MyNamespace
|
16
|
+
# class OpenStruct # not the OpenStruct we're looking for
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# def new_struct
|
20
|
+
# OpenStruct.new # resolves to MyNamespace::OpenStruct
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
# ```
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
#
|
27
|
+
# # bad
|
28
|
+
# point = OpenStruct.new(x: 0, y: 1)
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# Point = Struct.new(:x, :y)
|
32
|
+
# point = Point.new(0, 1)
|
33
|
+
#
|
34
|
+
# # also good
|
35
|
+
# point = { x: 0, y: 1 }
|
36
|
+
#
|
37
|
+
# # bad
|
38
|
+
# test_double = OpenStruct.new(a: 'b')
|
39
|
+
#
|
40
|
+
# # good (assumes test using rspec-mocks)
|
41
|
+
# test_double = double
|
42
|
+
# allow(test_double).to receive(:a).and_return('b')
|
43
|
+
#
|
44
|
+
class OpenStructUse < Base
|
45
|
+
MSG = 'Avoid using `OpenStruct`; use `Struct`, `Hash`, a class or test doubles instead.'
|
46
|
+
|
47
|
+
# @!method uses_open_struct?(node)
|
48
|
+
def_node_matcher :uses_open_struct?, <<-PATTERN
|
49
|
+
(const {nil? (cbase)} :OpenStruct)
|
50
|
+
PATTERN
|
51
|
+
|
52
|
+
def on_const(node)
|
53
|
+
return unless uses_open_struct?(node)
|
54
|
+
return if custom_class_or_module_definition?(node)
|
55
|
+
|
56
|
+
add_offense(node)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def custom_class_or_module_definition?(node)
|
62
|
+
parent = node.parent
|
63
|
+
|
64
|
+
(parent.class_type? || parent.module_type?) && node.left_siblings.empty?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -56,6 +56,7 @@ module RuboCop
|
|
56
56
|
class ParenthesesAroundCondition < Base
|
57
57
|
include SafeAssignment
|
58
58
|
include Parentheses
|
59
|
+
include RangeHelp
|
59
60
|
extend AutoCorrector
|
60
61
|
|
61
62
|
def on_if(node)
|
@@ -73,13 +74,14 @@ module RuboCop
|
|
73
74
|
|
74
75
|
# @!method control_op_condition(node)
|
75
76
|
def_node_matcher :control_op_condition, <<~PATTERN
|
76
|
-
(begin $_
|
77
|
+
(begin $_ $...)
|
77
78
|
PATTERN
|
78
79
|
|
79
80
|
def process_control_op(node)
|
80
81
|
cond = node.condition
|
81
82
|
|
82
|
-
control_op_condition(cond) do |first_child|
|
83
|
+
control_op_condition(cond) do |first_child, rest_children|
|
84
|
+
return if semicolon_separated_expressions?(first_child, rest_children)
|
83
85
|
return if modifier_op?(first_child)
|
84
86
|
return if parens_allowed?(cond)
|
85
87
|
|
@@ -90,6 +92,14 @@ module RuboCop
|
|
90
92
|
end
|
91
93
|
end
|
92
94
|
|
95
|
+
def semicolon_separated_expressions?(first_exp, rest_exps)
|
96
|
+
return false unless (second_exp = rest_exps.first)
|
97
|
+
|
98
|
+
range = range_between(first_exp.source_range.end_pos, second_exp.source_range.begin_pos)
|
99
|
+
|
100
|
+
range.source.include?(';')
|
101
|
+
end
|
102
|
+
|
93
103
|
def modifier_op?(node)
|
94
104
|
return false if node.if_type? && node.ternary?
|
95
105
|
return true if node.rescue_type?
|
@@ -46,7 +46,7 @@ module RuboCop
|
|
46
46
|
|
47
47
|
message = style == :single_quotes ? MSG_SINGLE : MSG_DOUBLE
|
48
48
|
|
49
|
-
if wrong_quotes?(node)
|
49
|
+
if wrong_quotes?(node) || invalid_double_quotes?(node.source)
|
50
50
|
add_offense(node, message: message) do |corrector|
|
51
51
|
opposite_style_detected
|
52
52
|
autocorrect(corrector, node)
|
@@ -58,6 +58,16 @@ module RuboCop
|
|
58
58
|
|
59
59
|
private
|
60
60
|
|
61
|
+
def invalid_double_quotes?(source)
|
62
|
+
return false unless style == :double_quotes
|
63
|
+
|
64
|
+
# The string needs single quotes if:
|
65
|
+
# 1. It contains a double quote
|
66
|
+
# 2. It contains text that would become an escape sequence with double quotes
|
67
|
+
# 3. It contains text that would become an interpolation with double quotes
|
68
|
+
!/" | (?<!\\)\\[aAbcdefkMnprsStuUxzZ0-7] | \#[@{$]/x.match?(source)
|
69
|
+
end
|
70
|
+
|
61
71
|
def autocorrect(corrector, node)
|
62
72
|
str = if hash_colon_key?(node)
|
63
73
|
# strip quotes
|
@@ -82,10 +82,20 @@ module RuboCop
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def autocorrect_single_variable_interpolation(corrector, embedded_node, node)
|
85
|
-
|
86
|
-
replacement = "#{variable_loc.expression.source}.to_s"
|
85
|
+
embedded_var = embedded_node.children.first
|
87
86
|
|
88
|
-
|
87
|
+
source = if require_parentheses?(embedded_var)
|
88
|
+
receiver = range_between(
|
89
|
+
embedded_var.loc.expression.begin_pos, embedded_var.loc.selector.end_pos
|
90
|
+
)
|
91
|
+
arguments = embedded_var.arguments.map(&:source).join(', ')
|
92
|
+
|
93
|
+
"#{receiver.source}(#{arguments})"
|
94
|
+
else
|
95
|
+
embedded_var.source
|
96
|
+
end
|
97
|
+
|
98
|
+
corrector.replace(node, "#{source}.to_s")
|
89
99
|
end
|
90
100
|
|
91
101
|
def autocorrect_other(corrector, embedded_node, node)
|
@@ -97,6 +107,10 @@ module RuboCop
|
|
97
107
|
corrector.replace(embedded_loc.begin, '(')
|
98
108
|
corrector.replace(embedded_loc.end, ').to_s')
|
99
109
|
end
|
110
|
+
|
111
|
+
def require_parentheses?(node)
|
112
|
+
node.send_type? && !node.arguments.count.zero? && !node.parenthesized_call?
|
113
|
+
end
|
100
114
|
end
|
101
115
|
end
|
102
116
|
end
|
@@ -80,7 +80,11 @@ module RuboCop
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def without_character_class(loc)
|
83
|
-
loc.source[1..-2]
|
83
|
+
without_character_class = loc.source[1..-2]
|
84
|
+
|
85
|
+
# Adds `\` to prevent auto-correction that changes to an interpolated string when `[#]`.
|
86
|
+
# e.g. From `/[#]{0}/` to `/#{0}/`
|
87
|
+
loc.source == '[#]' ? "\\#{without_character_class}" : without_character_class
|
84
88
|
end
|
85
89
|
|
86
90
|
def whitespace_in_free_space_mode?(node, elem)
|
@@ -218,11 +218,7 @@ module RuboCop
|
|
218
218
|
def find_matching_receiver_invocation(method_chain, checked_variable)
|
219
219
|
return nil unless method_chain
|
220
220
|
|
221
|
-
receiver =
|
222
|
-
method_chain.send_node.receiver
|
223
|
-
else
|
224
|
-
method_chain.receiver
|
225
|
-
end
|
221
|
+
receiver = method_chain.receiver
|
226
222
|
|
227
223
|
return receiver if receiver == checked_variable
|
228
224
|
|
@@ -83,8 +83,9 @@ module RuboCop
|
|
83
83
|
return if block_node.body.begin_type?
|
84
84
|
return if receiver_allowed?(block_node.receiver)
|
85
85
|
return unless (regexp_method_send_node = extract_send_node(block_node))
|
86
|
+
return if match_predicate_without_receiver?(regexp_method_send_node)
|
86
87
|
|
87
|
-
regexp = find_regexp(regexp_method_send_node)
|
88
|
+
regexp = find_regexp(regexp_method_send_node, block_node)
|
88
89
|
register_offense(node, block_node, regexp)
|
89
90
|
end
|
90
91
|
|
@@ -118,15 +119,20 @@ module RuboCop
|
|
118
119
|
regexp_method_send_node
|
119
120
|
end
|
120
121
|
|
121
|
-
def find_regexp(node)
|
122
|
+
def find_regexp(node, block)
|
122
123
|
return node.child_nodes.first if node.match_with_lvasgn_type?
|
123
124
|
|
124
|
-
if node.receiver.lvar_type?
|
125
|
+
if node.receiver.lvar_type? &&
|
126
|
+
(block.numblock_type? || node.receiver.source == block.arguments.first.source)
|
125
127
|
node.first_argument
|
126
128
|
elsif node.first_argument.lvar_type?
|
127
129
|
node.receiver
|
128
130
|
end
|
129
131
|
end
|
132
|
+
|
133
|
+
def match_predicate_without_receiver?(node)
|
134
|
+
node.send_type? && node.method?(:match?) && node.receiver.nil?
|
135
|
+
end
|
130
136
|
end
|
131
137
|
end
|
132
138
|
end
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
39
39
|
return unless eligible_method?(node)
|
40
40
|
return unless eligible_arguments?(node)
|
41
41
|
|
42
|
-
method_name = node.
|
42
|
+
method_name = node.method_name
|
43
43
|
return if args_match?(method_name, node.arguments)
|
44
44
|
|
45
45
|
preferred_block_arguments = build_preferred_arguments_map(node, target_args(method_name))
|
@@ -81,7 +81,7 @@ module RuboCop
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def eligible_method?(node)
|
84
|
-
node.
|
84
|
+
node.receiver && method_names.include?(node.method_name)
|
85
85
|
end
|
86
86
|
|
87
87
|
def methods
|
@@ -82,7 +82,9 @@ module RuboCop
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def autocorrect(corrector, node, if_branch)
|
85
|
-
|
85
|
+
if node.condition.or_type? || node.condition.assignment?
|
86
|
+
corrector.wrap(node.condition, '(', ')')
|
87
|
+
end
|
86
88
|
|
87
89
|
correct_from_unless_to_if(corrector, node) if node.unless?
|
88
90
|
|
data/lib/rubocop/cop/team.rb
CHANGED
data/lib/rubocop/cop/util.rb
CHANGED
@@ -126,8 +126,18 @@ module RuboCop
|
|
126
126
|
StringInterpreter.interpret(string)
|
127
127
|
end
|
128
128
|
|
129
|
+
def line(node_or_range)
|
130
|
+
if node_or_range.respond_to?(:line)
|
131
|
+
node_or_range.line
|
132
|
+
elsif node_or_range.respond_to?(:loc)
|
133
|
+
node_or_range.loc.line
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
129
137
|
def same_line?(node1, node2)
|
130
|
-
|
138
|
+
line1 = line(node1)
|
139
|
+
line2 = line(node2)
|
140
|
+
line1 && line2 && line1 == line2
|
131
141
|
end
|
132
142
|
|
133
143
|
def indent(node, offset: 0)
|
@@ -23,12 +23,15 @@ module RuboCop
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
Summary = Struct.new(:offense_count, :inspected_files, :target_files, keyword_init: true)
|
27
|
+
FileOffenses = Struct.new(:path, :offenses, keyword_init: true)
|
28
|
+
|
26
29
|
attr_reader :files, :summary
|
27
30
|
|
28
31
|
def initialize(output, options = {})
|
29
32
|
super
|
30
33
|
@files = []
|
31
|
-
@summary =
|
34
|
+
@summary = Summary.new(offense_count: 0)
|
32
35
|
end
|
33
36
|
|
34
37
|
def started(target_files)
|
@@ -36,7 +39,7 @@ module RuboCop
|
|
36
39
|
end
|
37
40
|
|
38
41
|
def file_finished(file, offenses)
|
39
|
-
files <<
|
42
|
+
files << FileOffenses.new(path: file, offenses: offenses)
|
40
43
|
summary.offense_count += offenses.count
|
41
44
|
end
|
42
45
|
|
@@ -59,12 +59,15 @@ module RuboCop
|
|
59
59
|
end
|
60
60
|
|
61
61
|
# TODO: Consider better solution for Offense#real_column.
|
62
|
+
# The minimum value of `start_column: real_column` is 1.
|
63
|
+
# So, the minimum value of `last_column` should be 1.
|
64
|
+
# And non-zero value of `last_column` should be used as is.
|
62
65
|
def hash_for_location(offense)
|
63
66
|
{
|
64
67
|
start_line: offense.line,
|
65
68
|
start_column: offense.real_column,
|
66
69
|
last_line: offense.last_line,
|
67
|
-
last_column: offense.last_column,
|
70
|
+
last_column: offense.last_column.zero? ? 1 : offense.last_column,
|
68
71
|
length: offense.location.length,
|
69
72
|
# `line` and `column` exist for compatibility.
|
70
73
|
# Use `start_line` and `start_column` instead.
|