-
Notifications
You must be signed in to change notification settings - Fork 73
/
method_call_scanner.rb
172 lines (137 loc) · 4.61 KB
/
method_call_scanner.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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
require 'fasterer/method_call'
require 'fasterer/offense'
require 'fasterer/scanners/offensive'
module Fasterer
class MethodCallScanner
include Fasterer::Offensive
attr_reader :element
def initialize(element)
@element = element
check_offense
end
def method_call
@method_call ||= MethodCall.new(element)
end
private
def check_offense
case method_call.method_name
when :module_eval
check_module_eval_offense
when :gsub
check_gsub_offense
when :sort
check_sort_offense
when :each_with_index
check_each_with_index_offense
when :first
check_first_offense
when :each
check_each_offense
when :flatten
check_flatten_offense
when :fetch
check_fetch_offense
when :merge!
check_merge_bang_offense
when :last
check_last_offense
when :include?
check_range_include_offense
end
check_symbol_to_proc
end
def check_module_eval_offense
first_argument = method_call.arguments.first
return unless first_argument && first_argument.value.is_a?(String)
add_offense(:module_eval) if first_argument.value.include?('def')
end
def check_gsub_offense
first_argument = method_call.arguments[0]
second_argument = method_call.arguments[1]
return if first_argument.nil? || second_argument.nil?
if first_argument.value.is_a?(String) && first_argument.value.size == 1 &&
second_argument.value.is_a?(String) && second_argument.value.size == 1
add_offense(:gsub_vs_tr)
end
end
def check_sort_offense
if method_call.arguments.count > 0 || method_call.has_block?
add_offense(:sort_vs_sort_by)
end
end
def check_each_with_index_offense
add_offense(:each_with_index_vs_while)
end
def check_first_offense
return method_call unless method_call.receiver.is_a?(MethodCall)
case method_call.receiver.name
when :shuffle
add_offense(:shuffle_first_vs_sample)
when :select
return unless method_call.receiver.has_block?
return if method_call.arguments.count > 0
add_offense(:select_first_vs_detect)
end
end
def check_each_offense
return method_call unless method_call.receiver.is_a?(MethodCall)
case method_call.receiver.name
when :reverse
add_offense(:reverse_each_vs_reverse_each)
when :keys
if method_call.receiver.arguments.count.zero?
add_offense(:keys_each_vs_each_key)
end
end
end
def check_flatten_offense
return method_call unless method_call.receiver.is_a?(MethodCall)
if method_call.receiver.name == :map &&
method_call.arguments.count == 1 &&
method_call.arguments.first.value == 1
add_offense(:map_flatten_vs_flat_map)
end
end
def check_fetch_offense
if method_call.arguments.count == 2 && !method_call.has_block?
add_offense(:fetch_with_argument_vs_block)
end
end
# Need to refactor, fukken complicated conditions.
def check_symbol_to_proc
return unless method_call.block_argument_names.count == 1
return if method_call.block_body.nil?
return unless method_call.block_body.sexp_type == :call
return if method_call.arguments.count > 0
return if method_call.lambda_literal?
body_method_call = MethodCall.new(method_call.block_body)
return unless body_method_call.arguments.count.zero?
return if body_method_call.has_block?
return if body_method_call.receiver.nil?
return if body_method_call.receiver.is_a?(Fasterer::Primitive)
return if body_method_call.receiver.name != method_call.block_argument_names.first
add_offense(:block_vs_symbol_to_proc)
end
def check_merge_bang_offense
return unless method_call.arguments.count == 1
first_argument = method_call.arguments.first
return unless first_argument.type == :hash
if first_argument.element.drop(1).count == 2 # each key and value is an item by itself.
add_offense(:hash_merge_bang_vs_hash_brackets)
end
end
def check_last_offense
return method_call unless method_call.receiver.is_a?(MethodCall)
case method_call.receiver.name
when :select
return if method_call.arguments.count > 0
add_offense(:select_last_vs_reverse_detect)
end
end
def check_range_include_offense
if method_call.receiver.is_a?(Primitive) && method_call.receiver.range?
add_offense(:include_vs_cover_on_range)
end
end
end
end