rubocop 0.91.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +100 -54
- data/lib/rubocop.rb +10 -6
- data/lib/rubocop/cached_data.rb +2 -1
- data/lib/rubocop/cli/command/version.rb +1 -1
- data/lib/rubocop/comment_config.rb +9 -5
- 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_regeneration.rb +33 -0
- 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/commissioner.rb +34 -20
- 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/gemspec/required_ruby_version.rb +10 -10
- data/lib/rubocop/cop/layout/array_alignment.rb +1 -0
- data/lib/rubocop/cop/layout/case_indentation.rb +4 -7
- data/lib/rubocop/cop/layout/class_structure.rb +8 -1
- data/lib/rubocop/cop/layout/dot_position.rb +6 -9
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +2 -2
- data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +4 -13
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +13 -8
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +2 -2
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +1 -2
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +10 -1
- data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +4 -13
- data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -7
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +0 -4
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +4 -18
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -2
- data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +2 -2
- 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/constant_definition_in_block.rb +23 -3
- data/lib/rubocop/cop/lint/duplicate_rescue_exception.rb +2 -4
- data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +37 -0
- data/lib/rubocop/cop/lint/identity_comparison.rb +5 -3
- data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +2 -5
- data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +22 -12
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +14 -4
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +78 -0
- data/lib/rubocop/cop/lint/rescue_type.rb +0 -1
- data/lib/rubocop/cop/lint/shadowed_exception.rb +6 -6
- data/lib/rubocop/cop/lint/to_json.rb +1 -1
- data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -5
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +3 -9
- data/lib/rubocop/cop/lint/useless_times.rb +11 -2
- 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/metrics/utils/abc_size_calculator.rb +25 -16
- 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/rescue_node.rb +1 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -3
- data/lib/rubocop/cop/mixin/visibility_help.rb +4 -16
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +15 -2
- 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/array_coercion.rb +4 -0
- data/lib/rubocop/cop/style/case_like_if.rb +20 -4
- data/lib/rubocop/cop/style/class_equality_comparison.rb +64 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +13 -11
- data/lib/rubocop/cop/style/comment_annotation.rb +6 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +7 -8
- data/lib/rubocop/cop/style/date_time.rb +12 -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_as_last_array_item.rb +15 -6
- data/lib/rubocop/cop/style/if_unless_modifier.rb +0 -4
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -6
- 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_usage.rb +7 -27
- data/lib/rubocop/cop/style/multiline_block_chain.rb +2 -2
- data/lib/rubocop/cop/style/multiline_when_then.rb +1 -0
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +2 -0
- data/lib/rubocop/cop/style/one_line_conditional.rb +3 -1
- data/lib/rubocop/cop/style/optional_boolean_parameter.rb +12 -1
- data/lib/rubocop/cop/style/raise_args.rb +0 -3
- data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
- data/lib/rubocop/cop/style/redundant_assignment.rb +1 -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_conditional.rb +4 -5
- data/lib/rubocop/cop/style/redundant_interpolation.rb +6 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +6 -3
- data/lib/rubocop/cop/style/redundant_percent_q.rb +9 -11
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +39 -24
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -15
- data/lib/rubocop/cop/style/redundant_return.rb +17 -17
- data/lib/rubocop/cop/style/redundant_self.rb +10 -9
- data/lib/rubocop/cop/style/redundant_sort.rb +13 -24
- data/lib/rubocop/cop/style/redundant_sort_by.rb +5 -9
- data/lib/rubocop/cop/style/rescue_standard_error.rb +20 -16
- data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
- data/lib/rubocop/cop/style/string_concatenation.rb +14 -2
- data/lib/rubocop/cop/style/ternary_parentheses.rb +2 -3
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +4 -3
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
- data/lib/rubocop/cop/util.rb +0 -1
- data/lib/rubocop/cop/variable_force/branch.rb +0 -4
- data/lib/rubocop/directive_comment.rb +32 -0
- data/lib/rubocop/ext/regexp_node.rb +20 -4
- data/lib/rubocop/formatter/disabled_config_formatter.rb +12 -5
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/options.rb +22 -17
- data/lib/rubocop/result_cache.rb +8 -2
- data/lib/rubocop/rspec/cop_helper.rb +1 -1
- data/lib/rubocop/rspec/expect_offense.rb +5 -5
- data/lib/rubocop/runner.rb +9 -5
- data/lib/rubocop/target_finder.rb +27 -26
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +61 -6
- metadata +14 -17
- data/lib/rubocop/cop/mixin/regexp_literal_help.rb +0 -43
- data/lib/rubocop/cop/tokens_util.rb +0 -84
@@ -66,7 +66,7 @@ module RuboCop
|
|
66
66
|
def on_send(node)
|
67
67
|
return if node.multiline?
|
68
68
|
|
69
|
-
tokens =
|
69
|
+
tokens = processed_source.tokens_within(node)
|
70
70
|
left_token = left_ref_bracket(node, tokens)
|
71
71
|
return unless left_token
|
72
72
|
|
@@ -98,7 +98,7 @@ module RuboCop
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def reference_brackets(node)
|
101
|
-
tokens =
|
101
|
+
tokens = processed_source.tokens_within(node)
|
102
102
|
left = left_ref_bracket(node, tokens)
|
103
103
|
[left, closing_bracket(tokens, left)]
|
104
104
|
end
|
@@ -54,8 +54,8 @@ module RuboCop
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def delimiters(begin_node)
|
57
|
-
left = processed_source.
|
58
|
-
right = processed_source.
|
57
|
+
left = processed_source.first_token_of(begin_node)
|
58
|
+
right = processed_source.last_token_of(begin_node)
|
59
59
|
[left, right]
|
60
60
|
end
|
61
61
|
end
|
@@ -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.insert_after(range, '#{}') unless static?(heredoc) # rubocop:disable Lint/InterpolationCheck
|
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
|
@@ -6,8 +6,11 @@ module RuboCop
|
|
6
6
|
# Do not define constants within a block, since the block's scope does not
|
7
7
|
# isolate or namespace the constant in any way.
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# If you are trying to define that constant once, define it outside of
|
10
|
+
# the block instead, or use a variable or method if defining the constant
|
11
|
+
# in the outer scope would be problematic.
|
12
|
+
#
|
13
|
+
# For meta-programming, use `const_set`.
|
11
14
|
#
|
12
15
|
# @example
|
13
16
|
# # bad
|
@@ -20,6 +23,14 @@ module RuboCop
|
|
20
23
|
# class TestRequest; end
|
21
24
|
# end
|
22
25
|
#
|
26
|
+
# # bad
|
27
|
+
# module M
|
28
|
+
# extend ActiveSupport::Concern
|
29
|
+
# included do
|
30
|
+
# LIST = []
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
23
34
|
# # good
|
24
35
|
# task :lint do
|
25
36
|
# files_to_lint = Dir['lib/*.rb']
|
@@ -28,9 +39,18 @@ module RuboCop
|
|
28
39
|
# # good
|
29
40
|
# describe 'making a request' do
|
30
41
|
# let(:test_request) { Class.new }
|
42
|
+
# # see also `stub_const` for RSpec
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# # good
|
46
|
+
# module M
|
47
|
+
# extend ActiveSupport::Concern
|
48
|
+
# included do
|
49
|
+
# const_set(:LIST, [])
|
50
|
+
# end
|
31
51
|
# end
|
32
52
|
class ConstantDefinitionInBlock < Base
|
33
|
-
MSG = 'Do not define constants within a block.'
|
53
|
+
MSG = 'Do not define constants this way within a block.'
|
34
54
|
|
35
55
|
def_node_matcher :constant_assigned_in_block?, <<~PATTERN
|
36
56
|
({^block_type? [^begin_type? ^^block_type?]} nil? ...)
|
@@ -33,10 +33,8 @@ module RuboCop
|
|
33
33
|
def on_rescue(node)
|
34
34
|
return if rescue_modifier?(node)
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
resbodies.each_with_object(Set.new) do |resbody, previous|
|
39
|
-
rescued_exceptions = rescued_exceptions(resbody)
|
36
|
+
node.resbody_branches.each_with_object(Set.new) do |resbody, previous|
|
37
|
+
rescued_exceptions = resbody.exceptions
|
40
38
|
|
41
39
|
rescued_exceptions.each do |exception|
|
42
40
|
add_offense(exception) unless previous.add?(exception)
|
@@ -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
|
@@ -26,9 +26,11 @@ module RuboCop
|
|
26
26
|
return unless compare_between_object_id_by_double_equal?(node)
|
27
27
|
|
28
28
|
add_offense(node) do |corrector|
|
29
|
-
receiver = node.receiver.receiver
|
30
|
-
argument = node.first_argument.receiver
|
31
|
-
|
29
|
+
receiver = node.receiver.receiver
|
30
|
+
argument = node.first_argument.receiver
|
31
|
+
return unless receiver && argument
|
32
|
+
|
33
|
+
replacement = "#{receiver.source}.equal?(#{argument.source})"
|
32
34
|
|
33
35
|
corrector.replace(node, replacement)
|
34
36
|
end
|
@@ -58,12 +58,9 @@ module RuboCop
|
|
58
58
|
PATTERN
|
59
59
|
|
60
60
|
def on_class(node)
|
61
|
-
check_node(node.
|
62
|
-
end
|
63
|
-
|
64
|
-
def on_module(node)
|
65
|
-
check_node(node.children[1]) # module body
|
61
|
+
check_node(node.body)
|
66
62
|
end
|
63
|
+
alias on_module on_class
|
67
64
|
|
68
65
|
private
|
69
66
|
|
@@ -56,19 +56,29 @@ module RuboCop
|
|
56
56
|
|
57
57
|
private
|
58
58
|
|
59
|
+
def previous_line_blank?(range)
|
60
|
+
processed_source.buffer.source_line(range.line - 1).blank?
|
61
|
+
end
|
62
|
+
|
59
63
|
def comment_range_with_surrounding_space(range)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
64
|
+
if previous_line_blank?(range)
|
65
|
+
# When the previous line is blank, it should be retained
|
66
|
+
range_with_surrounding_space(range: range, side: :right)
|
67
|
+
else
|
68
|
+
# Eat the entire comment, the preceding space, and the preceding
|
69
|
+
# newline if there is one.
|
70
|
+
original_begin = range.begin_pos
|
71
|
+
range = range_with_surrounding_space(range: range,
|
72
|
+
side: :left,
|
73
|
+
newlines: true)
|
74
|
+
|
75
|
+
range_with_surrounding_space(range: range,
|
76
|
+
side: :right,
|
77
|
+
# Special for a comment that
|
78
|
+
# begins the file: remove
|
79
|
+
# the newline at the end.
|
80
|
+
newlines: original_begin.zero?)
|
81
|
+
end
|
72
82
|
end
|
73
83
|
|
74
84
|
def directive_range_in_list(range, ranges)
|
@@ -45,18 +45,28 @@ module RuboCop
|
|
45
45
|
return if processed_source.blank?
|
46
46
|
|
47
47
|
offenses = processed_source.comment_config.extra_enabled_comments
|
48
|
-
offenses.each
|
48
|
+
offenses.each { |comment, cop_names| register_offense(comment, cop_names) }
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def register_offense(comment, cop_names)
|
54
|
+
directive = DirectiveComment.new(comment)
|
55
|
+
|
56
|
+
cop_names.each do |name|
|
49
57
|
add_offense(
|
50
58
|
range_of_offense(comment, name),
|
51
59
|
message: format(MSG, cop: all_or_name(name))
|
52
60
|
) do |corrector|
|
53
|
-
|
61
|
+
if directive.match?(cop_names)
|
62
|
+
corrector.remove(range_with_surrounding_space(range: directive.range, side: :right))
|
63
|
+
else
|
64
|
+
corrector.remove(range_with_comma(comment, name))
|
65
|
+
end
|
54
66
|
end
|
55
67
|
end
|
56
68
|
end
|
57
69
|
|
58
|
-
private
|
59
|
-
|
60
70
|
def range_of_offense(comment, name)
|
61
71
|
start_pos = comment_start(comment) + cop_name_indention(comment, name)
|
62
72
|
range_between(start_pos, start_pos + name.size)
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for redundant safe navigation calls.
|
7
|
+
# `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`, and `equal?` methods
|
8
|
+
# are checked by default. These are customizable with `AllowedMethods` option.
|
9
|
+
#
|
10
|
+
# This cop is marked as unsafe, because auto-correction can change the
|
11
|
+
# return type of the expression. An offending expression that previously
|
12
|
+
# could return `nil` will be auto-corrected to never return `nil`.
|
13
|
+
#
|
14
|
+
# In the example below, the safe navigation operator (`&.`) is unnecessary
|
15
|
+
# because `NilClass` has methods like `respond_to?` and `is_a?`.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# # bad
|
19
|
+
# do_something if attrs&.respond_to?(:[])
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# do_something if attrs.respond_to?(:[])
|
23
|
+
#
|
24
|
+
# # bad
|
25
|
+
# while node&.is_a?(BeginNode)
|
26
|
+
# node = node.parent
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # good
|
30
|
+
# while node.is_a?(BeginNode)
|
31
|
+
# node = node.parent
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# # good - without `&.` this will always return `true`
|
35
|
+
# foo&.respond_to?(:to_a)
|
36
|
+
#
|
37
|
+
class RedundantSafeNavigation < Base
|
38
|
+
include AllowedMethods
|
39
|
+
include RangeHelp
|
40
|
+
extend AutoCorrector
|
41
|
+
|
42
|
+
MSG = 'Redundant safe navigation detected.'
|
43
|
+
|
44
|
+
NIL_SPECIFIC_METHODS = (nil.methods - Object.new.methods).to_set.freeze
|
45
|
+
|
46
|
+
def_node_matcher :respond_to_nil_specific_method?, <<~PATTERN
|
47
|
+
(csend _ :respond_to? (sym %NIL_SPECIFIC_METHODS))
|
48
|
+
PATTERN
|
49
|
+
|
50
|
+
def on_csend(node)
|
51
|
+
return unless check?(node) && allowed_method?(node.method_name)
|
52
|
+
return if respond_to_nil_specific_method?(node)
|
53
|
+
|
54
|
+
range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
|
55
|
+
add_offense(range) do |corrector|
|
56
|
+
corrector.replace(node.loc.dot, '.')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def check?(node)
|
63
|
+
parent = node.parent
|
64
|
+
return false unless parent
|
65
|
+
|
66
|
+
condition?(parent, node) ||
|
67
|
+
parent.and_type? ||
|
68
|
+
parent.or_type? ||
|
69
|
+
(parent.send_type? && parent.negation_method?)
|
70
|
+
end
|
71
|
+
|
72
|
+
def condition?(parent, node)
|
73
|
+
(parent.conditional? || parent.post_condition_loop?) && parent.condition == node
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|