/
hash_compare_by_identity.rb
47 lines (43 loc) · 1.42 KB
/
hash_compare_by_identity.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# frozen_string_literal: true
module RuboCop
module Cop
module Lint
# Prefer using `Hash#compare_by_identity` rather than using `object_id`
# for hash keys.
#
# This cop looks for hashes being keyed by objects' `object_id`, using
# one of these methods: `key?`, `has_key?`, `fetch`, `[]` and `[]=`.
#
# @safety
# This cop is unsafe. Although unlikely, the hash could store both object
# ids and other values that need be compared by value, and thus
# could be a false positive.
#
# Furthermore, this cop cannot guarantee that the receiver of one of the
# methods (`key?`, etc.) is actually a hash.
#
# @example
# # bad
# hash = {}
# hash[foo.object_id] = :bar
# hash.key?(baz.object_id)
#
# # good
# hash = {}.compare_by_identity
# hash[foo] = :bar
# hash.key?(baz)
#
class HashCompareByIdentity < Base
RESTRICT_ON_SEND = %i[key? has_key? fetch [] []=].freeze
MSG = 'Use `Hash#compare_by_identity` instead of using `object_id` for keys.'
# @!method id_as_hash_key?(node)
def_node_matcher :id_as_hash_key?, <<~PATTERN
(send _ {:key? :has_key? :fetch :[] :[]=} (send _ :object_id) ...)
PATTERN
def on_send(node)
add_offense(node) if id_as_hash_key?(node)
end
end
end
end
end