From 5982bb01b3adc6f9015b50abd96f5c58009dfc9a Mon Sep 17 00:00:00 2001 From: ahogappa Date: Tue, 11 Aug 2026 08:14:58 +0900 Subject: [PATCH 1/2] Fix crash on anonymous splat in find pattern (`[*, x, *]`) Prism represents an anonymous splat as a `SplatNode` whose `expression` is nil, so `FindPatternNode#initialize` passed nil into `AST.create_pattern_node` and crashed with: undefined method 'type' for nil (NoMethodError) lib/typeprof/core/ast.rb:334:in 'AST.create_pattern_node' lib/typeprof/core/ast/pattern.rb:61:in 'FindPatternNode#initialize' `left` and `right` are never nil (the grammar requires both splats), so check `expression` instead, as `ArrayPatternNode` already does for its rest pattern. Co-Authored-By: Claude Opus 5 (1M context) --- lib/typeprof/core/ast/pattern.rb | 4 ++-- scenario/patterns/find_pat.rb | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/typeprof/core/ast/pattern.rb b/lib/typeprof/core/ast/pattern.rb index d418a3611..ae38f5c08 100644 --- a/lib/typeprof/core/ast/pattern.rb +++ b/lib/typeprof/core/ast/pattern.rb @@ -58,9 +58,9 @@ def install0(genv) class FindPatternNode < Node def initialize(raw_node, lenv) super(raw_node, lenv) - @left = raw_node.left ? AST.create_pattern_node(raw_node.left.expression, lenv) : nil + @left = raw_node.left.expression ? AST.create_pattern_node(raw_node.left.expression, lenv) : nil @requireds = raw_node.requireds.map {|raw_elem| AST.create_pattern_node(raw_elem, lenv) } - @right = raw_node.right ? AST.create_pattern_node(raw_node.right.expression, lenv) : nil + @right = raw_node.right.expression ? AST.create_pattern_node(raw_node.right.expression, lenv) : nil end attr_reader :left, :requireds, :right diff --git a/scenario/patterns/find_pat.rb b/scenario/patterns/find_pat.rb index 57834385d..74e7bf0ae 100644 --- a/scenario/patterns/find_pat.rb +++ b/scenario/patterns/find_pat.rb @@ -5,6 +5,12 @@ def check(x) :foo in *a, String, *b :bar # TODO: this should be excluded + in [*, Integer, *b] + :anon_left + in [*a, Integer, *] + :anon_right + in [*, Integer, *] + :anon_both else :zzz end @@ -14,5 +20,5 @@ def check(x) ## assert class Object - def check: (Array[Integer]) -> (:bar | :foo | :zzz) + def check: (Array[Integer]) -> (:anon_both | :anon_left | :anon_right | :bar | :foo | :zzz) end From 2aa3eddf7a679419b27c064f3106754a8191dca4 Mon Sep 17 00:00:00 2001 From: ahogappa Date: Tue, 11 Aug 2026 08:15:32 +0900 Subject: [PATCH 2/2] Fix crash on `{**nil}` pattern `HashPatternNode#rest` is `AssocSplatNode | NoKeywordsParameterNode | nil`, but the code assumed it always responds to `value`, so `in {**nil}` crashed with: undefined method 'value' for an instance of Prism::NoKeywordsParameterNode lib/typeprof/core/ast/pattern.rb:42:in 'HashPatternNode#initialize' Dispatch on the node class, mirroring `ArrayPatternNode#initialize`. Co-Authored-By: Claude Opus 5 (1M context) --- lib/typeprof/core/ast/pattern.rb | 9 ++++++++- scenario/patterns/hash_pat.rb | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/typeprof/core/ast/pattern.rb b/lib/typeprof/core/ast/pattern.rb index ae38f5c08..f41e91b2a 100644 --- a/lib/typeprof/core/ast/pattern.rb +++ b/lib/typeprof/core/ast/pattern.rb @@ -39,7 +39,14 @@ def initialize(raw_node, lenv) @keys = raw_node.elements.map {|raw_assoc| raw_assoc.key.value.to_sym } @values = raw_node.elements.map {|raw_assoc| AST.create_pattern_node(raw_assoc.value, lenv) } @rest = !!raw_node.rest - @rest_pattern = raw_node.rest && raw_node.rest.value ? AST.create_pattern_node(raw_node.rest.value, lenv) : nil + @rest_pattern = case raw_node.rest + when Prism::AssocSplatNode + AST.create_pattern_node(raw_node.rest.value, lenv) if raw_node.rest.value + when Prism::NoKeywordsParameterNode, nil + nil + else + raise + end end attr_reader :keys, :values, :rest, :rest_pattern diff --git a/scenario/patterns/hash_pat.rb b/scenario/patterns/hash_pat.rb index 70e8c494f..d5d65cc23 100644 --- a/scenario/patterns/hash_pat.rb +++ b/scenario/patterns/hash_pat.rb @@ -12,6 +12,8 @@ def check(x) :baz in MyHash[a: Integer] :qux + in { **nil } + :no_kw else :zzz end @@ -23,5 +25,5 @@ def check(x) class MyHash end class Object - def check: ({ a: Integer }) -> (:bar | :baz | :foo | :qux | :zzz) + def check: ({ a: Integer }) -> (:bar | :baz | :foo | :no_kw | :qux | :zzz) end