rubocop 0.67.2 → 0.68.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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +86 -233
  4. data/exe/rubocop +0 -12
  5. data/lib/rubocop.rb +13 -30
  6. data/lib/rubocop/ast/builder.rb +4 -0
  7. data/lib/rubocop/ast/node/alias_node.rb +24 -0
  8. data/lib/rubocop/ast/node/class_node.rb +31 -0
  9. data/lib/rubocop/ast/node/module_node.rb +24 -0
  10. data/lib/rubocop/ast/node/range_node.rb +7 -0
  11. data/lib/rubocop/ast/node/resbody_node.rb +12 -0
  12. data/lib/rubocop/ast/node/self_class_node.rb +24 -0
  13. data/lib/rubocop/cli.rb +40 -4
  14. data/lib/rubocop/config.rb +9 -7
  15. data/lib/rubocop/config_loader.rb +48 -7
  16. data/lib/rubocop/config_loader_resolver.rb +5 -4
  17. data/lib/rubocop/cop/commissioner.rb +24 -0
  18. data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +18 -6
  19. data/lib/rubocop/cop/internal_affairs/node_destructuring.rb +12 -14
  20. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +9 -20
  21. data/lib/rubocop/cop/layout/align_arguments.rb +93 -0
  22. data/lib/rubocop/cop/layout/align_parameters.rb +57 -33
  23. data/lib/rubocop/cop/layout/class_structure.rb +5 -5
  24. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -8
  25. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +3 -6
  26. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +1 -2
  27. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +1 -0
  28. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +292 -0
  29. data/lib/rubocop/cop/layout/{first_parameter_indentation.rb → indent_first_argument.rb} +11 -12
  30. data/lib/rubocop/cop/layout/{indent_array.rb → indent_first_array_element.rb} +2 -2
  31. data/lib/rubocop/cop/layout/{indent_hash.rb → indent_first_hash_element.rb} +2 -2
  32. data/lib/rubocop/cop/layout/indent_first_parameter.rb +96 -0
  33. data/lib/rubocop/cop/layout/indentation_width.rb +4 -16
  34. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -4
  35. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -16
  36. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -2
  37. data/lib/rubocop/cop/lint/duplicate_methods.rb +6 -8
  38. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +4 -8
  39. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +157 -0
  40. data/lib/rubocop/cop/lint/inherit_exception.rb +3 -4
  41. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +18 -1
  42. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -5
  43. data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +25 -5
  44. data/lib/rubocop/cop/lint/useless_assignment.rb +2 -6
  45. data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -2
  46. data/lib/rubocop/cop/message_annotator.rb +1 -0
  47. data/lib/rubocop/cop/metrics/line_length.rb +139 -28
  48. data/lib/rubocop/cop/metrics/perceived_complexity.rb +3 -4
  49. data/lib/rubocop/cop/mixin/check_line_breakable.rb +190 -0
  50. data/lib/rubocop/cop/mixin/{array_hash_indentation.rb → multiline_element_indentation.rb} +3 -2
  51. data/lib/rubocop/cop/mixin/too_many_lines.rb +3 -7
  52. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +33 -4
  53. data/lib/rubocop/cop/rails/active_record_override.rb +23 -8
  54. data/lib/rubocop/cop/rails/delegate.rb +5 -8
  55. data/lib/rubocop/cop/rails/environment_comparison.rb +5 -3
  56. data/lib/rubocop/cop/rails/find_each.rb +1 -1
  57. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +3 -3
  58. data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
  59. data/lib/rubocop/cop/rails/skips_model_validations.rb +6 -7
  60. data/lib/rubocop/cop/rails/time_zone.rb +3 -10
  61. data/lib/rubocop/cop/rails/validation.rb +3 -0
  62. data/lib/rubocop/cop/registry.rb +3 -3
  63. data/lib/rubocop/cop/style/alias.rb +13 -7
  64. data/lib/rubocop/cop/style/block_delimiters.rb +20 -0
  65. data/lib/rubocop/cop/style/class_and_module_children.rb +19 -21
  66. data/lib/rubocop/cop/style/class_methods.rb +16 -24
  67. data/lib/rubocop/cop/style/conditional_assignment.rb +20 -49
  68. data/lib/rubocop/cop/style/documentation.rb +3 -7
  69. data/lib/rubocop/cop/style/format_string.rb +18 -21
  70. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  71. data/lib/rubocop/cop/style/inverse_methods.rb +4 -0
  72. data/lib/rubocop/cop/style/lambda.rb +12 -8
  73. data/lib/rubocop/cop/style/mixin_grouping.rb +8 -10
  74. data/lib/rubocop/cop/style/module_function.rb +2 -3
  75. data/lib/rubocop/cop/style/next.rb +10 -14
  76. data/lib/rubocop/cop/style/one_line_conditional.rb +5 -3
  77. data/lib/rubocop/cop/style/optional_arguments.rb +1 -4
  78. data/lib/rubocop/cop/style/random_with_offset.rb +44 -47
  79. data/lib/rubocop/cop/style/redundant_return.rb +6 -14
  80. data/lib/rubocop/cop/style/redundant_sort_by.rb +1 -1
  81. data/lib/rubocop/cop/style/safe_navigation.rb +3 -0
  82. data/lib/rubocop/cop/style/struct_inheritance.rb +2 -3
  83. data/lib/rubocop/cop/style/symbol_proc.rb +20 -40
  84. data/lib/rubocop/cop/style/unless_else.rb +1 -2
  85. data/lib/rubocop/cop/style/yoda_condition.rb +8 -7
  86. data/lib/rubocop/cop/util.rb +2 -4
  87. data/lib/rubocop/file_finder.rb +5 -10
  88. data/lib/rubocop/formatter/disabled_config_formatter.rb +5 -0
  89. data/lib/rubocop/node_pattern.rb +304 -170
  90. data/lib/rubocop/options.rb +4 -1
  91. data/lib/rubocop/rspec/shared_contexts.rb +3 -0
  92. data/lib/rubocop/version.rb +1 -1
  93. data/lib/rubocop/yaml_duplication_checker.rb +1 -1
  94. metadata +26 -50
  95. data/lib/rubocop/cop/performance/caller.rb +0 -69
  96. data/lib/rubocop/cop/performance/case_when_splat.rb +0 -177
  97. data/lib/rubocop/cop/performance/casecmp.rb +0 -108
  98. data/lib/rubocop/cop/performance/chain_array_allocation.rb +0 -78
  99. data/lib/rubocop/cop/performance/compare_with_block.rb +0 -122
  100. data/lib/rubocop/cop/performance/count.rb +0 -102
  101. data/lib/rubocop/cop/performance/detect.rb +0 -110
  102. data/lib/rubocop/cop/performance/double_start_end_with.rb +0 -94
  103. data/lib/rubocop/cop/performance/end_with.rb +0 -56
  104. data/lib/rubocop/cop/performance/fixed_size.rb +0 -97
  105. data/lib/rubocop/cop/performance/flat_map.rb +0 -78
  106. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +0 -99
  107. data/lib/rubocop/cop/performance/open_struct.rb +0 -46
  108. data/lib/rubocop/cop/performance/range_include.rb +0 -50
  109. data/lib/rubocop/cop/performance/redundant_block_call.rb +0 -93
  110. data/lib/rubocop/cop/performance/redundant_match.rb +0 -56
  111. data/lib/rubocop/cop/performance/redundant_merge.rb +0 -183
  112. data/lib/rubocop/cop/performance/regexp_match.rb +0 -265
  113. data/lib/rubocop/cop/performance/reverse_each.rb +0 -42
  114. data/lib/rubocop/cop/performance/size.rb +0 -77
  115. data/lib/rubocop/cop/performance/start_with.rb +0 -59
  116. data/lib/rubocop/cop/performance/string_replacement.rb +0 -173
  117. data/lib/rubocop/cop/performance/times_map.rb +0 -71
  118. data/lib/rubocop/cop/performance/unfreeze_string.rb +0 -50
  119. data/lib/rubocop/cop/performance/uri_default_parser.rb +0 -47
@@ -1,108 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Performance
6
- # This cop identifies places where a case-insensitive string comparison
7
- # can better be implemented using `casecmp`.
8
- #
9
- # @example
10
- # # bad
11
- # str.downcase == 'abc'
12
- # str.upcase.eql? 'ABC'
13
- # 'abc' == str.downcase
14
- # 'ABC'.eql? str.upcase
15
- # str.downcase == str.downcase
16
- #
17
- # # good
18
- # str.casecmp('ABC').zero?
19
- # 'abc'.casecmp(str).zero?
20
- class Casecmp < Cop
21
- MSG = 'Use `%<good>s` instead of `%<bad>s`.'.freeze
22
- CASE_METHODS = %i[downcase upcase].freeze
23
-
24
- def_node_matcher :downcase_eq, <<-PATTERN
25
- (send
26
- $(send _ ${:downcase :upcase})
27
- ${:== :eql? :!=}
28
- ${str (send _ {:downcase :upcase} ...) (begin str)})
29
- PATTERN
30
-
31
- def_node_matcher :eq_downcase, <<-PATTERN
32
- (send
33
- {str (send _ {:downcase :upcase} ...) (begin str)}
34
- ${:== :eql? :!=}
35
- $(send _ ${:downcase :upcase}))
36
- PATTERN
37
-
38
- def_node_matcher :downcase_downcase, <<-PATTERN
39
- (send
40
- $(send _ ${:downcase :upcase})
41
- ${:== :eql? :!=}
42
- $(send _ ${:downcase :upcase}))
43
- PATTERN
44
-
45
- def on_send(node)
46
- return unless downcase_eq(node) || eq_downcase(node)
47
- return unless (parts = take_method_apart(node))
48
-
49
- _, _, arg, variable = parts
50
- good_method = build_good_method(arg, variable)
51
-
52
- add_offense(
53
- node,
54
- message: format(MSG, good: good_method, bad: node.source)
55
- )
56
- end
57
-
58
- def autocorrect(node)
59
- return unless (parts = take_method_apart(node))
60
-
61
- receiver, method, arg, variable = parts
62
-
63
- correction(node, receiver, method, arg, variable)
64
- end
65
-
66
- private
67
-
68
- def take_method_apart(node)
69
- if downcase_downcase(node)
70
- receiver, method, rhs = *node
71
- arg, = *rhs
72
- elsif downcase_eq(node)
73
- receiver, method, arg = *node
74
- elsif eq_downcase(node)
75
- arg, method, receiver = *node
76
- else
77
- return
78
- end
79
-
80
- variable, = *receiver
81
-
82
- [receiver, method, arg, variable]
83
- end
84
-
85
- def correction(node, _receiver, method, arg, variable)
86
- lambda do |corrector|
87
- corrector.insert_before(node.loc.expression, '!') if method == :!=
88
-
89
- replacement = build_good_method(arg, variable)
90
-
91
- corrector.replace(node.loc.expression, replacement)
92
- end
93
- end
94
-
95
- def build_good_method(arg, variable)
96
- # We want resulting call to be parenthesized
97
- # if arg already includes one or more sets of parens, don't add more
98
- # or if method call already used parens, again, don't add more
99
- if arg.send_type? || !parentheses?(arg)
100
- "#{variable.source}.casecmp(#{arg.source}).zero?"
101
- else
102
- "#{variable.source}.casecmp#{arg.source}.zero?"
103
- end
104
- end
105
- end
106
- end
107
- end
108
- end
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Performance
6
- # This cop is used to identify usages of
7
- # @example
8
- # # bad
9
- # array = ["a", "b", "c"]
10
- # array.compact.flatten.map { |x| x.downcase }
11
- #
12
- # Each of these methods (`compact`, `flatten`, `map`) will generate a
13
- # new intermediate array that is promptly thrown away. Instead it is
14
- # faster to mutate when we know it's safe.
15
- #
16
- # @example
17
- # # good.
18
- # array = ["a", "b", "c"]
19
- # array.compact!
20
- # array.flatten!
21
- # array.map! { |x| x.downcase }
22
- # array
23
- class ChainArrayAllocation < Cop
24
- include RangeHelp
25
-
26
- # These methods return a new array but only sometimes. They must be
27
- # called with an argument. For example:
28
- #
29
- # [1,2].first # => 1
30
- # [1,2].first(1) # => [1]
31
- #
32
- RETURN_NEW_ARRAY_WHEN_ARGS = ':first :last :pop :sample :shift '.freeze
33
-
34
- # These methods return a new array only when called without a block.
35
- RETURNS_NEW_ARRAY_WHEN_NO_BLOCK = ':zip :product '.freeze
36
-
37
- # These methods ALWAYS return a new array
38
- # after they're called it's safe to mutate the the resulting array
39
- ALWAYS_RETURNS_NEW_ARRAY = ':* :+ :- :collect :compact :drop '\
40
- ':drop_while :flatten :map :reject ' \
41
- ':reverse :rotate :select :shuffle :sort ' \
42
- ':take :take_while :transpose :uniq ' \
43
- ':values_at :| '.freeze
44
-
45
- # These methods have a mutation alternative. For example :collect
46
- # can be called as :collect!
47
- HAS_MUTATION_ALTERNATIVE = ':collect :compact :flatten :map :reject '\
48
- ':reverse :rotate :select :shuffle :sort '\
49
- ':uniq '.freeze
50
- MSG = 'Use unchained `%<method>s!` and `%<second_method>s!` '\
51
- '(followed by `return array` if required) instead of chaining '\
52
- '`%<method>s...%<second_method>s`.'.freeze
53
-
54
- def_node_matcher :flat_map_candidate?, <<-PATTERN
55
- {
56
- (send (send _ ${#{RETURN_NEW_ARRAY_WHEN_ARGS}} {int lvar ivar cvar gvar}) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
57
- (send (block (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY} }) ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
58
- (send (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK}} ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
59
- }
60
- PATTERN
61
-
62
- def on_send(node)
63
- flat_map_candidate?(node) do |fm, sm, _|
64
- range = range_between(
65
- node.loc.dot.begin_pos,
66
- node.source_range.end_pos
67
- )
68
- add_offense(
69
- node,
70
- location: range,
71
- message: format(MSG, method: fm, second_method: sm)
72
- )
73
- end
74
- end
75
- end
76
- end
77
- end
78
- end
@@ -1,122 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Performance
6
- # This cop identifies places where `sort { |a, b| a.foo <=> b.foo }`
7
- # can be replaced by `sort_by(&:foo)`.
8
- # This cop also checks `max` and `min` methods.
9
- #
10
- # @example
11
- # # bad
12
- # array.sort { |a, b| a.foo <=> b.foo }
13
- # array.max { |a, b| a.foo <=> b.foo }
14
- # array.min { |a, b| a.foo <=> b.foo }
15
- # array.sort { |a, b| a[:foo] <=> b[:foo] }
16
- #
17
- # # good
18
- # array.sort_by(&:foo)
19
- # array.sort_by { |v| v.foo }
20
- # array.sort_by do |var|
21
- # var.foo
22
- # end
23
- # array.max_by(&:foo)
24
- # array.min_by(&:foo)
25
- # array.sort_by { |a| a[:foo] }
26
- class CompareWithBlock < Cop
27
- include RangeHelp
28
-
29
- MSG = 'Use `%<compare_method>s_by%<instead>s` instead of ' \
30
- '`%<compare_method>s { |%<var_a>s, %<var_b>s| %<str_a>s ' \
31
- '<=> %<str_b>s }`.'.freeze
32
-
33
- def_node_matcher :compare?, <<-PATTERN
34
- (block
35
- $(send _ {:sort :min :max})
36
- (args (arg $_a) (arg $_b))
37
- $send)
38
- PATTERN
39
-
40
- def_node_matcher :replaceable_body?, <<-PATTERN
41
- (send
42
- (send (lvar %1) $_method $...)
43
- :<=>
44
- (send (lvar %2) _method $...))
45
- PATTERN
46
-
47
- def on_block(node)
48
- compare?(node) do |send, var_a, var_b, body|
49
- replaceable_body?(body, var_a, var_b) do |method, args_a, args_b|
50
- return unless slow_compare?(method, args_a, args_b)
51
-
52
- range = compare_range(send, node)
53
-
54
- add_offense(
55
- node,
56
- location: range,
57
- message: message(send, method, var_a, var_b, args_a)
58
- )
59
- end
60
- end
61
- end
62
-
63
- def autocorrect(node)
64
- lambda do |corrector|
65
- send, var_a, var_b, body = compare?(node)
66
- method, arg, = replaceable_body?(body, var_a, var_b)
67
- replacement =
68
- if method == :[]
69
- "#{send.method_name}_by { |a| a[#{arg.first.source}] }"
70
- else
71
- "#{send.method_name}_by(&:#{method})"
72
- end
73
- corrector.replace(compare_range(send, node),
74
- replacement)
75
- end
76
- end
77
-
78
- private
79
-
80
- def slow_compare?(method, args_a, args_b)
81
- return false unless args_a == args_b
82
-
83
- if method == :[]
84
- return false unless args_a.size == 1
85
-
86
- key = args_a.first
87
- return false unless %i[sym str int].include?(key.type)
88
- else
89
- return false unless args_a.empty?
90
- end
91
- true
92
- end
93
-
94
- # rubocop:disable Metrics/MethodLength
95
- def message(send, method, var_a, var_b, args)
96
- compare_method = send.method_name
97
- if method == :[]
98
- key = args.first
99
- instead = " { |a| a[#{key.source}] }"
100
- str_a = "#{var_a}[#{key.source}]"
101
- str_b = "#{var_b}[#{key.source}]"
102
- else
103
- instead = "(&:#{method})"
104
- str_a = "#{var_a}.#{method}"
105
- str_b = "#{var_b}.#{method}"
106
- end
107
- format(MSG, compare_method: compare_method,
108
- instead: instead,
109
- var_a: var_a,
110
- var_b: var_b,
111
- str_a: str_a,
112
- str_b: str_b)
113
- end
114
- # rubocop:enable Metrics/MethodLength
115
-
116
- def compare_range(send, node)
117
- range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
118
- end
119
- end
120
- end
121
- end
122
- end
@@ -1,102 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Performance
6
- # This cop is used to identify usages of `count` on an `Enumerable` that
7
- # follow calls to `select` or `reject`. Querying logic can instead be
8
- # passed to the `count` call.
9
- #
10
- # @example
11
- # # bad
12
- # [1, 2, 3].select { |e| e > 2 }.size
13
- # [1, 2, 3].reject { |e| e > 2 }.size
14
- # [1, 2, 3].select { |e| e > 2 }.length
15
- # [1, 2, 3].reject { |e| e > 2 }.length
16
- # [1, 2, 3].select { |e| e > 2 }.count { |e| e.odd? }
17
- # [1, 2, 3].reject { |e| e > 2 }.count { |e| e.even? }
18
- # array.select(&:value).count
19
- #
20
- # # good
21
- # [1, 2, 3].count { |e| e > 2 }
22
- # [1, 2, 3].count { |e| e < 2 }
23
- # [1, 2, 3].count { |e| e > 2 && e.odd? }
24
- # [1, 2, 3].count { |e| e < 2 && e.even? }
25
- # Model.select('field AS field_one').count
26
- # Model.select(:value).count
27
- #
28
- # `ActiveRecord` compatibility:
29
- # `ActiveRecord` will ignore the block that is passed to `count`.
30
- # Other methods, such as `select`, will convert the association to an
31
- # array and then run the block on the array. A simple work around to
32
- # make `count` work with a block is to call `to_a.count {...}`.
33
- #
34
- # Example:
35
- # Model.where(id: [1, 2, 3].select { |m| m.method == true }.size
36
- #
37
- # becomes:
38
- #
39
- # Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }
40
- class Count < Cop
41
- include SafeMode
42
- include RangeHelp
43
-
44
- MSG = 'Use `count` instead of `%<selector>s...%<counter>s`.'.freeze
45
-
46
- def_node_matcher :count_candidate?, <<-PATTERN
47
- {
48
- (send (block $(send _ ${:select :reject}) ...) ${:count :length :size})
49
- (send $(send _ ${:select :reject} (:block_pass _)) ${:count :length :size})
50
- }
51
- PATTERN
52
-
53
- def on_send(node)
54
- return if rails_safe_mode?
55
-
56
- count_candidate?(node) do |selector_node, selector, counter|
57
- return unless eligible_node?(node)
58
-
59
- range = source_starting_at(node) do
60
- selector_node.loc.selector.begin_pos
61
- end
62
-
63
- add_offense(node,
64
- location: range,
65
- message: format(MSG, selector: selector,
66
- counter: counter))
67
- end
68
- end
69
-
70
- def autocorrect(node)
71
- selector_node, selector, _counter = count_candidate?(node)
72
- selector_loc = selector_node.loc.selector
73
-
74
- return if selector == :reject
75
-
76
- range = source_starting_at(node) { |n| n.loc.dot.begin_pos }
77
-
78
- lambda do |corrector|
79
- corrector.remove(range)
80
- corrector.replace(selector_loc, 'count')
81
- end
82
- end
83
-
84
- private
85
-
86
- def eligible_node?(node)
87
- !(node.parent && node.parent.block_type?)
88
- end
89
-
90
- def source_starting_at(node)
91
- begin_pos = if block_given?
92
- yield node
93
- else
94
- node.source_range.begin_pos
95
- end
96
-
97
- range_between(begin_pos, node.source_range.end_pos)
98
- end
99
- end
100
- end
101
- end
102
- end
@@ -1,110 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Performance
6
- # This cop is used to identify usages of
7
- # `select.first`, `select.last`, `find_all.first`, and `find_all.last`
8
- # and change them to use `detect` instead.
9
- #
10
- # @example
11
- # # bad
12
- # [].select { |item| true }.first
13
- # [].select { |item| true }.last
14
- # [].find_all { |item| true }.first
15
- # [].find_all { |item| true }.last
16
- #
17
- # # good
18
- # [].detect { |item| true }
19
- # [].reverse.detect { |item| true }
20
- #
21
- # `ActiveRecord` compatibility:
22
- # `ActiveRecord` does not implement a `detect` method and `find` has its
23
- # own meaning. Correcting ActiveRecord methods with this cop should be
24
- # considered unsafe.
25
- class Detect < Cop
26
- include SafeMode
27
-
28
- MSG = 'Use `%<prefer>s` instead of ' \
29
- '`%<first_method>s.%<second_method>s`.'.freeze
30
- REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of ' \
31
- '`%<first_method>s.%<second_method>s`.'.freeze
32
-
33
- def_node_matcher :detect_candidate?, <<-PATTERN
34
- {
35
- (send $(block (send _ {:select :find_all}) ...) ${:first :last} $...)
36
- (send $(send _ {:select :find_all} ...) ${:first :last} $...)
37
- }
38
- PATTERN
39
-
40
- def on_send(node)
41
- return if rails_safe_mode?
42
-
43
- detect_candidate?(node) do |receiver, second_method, args|
44
- return unless args.empty?
45
- return unless receiver
46
-
47
- receiver, _args, body = *receiver if receiver.block_type?
48
- return if accept_first_call?(receiver, body)
49
-
50
- register_offense(node, receiver, second_method)
51
- end
52
- end
53
-
54
- def autocorrect(node)
55
- receiver, first_method = *node
56
-
57
- replacement = if first_method == :last
58
- "reverse.#{preferred_method}"
59
- else
60
- preferred_method
61
- end
62
-
63
- first_range = receiver.source_range.end.join(node.loc.selector)
64
-
65
- receiver, _args, _body = *receiver if receiver.block_type?
66
-
67
- lambda do |corrector|
68
- corrector.remove(first_range)
69
- corrector.replace(receiver.loc.selector, replacement)
70
- end
71
- end
72
-
73
- private
74
-
75
- def accept_first_call?(receiver, body)
76
- caller, _first_method, args = *receiver
77
-
78
- # check that we have usual block or block pass
79
- return true if body.nil? && (args.nil? || !args.block_pass_type?)
80
-
81
- lazy?(caller)
82
- end
83
-
84
- def register_offense(node, receiver, second_method)
85
- _caller, first_method, _args = *receiver
86
- range = receiver.loc.selector.join(node.loc.selector)
87
-
88
- message = second_method == :last ? REVERSE_MSG : MSG
89
- formatted_message = format(message, prefer: preferred_method,
90
- first_method: first_method,
91
- second_method: second_method)
92
-
93
- add_offense(node, location: range, message: formatted_message)
94
- end
95
-
96
- def preferred_method
97
- config.for_cop('Style/CollectionMethods') \
98
- ['PreferredMethods']['detect'] || 'detect'
99
- end
100
-
101
- def lazy?(node)
102
- return false unless node
103
-
104
- receiver, method, _args = *node
105
- method == :lazy && !receiver.nil?
106
- end
107
- end
108
- end
109
- end
110
- end