Skip to content

Commit 618f045

Browse files
committed
Harden RBS signature loading: module-function keys, broader rescue, stale clearing
Three small fixes around `load_rbs_signatures` and the helper that walks RBS declarations: - `rbs_helper.rb` now emits both `Class.foo` and `Class#foo` keys when an RBS member's kind is `:singleton_instance` (i.e. `def self?.foo`). RDoc generates both a singleton and a private instance method for module-function-style definitions, so the previous singleton-only key left the instance method without type signature lines. - `rdoc.rb` widens the rescue in `load_rbs_signatures` from `RBS::ParsingError` to `RBS::BaseError`. Loader-level failures (duplicate declarations, generic-arity mismatches, etc.) inherit from `RBS::BaseError` / `RBS::LoadingError` rather than `ParsingError`, so they previously aborted documentation generation instead of warning and continuing. - `rdoc.rb` also calls `@store.clear_rbs_signatures` in the rescue. In server mode a previous successful load may have populated the long-lived store; without clearing, a now-broken sig file leaves stale `type_signature_lines` showing on regenerated pages alongside the warning.
1 parent f1985a8 commit 618f045

4 files changed

Lines changed: 71 additions & 3 deletions

File tree

lib/rdoc/rbs_helper.rb

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ def load_signatures(*dirs)
5454
decl.members.each do |member|
5555
case member
5656
when RBS::AST::Members::MethodDefinition
57-
key = member.singleton? ? "#{class_name}.#{member.name}" : "#{class_name}##{member.name}"
5857
sigs = member.overloads.map { |o| o.method_type.to_s }
59-
signatures[key] ||= sigs
58+
method_keys_for(class_name, member).each do |key|
59+
signatures[key] ||= sigs
60+
end
6061
when RBS::AST::Members::AttrReader, RBS::AST::Members::AttrWriter, RBS::AST::Members::AttrAccessor
6162
key = member.kind == :singleton ? "#{class_name}.#{member.name}" : "#{class_name}##{member.name}"
6263
signatures[key] ||= [member.type.to_s]
@@ -87,6 +88,20 @@ def signature_to_html(lines, lookup:, from_path:)
8788

8889
private
8990

91+
# `def self?.foo: ...` produces a member whose kind is :singleton_instance —
92+
# it defines both Class.foo (singleton) and a private Class#foo (instance),
93+
# so we need to register the signature under both keys.
94+
def method_keys_for(class_name, member)
95+
case member.kind
96+
when :singleton
97+
["#{class_name}.#{member.name}"]
98+
when :singleton_instance
99+
["#{class_name}.#{member.name}", "#{class_name}##{member.name}"]
100+
else
101+
["#{class_name}##{member.name}"]
102+
end
103+
end
104+
90105
def link_type_names_in_line(line, lookup, from_path)
91106
escaped = ERB::Util.html_escape(line)
92107

lib/rdoc/rdoc.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,11 @@ def load_rbs_signatures
562562
sig_dirs << sig_dir if File.directory?(sig_dir)
563563
signatures = RDoc::RbsHelper.load_signatures(*sig_dirs)
564564
@store.merge_rbs_signatures(signatures)
565-
rescue RBS::ParsingError, Errno::ENOENT, LoadError => e
565+
rescue RBS::BaseError, Errno::ENOENT, LoadError => e
566+
# In server mode, a previous successful load may have populated the store;
567+
# drop those signatures so a now-broken sig file doesn't keep showing
568+
# stale types alongside the warning.
569+
@store.clear_rbs_signatures
566570
@options.warn "Failed to load RBS type signatures: #{e.message}"
567571
end
568572

test/rdoc/rbs_helper_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ def greet: (String name) -> void
4040
end
4141
end
4242

43+
def test_load_signatures_registers_module_function_methods_under_both_keys
44+
Dir.mktmpdir do |dir|
45+
File.write(File.join(dir, 'test.rbs'), <<~RBS)
46+
class Greeter
47+
def self?.shout: (String text) -> String
48+
end
49+
RBS
50+
51+
sigs = RDoc::RbsHelper.load_signatures(dir)
52+
assert_equal ['(String text) -> String'], sigs['Greeter.shout']
53+
assert_equal ['(String text) -> String'], sigs['Greeter#shout']
54+
end
55+
end
56+
4357
def test_load_signatures_keeps_instance_and_singleton_attributes_separate
4458
Dir.mktmpdir do |dir|
4559
File.write(File.join(dir, 'test.rbs'), <<~RBS)

test/rdoc/rdoc_rdoc_test.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,41 @@ def greet: () -> String
110110
end
111111
end
112112

113+
def test_load_rbs_signatures_clears_stale_signatures_on_failure
114+
temp_dir do |dir|
115+
sig_dir = File.join dir, 'sig'
116+
sig = File.join sig_dir, 'example.rbs'
117+
FileUtils.mkdir_p sig_dir
118+
119+
File.write sig, <<~RBS
120+
class Example
121+
def greet: () -> String
122+
end
123+
RBS
124+
125+
@options.root = Pathname(dir)
126+
@options.op_dir = dir
127+
@rdoc.store = RDoc::Store.new(@options)
128+
129+
top_level = @rdoc.store.add_file 'example.rb'
130+
example = top_level.add_class RDoc::NormalClass, 'Example'
131+
method = RDoc::AnyMethod.new nil, 'greet'
132+
example.add_method method
133+
134+
@rdoc.load_rbs_signatures
135+
assert_equal ['() -> String'], method.type_signature_lines
136+
137+
File.write sig, "class Example\n def greet: ( -> "
138+
@options.verbosity = 2
139+
_out, err = capture_output do
140+
@rdoc.load_rbs_signatures
141+
end
142+
143+
assert_includes err, 'Failed to load RBS type signatures'
144+
assert_nil method.type_signature_lines
145+
end
146+
end
147+
113148
def test_document_with_dry_run # functional test
114149
options = RDoc::Options.new
115150
options.files = [File.expand_path('../xref_data.rb', __FILE__)]

0 commit comments

Comments
 (0)