rubocop 0.92.0 → 1.2.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 +21 -7
- data/config/default.yml +169 -59
- data/exe/rubocop +1 -1
- data/lib/rubocop.rb +15 -3
- data/lib/rubocop/cached_data.rb +2 -1
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
- data/lib/rubocop/cli/command/version.rb +1 -1
- data/lib/rubocop/comment_config.rb +1 -1
- data/lib/rubocop/config.rb +4 -0
- data/lib/rubocop/config_loader.rb +19 -2
- data/lib/rubocop/config_loader_resolver.rb +7 -5
- data/lib/rubocop/config_validator.rb +7 -6
- data/lib/rubocop/cop/badge.rb +9 -24
- data/lib/rubocop/cop/base.rb +16 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +23 -3
- data/lib/rubocop/cop/commissioner.rb +36 -22
- data/lib/rubocop/cop/corrector.rb +3 -1
- data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -2
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
- data/lib/rubocop/cop/force.rb +1 -1
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +10 -10
- data/lib/rubocop/cop/layout/class_structure.rb +7 -0
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +6 -9
- data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +7 -7
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
- data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
- data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
- data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -11
- data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +0 -4
- data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -0
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +18 -1
- data/lib/rubocop/cop/lint/boolean_symbol.rb +3 -0
- data/lib/rubocop/cop/lint/debugger.rb +2 -3
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
- data/lib/rubocop/cop/lint/else_layout.rb +29 -3
- data/lib/rubocop/cop/lint/empty_block.rb +59 -0
- data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
- data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +37 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +17 -3
- data/lib/rubocop/cop/lint/loop.rb +0 -4
- data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +78 -0
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +95 -0
- data/lib/rubocop/cop/lint/to_json.rb +1 -1
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +185 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
- data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
- data/lib/rubocop/cop/metrics/block_length.rb +3 -1
- data/lib/rubocop/cop/metrics/class_length.rb +14 -6
- data/lib/rubocop/cop/metrics/parameter_lists.rb +4 -1
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
- data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +12 -2
- data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
- data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
- data/lib/rubocop/cop/naming/variable_number.rb +82 -8
- data/lib/rubocop/cop/offense.rb +18 -5
- data/lib/rubocop/cop/security/open.rb +12 -10
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +6 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +3 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
- data/lib/rubocop/cop/style/case_like_if.rb +18 -6
- data/lib/rubocop/cop/style/class_equality_comparison.rb +64 -0
- data/lib/rubocop/cop/style/collection_compact.rb +85 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +8 -1
- data/lib/rubocop/cop/style/comment_annotation.rb +6 -0
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +67 -0
- data/lib/rubocop/cop/style/double_negation.rb +6 -1
- data/lib/rubocop/cop/style/explicit_block_argument.rb +6 -2
- data/lib/rubocop/cop/style/for.rb +0 -4
- data/lib/rubocop/cop/style/format_string_token.rb +48 -3
- data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -11
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
- data/lib/rubocop/cop/style/method_def_parentheses.rb +0 -4
- data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
- data/lib/rubocop/cop/style/multiple_comparison.rb +54 -7
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +99 -0
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +2 -0
- data/lib/rubocop/cop/style/raise_args.rb +21 -9
- data/lib/rubocop/cop/style/redundant_begin.rb +36 -8
- data/lib/rubocop/cop/style/redundant_condition.rb +5 -1
- data/lib/rubocop/cop/style/redundant_interpolation.rb +6 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +45 -24
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -15
- data/lib/rubocop/cop/style/redundant_self.rb +3 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
- data/lib/rubocop/cop/style/semicolon.rb +3 -0
- data/lib/rubocop/cop/style/string_concatenation.rb +14 -2
- data/lib/rubocop/cop/style/swap_values.rb +108 -0
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
- data/lib/rubocop/cop/team.rb +6 -1
- data/lib/rubocop/cop/util.rb +5 -1
- data/lib/rubocop/cop/variable_force/branch.rb +0 -4
- data/lib/rubocop/ext/regexp_node.rb +35 -11
- data/lib/rubocop/ext/regexp_parser.rb +84 -0
- data/lib/rubocop/formatter/formatter_set.rb +2 -1
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/magic_comment.rb +2 -2
- data/lib/rubocop/options.rb +6 -1
- data/lib/rubocop/result_cache.rb +8 -2
- data/lib/rubocop/rspec/cop_helper.rb +1 -1
- data/lib/rubocop/rspec/shared_contexts.rb +4 -0
- data/lib/rubocop/runner.rb +4 -4
- data/lib/rubocop/target_finder.rb +23 -25
- data/lib/rubocop/version.rb +56 -6
- metadata +22 -8
- data/lib/rubocop/cop/mixin/regexp_literal_help.rb +0 -43
@@ -14,14 +14,25 @@ module RuboCop
|
|
14
14
|
# # good
|
15
15
|
# x = 0
|
16
16
|
#
|
17
|
-
# @example AllowInHeredoc: false
|
17
|
+
# @example AllowInHeredoc: false (default)
|
18
18
|
# # The line in this example contains spaces after the 0.
|
19
19
|
# # bad
|
20
20
|
# code = <<~RUBY
|
21
21
|
# x = 0
|
22
22
|
# RUBY
|
23
23
|
#
|
24
|
-
#
|
24
|
+
# # ok
|
25
|
+
# code = <<~RUBY
|
26
|
+
# x = 0 #{}
|
27
|
+
# RUBY
|
28
|
+
#
|
29
|
+
# # good
|
30
|
+
# trailing_whitespace = ' '
|
31
|
+
# code = <<~RUBY
|
32
|
+
# x = 0#{trailing_whitespace}
|
33
|
+
# RUBY
|
34
|
+
#
|
35
|
+
# @example AllowInHeredoc: true
|
25
36
|
# # The line in this example contains spaces after the 0.
|
26
37
|
# # good
|
27
38
|
# code = <<~RUBY
|
@@ -35,36 +46,49 @@ module RuboCop
|
|
35
46
|
MSG = 'Trailing whitespace detected.'
|
36
47
|
|
37
48
|
def on_new_investigation
|
38
|
-
|
49
|
+
@heredocs = extract_heredocs(processed_source.ast)
|
39
50
|
processed_source.lines.each_with_index do |line, index|
|
40
|
-
lineno = index + 1
|
41
|
-
|
42
51
|
next unless line.end_with?(' ', "\t")
|
43
|
-
next if skip_heredoc? && inside_heredoc?(heredoc_ranges, lineno)
|
44
52
|
|
45
|
-
|
46
|
-
|
53
|
+
process_line(line, index + 1)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def process_line(line, lineno)
|
60
|
+
heredoc = find_heredoc(lineno)
|
61
|
+
return if skip_heredoc? && heredoc
|
62
|
+
|
63
|
+
range = offense_range(lineno, line)
|
64
|
+
add_offense(range) do |corrector|
|
65
|
+
if heredoc
|
66
|
+
corrector.wrap(range, "\#{'", "'}") unless static?(heredoc)
|
67
|
+
else
|
47
68
|
corrector.remove(range)
|
48
69
|
end
|
49
70
|
end
|
50
71
|
end
|
51
72
|
|
52
|
-
|
73
|
+
def static?(heredoc)
|
74
|
+
heredoc.loc.expression.source.end_with? "'"
|
75
|
+
end
|
53
76
|
|
54
77
|
def skip_heredoc?
|
55
78
|
cop_config.fetch('AllowInHeredoc', false)
|
56
79
|
end
|
57
80
|
|
58
|
-
def
|
59
|
-
|
81
|
+
def find_heredoc(line_number)
|
82
|
+
@heredocs.each { |node, r| return node if r.include?(line_number) }
|
83
|
+
nil
|
60
84
|
end
|
61
85
|
|
62
|
-
def
|
86
|
+
def extract_heredocs(ast)
|
63
87
|
return [] unless ast
|
64
88
|
|
65
89
|
ast.each_node(:str, :dstr, :xstr).select(&:heredoc?).map do |node|
|
66
90
|
body = node.location.heredoc_body
|
67
|
-
|
91
|
+
[node, body.first_line...body.last_line]
|
68
92
|
end
|
69
93
|
end
|
70
94
|
|
@@ -47,7 +47,24 @@ module RuboCop
|
|
47
47
|
regexp_node.source_range.begin_pos == diagnostic.location.begin_pos
|
48
48
|
end
|
49
49
|
|
50
|
-
node.parent
|
50
|
+
find_offense_node(node.parent, node)
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_offense_node(node, regexp_receiver)
|
54
|
+
return node unless node.parent
|
55
|
+
|
56
|
+
if node.parent.send_type? || method_chain_to_regexp_receiver?(node, regexp_receiver)
|
57
|
+
node = find_offense_node(node.parent, regexp_receiver)
|
58
|
+
end
|
59
|
+
|
60
|
+
node
|
61
|
+
end
|
62
|
+
|
63
|
+
def method_chain_to_regexp_receiver?(node, regexp_receiver)
|
64
|
+
return false unless (parent = node.parent)
|
65
|
+
return false unless (parent_receiver = parent.receiver)
|
66
|
+
|
67
|
+
parent.parent && parent_receiver.receiver == regexp_receiver
|
51
68
|
end
|
52
69
|
end
|
53
70
|
end
|
@@ -32,6 +32,9 @@ module RuboCop
|
|
32
32
|
def on_sym(node)
|
33
33
|
return unless boolean_symbol?(node)
|
34
34
|
|
35
|
+
parent = node.parent
|
36
|
+
return if parent&.array_type? && parent&.percent_literal?(:symbol)
|
37
|
+
|
35
38
|
add_offense(node, message: format(MSG, boolean: node.value)) do |corrector|
|
36
39
|
autocorrect(corrector, node)
|
37
40
|
end
|
@@ -37,7 +37,7 @@ module RuboCop
|
|
37
37
|
|
38
38
|
RESTRICT_ON_SEND = %i[
|
39
39
|
debugger byebug remote_byebug pry remote_pry pry_remote console rescue
|
40
|
-
save_and_open_page save_and_open_screenshot
|
40
|
+
save_and_open_page save_and_open_screenshot irb
|
41
41
|
].freeze
|
42
42
|
|
43
43
|
def_node_matcher :kernel?, <<~PATTERN
|
@@ -53,8 +53,7 @@ module RuboCop
|
|
53
53
|
{:pry :remote_pry :pry_remote :console} ...)
|
54
54
|
(send (const {nil? (cbase)} :Pry) :rescue ...)
|
55
55
|
(send nil? {:save_and_open_page
|
56
|
-
:save_and_open_screenshot
|
57
|
-
:save_screenshot} ...)}
|
56
|
+
:save_and_open_screenshot} ...)}
|
58
57
|
PATTERN
|
59
58
|
|
60
59
|
def_node_matcher :binding_irb_call?, <<~PATTERN
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for duplicate elements in Regexp character classes.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
# r = /[xyx]/
|
12
|
+
#
|
13
|
+
# # bad
|
14
|
+
# r = /[0-9x0-9]/
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# r = /[xy]/
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# r = /[0-9x]/
|
21
|
+
class DuplicateRegexpCharacterClassElement < Base
|
22
|
+
include RangeHelp
|
23
|
+
extend AutoCorrector
|
24
|
+
|
25
|
+
MSG_REPEATED_ELEMENT = 'Duplicate element inside regexp character class'
|
26
|
+
|
27
|
+
def on_regexp(node)
|
28
|
+
each_repeated_character_class_element_loc(node) do |loc|
|
29
|
+
add_offense(loc, message: MSG_REPEATED_ELEMENT) do |corrector|
|
30
|
+
corrector.remove(loc)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def each_repeated_character_class_element_loc(node)
|
36
|
+
node.parsed_tree&.each_expression do |expr|
|
37
|
+
next if expr.type != :set || expr.token == :intersection
|
38
|
+
|
39
|
+
seen = Set.new
|
40
|
+
|
41
|
+
expr.expressions.each do |child|
|
42
|
+
next if within_interpolation?(node, child)
|
43
|
+
|
44
|
+
child_source = child.to_s
|
45
|
+
|
46
|
+
yield child.expression if seen.include?(child_source)
|
47
|
+
|
48
|
+
seen << child_source
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Since we blank interpolations with a space for every char of the interpolation, we would
|
56
|
+
# mark every space (except the first) as duplicate if we do not skip regexp_parser nodes
|
57
|
+
# that are within an interpolation.
|
58
|
+
def within_interpolation?(node, child)
|
59
|
+
parse_tree_child_loc = child.expression
|
60
|
+
|
61
|
+
interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def interpolation_locs(node)
|
65
|
+
@interpolation_locs ||= {}
|
66
|
+
|
67
|
+
# Cache by loc, not by regexp content, as content can be repeated in multiple patterns
|
68
|
+
key = node.loc
|
69
|
+
|
70
|
+
@interpolation_locs[key] ||= node.children.select(&:begin_type?).map do |interpolation|
|
71
|
+
interpolation.loc.expression
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -3,10 +3,14 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
|
-
# This cop checks for odd else block layout - like
|
7
|
-
# having an expression on the same line as the else keyword,
|
6
|
+
# This cop checks for odd `else` block layout - like
|
7
|
+
# having an expression on the same line as the `else` keyword,
|
8
8
|
# which is usually a mistake.
|
9
9
|
#
|
10
|
+
# Its auto-correction tweaks layout to keep the syntax. So, this auto-correction
|
11
|
+
# is compatible correction for bad case syntax, but if your code makes a mistake
|
12
|
+
# with `elsif` and `else`, you will have to correct it manually.
|
13
|
+
#
|
10
14
|
# @example
|
11
15
|
#
|
12
16
|
# # bad
|
@@ -21,13 +25,25 @@ module RuboCop
|
|
21
25
|
#
|
22
26
|
# # good
|
23
27
|
#
|
28
|
+
# # This code is compatible with the bad case. It will be auto-corrected like this.
|
24
29
|
# if something
|
25
30
|
# # ...
|
26
31
|
# else
|
27
32
|
# do_this
|
28
33
|
# do_that
|
29
34
|
# end
|
35
|
+
#
|
36
|
+
# # This code is incompatible with the bad case.
|
37
|
+
# # If `do_this` is a condition, `elsif` should be used instead of `else`.
|
38
|
+
# if something
|
39
|
+
# # ...
|
40
|
+
# elsif do_this
|
41
|
+
# do_that
|
42
|
+
# end
|
30
43
|
class ElseLayout < Base
|
44
|
+
include RangeHelp
|
45
|
+
extend AutoCorrector
|
46
|
+
|
31
47
|
MSG = 'Odd `else` layout detected. Did you mean to use `elsif`?'
|
32
48
|
|
33
49
|
def on_if(node)
|
@@ -58,7 +74,17 @@ module RuboCop
|
|
58
74
|
return unless first_else
|
59
75
|
return unless first_else.source_range.line == node.loc.else.line
|
60
76
|
|
61
|
-
add_offense(first_else)
|
77
|
+
add_offense(first_else) do |corrector|
|
78
|
+
autocorrect(corrector, node, first_else)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def autocorrect(corrector, node, first_else)
|
83
|
+
corrector.insert_after(node.loc.else, "\n")
|
84
|
+
|
85
|
+
blank_range = range_between(node.loc.else.end_pos, first_else.loc.expression.begin_pos)
|
86
|
+
indentation = indent(node.else_branch.children[1])
|
87
|
+
corrector.replace(blank_range, indentation)
|
62
88
|
end
|
63
89
|
end
|
64
90
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for blocks without a body.
|
7
|
+
# Such empty blocks are typically an oversight or we should provide a comment
|
8
|
+
# be clearer what we're aiming for.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# items.each { |item| }
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# items.each { |item| puts item }
|
16
|
+
#
|
17
|
+
# @example AllowComments: true (default)
|
18
|
+
# # good
|
19
|
+
# items.each do |item|
|
20
|
+
# # TODO: implement later (inner comment)
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# items.each { |item| } # TODO: implement later (inline comment)
|
24
|
+
#
|
25
|
+
# @example AllowComments: false
|
26
|
+
# # bad
|
27
|
+
# items.each do |item|
|
28
|
+
# # TODO: implement later (inner comment)
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# items.each { |item| } # TODO: implement later (inline comment)
|
32
|
+
#
|
33
|
+
class EmptyBlock < Base
|
34
|
+
MSG = 'Empty block detected.'
|
35
|
+
|
36
|
+
def on_block(node)
|
37
|
+
return if node.body
|
38
|
+
return if cop_config['AllowComments'] && allow_comment?(node)
|
39
|
+
|
40
|
+
add_offense(node)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def allow_comment?(node)
|
46
|
+
return false unless processed_source.contains_comment?(node.source_range)
|
47
|
+
|
48
|
+
line_comment = processed_source.comment_at_line(node.source_range.line)
|
49
|
+
!line_comment || !comment_disables_cop?(line_comment.loc.expression.source)
|
50
|
+
end
|
51
|
+
|
52
|
+
def comment_disables_cop?(comment)
|
53
|
+
regexp_pattern = "# rubocop : (disable|todo) ([^,],)* (all|#{cop_name})"
|
54
|
+
Regexp.new(regexp_pattern.gsub(' ', '\s*')).match?(comment)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -3,8 +3,14 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
|
-
# This cop looks for uses of flip-flop operator
|
7
|
-
#
|
6
|
+
# This cop looks for uses of flip-flop operator
|
7
|
+
# based on the Ruby Style Guide.
|
8
|
+
#
|
9
|
+
# Here is the history of flip-flops in Ruby.
|
10
|
+
# flip-flop operator is deprecated in Ruby 2.6.0 and
|
11
|
+
# the deprecation has been reverted by Ruby 2.7.0 and
|
12
|
+
# backported to Ruby 2.6.
|
13
|
+
# See: https://bugs.ruby-lang.org/issues/5400
|
8
14
|
#
|
9
15
|
# @example
|
10
16
|
# # bad
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Prefer using `Hash#compare_by_identity` than using `object_id` for hash keys.
|
7
|
+
#
|
8
|
+
# This cop is marked as unsafe as a hash possibly can contain other keys
|
9
|
+
# besides `object_id`s.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# # bad
|
13
|
+
# hash = {}
|
14
|
+
# hash[foo.object_id] = :bar
|
15
|
+
# hash.key?(baz.object_id)
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# hash = {}.compare_by_identity
|
19
|
+
# hash[foo] = :bar
|
20
|
+
# hash.key?(baz)
|
21
|
+
#
|
22
|
+
class HashCompareByIdentity < Base
|
23
|
+
RESTRICT_ON_SEND = %i[key? has_key? fetch [] []=].freeze
|
24
|
+
|
25
|
+
MSG = 'Use `Hash#compare_by_identity` instead of using `object_id` for keys.'
|
26
|
+
|
27
|
+
def_node_matcher :id_as_hash_key?, <<~PATTERN
|
28
|
+
(send _ {:key? :has_key? :fetch :[] :[]=} (send _ :object_id) ...)
|
29
|
+
PATTERN
|
30
|
+
|
31
|
+
def on_send(node)
|
32
|
+
add_offense(node) if id_as_hash_key?(node)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -31,12 +31,18 @@ module RuboCop
|
|
31
31
|
return if special_keyword?(final_node)
|
32
32
|
return unless prints_as_self?(final_node)
|
33
33
|
|
34
|
+
# %W and %I split the content into words before expansion
|
35
|
+
# treating each interpolation as a word component, so
|
36
|
+
# interpolation should not be removed if the expanded value
|
37
|
+
# contains a space character.
|
38
|
+
expanded_value = autocorrected_value(final_node)
|
39
|
+
return if in_array_percent_literal?(begin_node) &&
|
40
|
+
/\s/.match?(expanded_value)
|
41
|
+
|
34
42
|
add_offense(final_node) do |corrector|
|
35
43
|
return if final_node.dstr_type? # nested, fixed in next iteration
|
36
44
|
|
37
|
-
|
38
|
-
|
39
|
-
corrector.replace(final_node.parent, value)
|
45
|
+
corrector.replace(final_node.parent, expanded_value)
|
40
46
|
end
|
41
47
|
end
|
42
48
|
|
@@ -92,6 +98,14 @@ module RuboCop
|
|
92
98
|
(COMPOSITE.include?(node.type) &&
|
93
99
|
node.children.all? { |child| prints_as_self?(child) })
|
94
100
|
end
|
101
|
+
|
102
|
+
def in_array_percent_literal?(node)
|
103
|
+
parent = node.parent
|
104
|
+
return false unless parent.dstr_type? || parent.dsym_type?
|
105
|
+
|
106
|
+
grandparent = parent.parent
|
107
|
+
grandparent&.array_type? && grandparent&.percent_literal?
|
108
|
+
end
|
95
109
|
end
|
96
110
|
end
|
97
111
|
end
|