From 66542956562f54aa70fc48be89965e9d2d4770a9 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 3 Apr 2026 19:32:29 +0900 Subject: [PATCH] Support .nil? narrowing for local and instance variables `x.nil?` in conditions now narrows the variable type: the then branch treats x as nil, and the else branch excludes nil. Works with both local variables and instance variables. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/typeprof/core/ast/call.rb | 14 ++++++++++++++ scenario/flow/nil_check.rb | 13 +++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 scenario/flow/nil_check.rb diff --git a/lib/typeprof/core/ast/call.rb b/lib/typeprof/core/ast/call.rb index d228e823..96685150 100644 --- a/lib/typeprof/core/ast/call.rb +++ b/lib/typeprof/core/ast/call.rb @@ -270,6 +270,20 @@ def narrowings else super end + when :nil? + if @recv.is_a?(LocalVariableReadNode) + [ + Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(true) }), + Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(false) }) + ] + elsif @recv.is_a?(InstanceVariableReadNode) + [ + Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(true) }), + Narrowing.new({ @recv.var => Narrowing::NilConstraint.new(false) }) + ] + else + super + end when :! then_narrowing, else_narrowing = @recv.narrowings [else_narrowing, then_narrowing] diff --git a/scenario/flow/nil_check.rb b/scenario/flow/nil_check.rb new file mode 100644 index 00000000..6920817d --- /dev/null +++ b/scenario/flow/nil_check.rb @@ -0,0 +1,13 @@ +## update +def foo(x) + unless x.nil? + x.to_sym + end +end +foo(nil) +foo("hello") + +## assert +class Object + def foo: (String?) -> Symbol? +end