rubocop 1.22.3 → 1.26.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +2 -3
- data/config/default.yml +110 -19
- 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 +2 -1
- data/lib/rubocop/config_loader_resolver.rb +1 -1
- data/lib/rubocop/cop/badge.rb +7 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
- 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 +145 -0
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +10 -3
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -10
- data/lib/rubocop/cop/generator.rb +7 -11
- data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +46 -0
- data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +47 -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/argument_alignment.rb +36 -9
- data/lib/rubocop/cop/layout/case_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/comment_indentation.rb +31 -2
- data/lib/rubocop/cop/layout/dot_position.rb +4 -0
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +22 -1
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +5 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +7 -2
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +7 -8
- 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_first_arg.rb +4 -0
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +11 -5
- data/lib/rubocop/cop/lint/ambiguous_range.rb +2 -2
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +5 -1
- 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_open_ssl_constant.rb +6 -0
- data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -1
- data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +10 -5
- data/lib/rubocop/cop/lint/inherit_exception.rb +19 -28
- data/lib/rubocop/cop/lint/number_conversion.rb +5 -2
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +10 -6
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +5 -0
- data/lib/rubocop/cop/lint/symbol_conversion.rb +3 -2
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +117 -0
- data/lib/rubocop/cop/lint/useless_times.rb +13 -9
- 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 +1 -1
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +1 -1
- 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 +82 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +17 -6
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -1
- data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -5
- data/lib/rubocop/cop/naming/block_forwarding.rb +121 -0
- data/lib/rubocop/cop/naming/file_name.rb +37 -4
- data/lib/rubocop/cop/naming/method_parameter_name.rb +1 -1
- data/lib/rubocop/cop/security/open.rb +11 -1
- data/lib/rubocop/cop/security/yaml_load.rb +9 -3
- 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/def_with_parentheses.rb +16 -11
- 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 +124 -0
- data/lib/rubocop/cop/style/for.rb +4 -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 +36 -0
- data/lib/rubocop/cop/style/hash_transform_keys.rb +6 -6
- data/lib/rubocop/cop/style/hash_transform_values.rb +6 -6
- data/lib/rubocop/cop/style/if_inside_else.rb +15 -0
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
- data/lib/rubocop/cop/style/lambda_call.rb +12 -20
- data/lib/rubocop/cop/style/map_to_hash.rb +68 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +20 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -1
- data/lib/rubocop/cop/style/method_def_parentheses.rb +17 -13
- data/lib/rubocop/cop/style/nested_file_dirname.rb +66 -0
- 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/optional_boolean_parameter.rb +3 -2
- 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_begin.rb +2 -6
- 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/sample.rb +5 -3
- data/lib/rubocop/cop/style/select_by_regexp.rb +6 -1
- data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +53 -13
- data/lib/rubocop/cop/style/string_concatenation.rb +7 -1
- data/lib/rubocop/cop/style/swap_values.rb +2 -0
- data/lib/rubocop/cop/style/ternary_parentheses.rb +16 -2
- data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +1 -1
- data/lib/rubocop/cop/style/unless_else.rb +4 -0
- data/lib/rubocop/cop/team.rb +1 -1
- data/lib/rubocop/cop/util.rb +9 -1
- data/lib/rubocop/cop/variable_force.rb +1 -5
- data/lib/rubocop/cops_documentation_generator.rb +2 -2
- data/lib/rubocop/formatter/disabled_config_formatter.rb +16 -2
- data/lib/rubocop/formatter/html_formatter.rb +5 -2
- data/lib/rubocop/formatter/json_formatter.rb +4 -1
- data/lib/rubocop/options.rb +14 -3
- data/lib/rubocop/remote_config.rb +2 -4
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/rspec/shared_contexts.rb +4 -0
- data/lib/rubocop/runner.rb +1 -1
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/yaml_duplication_checker.rb +1 -1
- data/lib/rubocop.rb +12 -0
- metadata +22 -7
@@ -6,8 +6,13 @@ module RuboCop
|
|
6
6
|
# This cop checks for parentheses around the arguments in method
|
7
7
|
# definitions. Both instance and class/singleton methods are checked.
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# Regardless of style, parentheses are necessary for:
|
10
|
+
#
|
11
|
+
# 1. Endless methods
|
12
|
+
# 2. Argument lists containing a `forward-arg` (`...`)
|
13
|
+
# 3. Argument lists containing an anonymous block forwarding (`&`)
|
14
|
+
#
|
15
|
+
# Removing the parens would be a syntax error here.
|
11
16
|
#
|
12
17
|
# @example EnforcedStyle: require_parentheses (default)
|
13
18
|
# # The `require_parentheses` style requires method definitions
|
@@ -121,21 +126,13 @@ module RuboCop
|
|
121
126
|
corrector.remove(arg_node.loc.end)
|
122
127
|
end
|
123
128
|
|
124
|
-
def correct_definition(def_node, corrector)
|
125
|
-
arguments_range = def_node.arguments.source_range
|
126
|
-
args_with_space = range_with_surrounding_space(range: arguments_range, side: :left)
|
127
|
-
leading_space = range_between(args_with_space.begin_pos, arguments_range.begin_pos)
|
128
|
-
corrector.replace(leading_space, '(')
|
129
|
-
corrector.insert_after(arguments_range, ')')
|
130
|
-
end
|
131
|
-
|
132
129
|
def forced_parentheses?(node)
|
133
130
|
# Regardless of style, parentheses are necessary for:
|
134
131
|
# 1. Endless methods
|
135
132
|
# 2. Argument lists containing a `forward-arg` (`...`)
|
133
|
+
# 3. Argument lists containing an anonymous block forwarding (`&`)
|
136
134
|
# Removing the parens would be a syntax error here.
|
137
|
-
|
138
|
-
node.endless? || node.arguments.any?(&:forward_arg_type?)
|
135
|
+
node.endless? || node.arguments.any?(&:forward_arg_type?) || anonymous_block_arg?(node)
|
139
136
|
end
|
140
137
|
|
141
138
|
def require_parentheses?(args)
|
@@ -151,7 +148,8 @@ module RuboCop
|
|
151
148
|
location = node.arguments.source_range
|
152
149
|
|
153
150
|
add_offense(location, message: MSG_MISSING) do |corrector|
|
154
|
-
|
151
|
+
add_parentheses(node.arguments, corrector)
|
152
|
+
|
155
153
|
unexpected_style_detected 'require_no_parentheses'
|
156
154
|
end
|
157
155
|
end
|
@@ -163,6 +161,12 @@ module RuboCop
|
|
163
161
|
unexpected_style_detected 'require_parentheses'
|
164
162
|
end
|
165
163
|
end
|
164
|
+
|
165
|
+
def anonymous_block_arg?(node)
|
166
|
+
return false unless (last_argument = node.arguments.last)
|
167
|
+
|
168
|
+
last_argument.blockarg_type? && last_argument.name.nil?
|
169
|
+
end
|
166
170
|
end
|
167
171
|
end
|
168
172
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop checks for nested `File.dirname`.
|
7
|
+
# It replaces nested `File.dirname` with the level argument introduced in Ruby 3.1.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# # bad
|
12
|
+
# File.dirname(File.dirname(path))
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# File.dirname(path, 2)
|
16
|
+
#
|
17
|
+
class NestedFileDirname < Base
|
18
|
+
include RangeHelp
|
19
|
+
extend AutoCorrector
|
20
|
+
extend TargetRubyVersion
|
21
|
+
|
22
|
+
MSG = 'Use `dirname(%<path>s, %<level>s)` instead.'
|
23
|
+
RESTRICT_ON_SEND = %i[dirname].freeze
|
24
|
+
|
25
|
+
minimum_target_ruby_version 3.1
|
26
|
+
|
27
|
+
# @!method file_dirname?(node)
|
28
|
+
def_node_matcher :file_dirname?, <<~PATTERN
|
29
|
+
(send
|
30
|
+
(const {cbase nil?} :File) :dirname ...)
|
31
|
+
PATTERN
|
32
|
+
|
33
|
+
def on_send(node)
|
34
|
+
return if file_dirname?(node.parent) || !file_dirname?(node.first_argument)
|
35
|
+
|
36
|
+
path, level = path_with_dir_level(node, 1)
|
37
|
+
return if level < 2
|
38
|
+
|
39
|
+
message = format(MSG, path: path, level: level)
|
40
|
+
range = offense_range(node)
|
41
|
+
|
42
|
+
add_offense(range, message: message) do |corrector|
|
43
|
+
corrector.replace(range, "dirname(#{path}, #{level})")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def path_with_dir_level(node, level)
|
50
|
+
first_argument = node.first_argument
|
51
|
+
|
52
|
+
if file_dirname?(first_argument)
|
53
|
+
level += 1
|
54
|
+
path_with_dir_level(first_argument, level)
|
55
|
+
else
|
56
|
+
[first_argument.source, level]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def offense_range(node)
|
61
|
+
range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
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
|
@@ -54,8 +54,9 @@ module RuboCop
|
|
54
54
|
private
|
55
55
|
|
56
56
|
def format_message(argument)
|
57
|
-
|
58
|
-
|
57
|
+
replacement = "#{argument.name}: #{argument.default_value.source}"
|
58
|
+
|
59
|
+
format(MSG, original: argument.source, replacement: replacement)
|
59
60
|
end
|
60
61
|
end
|
61
62
|
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
|
@@ -97,7 +97,7 @@ module RuboCop
|
|
97
97
|
offense_range = node.loc.begin
|
98
98
|
|
99
99
|
add_offense(offense_range) do |corrector|
|
100
|
-
if
|
100
|
+
if node.parent&.assignment?
|
101
101
|
replace_begin_with_statement(corrector, offense_range, node)
|
102
102
|
else
|
103
103
|
corrector.remove(offense_range)
|
@@ -170,11 +170,7 @@ module RuboCop
|
|
170
170
|
end
|
171
171
|
|
172
172
|
def valid_begin_assignment?(node)
|
173
|
-
|
174
|
-
end
|
175
|
-
|
176
|
-
def any_ancestor_assignment_node?(node)
|
177
|
-
node.each_ancestor.any?(&:assignment?)
|
173
|
+
node.parent&.assignment? && !node.children.one?
|
178
174
|
end
|
179
175
|
end
|
180
176
|
end
|
@@ -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
|
|
@@ -91,11 +91,12 @@ module RuboCop
|
|
91
91
|
second.int_type? ? second.to_a.first : :unknown
|
92
92
|
end
|
93
93
|
|
94
|
-
|
94
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
95
|
+
def range_size(range_node)
|
95
96
|
vals = range_node.to_a
|
96
|
-
return :unknown unless vals.all?
|
97
|
+
return :unknown unless vals.all? { |val| val.nil? || val.int_type? }
|
97
98
|
|
98
|
-
low, high = vals.map { |val| val.children[0] }
|
99
|
+
low, high = vals.map { |val| val.nil? ? 0 : val.children[0] }
|
99
100
|
return :unknown unless low.zero? && high >= 0
|
100
101
|
|
101
102
|
case range_node.type
|
@@ -105,6 +106,7 @@ module RuboCop
|
|
105
106
|
(low..high).size
|
106
107
|
end
|
107
108
|
end
|
109
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
108
110
|
|
109
111
|
def source_range(shuffle_node, node)
|
110
112
|
Parser::Source::Range.new(shuffle_node.source_range.source_buffer,
|
@@ -69,6 +69,11 @@ module RuboCop
|
|
69
69
|
}
|
70
70
|
PATTERN
|
71
71
|
|
72
|
+
# @!method env_const?(node)
|
73
|
+
def_node_matcher :env_const?, <<~PATTERN
|
74
|
+
(const {nil? cbase} :ENV)
|
75
|
+
PATTERN
|
76
|
+
|
72
77
|
# @!method calls_lvar?(node, name)
|
73
78
|
def_node_matcher :calls_lvar?, <<~PATTERN
|
74
79
|
{
|
@@ -94,7 +99,7 @@ module RuboCop
|
|
94
99
|
def receiver_allowed?(node)
|
95
100
|
return false unless node
|
96
101
|
|
97
|
-
node.hash_type? || creates_hash?(node)
|
102
|
+
node.hash_type? || creates_hash?(node) || env_const?(node)
|
98
103
|
end
|
99
104
|
|
100
105
|
def register_offense(node, block_node, regexp)
|
@@ -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
|