Skip to content

Commit 053dc89

Browse files
authored
Merge pull request #38 from ruby/it_parameter
Support it parameter
2 parents 806645f + a1efd9d commit 053dc89

File tree

4 files changed

+34
-3
lines changed

4 files changed

+34
-3
lines changed

lib/repl_type_completor.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ def analyze_code(code, binding = Object::TOPLEVEL_BINDING)
101101
[op == '::' ? :call_or_const : :call, name, receiver_type, self_call]
102102
when Prism::LocalVariableReadNode, Prism::LocalVariableTargetNode
103103
[:lvar_or_method, target_node.name.to_s, calculate_scope.call]
104+
when Prism::ItLocalVariableReadNode
105+
[:lvar_or_method, 'it', calculate_scope.call]
104106
when Prism::ConstantPathNode, Prism::ConstantPathTargetNode
105107
name = target_node.name.to_s
106108
if target_node.parent # A::B
@@ -122,8 +124,8 @@ def analyze_code(code, binding = Object::TOPLEVEL_BINDING)
122124
end
123125

124126
def find_target(node, position)
125-
# Skip because NumberedParametersNode#location gives location of whole block
126-
return if node.is_a? Prism::NumberedParametersNode
127+
# Skip because location of these nodes gives location of whole block
128+
return if node.is_a?(Prism::NumberedParametersNode) || node.is_a?(Prism::ItParametersNode)
127129

128130
node.compact_child_nodes.each do |n|
129131
match = find_target(n, position)

lib/repl_type_completor/type_analyzer.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ def evaluate_reference_read(node, scope)
229229
alias evaluate_class_variable_read_node evaluate_reference_read
230230
alias evaluate_instance_variable_read_node evaluate_reference_read
231231

232+
def evaluate_it_local_variable_read_node(_node, scope)
233+
# `it` is not a normal local variable. It can be overridden like `tap{p it; it=1}`.
234+
# Use the name `_1` instead of `it` to avoid conflict.
235+
scope['_1'] || Types::NIL
236+
end
232237

233238
def evaluate_call_node(node, scope)
234239
receiver_type = node.receiver ? evaluate(node.receiver, scope) : scope.self_type
@@ -262,6 +267,8 @@ def evaluate_call_node(node, scope)
262267
assign_numbered_parameters node.block.parameters.maximum, block_scope, block_args, {}
263268
when Prism::BlockParametersNode
264269
assign_parameters node.block.parameters.parameters, block_scope, block_args, {}
270+
when Prism::ItParametersNode
271+
scope['_1'] = block_args.first || Types::NIL
265272
end
266273
result = node.block.body ? evaluate(node.block.body, block_scope) : Types::NIL
267274
block_scope.merge_jumps
@@ -425,9 +432,14 @@ def evaluate_constant_path_write_node(node, scope)
425432

426433
def evaluate_lambda_node(node, scope)
427434
local_table = node.locals.to_h { [_1.to_s, Types::OBJECT] }
435+
436+
# `it` is not added to local_table because it is not a normal local variable.
437+
# We need to explicitly add it to the scope.
438+
local_table['_1'] = Types::OBJECT if node.parameters.is_a?(Prism::ItParametersNode)
439+
428440
block_scope = Scope.new scope, { **local_table, Scope::BREAK_RESULT => nil, Scope::NEXT_RESULT => nil, Scope::RETURN_RESULT => nil }
429441
block_scope.conditional do |s|
430-
assign_parameters node.parameters.parameters, s, [], {} if node.parameters&.parameters
442+
assign_parameters node.parameters.parameters, s, [], {} if node.parameters.is_a?(Prism::ParametersNode) && node.parameters.parameters
431443
evaluate node.body, s if node.body
432444
end
433445
block_scope.merge_jumps

test/repl_type_completor/test_repl_type_completor.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ def test_lvar
9292
assert_doc_namespace('lvar = ""; lvar.ascii_only?', 'String#ascii_only?', binding: bind)
9393
end
9494

95+
def test_confusing_lvar_method_it
96+
bind = eval('item = 1; ins=1; random = 1; binding')
97+
assert_completion('->{it', binding: bind, include: ['em', 'self'])
98+
assert_completion('->{ins', binding: bind, include: 'pect')
99+
assert_completion('->{rand', binding: bind, include: 'om')
100+
end
101+
95102
def test_const
96103
assert_completion('Ar', include: 'ray')
97104
assert_completion('::Ar', include: 'ray')

test/repl_type_completor/test_type_analyze.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,16 @@ def test_numbered_parameter
552552
assert_call('[:a].each_with_index{_2.', include: Integer, exclude: Symbol)
553553
end
554554

555+
def test_it_parameter
556+
assert_call('->{it.', include: Object, exclude: NilClass)
557+
assert_call('->{p it; it=:a; it.', include: Symbol, exclude: Object)
558+
assert_call('1.tap{it.', include: Integer)
559+
assert_call('1.tap{p it; it=:a; it.', include: Symbol, exclude: Integer)
560+
assert_call('[1].tap{it.', include: Array)
561+
assert_call('loop{it.', include: NilClass, exclude: Object)
562+
assert_call('[:a].each_with_index{it.', include: Symbol, exclude: [Integer, Array])
563+
end
564+
555565
def test_if_unless
556566
assert_call('if cond; 1; end.', include: Integer)
557567
assert_call('unless true; 1; end.', include: Integer)

0 commit comments

Comments
 (0)