From 093ecd3d2a18de0bcb4f17169abcca03297c7066 Mon Sep 17 00:00:00 2001 From: pvcresin Date: Tue, 16 Jun 2026 21:15:44 +0900 Subject: [PATCH] Merge duplicate constant declarations into a union type When a constant was assigned more than once (e.g. `D = 1; D = "str"`), `Service#dump_declarations` emitted one declaration line per assignment, so the same constant appeared multiple times (`D: Integer` followed by `D: String`). Deduplicate constant writes by their static cpath so that each constant yields a single declaration, and show the union of its types (`D: (Integer | String)`) when it is assigned more than once. A single assignment keeps using the assigned value (`node.ret`) rather than the constant entity's type, so that a same-named class or module (e.g. one declared in RBS) is not mixed into the declaration; see scenario/rbs/remove-class.rb. The deduplication mirrors the existing seen_ivars/seen_cvars/seen_gvars handling in the same method. Promote scenario/known-issues/multi-const-write.rb to scenario/const/ and update scenario/variable/operator_write.rb, which previously encoded the duplicated output. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/typeprof/core/service.rb | 10 +++++++++- scenario/{known-issues => const}/multi-const-write.rb | 0 scenario/variable/operator_write.rb | 2 -- 3 files changed, 9 insertions(+), 3 deletions(-) rename scenario/{known-issues => const}/multi-const-write.rb (100%) diff --git a/lib/typeprof/core/service.rb b/lib/typeprof/core/service.rb index f0fa098e8..ce5ffd604 100644 --- a/lib/typeprof/core/service.rb +++ b/lib/typeprof/core/service.rb @@ -456,6 +456,7 @@ def superclass_source_is_toplevel?(const_node) def dump_declarations(path) stack = [] out = [] + seen_consts = Set.empty @rb_text_nodes[path]&.traverse do |event, node| case node when AST::ModuleNode @@ -508,7 +509,14 @@ def dump_declarations(path) when AST::ConstantWriteNode if node.static_cpath if event == :enter - out << " " * stack.size + "#{ format_declared_const_path(node.static_cpath, stack) }: #{ node.ret.show }" + unless seen_consts.include?(node.static_cpath) + seen_consts << node.static_cpath + cdef = @genv.resolve_const(node.static_cpath) + # Use the assigned value for a single write so a same-named class + # or module (e.g. declared in RBS) is not mixed in; union the types otherwise. + ret = cdef.defs.size > 1 ? cdef.vtx : node.ret + out << " " * stack.size + "#{ format_declared_const_path(node.static_cpath, stack) }: #{ ret.show }" + end end end else diff --git a/scenario/known-issues/multi-const-write.rb b/scenario/const/multi-const-write.rb similarity index 100% rename from scenario/known-issues/multi-const-write.rb rename to scenario/const/multi-const-write.rb diff --git a/scenario/variable/operator_write.rb b/scenario/variable/operator_write.rb index d4fb7094e..34dbb7e92 100644 --- a/scenario/variable/operator_write.rb +++ b/scenario/variable/operator_write.rb @@ -42,7 +42,6 @@ def test_attr=(x) ## diagnostics ## assert class C - D: Integer D: Integer def get_lv: -> Integer def set_iv: -> Integer @@ -56,4 +55,3 @@ def test_attr: -> Integer def test_attr=: (Integer) -> Integer end C::E: Integer -C::E: Integer