@@ -217,6 +217,63 @@ def visit_var_ref(node)
217
217
super
218
218
end
219
219
220
+ # When using regex named capture groups, vcalls might actually be a variable
221
+ def visit_vcall ( node )
222
+ value = node . value
223
+ definition = current_scope . find_local ( value . value )
224
+ current_scope . add_local_usage ( value , definition . type ) if definition
225
+
226
+ super
227
+ end
228
+
229
+ # Visit for capturing local variables defined in regex named capture groups
230
+ def visit_binary ( node )
231
+ if node . operator == :=~
232
+ left = node . left
233
+
234
+ if left . is_a? ( RegexpLiteral ) && left . parts . length == 1 &&
235
+ left . parts . first . is_a? ( TStringContent )
236
+ content = left . parts . first
237
+
238
+ value = content . value
239
+ location = content . location
240
+ start_line = location . start_line
241
+
242
+ Regexp
243
+ . new ( value , Regexp ::FIXEDENCODING )
244
+ . names
245
+ . each do |name |
246
+ offset = value . index ( /\( \? <#{ Regexp . escape ( name ) } >/ )
247
+ line = start_line + value [ 0 ...offset ] . count ( "\n " )
248
+
249
+ # We need to add 3 to account for these three characters
250
+ # prefixing a named capture (?<
251
+ column = location . start_column + offset + 3
252
+ if value [ 0 ...offset ] . include? ( "\n " )
253
+ column =
254
+ value [ 0 ...offset ] . length - value [ 0 ...offset ] . rindex ( "\n " ) +
255
+ 3 - 1
256
+ end
257
+
258
+ ident_location =
259
+ Location . new (
260
+ start_line : line ,
261
+ start_char : location . start_char + offset ,
262
+ start_column : column ,
263
+ end_line : line ,
264
+ end_char : location . start_char + offset + name . length ,
265
+ end_column : column + name . length
266
+ )
267
+
268
+ identifier = Ident . new ( value : name , location : ident_location )
269
+ current_scope . add_local_definition ( identifier , :variable )
270
+ end
271
+ end
272
+ end
273
+
274
+ super
275
+ end
276
+
220
277
private
221
278
222
279
def add_argument_definitions ( list )
0 commit comments